RetroZilla/browser/components/places/content/tree.xml
2015-10-20 23:03:22 -04:00

1014 lines
40 KiB
XML

<?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 Tree 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):
# Ben Goodger <beng@google.com>
# Annie Sullivan <annie.sullivan@gmail.com>
#
# 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 *****
<bindings id="placesTreeBindings"
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-tree" extends="chrome://global/content/bindings/tree.xml#tree">
<implementation implements="nsINavHistoryResultViewObserver">
<constructor><![CDATA[
// Apply meta-model rules to this view.
ViewConfigurator.configure(this);
this.history = PlacesController.history;
this.controllers.appendController(PlacesController);
// Force an initial build.
this.place = this.place;
]]></constructor>
<field name="history">null</field>
<!-- overriding -->
<property name="view">
<getter><![CDATA[
return this.treeBoxObject.view;
]]></getter>
<setter><![CDATA[
// Make sure the last result doesn't hold a reference to us anymore
var resultview = this.getResultView();
if (resultview)
resultview.removeViewObserver(this._viewObserver);
this.treeBoxObject.view = val;
val.QueryInterface(Ci.nsINavHistoryResultViewer).addViewObserver(
this._viewObserver, false);
]]></setter>
</property>
<method name="getBestOptions">
<body><![CDATA[
// Get the best set of grouping options to use, either reuse the
// existing ones or create new ones.
var options = this.getResult().queryOptions;
if (!options)
options = this.history.getNewQueryOptions();
return options;
]]></body>
</method>
<method name="applyFilter">
<parameter name="filterString"/>
<parameter name="onlyBookmarks"/>
<parameter name="folderRestrict"/>
<parameter name="optionsFilter"/>
<body><![CDATA[
// preserve grouping
var queryNode = asQuery(this.getResult().root);
var options = queryNode.queryOptions.clone();
if (optionsFilter)
options = optionsFilter.filter(queryNode.getQueries({}), options,
optionsFilter.historyHandler);
var query = this.history.getNewQuery();
query.searchTerms = filterString;
query.onlyBookmarked = onlyBookmarks;
// If we're searching over everything, or this is not an exclusively
// bookmarks search, we need to make sure that we remove "group by
// folder" from the options list, because otherwise we will end up
// with _no_ results if the user searches while a bookmarks folder
// is selected, since the options for the current view say group by
// folder and none of the results will match!
if (!onlyBookmarks) {
var groupings = options.getGroupingMode({});
function isFolderGrouping(grouping, index, ary) {
return grouping != Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER;
}
var folderGroupings = groupings.filter(isFolderGrouping);
options.setGroupingMode(folderGroupings, folderGroupings.length);
}
if (onlyBookmarks)
query.setFolders(folderRestrict, folderRestrict.length);
this.load([query], options);
]]></body>
</method>
<method name="load">
<parameter name="queries"/>
<parameter name="options"/>
<body><![CDATA[
// override with our local options
options.excludeItems = this.excludeItems;
options.excludeQueries = this.excludeQueries;
options.expandQueries = this.expandQueries;
var result = this.history.executeQueries(queries, queries.length,
options);
var treeviewer =
Cc["@mozilla.org/browser/nav-history/result-tree-viewer;1"].
createInstance(Ci.nsINavHistoryResultViewer);
result.viewer = treeviewer;
treeviewer.QueryInterface(Ci.nsITreeView);
this.view = treeviewer;
]]></body>
</method>
<!--
Causes a particular node represented by the specified placeURI to be
selected in the tree. All containers above the node in the hierarchy
will be opened, so that the node is visible.
-->
<method name="selectPlaceURI">
<parameter name="placeURI"/>
<body><![CDATA[
function findNode(container, placeURI) {
container.containerOpen = true;
for (var i = 0; i < container.childCount; ++i) {
var child = container.getChild(i);
if (child.uri == placeURI)
return child;
else if (PlacesController.nodeIsContainer(child)) {
var nested = findNode(asContainer(child), placeURI);
if (nested)
return nested;
}
}
container.containerOpen = false;
return null;
}
var container = this._getRootNode();
NS_ASSERT(container, "No result, cannot select place URI!");
if (!container)
return;
var child = findNode(container, placeURI);
container.containerOpen = true;
if (child)
this.selectNode(child);
else {
// If the specified child could not be located, just select the first
// item in the list.
if (this.view.rowCount)
this.view.selection.select(0);
}
]]></body>
</method>
<!--
Causes a particular node to be selected in the tree, resulting in all
containers above the node in the hierarchy to be opened, so that the
node is visible.
-->
<method name="selectNode">
<parameter name="node"/>
<body><![CDATA[
var parent = node.parent;
// Build a list of all of the nodes that are the parent of this one
// in the result.
var parents = [];
var root = this._getRootNode();
while (parent && parent != root) {
parents.push(parent);
parent = parent.parent;
}
// Walk the list backwards (opening from the root of the hierarchy)
// opening each folder as we go.
var resultview = this.getResultView();
var view = this.view;
for (var i = parents.length - 1; i >= 0; --i) {
var index = resultview.treeIndexForNode(parents[i]);
if (view.isContainer(index) && !view.isContainerOpen(index))
view.toggleOpenState(index);
}
// Select the specified node...
index = resultview.treeIndexForNode(node);
view.selection.select(index);
// ... and ensure it's visible, not scrolled off somewhere.
this.treeBoxObject.ensureRowIsVisible(index);
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getResult">
<body><![CDATA[
try {
return this.view.QueryInterface(Ci.nsINavHistoryResultViewer).result;
}
catch (e) {
return null;
}
]]></body>
</method>
<method name="getResultView">
<body><![CDATA[
try {
return this.view.QueryInterface(Ci.nsINavHistoryResultTreeViewer);
}
catch (e) {
}
return null;
]]></body>
</method>
<!-- nsIPlacesView -->
<property name="place">
<getter><![CDATA[
return this.getAttribute("place");
]]></getter>
<setter><![CDATA[
this.setAttribute("place", val);
var queriesRef = { };
var queryCountRef = { };
var optionsRef = { };
this.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
if (queryCountRef.value == 0)
queriesRef.value = [this.history.getNewQuery()];
if (!optionsRef.value)
optionsRef.value = this.history.getNewQueryOptions();
this.load(queriesRef.value, optionsRef.value);
return val;
]]></setter>
</property>
<!-- nsIPlacesView -->
<property name="hasSelection">
<getter><![CDATA[
return this.view.selection.count >= 1;
]]></getter>
</property>
<!-- nsIPlacesView -->
<property name="hasSingleSelection">
<getter><![CDATA[
return this.view.selection.count == 1;
]]></getter>
</property>
<!-- nsIPlacesView -->
<method name="getSelectionNodes">
<body><![CDATA[
var selection = this.view.selection;
var rc = selection.getRangeCount();
var nodes = [];
var resultview = this.getResultView();
for (var i = 0; i < rc; ++i) {
var min = { }, max = { };
selection.getRangeAt(i, min, max);
for (var j = min.value; j <= max.value; ++j)
nodes.push(resultview.nodeForTreeIndex(j));
}
return nodes;
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getRemovableSelectionRanges">
<body><![CDATA[
// This function exists in addition to getSelectionNodes because it
// encodes selection ranges (which only occur in list views) into
// the return value. For each removed range, the index at which items
// will be re-inserted upon the remove transaction being performed is
// the first index of the range, so that the view updates correctly.
//
// For example, if we remove rows 2,3,4 and 7,8 from a list, when we
// undo that operation, if we insert what was at row 3 at row 3 again,
// it will show up _after_ the item that was at row 5. So we need to
// insert all items at row 2, and the tree view will update correctly.
//
// Also, this function collapses the selection to remove redundant
// data, e.g. when deleting this selection:
//
// http://www.foo.com/
// (-) Some Folder
// http://www.bar.com/
//
// ... returning http://www.bar.com/ as part of the selection is
// redundant because it is implied by removing "Some Folder". We
// filter out all such redundancies since some partial amount of
// the folder's children may be selected.
//
var selection = this.view.selection;
var rc = selection.getRangeCount();
var nodes = [];
var resultview = this.getResultView();
// This list is kept independently of the range selected (i.e. OUTSIDE
// the for loop) since the row index of a container is unique for the
// entire view, and we could have some really wacky selection and we
// don't want to blow up.
var containers = { };
for (var i = 0; i < rc; ++i) {
var range = [];
var min = { }, max = { };
selection.getRangeAt(i, min, max);
for (var j = min.value; j <= max.value; ++j) {
if (this.view.isContainer(j))
containers[j] = true;
if (!(this.view.getParentIndex(j) in containers))
range.push(resultview.nodeForTreeIndex(j));
}
nodes.push(range);
}
return nodes;
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getCopyableSelection">
<body><![CDATA[
// XXXben implement me!
return this.getSelectionNodes();
]]></body>
</method>
<!-- nsIPlacesView -->
<method name="getDragableSelection">
<body><![CDATA[
var nodes = this.getSelectionNodes();
for (var i = nodes.length - 1; i >= 0; i--) {
if (PlacesController.nodeIsReadOnly(nodes[i].parent))
nodes.splice(i, 1);
}
return nodes;
]]></body>
</method>
<!-- nsIPlacesView -->
<property name="selectedNode">
<getter><![CDATA[
var view = this.view;
var selection = view.selection;
var rc = selection.getRangeCount();
if (rc != 1)
return null;
var min = { }, max = { };
selection.getRangeAt(0, min, max);
return this.getResultView().nodeForTreeIndex(min.value);
]]></getter>
</property>
<!-- nsIPlacesView -->
<property name="selectedURINode">
<getter><![CDATA[
var view = this.view;
var selection = view.selection;
var rc = selection.getRangeCount();
if (rc != 1)
return null;
var min = { }, max = { };
selection.getRangeAt(0, min, max);
// only URI nodes should be returned
var node = this.getResultView().nodeForTreeIndex(min.value);
if (PlacesController.nodeIsURI(node))
return node;
return null;
]]></getter>
</property>
<!-- nsIPlacesView -->
<property name="insertionPoint">
<getter><![CDATA[
var orientation = NHRVO.DROP_AFTER;
// If there is no selection, insert at the end of the container.
if (!this.hasSelection) {
var index = this.view.rowCount - 1;
return this._getInsertionPoint(index, orientation);
}
// This is a two-part process. The first part is determining the drop
// orientation.
// * The default orientation is to drop _after_ the selected item.
// * If the selected item is an open container, the default
// orientation is to drop _into_ that container.
// * If the selected item is in the system area, the default action
// is to insert before the first user-editable item.
//
// Warning: It may be tempting to use tree indexes in this code, but
// you must not, since the tree is nested and as your tree
// index may change when folders before you are opened and
// closed. You must convert your tree index to a node, and
// then use getIndexOfNode to find your absolute index in
// the parent container instead.
//
var selection = this.view.selection;
var rc = selection.getRangeCount();
var min = { }, max = { };
selection.getRangeAt(rc - 1, min, max);
var resultView = this.getResultView();
// If the sole selection is an open container, insert into it rather
// than adjacent to it. Note that this only applies to _single_
// selections - if the last element within a multi-selection is an
// open folder, insert _adajacent_ to the selection.
if (this.hasSingleSelection && this.view.isContainer(max.value) &&
this.view.isContainerOpen(max.value))
orientation = NHRVO.DROP_ON;
else {
// If the selection is in the static "system" area, the insertion
// point is at the start of the user-editable area, i.e. before the
// first user editable item. This index (relative to the container,
// not the tree) is defined in this.peerDropIndex.
// We walk the list of top level ("system" area only applies to the
// top level) children, looking for the last selected node. If the
// last selected node is inside the "system" area, get the node for
// the first user editable item instead, compute its tree index, and
// insert _before_ that.
node = resultView.nodeForTreeIndex(max.value);
var container = asContainer(this.getResult().root);
var cc = container.childCount;
for (var i = 0; i < cc; ++i) {
if (container.getChild(i) == node &&
i < this.peerDropIndex) {
var firstUserChild = container.getChild(this.peerDropIndex);
max.value = resultView.treeIndexForNode(firstUserChild);
orientation = NHRVO.DROP_BEFORE;
break;
}
}
}
return this._getInsertionPoint(max.value, orientation);
]]></getter>
</property>
<method name="_getInsertionPoint">
<parameter name="index"/>
<parameter name="orientation"/>
<body><![CDATA[
var result = this.getResult();
var resultview = this.getResultView();
var container = result.root;
NS_ASSERT(container, "null container");
// When there's no selection, assume the container is the container
// the view is populated from (i.e. the result's folderId).
if (index != -1) {
var lastSelected = resultview.nodeForTreeIndex(index);
if (resultview.isContainer(index) && orientation == NHRVO.DROP_ON) {
// If the last selected item is an open container, append _into_
// it, rather than insert adjacent to it.
container = lastSelected;
index = -1;
}
else {
// Any visible selected item will always have a parent. The parent of
// an item at the root is the result itself, which can be QI'ed to
// nsINavHistoryResult
container = lastSelected.parent;
var lsi = PlacesController.getIndexOfNode(lastSelected);
index = orientation == NHRVO.DROP_BEFORE ? lsi : lsi + 1;
}
}
if (PlacesController.nodeIsFolder(container))
return new InsertionPoint(asFolder(container).folderId, index, orientation);
return null;
]]></body>
</method>
<!-- nsIPlacesView -->
<field name="peerDropIndex">0</field>
<!-- nsIPlacesView -->
<field name="peerDropTypes">ViewConfig.GENERIC_DROP_TYPES</field>
<!-- nsIPlacesView -->
<field name="childDropTypes">ViewConfig.GENERIC_DROP_TYPES</field>
<!-- nsIPlacesView -->
<field name="excludeItems">false</field>
<!-- nsIPlacesView -->
<field name="excludeQueries">false</field>
<!-- nsIPlacesView -->
<field name="expandQueries">false</field>
<!-- nsIPlacesView -->
<method name="selectAll">
<body><![CDATA[
this.view.selection.selectAll();
]]></body>
</method>
<!-- This method returns the root node of the query results as
a container query result node. If it is unable to do this
(for instance, if we have no query results), it will
return null.
-->
<method name="_getRootNode">
<body><![CDATA[
var result = this.getResult();
if (!result)
return null;
return asContainer(result.root);
]]></body>
</method>
<!-- This method, when given a list of folder IDs, will select all
the nodes that match those IDs in the current tree.
It will open any parent nodes that it needs to in order to
show the selected folders.
-->
<method name="selectFolders">
<parameter name="ids"/>
<body><![CDATA[
/**
* Returns the number of properties on the object "obj", since
* we're using objects as sets and dictionaries in the
* code below.
*/
function len(obj) {
var count = 0;
for (property in obj)
++count;
return count;
}
/**
* Recursively search through a node's children for folders
* with IDs in the set "indexes". When a matching folder
* is found, remove its ID from "indexes" and store the
* node in the dictionary "nodes".
*
* NOTE: This method will leave open any node that had
* matching folders in its subtree.
*
* @param node The root of the subtree to search for the folders.
* "node" itself will not be examined for a match.
* @param indexes A set of folder IDs, expressed as an object
* @param nodes A dictionary mapping folder IDs to folder
* result nodes, expressed as an object. This is
* empty at the initial start of the recursion and
* gets filled in as the recursion progresses.
*/
function findNodes(node, indexes, nodes) {
// Remember the beginning state so that we can re-close
// this node if we don't find any additional results here.
var previousOpenness = node.containerOpen;
var previousMatches = len(nodes);
node.containerOpen = true;
for (var child = 0;
(child < node.childCount) && (len(indexes) > 0);
child++) {
var childNode = node.getChild(child);
if (!PlacesController.nodeIsFolder(childNode))
continue;
var childFolder = asFolder(childNode);
// See if child matches an ID we wanted; add to results.
if (childFolder.folderId in indexes) {
nodes[childFolder.folderId] = childFolder;
delete indexes[childFolder.folderId];
}
// Search down that child's subtree.
findNodes(childFolder, indexes, nodes);
}
// If we didn't find any additional matches in this node's
// subtree, revert the node to its previous openness.
if (len(nodes) == previousMatches)
node.containerOpen = previousOpenness;
}
var indexes = {}; // Set of folder IDs to search for.
var nodes = {}; // Dictionary of found IDs to found nodes.
// Initialize input set
for (var i = 0; i < ids.length; i++)
indexes[ids[i]] = true;
var root = this._getRootNode();
if (!root) {
NS_ASSERT(root, "Couldn't select folders because no root node was available.");
return;
}
findNodes(root, indexes, nodes);
// For all the nodes we've found, highlight the corresponding
// index in the tree.
var resultview = this.getResultView();
var selection = this.view.selection;
selection.clearSelection();
for(idx in nodes) {
var index = resultview.treeIndexForNode(nodes[idx]);
selection.rangedSelect(index, index, true);
}
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="onDragStart">
<parameter name="event"/>
<parameter name="xferData"/>
<parameter name="dragAction"/>
<body><![CDATA[
// Drag and Drop does not work while a tree view is sorted.
if (this.getAttribute("sortActive") == "true")
throw Cr.NS_OK;
// Items that are "static" - i.e. above the user-configurable area
// of the view - can not be moved.
var nodes = this.getSelectionNodes();
var bms =
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
// If this node is part of a readonly folder (e.g. a livemark) it
// cannot be moved, only copied, so we must change the action used
// by the drag session.
var parent = node.parent;
if (PlacesController.nodeIsFolder(parent) &&
bms.getFolderReadonly(asFolder(parent).folderId))
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
// "static" top level "special" items cannot be dragged. They are
// at the root level of the view and are at a lower index in the
// root container than user-configurable items.
var treeIndex = this.getResultView().treeIndexForNode(nodes[i]);
if (this.view.getLevel(treeIndex) == 0) {
var index = PlacesController.getIndexOfNode(nodes[i]);
if (index < this.peerDropIndex)
throw Cr.NS_OK;
}
}
// 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 = PlacesController.getTransferData(dragAction.action);
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="canDrop">
<parameter name="event"/>
<parameter name="session"/>
<body><![CDATA[
return this._viewObserver.canDrop(-1, -1);
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="onDragOver">
<parameter name="event"/>
<parameter name="flavor"/>
<parameter name="session"/>
<body><![CDATA[
// When the user is dragging over an empty area of the tree, make
// sure canDrop is set to true to indicate dropping into the bucket.
var row = { }, col = { }, child = { };
this.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col,
child);
if (row.value == -1) {
var dragService =
Cc["@mozilla.org/widget/dragservice;1"].
getService(Ci.nsIDragService);
var dragSession = dragService.getCurrentSession();
dragSession.canDrop = this._viewObserver.canDrop(-1, 1);
}
]]></body>
</method>
<!-- nsDragAndDrop -->
<method name="getSupportedFlavours">
<body><![CDATA[
var flavorSet = new FlavourSet();
for (var i = 0; i < this.peerDropTypes.length; ++i)
flavorSet.appendFlavour(this.peerDropTypes[i]);
return flavorSet;
]]></body>
</method>
<!--
Gets the nsINavHistoryResultNode adjacent to the specified InsertionPoint.
@param insertionPoint
The InsertionPoint where items are being inserted
@param excludeItems
true if leaf nodes should be excluded for this view, false
otherwise.
@returns a nsINavHistoryResultNode that is adjacent to the specified
InsertionPoint
-->
<method name="_getInsertionNode">
<parameter name="insertionPoint"/>
<parameter name="excludeItems"/>
<body><![CDATA[
// The InsertionPoint defines a place within the view hierarchy where
// items may be inserted. This function returns the node adjacent to
// that point. In order to get the right node, we must enumerate the
// contents of the containing folder, but we cannot construct a new
// query for this, because if we do so the node will be a member of
// another result node (that constructed by |getFolderContents| say,
// instead of the displayed tree view). So we need to walk the
// children of the result to find the right folder.
function findFolder(folderId, container) {
var wasOpen = container.containerOpen;
container.containerOpen = true;
var cc = container.childCount;
var foundNode = null;
for (var i = 0; i < cc; ++i) {
var node = container.getChild(i);
if (PlacesController.nodeIsFolder(node)) {
var folder = asFolder(node);
if (folder.folderId == folderId) {
foundNode = node;
break;
}
foundNode = findFolder(folderId, folder);
if (foundNode)
break;
}
}
container.containerOpen = wasOpen;
return foundNode;
}
var folder = null;
var root = asContainer(this.getResult().root);
if (PlacesController.nodeIsFolder(root) &&
asFolder(root).folderId == insertionPoint.folderId)
folder = root;
else
folder = findFolder(insertionPoint.folderId, root);
// Since we find the folder manually, using findFolder instead of
// PlacesController.getFolderContents, the folder is not opened for
// us. We need to do that ourselves (and remember if it was closed,
// so that we can close it again later).
var folderWasOpen = folder.containerOpen;
folder.containerOpen = true;
var index = insertionPoint.index;
if (insertionPoint.index == -1 || insertionPoint.index >= folder.childCount)
index = folder.childCount - 1;
NS_ASSERT(index < folder.childCount,
"index out of range: " + index + " > " + folder);
var insertionNode = index > -1 ? folder.getChild(index) : null;
// Now close the folder again, if it was before.
folder.containerOpen = folderWasOpen;
return insertionNode;
]]></body>
</method>
<!--
Gets the tree-index of the node adjacent to the specified InsertionPoint
@param insertionPoint
The InsertionPoint where items are being inserted
@returns the tree-index of the node adjacent to the specified InsertionPoint
-->
<method name="_getInsertionIndex">
<parameter name="insertionPoint"/>
<body><![CDATA[
// Insert index of insertion and number of rows to insert
var excludeItems = this.excludeItems;
// This is a bit of a hack. Assume any container you drop into is
// itself showing all item types, not just folders or items.
if (insertionPoint.orientation == NHRVO.DROP_ON)
excludeItems = false;
var node = this._getInsertionNode(insertionPoint, excludeItems);
// This is the insertion index of the pivot.
if (node)
return this.getResultView().treeIndexForNode(node);
else if (insertionPoint.orientation == NHRVO.DROP_ON)
return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
return -1;
]]></body>
</method>
<field name="_viewObserver"><![CDATA[({
_self: this,
canDrop: function VO_canDrop(index, orientation) {
var result = this._self.getResult();
var resultview = this._self.getResultView();
var node = index != -1 ? resultview.nodeForTreeIndex(index) : result.root;
// Cannot drop before fixed items in the list.
if (node.parent == result.root &&
PlacesController.getIndexOfNode(node) < this._self.peerDropIndex &&
orientation != NHRVO.DROP_ON)
return false;
if (orientation == NHRVO.DROP_ON) {
// The user cannot drop an item into itself or a read-only container
var droppingOnSelf =
this._getSourceView() == this._self &&
this._self.view.selection.isSelected(index);
if (droppingOnSelf || node.containerReadOnly)
return false;
}
else if (node.parent && node.parent.containerReadOnly)
return false;
return PlacesControllerDragHelper.canDrop(this._self, orientation);
},
/**
* Adjusts an InsertionPoint's insertion index using these rules:
* XXXben define rules
*/
_adjustInsertionPoint: function VO__adjustInsertionPoint(insertionPoint) {
var index = this._self._getInsertionIndex(insertionPoint);
// If the dropped items are invisible, we don't need to adjust the
// insertion point.
if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
return;
var selection = this._self.view.selection;
var rc = selection.getRangeCount();
var selectionAbove = 0;
for (var i = 0; i < rc; ++i) {
var min = { }, max = { };
selection.getRangeAt(i, min, max);
if (min.value > index)
break;
if (max.value >= index)
selectionAbove += index - min.value;
else
selectionAbove += max.value - min.value + 1;
}
insertionPoint.index -= selectionAbove;
},
/**
* @returns the view where the drag was initiated.
*/
_getSourceView: function VO__getSourceView() {
var session = this._getCurrentSession();
var sourceView = session.sourceNode;
while (sourceView && sourceView.localName != "tree")
sourceView = sourceView.parentNode;
return sourceView;
},
/**
* @returns the current drag session.
*/
_getCurrentSession: function VO__getCurrentSession() {
var dragService =
Cc["@mozilla.org/widget/dragservice;1"].
getService(Ci.nsIDragService);
return dragService.getCurrentSession();
},
/**
* Handles a drop operation on this view
* @param index
* The index at which content was dropped
* @param orientation
* The orientation relative to the drop index where content
* should be inserted.
*/
onDrop: function VO_onDrop(index, orientation) {
LOG("VO: onDrop: " + index + ", orientation: " + orientation);
if (!this.canDrop(index, orientation))
return;
var sourceView = this._getSourceView();
// We are responsible for translating the |index| and |orientation|
// parameters into a container id and index within the container,
// since this information is specific to the tree view.
var ip = this._self._getInsertionPoint(index, orientation);
if (!ip)
throw Cr.NS_ERROR_NOT_AVAILABLE;
if (sourceView == this._self) {
// We are moving nodes within the same view, we need to adjust the
// insertion point to take into account the fact that rows may
// disappear above it, causing its index to be incorrect.
this._adjustInsertionPoint(ip);
}
PlacesControllerDragHelper.onDrop(sourceView, this._self, ip);
},
onToggleOpenState: function VO_onToggleOpenState(index) { },
onSelectionChanged: function VO_onSelectionChanged() { },
onCycleHeader: function VO_onCycleHeader(column) { },
onCycleCell: function VO_onCycleCell(row, column) { },
onPerformAction: function VO_onPerformAction(action) { },
onPerformActionOnRow: function VO_onPerformActionOnRow(action, row) { },
onPerformActionOnCell: function VO_onPerformActionOnCell(action, row, column) { }
})]]></field>
<field name="_nextSelection">[]</field>
<field name="SAVE_SELECTION_RELOAD">0</field>
<field name="SAVE_SELECTION_INSERT">1</field>
<field name="SAVE_SELECTION_REMOVE">2</field>
<method name="saveSelection">
<parameter name="mode"/>
<body><![CDATA[
// mode can be one of any of the SAVE_SELECTION field values,
// specifying how selection is to be saved.
var s = this.view.selection;
var rc = s.getRangeCount();
switch (mode) {
case this.SAVE_SELECTION_REMOVE:
var min = { }, max = { };
s.getRangeAt(rc - 1, min, max);
var rowCount = this.view.rowCount;
var index = -1;
if (max.value == (rowCount - 1) ||
this.view.getLevel(max.value + 1) != this.view.getLevel(max.value))
index = max.value - s.count;
else
index = max.value - s.count + 1;
this._nextSelection = [{ min: index, max: index }];
break;
case this.SAVE_SELECTION_INSERT:
var min = { }, max = { };
s.getRangeAt(rc - 1, min, max);
this._nextSelection = [{ min: max.value, max: max.value }];
break;
case this.SAVE_SELECTION_RELOAD:
this._nextSelection = [];
for (var i = 0; i < rc; ++i) {
var min = { }, max = { };
s.getRangeAt(i, range.min, range.max);
this._nextSelection.push({ min: range.min, max: range.max });
}
break;
}
]]></body>
</method>
<method name="restoreSelection">
<body><![CDATA[
for (var i = 0; i < this._nextSelection.length; ++i) {
var range = this._nextSelection[i];
if (range.min > -1 && range.max > -1)
this.view.selection.rangedSelect(range.min, range.max, true);
}
]]></body>
</method>
</implementation>
<handlers>
<handler event="focus"><![CDATA[
PlacesController.activeView = this;
document.commandDispatcher.updateCommands("focus");
]]></handler>
<handler event="select"><![CDATA[
document.commandDispatcher.updateCommands("select");
]]></handler>
<handler event="draggesture"><![CDATA[
// XXXben ew.
if (event.target.localName == "treechildren")
nsDragAndDrop.startDrag(event, this);
]]></handler>
<handler event="dragover"><![CDATA[
if (event.target.localName == "treechildren")
nsDragAndDrop.dragOver(event, this);
]]></handler>
</handlers>
</binding>
</bindings>