2007-03-22 10:30:00 -07:00
|
|
|
<?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">
|
2008-01-29 12:04:43 -08:00
|
|
|
<implementation>
|
2007-03-22 10:30:00 -07:00
|
|
|
<constructor><![CDATA[
|
2007-10-24 19:37:05 -07:00
|
|
|
// Force an initial build.
|
|
|
|
if (this.place)
|
|
|
|
this.place = this.place;
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></constructor>
|
|
|
|
|
2007-05-20 15:27:04 -07:00
|
|
|
<destructor><![CDATA[
|
|
|
|
// Break the treeviewer->result->treeviewer cycle.
|
|
|
|
// Note: unsetting the result's viewer also unsets
|
|
|
|
// the viewer's reference to our treeBoxObject.
|
|
|
|
var result = this.getResult();
|
|
|
|
if (result)
|
|
|
|
result.viewer = null;
|
2007-05-19 13:29:48 -07:00
|
|
|
this.view = null;
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></destructor>
|
|
|
|
|
|
|
|
<property name="controller"
|
|
|
|
readonly="true"
|
|
|
|
onget="return this._controller;"/>
|
|
|
|
|
|
|
|
<!-- overriding -->
|
|
|
|
<property name="view">
|
|
|
|
<getter><![CDATA[
|
2008-02-21 10:48:14 -08:00
|
|
|
return this.treeBoxObject.view.QueryInterface(Ci.nsINavHistoryResultTreeViewer);
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></getter>
|
|
|
|
<setter><![CDATA[
|
2008-01-29 12:04:43 -08:00
|
|
|
return this.treeBoxObject.view = val;
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></setter>
|
|
|
|
</property>
|
2007-05-20 15:27:04 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<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 = PlacesUtils.history.getNewQueryOptions();
|
|
|
|
return options;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-05-20 15:27:04 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<method name="applyFilter">
|
|
|
|
<parameter name="filterString"/>
|
|
|
|
<parameter name="folderRestrict"/>
|
2007-05-25 16:50:21 -07:00
|
|
|
<body><![CDATA[
|
2007-03-22 10:30:00 -07:00
|
|
|
// preserve grouping
|
2007-07-21 01:30:07 -07:00
|
|
|
var queryNode = asQuery(this.getResultNode());
|
2007-03-22 10:30:00 -07:00
|
|
|
var options = queryNode.queryOptions.clone();
|
2007-10-27 14:55:53 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
var query = PlacesUtils.history.getNewQuery();
|
|
|
|
query.searchTerms = filterString;
|
2007-10-27 14:55:53 -07:00
|
|
|
|
2007-11-20 17:35:14 -08:00
|
|
|
if (folderRestrict) {
|
2007-03-22 10:30:00 -07:00
|
|
|
query.setFolders(folderRestrict, folderRestrict.length);
|
2007-11-20 17:35:14 -08:00
|
|
|
options.queryType = options.QUERY_TYPE_BOOKMARKS;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
this.load([query], options);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="load">
|
|
|
|
<parameter name="queries"/>
|
|
|
|
<parameter name="options"/>
|
2007-05-25 16:50:21 -07:00
|
|
|
<body><![CDATA[
|
2007-03-22 10:30:00 -07:00
|
|
|
var result = PlacesUtils.history.executeQueries(queries, queries.length,
|
2007-03-25 05:28:29 -07:00
|
|
|
options);
|
2008-02-16 22:17:34 -08:00
|
|
|
var callback;
|
|
|
|
if (this.flatList) {
|
|
|
|
var onOpenFlatContainer = this.onOpenFlatContainer;
|
|
|
|
if (onOpenFlatContainer)
|
|
|
|
callback = new Function("aContainer", onOpenFlatContainer);
|
|
|
|
}
|
|
|
|
|
|
|
|
var treeView = new PlacesTreeView(this.showRoot, this.flatList, callback);
|
2007-03-25 05:28:29 -07:00
|
|
|
result.viewer = treeView;
|
|
|
|
this.view = treeView;
|
2007-11-19 18:01:53 -08:00
|
|
|
if (!this._controller) {
|
|
|
|
this._controller = new PlacesController(this);
|
|
|
|
this.controllers.appendController(this._controller);
|
|
|
|
}
|
2007-09-19 01:13:20 -07:00
|
|
|
this._cachedInsertionPoint = undefined;
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-03-25 05:28:29 -07:00
|
|
|
|
2007-03-27 17:28:34 -07:00
|
|
|
<property name="showRoot">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.getAttribute("showRoot") == "true";
|
|
|
|
]]></getter>
|
|
|
|
<setter><![CDATA[
|
|
|
|
if (this.showRoot != val) {
|
|
|
|
this.setAttribute("showRoot", val);
|
|
|
|
// reload with the last place set
|
2007-10-24 19:37:05 -07:00
|
|
|
if (this.place)
|
|
|
|
this.place = this.place;
|
2007-03-27 17:28:34 -07:00
|
|
|
}
|
|
|
|
return val;
|
|
|
|
]]></setter>
|
|
|
|
</property>
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
<property name="flatList">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.getAttribute("flatList") == "true";
|
|
|
|
]]></getter>
|
|
|
|
<setter><![CDATA[
|
|
|
|
if (this.flatList != val) {
|
|
|
|
this.setAttribute("flatList", val);
|
|
|
|
// reload with the last place set
|
|
|
|
if (this.place)
|
|
|
|
this.place = this.place;
|
2008-02-16 22:17:34 -08:00
|
|
|
}
|
|
|
|
return val;
|
|
|
|
]]></setter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="onOpenFlatContainer">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.getAttribute("onopenflatcontainer");
|
|
|
|
]]></getter>
|
|
|
|
<setter><![CDATA[
|
|
|
|
if (this.onOpenFlatContainer != val) {
|
|
|
|
this.setAttribute("onopenflatcontainer", val);
|
|
|
|
// reload with the last place set
|
|
|
|
if (this.place)
|
|
|
|
this.place = this.place;
|
2007-11-19 18:01:53 -08:00
|
|
|
}
|
|
|
|
return val;
|
|
|
|
]]></setter>
|
|
|
|
</property>
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!--
|
|
|
|
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[
|
2007-12-13 06:29:11 -08:00
|
|
|
// Do nothing if a node matching the given uri is already selected
|
|
|
|
if (this.hasSelection && this.selectedNode.uri == placeURI)
|
|
|
|
return;
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
function findNode(container, placeURI, nodesURIChecked) {
|
|
|
|
var containerURI = container.uri;
|
|
|
|
if (containerURI == placeURI)
|
2007-05-27 20:57:20 -07:00
|
|
|
return container;
|
2007-11-19 18:01:53 -08:00
|
|
|
if (nodesURIChecked.indexOf(containerURI) != -1)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// never check the contents of the same query
|
|
|
|
nodesURIChecked.push(containerURI);
|
2007-05-30 09:08:11 -07:00
|
|
|
|
2007-12-13 06:29:11 -08:00
|
|
|
var wasOpen = container.containerOpen;
|
|
|
|
if (!wasOpen)
|
|
|
|
container.containerOpen = true;
|
2007-03-22 10:30:00 -07:00
|
|
|
for (var i = 0; i < container.childCount; ++i) {
|
|
|
|
var child = container.getChild(i);
|
2007-11-19 18:01:53 -08:00
|
|
|
var childURI = child.uri;
|
|
|
|
if (childURI == placeURI)
|
2007-03-22 10:30:00 -07:00
|
|
|
return child;
|
|
|
|
else if (PlacesUtils.nodeIsContainer(child)) {
|
2007-11-19 18:01:53 -08:00
|
|
|
var nested = findNode(asContainer(child), placeURI, nodesURIChecked);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (nested)
|
|
|
|
return nested;
|
|
|
|
}
|
|
|
|
}
|
2007-12-13 06:29:11 -08:00
|
|
|
|
|
|
|
if (!wasOpen)
|
|
|
|
container.containerOpen = false;
|
2007-11-19 18:01:53 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return null;
|
|
|
|
}
|
2007-12-13 06:29:11 -08:00
|
|
|
|
|
|
|
var container = this.getResultNode();
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ASSERT(container, "No result, cannot select place URI!");
|
|
|
|
if (!container)
|
|
|
|
return;
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
var child = findNode(container, placeURI, []);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (child)
|
|
|
|
this.selectNode(child);
|
|
|
|
else {
|
2007-12-13 06:29:11 -08:00
|
|
|
// If the specified child could not be located, clear the selection
|
|
|
|
var selection = this.view.selection;
|
|
|
|
selection.clearSelection();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-12-13 06:29:11 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!--
|
|
|
|
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[
|
2007-12-13 06:29:11 -08:00
|
|
|
var view = this.getResultView();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
var parent = node.parent;
|
2007-12-13 06:29:11 -08:00
|
|
|
if (parent && !parent.containerOpen) {
|
|
|
|
// Build a list of all of the nodes that are the parent of this one
|
|
|
|
// in the result.
|
|
|
|
var parents = [];
|
|
|
|
var root = this.getResultNode();
|
|
|
|
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.
|
|
|
|
for (var i = parents.length - 1; i >= 0; --i) {
|
|
|
|
var index = view.treeIndexForNode(parents[i]);
|
|
|
|
if (view.isContainer(index) && !view.isContainerOpen(index))
|
|
|
|
view.toggleOpenState(index);
|
|
|
|
}
|
|
|
|
// Select the specified node...
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-12-13 06:29:11 -08:00
|
|
|
|
|
|
|
var index = view.treeIndexForNode(node);
|
2007-03-22 10:30:00 -07:00
|
|
|
view.selection.select(index);
|
|
|
|
// ... and ensure it's visible, not scrolled off somewhere.
|
|
|
|
this.treeBoxObject.ensureRowIsVisible(index);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-12-13 06:29:11 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<method name="getResult">
|
|
|
|
<body><![CDATA[
|
|
|
|
try {
|
|
|
|
return this.view.QueryInterface(Ci.nsINavHistoryResultViewer).result;
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<method name="getResultNode">
|
|
|
|
<body><![CDATA[
|
|
|
|
return this.getResult().root;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="getResultView">
|
|
|
|
<body><![CDATA[
|
|
|
|
try {
|
|
|
|
return this.view.QueryInterface(Ci.nsINavHistoryResultTreeViewer);
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<property name="place">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.getAttribute("place");
|
|
|
|
]]></getter>
|
2007-10-27 14:55:53 -07:00
|
|
|
<setter><![CDATA[
|
2007-03-22 10:30:00 -07:00
|
|
|
this.setAttribute("place", val);
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
var queriesRef = { };
|
|
|
|
var queryCountRef = { };
|
|
|
|
var optionsRef = { };
|
|
|
|
PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
|
|
|
|
if (queryCountRef.value == 0)
|
|
|
|
queriesRef.value = [PlacesUtils.history.getNewQuery()];
|
|
|
|
if (!optionsRef.value)
|
|
|
|
optionsRef.value = PlacesUtils.history.getNewQueryOptions();
|
|
|
|
|
|
|
|
this.load(queriesRef.value, optionsRef.value);
|
|
|
|
|
|
|
|
return val;
|
|
|
|
]]></setter>
|
|
|
|
</property>
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<property name="hasSelection">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this.view.selection.count >= 1;
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
2007-03-25 05:28:29 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- 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);
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
for (var j = min.value; j <= max.value; ++j)
|
|
|
|
nodes.push(resultview.nodeForTreeIndex(j));
|
|
|
|
}
|
|
|
|
return nodes;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- 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="getDragableSelection">
|
|
|
|
<body><![CDATA[
|
2008-02-25 13:45:22 -08:00
|
|
|
return this.getSelectionNodes();
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<property name="selectedNode">
|
|
|
|
<getter><![CDATA[
|
|
|
|
var view = this.view;
|
2008-02-25 13:45:22 -08:00
|
|
|
if (view.selection.count != 1)
|
2007-03-22 10:30:00 -07:00
|
|
|
return null;
|
2008-01-11 14:04:01 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
var selection = view.selection;
|
|
|
|
var min = { }, max = { };
|
|
|
|
selection.getRangeAt(0, min, max);
|
|
|
|
|
2008-02-25 13:45:22 -08:00
|
|
|
return this.getResultView().nodeForTreeIndex(min.value);
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></getter>
|
|
|
|
</property>
|
2007-09-19 01:13:20 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<property name="insertionPoint">
|
|
|
|
<getter><![CDATA[
|
2007-09-19 01:13:20 -07:00
|
|
|
// invalidated on selection and focus changes
|
|
|
|
if (this._cachedInsertionPoint !== undefined)
|
|
|
|
return this._cachedInsertionPoint;
|
|
|
|
|
2007-07-10 09:20:54 -07:00
|
|
|
// there is no insertion point for history queries
|
|
|
|
// so bail out now and save a lot of work when updating commands
|
2007-07-21 01:30:07 -07:00
|
|
|
var resultNode = this.getResultNode();
|
|
|
|
if (PlacesUtils.nodeIsQuery(resultNode)) {
|
|
|
|
var options = asQuery(resultNode).queryOptions;
|
2007-09-19 01:13:20 -07:00
|
|
|
if (options.queryType == options.QUERY_TYPE_HISTORY)
|
|
|
|
return this._cachedInsertionPoint = null;
|
2007-07-21 01:30:07 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-04-28 02:33:40 -07:00
|
|
|
var orientation = Ci.nsITreeView.DROP_BEFORE;
|
2007-03-22 10:30:00 -07:00
|
|
|
// If there is no selection, insert at the end of the container.
|
|
|
|
if (!this.hasSelection) {
|
|
|
|
var index = this.view.rowCount - 1;
|
2007-09-19 01:13:20 -07:00
|
|
|
this._cachedInsertionPoint =
|
|
|
|
this._getInsertionPoint(index, orientation);
|
|
|
|
return this._cachedInsertionPoint;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// This is a two-part process. The first part is determining the drop
|
2007-03-22 16:43:56 -07:00
|
|
|
// orientation.
|
2008-04-28 02:33:40 -07:00
|
|
|
// * The default orientation is to drop _before_ the selected item.
|
|
|
|
// * If the selected item is a container, the default orientation
|
|
|
|
// is to drop _into_ that container.
|
2007-03-22 10:30:00 -07:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2007-03-25 14:56:52 -07:00
|
|
|
var resultView = this.getResultView();
|
|
|
|
var selection = resultView.selection;
|
2007-03-22 10:30:00 -07:00
|
|
|
var rc = selection.getRangeCount();
|
|
|
|
var min = { }, max = { };
|
|
|
|
selection.getRangeAt(rc - 1, min, max);
|
|
|
|
|
2008-04-28 02:33:40 -07:00
|
|
|
// If the sole selection is a container, and we are not in
|
|
|
|
// a flatlist, insert into it.
|
|
|
|
// Note that this only applies to _single_ selections,
|
|
|
|
// if the last element within a multi-selection is a
|
|
|
|
// container, insert _adjacent_ to the selection.
|
2007-03-25 14:56:52 -07:00
|
|
|
//
|
2007-07-10 09:20:54 -07:00
|
|
|
// If the sole selection is the bookmarks toolbar folder, we insert
|
2007-03-25 14:56:52 -07:00
|
|
|
// into it even if it is not opened
|
2008-01-25 11:01:18 -08:00
|
|
|
var itemId =
|
|
|
|
PlacesUtils.getConcreteItemId(resultView.nodeForTreeIndex(max.value));
|
2008-02-25 13:45:22 -08:00
|
|
|
if (selection.count == 1 && resultView.isContainer(max.value) &&
|
2008-04-28 02:33:40 -07:00
|
|
|
!this.flatList)
|
2008-01-29 12:04:43 -08:00
|
|
|
orientation = Ci.nsITreeView.DROP_ON;
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-09-19 01:13:20 -07:00
|
|
|
this._cachedInsertionPoint =
|
|
|
|
this._getInsertionPoint(max.value, orientation);
|
|
|
|
return this._cachedInsertionPoint;
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
2007-05-22 13:36:43 -07:00
|
|
|
<method name="_disallowInsertion">
|
|
|
|
<parameter name="aContainer"/>
|
|
|
|
<body><![CDATA[
|
2008-04-11 09:22:01 -07:00
|
|
|
// allow dropping into Tag containers
|
|
|
|
if (PlacesUtils.nodeIsTagQuery(aContainer))
|
|
|
|
return false;
|
2007-05-22 13:36:43 -07:00
|
|
|
// Disallow insertion of items under readonly folders
|
|
|
|
return (!PlacesUtils.nodeIsFolder(aContainer) ||
|
2008-01-25 11:01:18 -08:00
|
|
|
PlacesUtils.nodeIsReadOnly(aContainer));
|
2007-05-22 13:36:43 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<method name="_getInsertionPoint">
|
|
|
|
<parameter name="index"/>
|
|
|
|
<parameter name="orientation"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
var result = this.getResult();
|
|
|
|
var resultview = this.getResultView();
|
|
|
|
var container = result.root;
|
2008-04-25 16:44:10 -07:00
|
|
|
var dropNearItemId = -1;
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ASSERT(container, "null container");
|
|
|
|
// When there's no selection, assume the container is the container
|
2007-05-14 14:56:38 -07:00
|
|
|
// the view is populated from (i.e. the result's itemId).
|
2007-03-22 10:30:00 -07:00
|
|
|
if (index != -1) {
|
|
|
|
var lastSelected = resultview.nodeForTreeIndex(index);
|
2008-01-29 12:04:43 -08:00
|
|
|
if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// If the last selected item is an open container, append _into_
|
|
|
|
// it, rather than insert adjacent to it.
|
|
|
|
container = lastSelected;
|
|
|
|
index = -1;
|
|
|
|
}
|
2007-06-26 18:38:50 -07:00
|
|
|
else if (!this._disallowInsertion(lastSelected) &&
|
|
|
|
lastSelected.containerOpen &&
|
2008-04-25 16:44:10 -07:00
|
|
|
orientation == Ci.nsITreeView.DROP_AFTER &&
|
|
|
|
lastSelected.hasChildren) {
|
2007-06-26 18:38:50 -07:00
|
|
|
// 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;
|
2008-01-29 12:04:43 -08:00
|
|
|
orientation = Ci.nsITreeView.DROP_BEFORE;
|
2007-06-26 18:38:50 -07:00
|
|
|
index = 0;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
else {
|
2007-06-24 17:00:17 -07:00
|
|
|
// Use the last-selected node's container unless the root node
|
|
|
|
// is selected, in which case we use the root node itself as the
|
|
|
|
// insertion point.
|
|
|
|
container = lastSelected.parent || container;
|
|
|
|
|
2007-05-22 13:36:43 -07:00
|
|
|
// avoid the potentially expensive call to getIndexOfNode()
|
|
|
|
// if we know this container doesn't allow insertion
|
|
|
|
if (this._disallowInsertion(container))
|
|
|
|
return null;
|
|
|
|
|
2008-03-13 17:52:21 -07:00
|
|
|
var queryOptions = asQuery(result.root).queryOptions;
|
2008-04-25 16:44:10 -07:00
|
|
|
if (queryOptions.sortingMode !=
|
|
|
|
Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
|
|
|
|
// If we are within a sorted view, insert at the end
|
|
|
|
index = -1;
|
|
|
|
}
|
|
|
|
else if (queryOptions.excludeItems ||
|
|
|
|
queryOptions.excludeQueries ||
|
|
|
|
queryOptions.excludeReadOnlyFolders) {
|
|
|
|
// Some item may be invisible, insert near last selected one.
|
|
|
|
// We don't replace index here to avoid requests to the db,
|
|
|
|
// instead it will be calculated later by the controller.
|
|
|
|
index = -1;
|
|
|
|
dropNearItemId = lastSelected.itemId;
|
2008-03-13 17:52:21 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
var lsi = PlacesUtils.getIndexOfNode(lastSelected);
|
|
|
|
index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-03-25 14:56:52 -07:00
|
|
|
|
2007-05-22 13:36:43 -07:00
|
|
|
if (this._disallowInsertion(container))
|
|
|
|
return null;
|
|
|
|
|
2008-01-25 11:01:18 -08:00
|
|
|
return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
|
2008-04-11 09:22:01 -07:00
|
|
|
index, orientation,
|
2008-04-25 16:44:10 -07:00
|
|
|
PlacesUtils.nodeIsTagQuery(container),
|
|
|
|
dropNearItemId);
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-03-22 16:43:56 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- nsIPlacesView -->
|
|
|
|
<method name="selectAll">
|
|
|
|
<body><![CDATA[
|
|
|
|
this.view.selection.selectAll();
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
<!-- This method will select the first node in the tree that matches
|
|
|
|
each given item id. It will open any parent nodes that it needs
|
|
|
|
to in order to show the selected items.
|
2007-03-22 10:30:00 -07:00
|
|
|
-->
|
2007-11-19 18:01:53 -08:00
|
|
|
<method name="selectItems">
|
|
|
|
<parameter name="aIDs"/>
|
2007-03-22 10:30:00 -07:00
|
|
|
<body><![CDATA[
|
2007-11-19 18:01:53 -08:00
|
|
|
var ids = aIDs; // don't manipulate the caller's array
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
// Array of nodes found by findNodes which are to be selected
|
|
|
|
var nodes = [];
|
|
|
|
|
|
|
|
// A set of URIs of container-nodes that were previously searched,
|
|
|
|
// and thus shouldn't be searched again. This is empty at the initial
|
|
|
|
// start of the recursion and gets filled in as the recursion
|
|
|
|
// progresses.
|
|
|
|
var nodesURIChecked = [];
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/**
|
2007-11-19 18:01:53 -08:00
|
|
|
* Recursively search through a node's children for items
|
|
|
|
* with the given IDs. When a matching item is found, remove its ID
|
|
|
|
* from the IDs array, and add the found node to the nodes dictionary.
|
2007-03-22 10:30:00 -07:00
|
|
|
*
|
2007-11-19 18:01:53 -08:00
|
|
|
* NOTE: This method will leave open any node that had matching items
|
|
|
|
* in its subtree.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
2007-11-19 18:01:53 -08:00
|
|
|
function findNodes(node) {
|
|
|
|
var foundOne = false;
|
2007-05-30 09:08:11 -07:00
|
|
|
// See if node matches an ID we wanted; add to results.
|
2008-01-25 11:01:18 -08:00
|
|
|
// For simple folder queries, check both itemId and the concrete
|
|
|
|
// item id.
|
2007-11-19 18:01:53 -08:00
|
|
|
var index = ids.indexOf(node.itemId);
|
2008-01-25 11:01:18 -08:00
|
|
|
if (index == -1 &&
|
|
|
|
node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
|
|
|
|
index = ids.indexOf(asQuery(node).folderItemId);
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
if (index != -1) {
|
|
|
|
nodes.push(node);
|
|
|
|
foundOne = true;
|
|
|
|
ids.splice(index, 1);
|
2007-05-30 09:08:11 -07:00
|
|
|
}
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
|
|
|
|
nodesURIChecked.indexOf(node.uri) != -1)
|
|
|
|
return foundOne;
|
|
|
|
|
|
|
|
nodesURIChecked.push(node.uri);
|
|
|
|
asContainer(node);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// 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;
|
|
|
|
node.containerOpen = true;
|
2007-11-19 18:01:53 -08:00
|
|
|
for (var child = 0; child < node.childCount && ids.length > 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
child++) {
|
|
|
|
var childNode = node.getChild(child);
|
2007-11-19 18:01:53 -08:00
|
|
|
var found = findNodes(childNode);
|
|
|
|
if (!foundOne)
|
|
|
|
foundOne = found;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't find any additional matches in this node's
|
|
|
|
// subtree, revert the node to its previous openness.
|
2007-11-19 18:01:53 -08:00
|
|
|
if (!foundOne)
|
2007-03-22 10:30:00 -07:00
|
|
|
node.containerOpen = previousOpenness;
|
2007-11-19 18:01:53 -08:00
|
|
|
return foundOne;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-11-19 18:01:53 -08:00
|
|
|
findNodes(this.getResultNode());
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// 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();
|
2007-11-19 18:01:53 -08:00
|
|
|
for (var i=0; i < nodes.length; i++) {
|
|
|
|
var index = resultview.treeIndexForNode(nodes[i]);
|
2007-03-22 10:30:00 -07:00
|
|
|
selection.rangedSelect(index, index, true);
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2007-11-19 18:01:53 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- 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;
|
2007-05-20 15:27:04 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
var nodes = this.getSelectionNodes();
|
|
|
|
for (var i = 0; i < nodes.length; ++i) {
|
|
|
|
var node = nodes[i];
|
2007-06-15 18:55:15 -07:00
|
|
|
|
|
|
|
// Disallow dragging the root node of a tree
|
|
|
|
var parent = node.parent;
|
|
|
|
if (!parent)
|
|
|
|
throw Cr.NS_OK;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// 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.
|
2008-04-25 14:36:40 -07:00
|
|
|
if (PlacesUtils.nodeIsTagQuery(parent) ||
|
|
|
|
!PlacesControllerDragHelper.canMoveContainerNode(node)) {
|
|
|
|
// XXX DOES NOTHING! dragAction doesn't persist
|
2007-03-22 10:30:00 -07:00
|
|
|
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
2007-05-20 15:27:04 -07:00
|
|
|
break;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-05-20 15:27:04 -07:00
|
|
|
|
2007-03-22 16:43:56 -07:00
|
|
|
// XXXben - the drag wrapper should do this automatically.
|
2007-03-22 10:30:00 -07:00
|
|
|
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);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<!-- nsDragAndDrop -->
|
|
|
|
<method name="canDrop">
|
|
|
|
<parameter name="event"/>
|
|
|
|
<parameter name="session"/>
|
|
|
|
<body><![CDATA[
|
2008-04-23 09:00:42 -07:00
|
|
|
var row = { }, col = { }, child = { };
|
|
|
|
this.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col,
|
|
|
|
child);
|
|
|
|
return this.view.canDrop(row.value, -1);
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<!-- nsDragAndDrop -->
|
|
|
|
<method name="onDragOver">
|
|
|
|
<parameter name="event"/>
|
|
|
|
<parameter name="flavor"/>
|
|
|
|
<parameter name="session"/>
|
|
|
|
<body><![CDATA[
|
2008-04-23 09:00:42 -07:00
|
|
|
var dragService =
|
|
|
|
Cc["@mozilla.org/widget/dragservice;1"].
|
|
|
|
getService(Ci.nsIDragService);
|
|
|
|
var dragSession = dragService.getCurrentSession();
|
|
|
|
dragSession.canDrop = this.canDrop(event, session);
|
2007-03-22 10:30:00 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
2008-01-29 12:04:43 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<!-- nsDragAndDrop -->
|
|
|
|
<method name="getSupportedFlavours">
|
|
|
|
<body><![CDATA[
|
|
|
|
var flavorSet = new FlavourSet();
|
2008-03-13 12:25:49 -07:00
|
|
|
var types = PlacesUIUtils.GENERIC_VIEW_DROP_TYPES;
|
2008-01-29 12:04:43 -08:00
|
|
|
for (var i = 0; i < types.length; ++i)
|
|
|
|
flavorSet.appendFlavour(types[i]);
|
2007-03-22 10:30:00 -07:00
|
|
|
return flavorSet;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
2008-01-29 12:04:43 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
<method name="buildContextMenu">
|
|
|
|
<parameter name="aPopup"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
return this.controller.buildContextMenu(aPopup);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="destroyContextMenu">
|
|
|
|
<parameter name="aPopup"/>
|
|
|
|
<body/>
|
|
|
|
</method>
|
|
|
|
</implementation>
|
|
|
|
<handlers>
|
|
|
|
<handler event="focus"><![CDATA[
|
2007-09-19 01:13:20 -07:00
|
|
|
this._cachedInsertionPoint = undefined;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// See select handler. We need the sidebar's places commandset to be
|
|
|
|
// updated as well
|
|
|
|
document.commandDispatcher.updateCommands("focus");
|
|
|
|
]]></handler>
|
|
|
|
<handler event="select"><![CDATA[
|
2007-09-19 01:13:20 -07:00
|
|
|
this._cachedInsertionPoint = undefined;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// This additional complexity is here for the sidebars
|
|
|
|
var win = window;
|
|
|
|
while (true) {
|
|
|
|
win.document.commandDispatcher.updateCommands("focus");
|
|
|
|
if (win == window.top)
|
|
|
|
break;
|
|
|
|
|
|
|
|
win = win.parent;
|
|
|
|
}
|
|
|
|
]]></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>
|
|
|
|
|