Bug 418671 - Clean up places views drag and drop code, r=mano

This commit is contained in:
Marco Bonardo 2008-09-19 17:47:45 +02:00
Родитель af9cc41693
Коммит 5ca634dfa3
10 изменённых файлов: 518 добавлений и 440 удалений

Просмотреть файл

@ -384,7 +384,7 @@
key="showAllHistoryKb"
#endif
command="Browser:ShowAllHistory"/>
<menuseparator id="startHistorySeparator" builder="start"/>
<menuseparator id="startHistorySeparator"/>
<menuseparator id="endHistorySeparator" builder="end"/>
<menu id="historyUndoMenu" label="&historyUndoMenu.label;" disabled="true">
<menupopup id="historyUndoPopup" onpopupshowing="HistoryMenu.populateUndoSubmenu();"/>
@ -396,7 +396,8 @@
label="&bookmarksMenu.label;" accesskey="&bookmarksMenu.accesskey;"
ondragenter="PlacesMenuDNDController.onBookmarksMenuDragEnter(event);"
ondragdrop="nsDragAndDrop.drop(event, BookmarksMenuDropHandler);"
ondragover="nsDragAndDrop.dragOver(event, BookmarksMenuDropHandler);">
ondragover="nsDragAndDrop.dragOver(event, BookmarksMenuDropHandler);"
ondragexit="nsDragAndDrop.dragExit(event, BookmarksMenuDropHandler);">
<menupopup id="bookmarksMenuPopup"
type="places"
place="place:folder=BOOKMARKS_MENU"
@ -437,7 +438,7 @@
context="placesContext"
onpopupshowing="BookmarksEventHandler.onPopupShowing(event);"/>
</menu>
<menuseparator builder="start"/>
<menuseparator/>
</menupopup>
</menu>

Просмотреть файл

