gecko/browser/components/places/content/toolbar.xml
2008-03-04 12:04:14 -08:00

1117 lines
40 KiB
XML
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0"?>
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Places Toolbar View.
#
# The Initial Developer of the Original Code is Google Inc.
# Portions created by the Initial Developer are Copyright (C) 2005-2006
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Annie Sullivan <annie.sullivan@gmail.com>
# Ben Goodger <beng@google.com>
# Myk Melez <myk@mozilla.org>
#
# 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
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
%browserDTD;
]>
<bindings id="placesToolbarBindings"
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-bar">
<resources>
<stylesheet src="chrome://browser/skin/places/places.css"/>
</resources>
<content>
<xul:vbox>
<xul:hbox class="toolbar-drop-indicator-bar">
<xul:hbox class="toolbar-drop-indicator"/>
</xul:hbox>
<xul:hbox flex="1">
<xul:hbox class="bookmarks-toolbar-items places-toolbar-items" flex="1">
<children/>
</xul:hbox>
<xul:toolbarbutton class="bookmark-item bookmarks-toolbar-customize"
mousethrough="never"
label="&bookmarksToolbarItem.label;"/>
</xul:hbox>
</xul:vbox>
<xul:hbox mousethrough="always"
flex="1"
pack="end">
<xul:toolbarbutton type="menu"
class="chevron"
mousethrough="never"
collapsed="true"
onpopupshowing="chevronPopupShowing(event);">
<xul:menupopup anonid="chevronPopup"
#ifndef XP_MACOSX
context="placesContext"
#endif
/>
</xul:toolbarbutton>
</xul:hbox>
</content>
<implementation>
<constructor><![CDATA[
this._init();
]]></constructor>
<destructor><![CDATA[
if (this._result) {
this._result.viewer = null;
this._result = null;
}
]]></destructor>
<property name="controller"
readonly="true"
onget="return this._controller;"/>
<method name="_init">
<body><![CDATA[
this._controller = new PlacesController(this);
this.controllers.appendController(this._controller);
var t = this;
window.addEventListener("resize",
function f(e) { t.updateChevron(e); },
false);
if (this.hasAttribute("place")) {
// Do the initial build.
this.place = this.place;
}
]]></body>
</method>
<field name="_dropIndicatorBar">document.getAnonymousElementByAttribute(this, "class", "toolbar-drop-indicator-bar")</field>
<field name="_chevron">document.getAnonymousElementByAttribute(this, "class", "chevron")</field>
<field name="_openedMenuButton">null</field>
<field name="_result">null</field>
<!-- nsIPlacesView -->
<method name="getResult">
<body><![CDATA[
return this._result;
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getResultNode">
<body><![CDATA[
return this._result.root;
]]></body>
</method>
<method name="_rebuild">
<body><![CDATA[
// Clear out references to existing nodes, since we'll be deleting and re-adding.
if (this._DNDObserver._overFolder.node)
this._DNDObserver._clearOverFolder();
this._openedMenuButton = null;
while (this.hasChildNodes())
this.removeChild(this.firstChild);
var rootNode = this._result.root;
var cc = rootNode.childCount;
for (var i = 0; i < cc; ++i)
this.insertNewItem(rootNode.getChild(i), null);
var chevronPopup = this._chevron.lastChild;
if (chevronPopup.hasAttribute("type")) {
// Otherwise we'll set it when the chevron is enabled (see updateChevron)
chevronPopup.place = this.place;
}
while (chevronPopup.hasChildNodes())
chevronPopup.removeChild(chevronPopup.lastChild);
// This needs to be in a timeout to make sure our boxObject has time
// to get its proper size
var t = this;
setTimeout(function() { t.updateChevron(); }, 0);
]]></body>
</method>
<method name="insertNewItem">
<parameter name="aChild"/>
<parameter name="aBefore"/>
<body><![CDATA[
var type = aChild.type;
var button;
if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
button = document.createElement("toolbarseparator");
else {
button = document.createElement("toolbarbutton");
button.className = "bookmark-item";
button.setAttribute("label", aChild.title);
var iconURI = aChild.icon;
var iconURISpec = "";
if (iconURI) {
iconURISpec = iconURI.spec;
button.setAttribute("image", iconURISpec);
}
if (PlacesUtils.containerTypes.indexOf(type) != -1) {
button.setAttribute("type", "menu");
button.setAttribute("container", "true");
if (PlacesUtils.nodeIsLivemarkContainer(aChild))
button.setAttribute("livemark", "true");
var popup = document.createElement("menupopup");
popup.setAttribute("placespopup", "true");
button.appendChild(popup);
popup._result = this._result;
popup._resultNode = asContainer(aChild);
#ifndef XP_MACOSX
popup.setAttribute("context", "placesContext");
#endif
this._containerNodesMap.push({ resultNode: aChild,
domNode: popup });
}
}
button.node = aChild;
button.node.viewIndex = 0;
if (aBefore)
this.insertBefore(button, aBefore);
else
this.appendChild(button);
]]></body>
</method>
<method name="removeItem">
<parameter name="child"/>
<body><![CDATA[
if (PlacesUtils.nodeIsContainer(child.node)) {
for (var i=0; i < this._containerNodesMap.length; i++) {
if (this._containerNodesMap[i].resultNode == child.node) {
this._containerNodesMap.splice(i, 1);
break;
}
}
}
// if document.popupNode pointed to this child, null it out,
// otherwise controller's command-updating may rely on the removed
// item still being "selected".
if (document.popupNode == child)
document.popupNode = null;
child.parentNode.removeChild(child);
]]></body>
</method>
<method name="chevronPopupShowing">
<parameter name="aEvent"/>
<body><![CDATA[
var popup = aEvent.target;
if (popup != this._chevron.firstChild)
return;
for (var i = 0; i < popup.childNodes.length; i++)
popup.childNodes[i].hidden = !this.childNodes[i].collapsed;
]]></body>
</method>
<method name="getElementWidth">
<parameter name="element"/>
<body><![CDATA[
var style = document.defaultView.getComputedStyle(element, "");
var leftMargin = style.getPropertyValue("margin-left");
leftMargin = leftMargin ? Math.round(parseFloat(leftMargin)) : 0;
var rightMargin = style.getPropertyValue("margin-right");
rightMargin = rightMargin ? Math.round(parseFloat(rightMargin)) : 0;
return element.boxObject.width + leftMargin + rightMargin;
]]></body>
</method>
<method name="updateChevron">
<parameter name="event"/>
<body><![CDATA[
// Ignore events that aren't on the document or the window
// (html document, tooltips, etc)
// Do not ignore content window resizes, because they may
// be the result of the toolbar being shown/hidden
if (event && event.target != document && event.target != window &&
event.target != content)
return;
if (this.childNodes.length == 0) {
this._chevron.collapsed = true;
return;
}
var spaceLeft = this.boxObject.width;
this._chevron.collapsed = false;
var chevronWidth = this._chevron.boxObject.width;
var overflowed = false;
for (var i = 0; i < this.childNodes.length; i++) {
var child = this.childNodes[i];
child.collapsed = false;
spaceLeft -= this.getElementWidth(child);
var spaceNeeded = (i == this.childNodes.length - 1) ? 0 : chevronWidth;
if (spaceLeft < spaceNeeded) {
overflowed = true;
child.collapsed = true;
}
}
if (!(this._chevron.collapsed = !overflowed)) {
// Attach the popup binding to the chevron popup
var popup = this._chevron.firstChild;
if (!popup.hasAttribute("type")) {
popup.setAttribute("place", this.place);
popup.setAttribute("type", "places");
}
}
]]></body>
</method>
<!-- nsIPlacesView -->
<property name="place">
<getter><![CDATA[
return this.getAttribute("place");
]]></getter>
<setter><![CDATA[
this.setAttribute("place", val);
var history = PlacesUtils.history;
var queries = { }, options = { };
history.queryStringToQueries(val, queries, { }, options);
if (!queries.value.length)
queries.value = [history.getNewQuery()];
try {
var result =
history.executeQueries(queries.value, queries.value.length,
options.value);
result.viewer = this._viewer;
}
catch(ex) {
// Invalid query, or had no results.
// This is valid, eg: user deletes their bookmarks toolbar folder.
}
return val;
]]></setter>
</property>
<!-- nsIPlacesView -->
<property name="hasSelection">
<getter><![CDATA[
return this.selectedNode != null;
]]></getter>
</property>
<!-- nsIPlacesView -->
<method name="getSelectionNodes">
<body><![CDATA[
var selectedNode = this.selectedNode;
return selectedNode ? [selectedNode] : [];
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getRemovableSelectionRanges">
<body><![CDATA[
return [this.getSelectionNodes()];
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getDragableSelection">
<body><![CDATA[
return [this._draggedNode];
]]></body>
</method>
<!-- nsIPlacesView -->
<property name="selectedNode">
<getter><![CDATA[
if (this._contextMenuShown) {
var popupNode = document.popupNode;
return popupNode.node || popupNode.parentNode._resultNode || null;
}
return null;
]]></getter>
</property>
<!-- nsIPlacesView -->
<property name="insertionPoint">
<getter><![CDATA[
// By default, the insertion point is at the top level, at the end.
var index = -1;
var folderId = PlacesUtils.getConcreteItemId(this._result.root);
var selectedNode = this.selectedNode;
if (selectedNode) {
if (PlacesUtils.nodeIsFolder(selectedNode)) {
// If there is a folder selected, the insertion point is the
// end of the folder.
folderId = PlacesUtils.getConcreteItemId(selectedNode);
}
else {
// If there is another type of node selected, the insertion point
// is after that node.
folderId = PlacesUtils.getConcreteItemId(selectedNode.parent);
index = PlacesUtils.getIndexOfNode(selectedNode);
}
}
return new InsertionPoint(folderId, index, 1);
]]></getter>
</property>
<!-- nsIPlacesView -->
<method name="selectAll">
<body><![CDATA[
// Nothing
]]></body>
</method>
<method name="selectItems">
<body><![CDATA[
// Nothing
]]></body>
</method>
<!-- nsINavHistoryResultViewer -->
<field name="_viewer"><![CDATA[({
_self: this,
_getPopupForContainer:
function PMV__getPopupForContainer(aNode) {
if (this._self._resultNode == aNode)
return this._self;
for (var i=0; i < this._self._containerNodesMap.length; i++) {
if (this._self._containerNodesMap[i].resultNode == aNode)
return this._self._containerNodesMap[i].domNode;
}
throw("Container view not found");
},
get result() {
return this._self._result;
},
set result(val) {
// some methods (e.g. getURLsFromContainer) temporarily null out the
// viewer when they do temporary changes to the view, this does _not_
// call setResult(null), but then, we're called again with the result
// object which is already set for this viewer. At that point,
// we should do nothing.
if (this._self._result != val) {
this._self._containerNodesMap = [];
this._self._result = val;
if (val) // this calls _rebuild through invalidateContainer
val.root.containerOpen = true;
}
return val;
},
itemInserted: function TV_V_itemInserted(aParentNode, aNode, aIndex) {
// don't insert new items into the toolbar
// if the parent is not the root
if (aParentNode == this._self.getResultNode()) {
var children = this._self.childNodes;
this._self.insertNewItem(aNode,
aIndex < children.length ? children[aIndex] : null);
this._self.updateChevron();
}
else {
var popup = this._getPopupForContainer(aParentNode);
if (!popup._built)
return;
var before = popup.childNodes[aIndex] || null;
this._self.insertNewItemToPopup(aNode, popup, before);
if (popup._emptyMenuItem)
popup._emptyMenuItem.hidden = true;
}
},
itemRemoved: function TV_V_itemRemoved(aParentNode, aNode, aIndex) {
if (aParentNode == this._self.getResultNode()) {
var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) {
this._self.removeItem(children[i]);
this._self.updateChevron();
return;
}
}
}
else {
var popup = this._getPopupForContainer(aParentNode);
if (!popup._built)
return;
var children = popup.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) {
this._self.removeItem(children[i]);
if (!popup.hasChildNodes() ||
(popup.childNodes.length == 1 &&
popup.firstChild == popup._emptyMenuItem)) {
this._self._showEmptyMenuItem(popup);
}
}
}
}
},
itemMoved:
function TV_V_itemMoved(aItem, aOldParent, aOldIndex, aNewParent,
aNewIndex) {
// This cannot actually happen yet (see IDL)
if (aNewParent != aOldParent)
return;
if (aNewParent == this._self.getResultNode()) {
var children = this._self.childNodes;
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]);
this._self.updateChevron();
return;
}
}
}
var popup = this._getPopupForContainer(aNewParent);
var children = popup.childNodes;
for (var i = 0; i < children.length; i++) {
var menuItem = children[i];
if (menuItem.node == aItem) {
popup.removeChild(menuItem);
popup.insertBefore(menuItem, children[aNewIndex]);
return;
}
}
},
itemChanged: function TV_V_itemChanged(aNode) {
// this check can be removed once we fix bug #382397
var parentNode = aNode.parent;
if (!parentNode)
return;
var element;
var onToolbar = false;
if (parentNode == this._self.getResultNode()) {
onToolbar = true;
var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) {
element = children[i];
break;
}
}
}
else {
var popup = this._getPopupForContainer(parentNode);
if (!popup._built)
return;
var children = popup.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aNode) {
element = children[i];
break;
}
}
}
if (PlacesUtils.nodeIsSeparator(aNode)) {
// nothing to do when a separator changes
return;
}
var iconURI = aNode.icon;
if (iconURI) {
var spec = iconURI.spec;
if (element.getAttribute("image") != spec)
element.setAttribute("image", spec);
}
else
element.removeAttribute("image");
var title = aNode.title;
if (element.getAttribute("label") != title) {
element.setAttribute("label", title);
if (onToolbar)
this._self.updateChevron();
}
if (!element.hasAttribute("livemark") &&
PlacesUtils.nodeIsLivemarkContainer(aNode))
element.setAttribute("livemark", "true");
},
itemReplaced:
function TV_V_itemReplaced(aParentNode, aOldNode, aNewNode, aIndex) {
if (aParentNode == this._self.getResultNode()) {
var children = this._self.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].node == aOldNode) {
var next = children[i].nextSibling;
this._self.removeItem(children[i]);
this._self.insertNewItem(aNewNode, next);
this._self.updateChevron();
return;
}
}
}
else
this._forwardToChildView(aParentNode, "itemReplaced", arguments);
},
containerOpened: function TV_V_containerOpened(aNode) {
this.invalidateContainer(aNode);
},
containerClosed: function TV_V_containerClosed(aNode) {
this.invalidateContainer(aNode);
},
invalidateContainer: function TV_V_invalidateContainer(aContainer) {
if (aContainer == this._self.getResultNode()) {
this._self._containerNodesMap.splice(0);
this._self._rebuild();
return;
}
function isChildOf(node, container) {
var parent = node.parent;
while (parent) {
if (parent == container)
return true;
parent = parent.parent;
}
return false;
}
var popupToRebuild = null;
for (var i=0; i < this._self._containerNodesMap.length; i++) {
var node = this._self._containerNodesMap[i].resultNode;
if (node == aContainer)
popupToRebuild = this._self._containerNodesMap[i].domNode;
if (isChildOf(node, aContainer)) {
this._self._containerNodesMap.splice(i,1);
i--;
}
}
if (popupToRebuild)
popupToRebuild._built = false;
},
invalidateAll: function TV_V_invalidateAll() {
this._self._containerNodesMap.splice(0);
this._self._rebuild();
},
sortingChanged: function TV_V_sortingChanged(aSortingMode) {
}
})]]></field>
<field name="_DNDObserver"><![CDATA[({
// Inside the _DNDObserver object's functions, this points to
// the _DNDObserver object. _self points to the toolbar xbl object.
_self: this,
// 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},
// timer for turning of indicator bar, to get rid of flicker
_ibTimer: 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 turn off indicator bar.
if (timer == this._ibTimer) {
ib = this._self._dropIndicatorBar.removeAttribute('dragging');
this._ibTimer = null;
}
// Timer to open a menubutton that's being dragged over.
if (timer == this._overFolder.openTimer) {
// Set the autoopen attribute on the folder's menupopup so that
// the menu will automatically close when the mouse drags off of it.
this._overFolder.node.lastChild.setAttribute("autoopened", "true");
this._overFolder.node.open = true;
this._overFolder.openTimer = null;
}
// Timer to close a menubutton that's been dragged off of.
if (timer == this._overFolder.closeTimer) {
// Only close the menubutton if the drag session isn't currently over
// it or one of its children. (The autoopened attribute will let the menu
// know to close later if the menu is still being dragged over.)
var currentNode = PlacesControllerDragHelper.currentDropTarget;
var inHierarchy = false;
while (currentNode) {
if (currentNode == this._self) {
inHierarchy = true;
break;
}
currentNode = currentNode.parentNode;
}
// The _clearOverFolder() function will close the menu for _overFolder.node.
// So null it out if we don't want to close it.
if (inHierarchy)
this._overFolder.node = null;
// Clear out the folder and all associated timers.
this._clearOverFolder();
}
},
// 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.removeAttribute("dragover");
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 toolbar isn't a folder.
var result = this._self.getResult();
if (!PlacesUtils.nodeIsFolder(result.root))
return null;
var dropPoint = { ip: null, beforeIndex: null, folderNode: null };
// Loop through all the nodes to see which one this should
// get dropped in/next to
for (var i = 0; i < this._self.childNodes.length; i++) {
var xulNode = this._self.childNodes[i];
if (PlacesUtils.nodeIsFolder(xulNode.node) &&
!PlacesUtils.nodeIsReadOnly(xulNode.node)) {
NS_ASSERT(xulNode.getAttribute("type") == "menu");
// This is a folder. If the mouse is in the left 25% of the
// node, drop to the left of the folder. If it's in the middle
// 50%, drop into the folder. If it's past that, drop to the right.
if (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),
i, -1);
dropPoint.beforeIndex = i;
return dropPoint;
}
else if (event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width * 0.75)) {
// Drop inside this folder.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(xulNode.node),
-1, 1);
dropPoint.beforeIndex = i;
dropPoint.folderNode = xulNode;
return dropPoint;
}
}
else {
// This is a non-folder node. If the mouse is left of the middle,
// drop to the left of the folder. If it's right, drop to the right.
if (event.clientX < xulNode.boxObject.x + (xulNode.boxObject.width / 2)) {
// Drop to the left of this bookmark.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
i, -1);
dropPoint.beforeIndex = i;
return dropPoint;
}
}
}
// Should drop to the right of the last node.
dropPoint.ip =
new InsertionPoint(PlacesUtils.getConcreteItemId(result.root),
-1, 1);
dropPoint.beforeIndex = -1;
return dropPoint;
},
onDragStart: function TBV_DO_onDragStart(event, xferData, dragAction) {
// sub menus have their own d&d handlers
if (event.target.parentNode != this._self)
return false;
if (event.target.localName == "toolbarbutton" &&
event.target.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)
return false;
event.target.firstChild.hidePopup();
#else
return;
#endif
}
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.focus();
xferData.data = this._self._controller.getTransferData(dragAction.action);
#ifdef XP_WIN
return true;
#endif
},
canDrop: function TBV_DO_canDrop(event, session) {
return PlacesControllerDragHelper.canDrop();
},
onDragOver: function TBV_DO_onDragOver(event, flavor, session) {
PlacesControllerDragHelper.currentDropTarget = event.target;
var dropPoint = this._getDropPoint(event);
var ib = this._self._dropIndicatorBar;
if (this._ibTimer) {
this._ibTimer.cancel();
this._ibTimer = null;
}
if (dropPoint.folderNode) {
// Dropping over a menubutton, set styles and timer to open folder.
if (this._overFolder.node != dropPoint.folderNode) {
this._clearOverFolder();
this._overFolder.node = dropPoint.folderNode;
this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime);
}
if (!this._overFolder.node.hasAttribute("dragover"))
this._overFolder.node.setAttribute("dragover", "true");
ib.removeAttribute("dragging");
}
else {
// Dragging over a normal toolbarbutton,
// show indicator bar and move it to the appropriate drop point.
if (!ib.hasAttribute("dragging"))
ib.setAttribute("dragging", "true");
var ind = ib.firstChild;
var halfInd = ind.boxObject.width / 2;
var direction = document.defaultView.getComputedStyle(this._self.parentNode, "").direction;
if (direction == "ltr") {
halfInd = Math.ceil(halfInd);
if (!this._self.childNodes.length)
ind.style.marginLeft = 0 - this._self.boxObject.x - halfInd + 'px'
else if (dropPoint.beforeIndex == -1)
ind.style.marginLeft = this._self.lastChild.boxObject.x +
this._self.lastChild.boxObject.width - this._self.boxObject.x - halfInd + 'px';
else
ind.style.marginLeft = this._self.childNodes[dropPoint.beforeIndex].boxObject.x -
this._self.boxObject.x - halfInd + 'px';
}
else {
halfInd = Math.floor(halfInd);
if (dropPoint.beforeIndex == -1 || !this._self.childNodes.length)
ind.style.marginRight = '0px';
else
ind.style.marginRight = (this._self.childNodes[this._self.childNodes.length - 1].boxObject.x +
this._self.childNodes[this._self.childNodes.length - 1].boxObject.width) -
(this._self.childNodes[dropPoint.beforeIndex].boxObject.x) - halfInd + 'px';
}
// Clear out old folder information
this._clearOverFolder();
}
},
onDrop: function TBV_DO_onDrop(event, dropData, session) {
var dropPoint = this._getDropPoint(event);
if (dropPoint == null)
return;
PlacesControllerDragHelper.onDrop(dropPoint.ip);
},
onDragExit: function TBV_DO_onDragExit(event, session) {
// Set timer to turn off indicator bar (if we turn it off
// here, dragenter might be called immediately after, creating
// flicker.)
if (this._ibTimer)
this._ibTimer.cancel();
this._ibTimer = this._setTimer(10);
// Close any folder being hovered over
if (this._overFolder.node)
this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
PlacesControllerDragHelper.currentDropTarget = null;
},
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;
}
})]]></field>
<method name="checkForMenuEvent">
<parameter name="event"/>
<parameter name="action"/>
<body><![CDATA[
// It seems that even if the menu drag/drop event
// handlers set their phase to capturing, toolbarbutton
// menu events come to the toolbar first, and don't bubble.
// So if this is a menu/menuitem, try to send the event to its
// xbl handler.
if (event.target.localName.indexOf("menu") == 0) {
var parent = event.target.parentNode;
// XULDocument has no getAttribute() function, so check for it before calling.
while (parent && parent.getAttribute) {
if (parent.getAttribute("type") == "places") {
nsDragAndDrop[action](event, parent._DNDObserver);
return true;
}
parent = parent.parentNode;
}
}
return false;
]]></body>
</method>
<property name="selType" onget="return 'single';"/>
<method name="buildContextMenu">
<parameter name="aPopup"/>
<body><![CDATA[
this._contextMenuShown = true;
this.focus();
return this.controller.buildContextMenu(aPopup);
]]></body>
</method>
<method name="destroyContextMenu">
<parameter name="aPopup"/>
<body><![CDATA[
this._contextMenuShown = false;
if (window.content)
window.content.focus();
]]></body>
</method>
<method name="_showEmptyMenuItem">
<parameter name="aPopup"/>
<body><![CDATA[
if (aPopup._emptyMenuItem) {
aPopup._emptyMenuItem.hidden = false;
return;
}
var label = PlacesUtils.getString("bookmarksMenuEmptyFolder");
aPopup._emptyMenuItem = document.createElement("menuitem");
aPopup._emptyMenuItem.setAttribute("label", label);
aPopup._emptyMenuItem.setAttribute("disabled", true);
aPopup.appendChild(aPopup._emptyMenuItem);
]]></body>
</method>
<method name="insertNewItemToPopup">
<parameter name="aChild"/>
<parameter name="aParentPopup"/>
<parameter name="aBefore"/>
<body><![CDATA[
var element =
PlacesUtils.createMenuItemForNode(aChild, this._containerNodesMap);
if (aBefore)
aParentPopup.insertBefore(element, aBefore);
else
aParentPopup.appendChild(element);
]]></body>
</method>
<method name="_containerPopupShowing">
<parameter name="aPopup"/>
<body><![CDATA[
if (aPopup._built)
return;
// remove previous menu items
while (aPopup.hasChildNodes())
aPopup.removeChild(aPopup.firstChild);
// restore the empty-menu item if has been created already
if (aPopup._emptyMenuItem)
aPopup.appendChild(aPopup._emptyMenuItem);
var resultNode = aPopup._resultNode;
if (!resultNode.containerOpen)
resultNode.containerOpen = true;
var cc = resultNode.childCount;
if (cc > 0) {
if (aPopup._emptyMenuItem)
aPopup._emptyMenuItem.hidden = true;
for (var i = 0; i < cc; ++i) {
var child = resultNode.getChild(i);
this.insertNewItemToPopup(child, aPopup, null);
}
}
else {
// add element to show it is empty.
this._showEmptyMenuItem(aPopup);
}
aPopup._built = true;
]]></body>
</method>
<method name="_isChevronChild">
<parameter name="aChild"/>
<body><![CDATA[
if (!this._chevron.firstChild.hasAttribute("type"))
return false;
var parent = aChild.parentNode;
while (parent != this) {
if (parent == this._chevron)
return true;
parent = parent.parentNode;
}
return false;
]]></body>
</method>
</implementation>
<handlers>
<handler event="mouseover"><![CDATA[
var button = event.target;
if (button.parentNode == this && button.node &&
PlacesUtils.nodeIsURI(button.node))
window.XULBrowserWindow.setOverLink(event.target.node.uri, null);
]]></handler>
<handler event="mouseout"><![CDATA[
window.XULBrowserWindow.setOverLink("", null);
]]></handler>
<handler event="draggesture"><![CDATA[
if (event.target.localName == "toolbarbutton" ||
event.target.localName == "toolbarseparator")
nsDragAndDrop.startDrag(event, this._DNDObserver);
]]></handler>
<handler event="dragover"><![CDATA[
if (!this.checkForMenuEvent(event, "dragOver"))
nsDragAndDrop.dragOver(event, this._DNDObserver);
]]></handler>
<handler event="dragdrop"><![CDATA[
if (!this.checkForMenuEvent(event, "drop"))
nsDragAndDrop.drop(event, this._DNDObserver);
]]></handler>
<handler event="dragexit"><![CDATA[
if (!this.checkForMenuEvent(event, "dragExit"))
nsDragAndDrop.dragExit(event, this._DNDObserver);
]]></handler>
<handler event="popupshowing" phase="capturing"><![CDATA[
var target = event.originalTarget;
// the chevron has its own view
if (this._isChevronChild(target))
return;
if (target._resultNode)
this._containerPopupShowing(target);
var targetParent = target.parentNode;
if (targetParent.localName == "toolbarbutton" &&
!PlacesControllerDragHelper.getSession())
this._openedMenuButton = targetParent;
]]></handler>
<handler event="popuphidden"><![CDATA[
var target = event.originalTarget;
if (!target._resultNode)
return;
// the chevron has its own view
if (this._isChevronChild(target))
return;
// UI performance: folder queries are cheap, keep the resultnode open
// so we don't rebuild its contents whenever the popup is reopened.
if (!PlacesUtils.nodeIsFolder(target._resultNode))
target._resultNode.containerOpen = false;
var targetParent = target.parentNode;
if (targetParent.localName == "toolbarbutton" &&
!PlacesControllerDragHelper.getSession())
this._openedMenuButton = null;
]]></handler>
<handler event="mousemove"><![CDATA[
if (this._openedMenuButton == null || PlacesControllerDragHelper.getSession())
return;
var target = event.originalTarget;
if (this._openedMenuButton != target &&
target.localName == "toolbarbutton" &&
target.type == "menu") {
this._openedMenuButton.open = false;
target.open = true;
}
]]></handler>
</handlers>
</binding>
</bindings>