@ -711,7 +711,6 @@ var BookmarksEventHandler = {
if (!target._endOptSeparator) {
// create a separator before options
target._endOptSeparator = document.createElement("menuseparator");
target._endOptSeparator.setAttribute("builder", "end");
target._endMarker = target.childNodes.length;
target.appendChild(target._endOptSeparator);
}
@ -789,7 +788,8 @@ var BookmarksMenuDropHandler = {
* state.
*/
onDragOver: function BMDH_onDragOver(event, flavor, session) {
session.canDrop = this.canDrop(event, session);
if (!this.canDrop(event, session))
event.dataTransfer.effectAllowed = "none";
},
/**
@ -812,6 +812,8 @@ var BookmarksMenuDropHandler = {
* otherwise.
*/
canDrop: function BMDH_canDrop(event, session) {
PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
var ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, -1);
return ip && PlacesControllerDragHelper.canDrop(ip);
},
@ -826,9 +828,21 @@ var BookmarksMenuDropHandler = {
* The active DragSession
*/
onDrop: function BMDH_onDrop(event, data, session) {
// Put the item at the end of bookmark menu
var ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, -1);
PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
// Put the item at the end of bookmark menu
var ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId, -1,
Ci.nsITreeView.DROP_ON);
PlacesControllerDragHelper.onDrop(ip);
},
/**
* Called when drop target leaves the menu or after a drop.
* @param aEvent
* A drop event
*/
onDragExit: function BMDH_onDragExit(event, session) {
PlacesControllerDragHelper.currentDataTransfer = null;
}
};
@ -903,8 +917,9 @@ var PlacesMenuDNDController = {
*` menu-toolbarbutton), false otherwise.
*/
_isContainer: function PMDC__isContainer(node) {
return node.localName == "menu" ||
node.localName == "toolbarbutton" && node.getAttribute("type") == "menu";
return node.localName == "menu" ||
(node.localName == "toolbarbutton" &&
node.getAttribute("type") == "menu");
},
/**

Просмотреть файл

@ -22,7 +22,7 @@
* Ben Goodger <beng@google.com>
* Myk Melez <myk@mozilla.org>
* Asaf Romano <mano@mozilla.com>
* Marco Bonardo <mak77@supereva.it>
* Marco Bonardo <mak77@bonardo.net>
*
* 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
@ -298,7 +298,7 @@ PlacesController.prototype = {
return false;
if (PlacesUtils.nodeIsFolder(nodes[i]) &&
!PlacesControllerDragHelper.canMoveContainerNode(nodes[i]))
!PlacesControllerDragHelper.canMoveNode(nodes[i]))
return false;
// We don't call nodeIsReadOnly here, because nodeIsReadOnly means that
@ -351,7 +351,7 @@ PlacesController.prototype = {
// if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely
// pasteable, with no need to unwrap all the nodes.
var flavors = PlacesUIUtils.placesFlavors;
var flavors = PlacesControllerDragHelper.placesFlavors;
var clipboard = PlacesUIUtils.clipboard;
var hasPlacesData =
clipboard.hasDataMatchingFlavors(flavors, flavors.length,
@ -1015,62 +1015,50 @@ PlacesController.prototype = {
},
/**
* Get a TransferDataSet containing the content of the selection that can be
* dropped elsewhere.
* @param dragAction
* The action to happen when dragging, i.e. copy
* @returns A TransferDataSet object that can be dragged and dropped
* elsewhere.
* Fills a DataTransfer object with the content of the selection that can be
* dropped elsewhere.
* @param aEvent
* The dragstart event.
*/
getTransferData: function PC_getTransferData(dragAction) {
var copy = dragAction == Ci.nsIDragService.DRAGDROP_ACTION_COPY;
setDataTransfer: function PC_setDataTransfer(aEvent) {
var dt = aEvent.dataTransfer;
var doCopy = dt.effectAllowed == "copyLink" || dt.effectAllowed == "copy";
var result = this._view.getResult();
var oldViewer = result.viewer;
try {
result.viewer = null;
var nodes = this._view.getDragableSelection();
if (dragAction == Ci.nsIDragService.DRAGDROP_ACTION_MOVE) {
nodes = nodes.filter(function(node) {
var parent = node.parent;
return parent && !PlacesUtils.nodeIsReadOnly(parent);
});
}
var dataSet = new TransferDataSet();
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
var data = new TransferData();
function addData(type, overrideURI) {
data.addDataForFlavour(type, PlacesUIUtils._wrapString(
PlacesUtils.wrapNode(node, type, overrideURI, copy)));
function addData(type, index, overrideURI) {
var wrapNode = PlacesUtils.wrapNode(node, type, overrideURI, doCopy);
dt.mozSetDataAt(type, wrapNode, index);
}
function addURIData(overrideURI) {
addData(PlacesUtils.TYPE_X_MOZ_URL, overrideURI);
addData(PlacesUtils.TYPE_UNICODE, overrideURI);
addData(PlacesUtils.TYPE_HTML, overrideURI);
function addURIData(index, overrideURI) {
addData(PlacesUtils.TYPE_X_MOZ_URL, index, overrideURI);
addData(PlacesUtils.TYPE_UNICODE, index, overrideURI);
addData(PlacesUtils.TYPE_HTML, index, overrideURI);
}
// This order is _important_! It controls how this and other
// applications select data to be inserted based on type.
addData(PlacesUtils.TYPE_X_MOZ_PLACE);
var uri;
// Allow dropping the feed uri of live-bookmark folders
addData(PlacesUtils.TYPE_X_MOZ_PLACE, i);
// Drop the feed uri for livemark containers
if (PlacesUtils.nodeIsLivemarkContainer(node))
uri = PlacesUtils.livemarks.getFeedURI(node.itemId).spec;
addURIData(uri);
dataSet.push(data);
addURIData(i, PlacesUtils.livemarks.getFeedURI(node.itemId).spec);
else if (node.uri)
addURIData(i);
}
}
finally {
if (oldViewer)
result.viewer = oldViewer;
}
return dataSet;
},
/**
@ -1111,7 +1099,7 @@ PlacesController.prototype = {
uri) + suffix);
var placeSuffix = i < (nodes.length - 1) ? "," : "";
var resolveShortcuts = !PlacesControllerDragHelper.canMoveContainerNode(node);
var resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
return PlacesUtils.wrapNode(node, type, overrideURI, resolveShortcuts) + placeSuffix;
}
@ -1248,6 +1236,18 @@ PlacesController.prototype = {
* Drop functions are passed the view that is being dropped on.
*/
var PlacesControllerDragHelper = {
/**
* DOM Element currently being dragged over
*/
currentDropTarget: null,
/**
* Current nsIDOMDataTransfer
* We need to cache this because we don't have access to the event in the
* treeView's canDrop or drop methods, and session.dataTransfer would not be
* filled for drag and drop from external sources (eg. the OS).
*/
currentDataTransfer: null,
/**
* Determines if the mouse is currently being dragged over a child node of
@ -1268,11 +1268,6 @@ var PlacesControllerDragHelper = {
return false;
},
/**
* DOM Element currently being dragged over
*/
currentDropTarget: null,
/**
* @returns The current active drag session. Returns null if there is none.
*/
@ -1282,6 +1277,19 @@ var PlacesControllerDragHelper = {
return dragService.getCurrentSession();
},
/**
* Extract the first accepted flavor from a flavors array.
* @param aFlavors
* The flavors array.
*/
getFirstValidFlavor: function PCDH_getFirstValidFlavor(aFlavors) {
for (var i = 0; i < aFlavors.length; i++) {
if (this.GENERIC_VIEW_DROP_TYPES.indexOf(aFlavors[i]) != -1)
return aFlavors[i];
}
return null;
},
/**
* Determines whether or not the data currently being dragged can be dropped
* on a places view.
@ -1289,35 +1297,28 @@ var PlacesControllerDragHelper = {
* The insertion point where the items should be dropped
*/
canDrop: function PCDH_canDrop(ip) {
var session = this.getSession();
if (!session)
return false;
var types = PlacesUIUtils.GENERIC_VIEW_DROP_TYPES;
var foundType = false;
for (var i = 0; i < types.length && !foundType; ++i) {
if (session.isDataFlavorSupported(types[i]))
foundType = true;
}
if (!foundType)
return false;
var dt = this.currentDataTransfer;
var dropCount = dt.mozItemCount;
// Check every dragged item
var xferable = this._initTransferable(session);
var dropCount = session.numDropItems;
for (i = 0; i < dropCount; i++) {
// Get the information of the dragged item
session.getData(xferable, i);
var data = { }, flavor = { };
xferable.getAnyTransferData(flavor, data, { });
data.value.QueryInterface(Ci.nsISupportsString);
var dragged = PlacesUtils.unwrapNodes(data.value.data, flavor.value)[0];
for (var i = 0; i < dropCount; i++) {
var flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
if (!flavor)
return false;
var data = dt.mozGetDataAt(flavor, i);
try {
var dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
} catch (e) {
return false;
}
// Only bookmarks and urls can be dropped into tag containers
if (ip.isTag && dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
(dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
/^place:/.test(dragged.uri)))
if (ip.isTag && ip.orientation == Ci.nsITreeView.DROP_ON &&
dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
(dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
/^place:/.test(dragged.uri)))
return false;
// The following loop disallows the dropping of a folder on itself or
@ -1332,39 +1333,36 @@ var PlacesControllerDragHelper = {
}
}
}
return true;
},
/**
* Determines if a container node can be moved.
* Determines if a node can be moved.
*
* @param aNode
* A bookmark folder node.
* @param [optional] aInsertionPoint
* The insertion point of the drop target.
* @returns True if the container can be moved.
* A nsINavHistoryResultNode node.
* @returns True if the node can be moved, false otherwise.
*/
canMoveContainerNode:
function PCDH_canMoveContainerNode(aNode, aInsertionPoint) {
canMoveNode:
function PCDH_canMoveNode(aNode) {
// can't move query root
if (!aNode.parent)
return false;
var targetId = aInsertionPoint ? aInsertionPoint.itemId : -1;
var parentId = PlacesUtils.getConcreteItemId(aNode.parent);
var concreteId = PlacesUtils.getConcreteItemId(aNode);
// can't move tag containers
if (PlacesUtils.nodeIsTagQuery(aNode))
// can't move children of tag containers
if (PlacesUtils.nodeIsTagQuery(aNode.parent))
return false;
// check is child of a read-only container
// can't move children of read-only containers
if (PlacesUtils.nodeIsReadOnly(aNode.parent))
return false;
// check for special folders, etc
if (!this.canMoveContainer(aNode.itemId, parentId))
if (PlacesUtils.nodeIsContainer(aNode) &&
!this.canMoveContainer(concreteId, parentId))
return false;
return true;
@ -1395,89 +1393,95 @@ var PlacesControllerDragHelper = {
if (aParentId == null || aParentId == -1)
aParentId = PlacesUtils.bookmarks.getFolderIdForItem(aId);
if(PlacesUtils.bookmarks.getFolderReadonly(aParentId))
if (PlacesUtils.bookmarks.getFolderReadonly(aParentId))
return false;
return true;
},
/**
* Creates a Transferable object that can be filled with data of types
* supported by a view.
* @param session
* The active drag session
* @returns An object implementing nsITransferable that can receive data
* dropped onto a view.
*/
_initTransferable: function PCDH__initTransferable(session) {
var xferable = Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
var types = PlacesUIUtils.GENERIC_VIEW_DROP_TYPES;
for (var i = 0; i < types.length; ++i) {
if (session.isDataFlavorSupported(types[i]))
xferable.addDataFlavor(types[i]);
}
return xferable;
},
/**
* Handles the drop of one or more items onto a view.
* @param insertionPoint
* The insertion point where the items should be dropped
*/
onDrop: function PCDH_onDrop(insertionPoint) {
var session = this.getSession();
// XXX dragAction is not valid, so we also set copy below by checking
// whether the dropped item is moveable, before creating the transaction
var copy = session.dragAction & Ci.nsIDragService.DRAGDROP_ACTION_COPY;
var dt = this.currentDataTransfer;
var doCopy = dt.dropEffect == "copy";
var transactions = [];
var xferable = this._initTransferable(session);
var dropCount = session.numDropItems;
var dropCount = dt.mozItemCount;
var movedCount = 0;
for (var i = 0; i < dropCount; ++i) {
session.getData(xferable, i);
var flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
if (!flavor)
return false;
var data = { }, flavor = { };
xferable.getAnyTransferData(flavor, data, { });
data.value.QueryInterface(Ci.nsISupportsString);
// There's only ever one in the D&D case.
var unwrapped = PlacesUtils.unwrapNodes(data.value.data,
flavor.value)[0];
var data = dt.mozGetDataAt(flavor, i);
// There's only ever one in the D&D case.
var unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
var index = insertionPoint.index;
// Adjust insertion index to prevent reversal of dragged items. When you
// drag multiple elts upward: need to increment index or each successive
// elt will be inserted at the same index, each above the previous.
if (index != -1 && index < unwrapped.index) {
index = index + movedCount;
movedCount++;
}
var dragginUp = insertionPoint.itemId == unwrapped.parent &&
index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
if (index != -1 && dragginUp)
index+= movedCount++;
// if dragging over a tag container we should tag the item
if (insertionPoint.isTag) {
if (insertionPoint.isTag &&
insertionPoint.orientation == Ci.nsITreeView.DROP_ON) {
var uri = PlacesUtils._uri(unwrapped.uri);
var tagItemId = insertionPoint.itemId;
transactions.push(PlacesUIUtils.ptm.tagURI(uri,[tagItemId]));
}
else {
if (unwrapped.id && !this.canMoveContainer(unwrapped.id, null))
copy = true;
else if (unwrapped.concreteId &&
!this.canMoveContainer(unwrapped.concreteId, null))
copy = true;
transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
flavor.value, insertionPoint.itemId,
index, copy));
flavor, insertionPoint.itemId,
index, doCopy));
}
}
var txn = PlacesUIUtils.ptm.aggregateTransactions("DropItems", transactions);
PlacesUIUtils.ptm.doTransaction(txn);
},
/**
* Checks if we can insert into a container.
* @param aContainer
* The container were we are want to drop
*/
disallowInsertion: function(aContainer) {
NS_ASSERT(aContainer, "empty container");
// allow dropping into Tag containers
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
},
placesFlavors: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
PlacesUtils.TYPE_X_MOZ_PLACE],
GENERIC_VIEW_DROP_TYPES: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
PlacesUtils.TYPE_X_MOZ_PLACE,
PlacesUtils.TYPE_X_MOZ_URL,
PlacesUtils.TYPE_UNICODE],
/**
* Returns our flavourSet
*/
get flavourSet() {
delete this.flavourSet;
var flavourSet = new FlavourSet();
var acceptedDropFlavours = this.GENERIC_VIEW_DROP_TYPES;
acceptedDropFlavours.forEach(flavourSet.appendFlavour, flavourSet);
return this.flavourSet = flavourSet;
}
};

Просмотреть файл

@ -24,7 +24,7 @@
# Ben Goodger <beng@google.com>
# Asaf Romano <mano@mozilla.com>
# Simon Bünzli <zeniko@gmail.com>
# Marco Bonardo <mak77@supereva.it>
# Marco Bonardo <mak77@bonardo.net>
#
# 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
@ -88,8 +88,11 @@
PlacesControllerDragHelper.currentDropTarget = aEvent.target;
// check if we have a valid dropPoint
var dropPoint = this._getDropPoint(aEvent);
if (!dropPoint)
if (!dropPoint || !dropPoint.ip ||
!PlacesControllerDragHelper.canDrop(dropPoint.ip)) {
aEvent.dataTransfer.effectAllowed = "none";
return;
}
// add a dragover attribute to this popup
this.setAttribute("dragover", "true");
@ -109,7 +112,7 @@
.setTimer(this._overFolder.hoverTime);
}
// since we are dropping into a folder set the corresponding style
dropPoint.folderNode.setAttribute("dragover-into", "true");
dropPoint.folderNode.setAttribute("_moz-menuactive", true);
}
else {
// We are not dragging over a folder
@ -117,6 +120,14 @@
this._overFolder.clear();
}
// 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);
}
// Check if we should hide the drop indicator for this target
if (!aDragSession.canDrop ||
!dropPoint || dropPoint.folderNode ||
@ -125,29 +136,22 @@
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 sbo = this._scrollBox.scrollBoxObject;
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;
newMarginTop = node ? node.boxObject.screenY - sbo.screenY :
sbo.height;
}
else if (scrollDir == 1)
newMarginTop = scrollBoxObject.height;
newMarginTop = sbo.height;
// set the new marginTop based on arrowscrollbox
newMarginTop += scrollBoxObject.y - this._scrollBox.boxObject.y;
newMarginTop += sbo.y - this._scrollBox.boxObject.y;
this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
this._indicatorBar.hidden = false;
]]></body>
@ -158,9 +162,8 @@
<parameter name="aDragSession"/>
<body><![CDATA[
PlacesControllerDragHelper.currentDropTarget = null;
PlacesControllerDragHelper.currentDataTransfer = 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
@ -177,7 +180,10 @@
// 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")) {
// 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);
}
@ -191,26 +197,32 @@
<parameter name="aXferData"/>
<parameter name="aDragAction"/>
<body><![CDATA[
// Force a copy action if parent node is a query or not-removable
if (aEvent.ctrlKey ||
PlacesUtils.nodeIsQuery(aEvent.target.node.parent) ||
!PlacesControllerDragHelper.canMoveContainerNode(aEvent.target.node))
aDragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
var draggedNode = aEvent.target.node;
// Force a copy action if parent node is a query or we are dragging a
// not-removable node
if (!PlacesControllerDragHelper.canMoveNode(draggedNode))
aEvent.dataTransfer.effectAllowed = "copyLink";
// activate the view and cache the dragged node
this._rootView._draggedNode = aEvent.target.node;
this._rootView._draggedNode = draggedNode;
this._rootView.focus();
aXferData.data = this._rootView.controller
.getTransferData(aDragAction.action);
// Fill the dataTransfer
this._rootView._controller.setDataTransfer(aEvent);
this.setAttribute("dragstart", "true");
]]></body>
</method>
<method name="onDrop">
<parameter name="aEvent"/>
<parameter name="aDropData"/>
<parameter name="aDropData"/>
<parameter name="aSession"/>
<body><![CDATA[
// Cache the dataTransfer
PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
var dropPoint = this._getDropPoint(aEvent);
if (!dropPoint)
return;
@ -222,10 +234,7 @@
<!-- This returns the FavourSet accepted by this popup -->
<method name="getSupportedFlavours">
<body><![CDATA[
var flavourSet = new FlavourSet();
var acceptedDropFlavours = PlacesUIUtils.GENERIC_VIEW_DROP_TYPES;
acceptedDropFlavours.forEach(flavourSet.appendFlavour, flavourSet);
return flavourSet;
return PlacesControllerDragHelper.flavourSet;
]]></body>
</method>
@ -246,7 +255,8 @@
betweenMarkers = false;
// hide the dropmarker if current node is not a places bookmark item
return !(target && target.node && betweenMarkers && this.canDrop());
return !(target && target.node && betweenMarkers &&
this.canDrop(aEvent));
]]></body>
</method>
@ -257,71 +267,87 @@
<body><![CDATA[
// Can't drop if the menu isn't a folder
var resultNode = this._resultNode;
if (!PlacesUtils.nodeIsFolder(resultNode))
if (!PlacesUtils.nodeIsFolder(resultNode) ||
PlacesControllerDragHelper.disallowInsertion(resultNode)) {
aEvent.dataTransfer.effectAllowed = "none";
return null;
}
var dropPoint = { ip: null, beforeIndex: null, folderNode: null };
var dropPoint = { ip: null, folderNode: null };
// set the limits for valid items
var start = 0;
var popup = this;
var end = popup.childNodes.length;
if (this._startMarker != -1)
start = this._startMarker + 1;
if (this._endMarker != -1)
end = this._endMarker;
// The node we are dragging over
var xulNode = aEvent.target;
// Loop through all the nodes to find the correct dropPoint
var popupY = popup.boxObject.y;
// we should add the scrollBox button height if visible
popupY += this._scrollBox.scrollBoxObject.y - popup.boxObject.y;
for (var i = start; i < end; i++) {
var xulNode = popup.childNodes[i];
var nodeY = xulNode.boxObject.y - popupY;
var nodeHeight = xulNode.boxObject.height;
if (xulNode.node &&
(PlacesUtils.nodeIsFolder(xulNode.node) ||
PlacesUtils.nodeIsTagQuery(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;
}
// Calculate positions taking care of arrowscrollbox
var sbo = this._scrollBox.scrollBoxObject;
var eventY = aEvent.layerY;
var nodeY = xulNode.boxObject.y - sbo.y;
var nodeHeight = xulNode.boxObject.height;
if (!xulNode.node) {
// if we are dragging over a non places node drop at the end
dropPoint.ip = new InsertionPoint(resultNode.itemId,
-1,
Ci.nsITreeView.DROP_ON);
return dropPoint;
}
else if ((PlacesUtils.nodeIsFolder(xulNode.node) ||
PlacesUtils.nodeIsTagQuery(xulNode.node)) &&
!PlacesUtils.nodeIsReadOnly(xulNode.node)) {
// This is a folder or a tag container.
if (eventY - nodeY < nodeHeight * 0.25) {
// If the mouse is in the top 25% of the node,
// drop above the folder.
dropPoint.ip = new InsertionPoint(
resultNode.itemId,
-1,
Ci.nsITreeView.DROP_BEFORE,
PlacesUtils.nodeIsTagQuery(xulNode.node),
xulNode.node.itemId);
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;
}
else if (eventY - nodeY < nodeHeight * 0.75) {
// If the mouse is before the 75 % of the node drop
// inside this folder.
dropPoint.ip = new InsertionPoint(
PlacesUtils.getConcreteItemId(xulNode.node),
-1,
Ci.nsITreeView.DROP_ON,
PlacesUtils.nodeIsTagQuery(xulNode.node));
dropPoint.folderNode = xulNode;
return dropPoint;
}
}
// Should drop below the last node.
dropPoint.ip = new InsertionPoint(resultNode.itemId, -1, 1);
dropPoint.beforeIndex = -1;
else if (eventY - nodeY <= nodeHeight / 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(
resultNode.itemId,
-1,
Ci.nsITreeView.DROP_BEFORE,
PlacesUtils.nodeIsTagQuery(xulNode.node),
xulNode.node.itemId);
return dropPoint;
}
// Drop below the item.
dropPoint.ip = new InsertionPoint(
resultNode.itemId,
-1,
Ci.nsITreeView.DROP_AFTER,
PlacesUtils.nodeIsTagQuery(xulNode.node),
xulNode.node.itemId);
return dropPoint;
]]></body>
</method>
<method name="canDrop">
<parameter name="aEvent"/>
<body><![CDATA[
// Cache the dataTransfer
PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
var ip = this._rootView.insertionPoint;
return ip && PlacesControllerDragHelper.canDrop(ip);
]]></body>
@ -332,7 +358,10 @@
closing of folders when the mouse hovers. -->
<field name="_overFolder"><![CDATA[({
_self: this,
_folder: {node: null, openTimer: null, hoverTime: 350, closeTimer: null},
_folder: {node: null,
openTimer: null,
hoverTime: 350,
closeTimer: null},
_closeMenuTimer: null,
get node() {
@ -406,13 +435,17 @@
else if (aTimer == this.closeMenuTimer) {
// Timer to close this menu after the drag exit.
var popup = this._self;
if (!PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
// 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;
}
},
@ -439,8 +472,8 @@
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");
// remove menuactive style
this._folder.node.removeAttribute("_moz-menuactive");
this._folder.node = null;
}
if (this._folder.openTimer) {
@ -574,6 +607,9 @@
if (document.popupNode == child)
document.popupNode = null;
child.parentNode.removeChild(child);
if (this._endMarker != -1)
this._endMarker--;
]]></body>
</method>
@ -591,12 +627,16 @@
// Add the new element to the menu. If there is static content at
// the end of the menu, add the element before that. Otherwise,
// just add to the end.
if (aParentPopup._endMarker != -1)
if (aParentPopup._endMarker != -1) {
aParentPopup.insertBefore(element,
aParentPopup.childNodes[aParentPopup._endMarker++]);
aParentPopup.childNodes[aParentPopup._endMarker]);
}
else
aParentPopup.appendChild(element);
}
if (aParentPopup._endMarker != -1)
aParentPopup._endMarker++;
]]></body>
</method>
@ -702,6 +742,7 @@
popup.firstChild == popup._emptyMenuItem)) {
this._self._showEmptyMenuItem(popup);
}
return;
}
}
},
@ -900,15 +941,23 @@
<!-- nsIPlacesView -->
<property name="insertionPoint">
<getter><![CDATA[
// there is no insertion point for history queries
// so bail out now and save a lot of work when updating commands
var resultNode = this._resultNode;
if (PlacesUtils.nodeIsQuery(resultNode) &&
asQuery(resultNode).queryOptions.queryType ==
Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
return null;
// By default, the insertion point is at the top level, at the end.
var index = PlacesUtils.bookmarks.DEFAULT_INDEX;
var container = null;
var orientation = Ci.nsITreeView.DROP_BEFORE;
var isTag = false;
if (PlacesUtils.nodeIsFolder(this._resultNode)) {
container = this._resultNode;
isTag = PlacesUtils.nodeIsTagQuery(this._resultNode);
if (PlacesUtils.nodeIsFolder(resultNode)) {
container = resultNode;
isTag = PlacesUtils.nodeIsTagQuery(resultNode);
}
var selectedNode = this.selectedNode;
@ -928,7 +977,7 @@
}
}
if (this._disallowInsertion(container))
if (PlacesControllerDragHelper.disallowInsertion(container))
return null;
return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
@ -936,18 +985,6 @@
]]></getter>
</property>
<method name="_disallowInsertion">
<parameter name="aContainer"/>
<body><![CDATA[
// allow dropping into Tag containers
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="selectAll">
<body/>
@ -1017,6 +1054,7 @@
// automatically opened when dragged over. Turn off this attribute
// when the folder closes because it is no longer applicable.
popup.removeAttribute("autoopened");
popup.removeAttribute("dragstart");
]]></handler>
</handlers>
</binding>

Просмотреть файл

@ -23,6 +23,7 @@
# Annie Sullivan <annie.sullivan@gmail.com>
# Ben Goodger <beng@google.com>
# Myk Melez <myk@mozilla.org>
# Marco Bonardo <mak77@bonardo.net>
#
# 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
@ -307,6 +308,14 @@
popup.setAttribute("type", "places");
}
}
// We rebuild the chevron on popupShowing, so if it is open
// we must force a rebuild
if (this._chevron.open) {
var popup = this._chevron.firstChild;
for (var i = 0; i < popup.childNodes.length; i++)
popup.childNodes[i].hidden = !this.childNodes[i].collapsed;
}
]]></body>
</method>
@ -406,7 +415,7 @@
}
}
if (this._disallowInsertion(container))
if (PlacesControllerDragHelper.disallowInsertion(container))
return null;
return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
@ -414,18 +423,6 @@
]]></getter>
</property>
<method name="_disallowInsertion">
<parameter name="aContainer"/>
<body><![CDATA[
// allow dropping into Tag containers
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="selectAll">
<body><![CDATA[
@ -520,6 +517,9 @@
popup.firstChild == popup._emptyMenuItem)) {
this._self._showEmptyMenuItem(popup);
}
if (popup._endMarker != -1)
popup._endMarker--;
return;
}
}
}
@ -534,11 +534,19 @@
if (aNewParent == this._self.getResultNode()) {
var children = this._self.childNodes;
var chevronPopup = this._self._chevron.firstChild;
for (var i = 0; i < children.length; i++) {
var button = children[i];
if (button.node == aItem) {
this._self.removeChild(button);
this._self.insertBefore(button, children[aNewIndex]);
if (chevronPopup) {
// Maintain chevron in sync
menuitem = chevronPopup.childNodes[i];
chevronPopup.removeChild(menuitem);
chevronPopup.insertBefore(menuitem,
chevronPopup.childNodes[aNewIndex]);
}
this._self.updateChevron();
return;
}
@ -697,7 +705,10 @@
// Menu buttons 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},
_overFolder: {node: null,
openTimer: null,
hoverTime: 350,
closeTimer: null},
// timer for turning of indicator bar, to get rid of flicker
_ibTimer: null,
@ -794,8 +805,10 @@
// node (or 25% of the right, in RTL UI), drop before the folder.
// If it's in the middle 50%, drop into the folder. If it's past
// that, drop after.
if ((isRTL && event.clientX > xulNode.boxObject.x + (xulNode.boxObject.width * 0.75)) ||
(!isRTL && event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.25))) {
if ((isRTL && event.clientX > xulNode.boxObject.x +
(xulNode.boxObject.width * 0.75)) ||
(!isRTL && event.clientX < xulNode.boxObject.x +
(xulNode.boxObject.width * 0.25))) {
// Drop to the left of this folder.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
@ -803,12 +816,15 @@
dropPoint.beforeIndex = i;
return dropPoint;
}
else if ((isRTL && event.clientX > xulNode.boxObject.x + (xulNode.boxObject.width * 0.25)) ||
(!isRTL && event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.75))) {
else if ((isRTL && event.clientX > xulNode.boxObject.x +
(xulNode.boxObject.width * 0.25)) ||
(!isRTL && event.clientX < xulNode.boxObject.x +
(xulNode.boxObject.width * 0.75))) {
// Drop inside this folder.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(xulNode.node),
-1, 1);
-1, 1,
PlacesUtils.nodeIsTagQuery(xulNode.node));
dropPoint.beforeIndex = i;
dropPoint.folderNode = xulNode;
return dropPoint;
@ -836,39 +852,43 @@
dropPoint.beforeIndex = -1;
return dropPoint;
},
onDragStart: function TBV_DO_onDragStart(event, xferData, dragAction) {
onDragStart: function TBV_DO_onDragStart(aEvent, aXferData, aDragAction) {
var draggedXulNode = aEvent.target;
// sub menus have their own d&d handlers
if (event.target.parentNode != this._self)
if (draggedXulNode.parentNode != this._self)
return false;
if (event.target.localName == "toolbarbutton" &&
event.target.getAttribute("type") == "menu") {
if (draggedXulNode.localName == "toolbarbutton" &&
draggedXulNode.getAttribute("type") == "menu") {
#ifdef XP_WIN
// Support folder dragging on the personal toolbar when the user
// holds the "alt" key while they drag (Ctrl+drag has another
// meaning - Copy). This does not appear to work at all on Linux.
if (!event.shiftKey && !event.altKey && !event.ctrlKey)
// Support folder dragging on the personal toolbar when the user
// holds the "alt" or "shift" key while dragging.
// Ctrl+drag is Copy
if (!aEvent.shiftKey && !aEvent.altKey && !aEvent.ctrlKey)
return false;
event.target.firstChild.hidePopup();
#else
return;
// Support folder dragging on the personal toolbar when the user
// holds the "shift" key while dragging
// Ctrl+drag is Copy
if (!aEvent.shiftKey && !aEvent.ctrlKey)
return false;
#endif
draggedXulNode.firstChild.hidePopup();
}
if (event.ctrlKey)
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
// activate the view and cache the dragged node
this._self._draggedNode = event.target.node;
this._self._draggedNode = draggedXulNode.node;
this._self.focus();
xferData.data = this._self._controller.getTransferData(dragAction.action);
#ifdef XP_WIN
this._self._controller.setDataTransfer(aEvent);
return true;
#endif
},
canDrop: function TBV_DO_canDrop(event, session) {
canDrop: function TBV_DO_canDrop(aEvent, aDragSession) {
// Cache the dataTransfer
PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
var ip = this._self.insertionPoint;
return ip && PlacesControllerDragHelper.canDrop(ip);
},
@ -939,13 +959,19 @@
},
onDrop: function TBV_DO_onDrop(event, dropData, session) {
// Cache the dataTransfer
PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
var dropPoint = this._getDropPoint(event);
if (dropPoint == null)
if (!dropPoint)
return;
PlacesControllerDragHelper.onDrop(dropPoint.ip);
},
onDragExit: function TBV_DO_onDragExit(event, session) {
PlacesControllerDragHelper.currentDropTarget = null;
PlacesControllerDragHelper.currentDataTransfer = null;
// Set timer to turn off indicator bar (if we turn it off
// here, dragenter might be called immediately after, creating
// flicker.)
@ -955,17 +981,12 @@
// Close any folder being hovered over
if (this._overFolder.node)
this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
PlacesControllerDragHelper.currentDropTarget = null;
this._self._draggedNode = null;
},
getSupportedFlavours: function TBV_DO_getSupportedFlavours() {
var flavorSet = new FlavourSet();
var types = PlacesUIUtils.GENERIC_VIEW_DROP_TYPES;
for (var i = 0; i < types.length; ++i)
flavorSet.appendFlavour(types[i]);
return flavorSet;
return PlacesControllerDragHelper.flavourSet;
}
})]]></field>
@ -1056,11 +1077,14 @@
// just add to the end.
if (aParentPopup._endMarker != -1) {
aParentPopup.insertBefore(element,
aParentPopup.childNodes[aParentPopup._endMarker++]);
aParentPopup.childNodes[aParentPopup._endMarker]);
}
else
aParentPopup.appendChild(element);
}
if (aParentPopup._endMarker != -1)
aParentPopup._endMarker++;
]]></body>
</method>
@ -1130,6 +1154,17 @@
nsDragAndDrop.dragExit(event, this._DNDObserver);
]]></handler>
<handler event="popupshowing" phase="capturing"><![CDATA[
// Don't show the popup if we are dragging a container.
if (this._draggingContainer) {
this._draggingContainer = false;
#ifdef MOZ_WIDGET_GTK2
// Allow drag and drop of folders in Linux.
// We must prevent popupshowing event from firing when shift is pressed.
event.preventDefault();
return false;
#endif
}
var popup = event.originalTarget;
// Avoid handling popupshowing of inner views
@ -1158,8 +1193,18 @@
this._openedMenuButton = null;
]]></handler>
<handler event="mousedown"><![CDATA[
// Allow drag and drop of folders in Linux.
// We must prevent popupshowing event from firing when shift is pressed.
var target = event.originalTarget;
if (event.button == 1 && event.shiftKey &&
target.localName == "toolbarbutton" && target.type == "menu")
this._draggingContainer = true;
]]></handler>
<handler event="mousemove"><![CDATA[
if (this._openedMenuButton == null || PlacesControllerDragHelper.getSession())
if (this._openedMenuButton == null ||
PlacesControllerDragHelper.getSession())
return;
var target = event.originalTarget;

Просмотреть файл

@ -22,6 +22,7 @@
# Contributor(s):
# Ben Goodger <beng@google.com>
# Annie Sullivan <annie.sullivan@gmail.com>
# Marco Bonardo <mak77@bonardo.net>
#
# 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
@ -434,11 +435,10 @@
// there is no insertion point for history queries
// so bail out now and save a lot of work when updating commands
var resultNode = this.getResultNode();
if (PlacesUtils.nodeIsQuery(resultNode)) {
var options = asQuery(resultNode).queryOptions;
if (options.queryType == options.QUERY_TYPE_HISTORY)
if (PlacesUtils.nodeIsQuery(resultNode) &&
asQuery(resultNode).queryOptions.queryType ==
Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
return this._cachedInsertionPoint = null;
}
var orientation = Ci.nsITreeView.DROP_BEFORE;
// If there is no selection, insert at the end of the container.
@ -488,18 +488,6 @@
]]></getter>
</property>
<method name="_disallowInsertion">
<parameter name="aContainer"/>
<body><![CDATA[
// allow dropping into Tag containers
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
]]></body>
</method>
<method name="_getInsertionPoint">
<parameter name="index"/>
<parameter name="orientation"/>
@ -519,14 +507,13 @@
container = lastSelected;
index = -1;
}
else if (!this._disallowInsertion(lastSelected) &&
lastSelected.containerOpen &&
else if (lastSelected.containerOpen &&
orientation == Ci.nsITreeView.DROP_AFTER &&
lastSelected.hasChildren) {
// If the last selected item is an open container and the user is
// trying to drag into it as a first item, really insert into it.
container = lastSelected;
orientation = Ci.nsITreeView.DROP_BEFORE;
orientation = Ci.nsITreeView.DROP_ON;
index = 0;
}
else {
@ -537,7 +524,7 @@
// avoid the potentially expensive call to getIndexOfNode()
// if we know this container doesn't allow insertion
if (this._disallowInsertion(container))
if (PlacesControllerDragHelper.disallowInsertion(container))
return null;
var queryOptions = asQuery(result.root).queryOptions;
@ -562,7 +549,7 @@
}
}
if (this._disallowInsertion(container))
if (PlacesControllerDragHelper.disallowInsertion(container))
return null;
return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
@ -675,14 +662,10 @@
<!-- nsDragAndDrop -->
<method name="onDragStart">
<parameter name="event"/>
<parameter name="xferData"/>
<parameter name="dragAction"/>
<parameter name="aEvent"/>
<parameter name="aXferData"/>
<parameter name="aDragAction"/>
<body><![CDATA[
// Drag and Drop does not work while a tree view is sorted.
if (this.getAttribute("sortActive") == "true")
throw Cr.NS_OK;
var nodes = this.getSelectionNodes();
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
@ -690,61 +673,83 @@
// Disallow dragging the root node of a tree
var parent = node.parent;
if (!parent)
throw Cr.NS_OK;
return;
// If this node is part of a readonly container (e.g. a livemark) it
// cannot be moved, only copied, so we must change the action used
// by the drag session.
if (PlacesUtils.nodeIsTagQuery(parent) ||
!PlacesControllerDragHelper.canMoveContainerNode(node)) {
// XXX DOES NOTHING! dragAction doesn't persist
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
// If this node is child of a readonly container (e.g. a livemark)
// or cannot be moved, we must force a copy.
if (!PlacesControllerDragHelper.canMoveNode(node)) {
aEvent.dataTransfer.effectAllowed = "copyLink";
break;
}
}
// XXXben - the drag wrapper should do this automatically.
if (event.ctrlKey)
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
// Stuff the encoded selection into the transferable data object
xferData.data = this._controller.getTransferData(dragAction.action);
this._controller.setDataTransfer(aEvent);
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="canDrop">
<parameter name="event"/>
<parameter name="session"/>
<body><![CDATA[
<parameter name="aEvent"/>
<parameter name="aDragSession"/>
<body><![CDATA[
// Cache the dataTransfer for the view
PlacesControllerDragHelper.currentDataTransfer = aEvent.dataTransfer;
var row = { }, col = { }, child = { };
this.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col,
child);
return this.view.canDrop(row.value, -1);
this.treeBoxObject.getCellAt(aEvent.clientX, aEvent.clientY,
row, col, child);
var node = row.value != -1 ?
this.getResultView().nodeForTreeIndex(row.value) :
this.getResultNode();
// cache the dropTarget for the view
PlacesControllerDragHelper.currentDropTarget = node;
// We have to calculate the orientation since view.canDrop will use
// it and we want to be consistent with the dropfeedback
var tbo = this.treeBoxObject;
var rowHeight = tbo.rowHeight;
var eventY = aEvent.clientY - tbo.treeBody.boxObject.y -
rowHeight * (row.value - tbo.getFirstVisibleRow());
var orientation = Ci.nsITreeView.DROP_BEFORE;
if (row.value == -1) {
// If the row is not valid we try to insert inside the resultNode.
orientation = Ci.nsITreeView.DROP_ON;
}
else if (PlacesUtils.nodeIsContainer(node) &&
eventY > rowHeight * 0.75) {
// If we are below the 75% of a container the treeview we try
// to drop after the node.
orientation = Ci.nsITreeView.DROP_AFTER;
}
else if (PlacesUtils.nodeIsContainer(node) &&
eventY > rowHeight * 0.25) {
// If we are below the 25% of a container the treeview we try
// to drop inside the node.
orientation = Ci.nsITreeView.DROP_ON;
}
return this.view.canDrop(row.value, orientation);
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="onDragOver">
<parameter name="event"/>
<parameter name="flavor"/>
<parameter name="session"/>
<body><![CDATA[
var dragService =
Cc["@mozilla.org/widget/dragservice;1"].
getService(Ci.nsIDragService);
var dragSession = dragService.getCurrentSession();
dragSession.canDrop = this.canDrop(event, session);
<parameter name="aEvent"/>
<parameter name="aFlavour"/>
<parameter name="aDragSession"/>
<body><![CDATA[
if (!this.canDrop(aEvent, aDragSession))
aEvent.dataTransfer.effectAllowed = "none";
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="getSupportedFlavours">
<body><![CDATA[
var flavorSet = new FlavourSet();
var types = PlacesUIUtils.GENERIC_VIEW_DROP_TYPES;
for (var i = 0; i < types.length; ++i)
flavorSet.appendFlavour(types[i]);
return flavorSet;
return PlacesControllerDragHelper.flavourSet;
]]></body>
</method>
@ -782,7 +787,6 @@
}
]]></handler>
<handler event="draggesture"><![CDATA[
// XXXben ew.
if (event.target.localName == "treechildren")
nsDragAndDrop.startDrag(event, this);
]]></handler>
@ -790,6 +794,13 @@
if (event.target.localName == "treechildren")
nsDragAndDrop.dragOver(event, this);
]]></handler>
<handler event="drop"><![CDATA[
PlacesControllerDragHelper.currentDataTransfer = event.dataTransfer;
]]></handler>
<handler event="dragexit"><![CDATA[
PlacesControllerDragHelper.currentDataTransfer = null;
PlacesControllerDragHelper.currentDropTarget = null;
]]></handler>
</handlers>
</binding>

Просмотреть файл

@ -996,35 +996,14 @@ PlacesTreeView.prototype = {
if (!this._result)
throw Cr.NS_ERROR_UNEXPECTED;
var node = aRow != -1 ? this.nodeForTreeIndex(aRow) : this._result.root;
// drop position into a sorted treeview would be wrong
if (this.isSorted())
return false;
if (aOrientation == Ci.nsITreeView.DROP_ON) {
// The user cannot drop an item into itself or a read-only container
var dragService = Cc["@mozilla.org/widget/dragservice;1"].
getService(Ci.nsIDragService);
var dragSession = dragService.getCurrentSession();
var elt = dragSession.sourceNode.parentNode;
if (elt.localName == "tree" && elt.view == this &&
this.selection.isSelected(aRow))
return false;
}
var ip = this._getInsertionPoint(aRow, aOrientation);
return ip && PlacesControllerDragHelper.canDrop(ip);
},
// XXXmano: these two are copied over from tree.xml, to fix this we need to
// either add a helper to PlacesUtils or keep it here and add insertionPoint
// to the view interface.
_disallowInsertion: function PTV__disallowInsertion(aContainer) {
// allow dropping into Tag containers
if (PlacesUtils.nodeIsTagQuery(aContainer))
return false;
// Disallow insertion of items under readonly folders
return (!PlacesUtils.nodeIsFolder(aContainer) ||
PlacesUtils.nodeIsReadOnly(aContainer));
},
_getInsertionPoint: function PTV__getInsertionPoint(index, orientation) {
var container = this._result.root;
var dropNearItemId = -1;
@ -1038,14 +1017,13 @@ PlacesTreeView.prototype = {
container = lastSelected;
index = -1;
}
else if (!this._disallowInsertion(lastSelected) &&
lastSelected.containerOpen &&
else if (lastSelected.containerOpen &&
orientation == Ci.nsITreeView.DROP_AFTER &&
lastSelected.hasChildren) {
// If the last selected item is an open container and the user is
// trying to drag into it as a first item, really insert into it.
container = lastSelected;
orientation = Ci.nsITreeView.DROP_BEFORE;
orientation = Ci.nsITreeView.DROP_ON;
index = 0;
}
else {
@ -1056,12 +1034,13 @@ PlacesTreeView.prototype = {
// avoid the potentially expensive call to getIndexOfNode()
// if we know this container doesn't allow insertion
if (this._disallowInsertion(container))
if (PlacesControllerDragHelper.disallowInsertion(container))
return null;
var queryOptions = asQuery(this._result.root).queryOptions;
if (queryOptions.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
// If we are within a sorted view, insert at the end
if (queryOptions.sortingMode !=
Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
// If we are within a sorted view, insert at the ends
index = -1;
}
else if (queryOptions.excludeItems ||
@ -1080,7 +1059,7 @@ PlacesTreeView.prototype = {
}
}
if (this._disallowInsertion(container))
if (PlacesControllerDragHelper.disallowInsertion(container))
return null;
return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
@ -1095,7 +1074,7 @@ PlacesTreeView.prototype = {
// since this information is specific to the tree view.
var ip = this._getInsertionPoint(aRow, aOrientation);
if (!ip)
throw Cr.NS_ERROR_NOT_AVAILABLE;
return;
PlacesControllerDragHelper.onDrop(ip);
},

Просмотреть файл

@ -24,6 +24,7 @@
* Asaf Romano <mano@mozilla.com>
* Sungjoon Steve Won <stevewon@gmail.com>
* Dietrich Ayala <dietrich@mozilla.com>
* Marco Bonardo <mak77@bonardo.net>
*
* 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
@ -377,10 +378,12 @@ var PlacesUIUtils = {
return this.ptm.moveItem(data.id, container, index);
break;
default:
if (type == PlacesUtils.TYPE_X_MOZ_URL || type == PlacesUtils.TYPE_UNICODE) {
var title = (type == PlacesUtils.TYPE_X_MOZ_URL) ? data.title : data.uri;
return this.ptm.createItem(PlacesUtils._uri(data.uri), container, index,
title);
if (type == PlacesUtils.TYPE_X_MOZ_URL ||
type == PlacesUtils.TYPE_UNICODE) {
var title = (type == PlacesUtils.TYPE_X_MOZ_URL) ? data.title :
data.uri;
return this.ptm.createItem(PlacesUtils._uri(data.uri),
container, index, title);
}
}
return null;
@ -1035,51 +1038,45 @@ var PlacesUIUtils = {
},
cleanPlacesPopup: function PU_cleanPlacesPopup(aPopup) {
// Find static menuitems at the start and at the end of the menupopup,
// marked by builder="start" and builder="end" attributes, and set
// markers to keep track of their indices.
// Remove places popup children and update markers to keep track of
// their indices.
var start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
var end = aPopup._endMarker != -1 ? aPopup._endMarker :
aPopup.childNodes.length;
var items = [];
aPopup._startMarker = -1;
aPopup._endMarker = -1;
for (var i = 0; i < aPopup.childNodes.length; ++i) {
var placesNodeFound = false;
for (var i = start; i < end; ++i) {
var item = aPopup.childNodes[i];
if (item.getAttribute("builder") == "start") {
aPopup._startMarker = i;
continue;
}
if (item.getAttribute("builder") == "end") {
// we need to do this for menus that have static content at the end but
// are initially empty, eg. the history menu, we need to know where to
// start inserting new items.
aPopup._endMarker = i;
continue;
break;
}
if ((aPopup._startMarker != -1) && (aPopup._endMarker == -1))
if (item.node) {
items.push(item);
}
// If static items at the beginning were found, remove all items between
// them and the static content at the end.
for (var i = 0; i < items.length; ++i) {
// skip the empty menu item
if (aPopup._emptyMenuItem != items[i]) {
aPopup.removeChild(items[i]);
if (aPopup._endMarker > 0)
--aPopup._endMarker;
placesNodeFound = true;
}
}
// If no static items were found at the beginning, remove all items before
// the static items at the end.
if (aPopup._startMarker == -1) {
var end = aPopup._endMarker == -1 ?
aPopup.childNodes.length - 1 : aPopup._endMarker - 1;
for (var i = end; i >= 0; i--) {
// skip the empty menu item
if (aPopup._emptyMenuItem != aPopup.childNodes[i]) {
aPopup.removeChild(aPopup.childNodes[i]);
if (aPopup._endMarker > 0)
--aPopup._endMarker;
else {
// This is static content...
if (!placesNodeFound)
// ...at the start of the popup
// Initialized in menu.xml, in the base binding
aPopup._startMarker++;
else {
// ...after places nodes
aPopup._endMarker = i;
break;
}
}
}
for (var i = 0; i < items.length; ++i) {
aPopup.removeChild(items[i]);
if (aPopup._endMarker != -1)
aPopup._endMarker--;
}
},
getBestTitle: function PU_getBestTitle(aNode) {
@ -1232,13 +1229,3 @@ var PlacesUIUtils = {
return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"];
}
};
PlacesUIUtils.placesFlavors = [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
PlacesUtils.TYPE_X_MOZ_PLACE];
PlacesUIUtils.GENERIC_VIEW_DROP_TYPES = [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
PlacesUtils.TYPE_X_MOZ_PLACE,
PlacesUtils.TYPE_X_MOZ_URL,
PlacesUtils.TYPE_UNICODE];

Просмотреть файл

@ -1809,12 +1809,6 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
color: HighlightText !important;
}
/* Bookmark drag and drop styles */
.bookmark-item[dragover-into="true"] > .menu-iconic-text {
background: Highlight !important;
color: HighlightText !important;
}
/* rules for menupopup drop indicators */
.menupopup-drop-indicator-bar {
position: relative;

Просмотреть файл

@ -606,6 +606,10 @@ var PlacesUtils = {
var parts = blob.split("\n");
for (var i = 0; i < parts.length; i++) {
var uriString = parts[i];
// text/uri-list is converted to TYPE_UNICODE but it could contain
// comments line prepended by #, we should skip them
if (uriString.substr(0, 1) == '\x23')
continue;
// note: this._uri() will throw if uriString is not a valid URI
if (uriString != "" && this._uri(uriString))
nodes.push({ uri: uriString,