mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-07 20:17:37 +00:00
549 lines
20 KiB
XML
549 lines
20 KiB
XML
<?xml version="1.0"?>
|
|
|
|
<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[
|
|
this._places =
|
|
Cc["@mozilla.org/browser/nav-history-service;1"].
|
|
getService(Ci.nsINavHistoryService);
|
|
|
|
// Let's all do the happy QI/IRQ dance.
|
|
// Go from window->docshell->window.
|
|
window.QueryInterface(Ci.nsIInterfaceRequestor);
|
|
var shell = window.getInterface(Ci.nsIWebNavigation);
|
|
shell.QueryInterface(Ci.nsIDocShell);
|
|
var item = shell.QueryInterface(Ci.nsIDocShellTreeItem);
|
|
while (item.parent)
|
|
item = item.parent;
|
|
item.QueryInterface(Ci.nsIInterfaceRequestor);
|
|
this._browserWindow = item.getInterface(Ci.nsIDOMWindowInternal);
|
|
]]></constructor>
|
|
|
|
<method name="init">
|
|
<parameter name="viewConfig"/>
|
|
<body><![CDATA[
|
|
this.supportedDropTypes = viewConfig.dropTypes;
|
|
this.supportedDropOnTypes = viewConfig.dropOnTypes;
|
|
this.excludeItems = viewConfig.excludeItems;
|
|
this.firstDropIndex = viewConfig.firstDropIndex;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_fireEvent">
|
|
<parameter name="name"/>
|
|
<body><![CDATA[
|
|
var event = document.createEvent("events");
|
|
event.initEvent("placestree-" + name, false, true);
|
|
|
|
var canceled = !this.dispatchEvent(event);
|
|
|
|
var handler = this.getAttribute("onplacestree" + name);
|
|
if (handler != "") {
|
|
var fn = new Function("event", handler);
|
|
if (!fn(event))
|
|
canceled = true;
|
|
}
|
|
return !canceled;
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- AVI Method -->
|
|
<method name="getResult">
|
|
<body><![CDATA[
|
|
try {
|
|
return this.view.QueryInterface(Ci.nsINavHistoryResult);
|
|
}
|
|
catch (e) {
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- 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 result = this.getResult();
|
|
if (result)
|
|
result.removeObserver(this._viewObserver);
|
|
this.treeBoxObject.view = val;
|
|
this.getResult().addObserver(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._places.getNewQueryOptions();
|
|
return options;
|
|
]]></body>
|
|
</method>
|
|
|
|
<property name="filterString">
|
|
<getter><![CDATA[
|
|
var queries = this.getResult().getQueries({ });
|
|
if (queries[i].hasSearchTerms)
|
|
return queries[i].searchTerms;
|
|
return null;
|
|
]]></getter>
|
|
<setter><![CDATA[
|
|
var query = this._places.getNewQuery();
|
|
query.searchTerms = val;
|
|
|
|
this.load([query], this.getBestOptions());
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="applyFilter">
|
|
<parameter name="filterString"/>
|
|
<parameter name="onlyBookmarks"/>
|
|
<parameter name="folderRestrict"/>
|
|
<body><![CDATA[
|
|
// preserve grouping
|
|
var options = this.getResult().queryOptions;
|
|
if (!options)
|
|
options = this._places.getNewQueryOptions();
|
|
|
|
var query = this._places.getNewQuery();
|
|
query.searchTerms = filterString;
|
|
query.onlyBookmarked = onlyBookmarks;
|
|
//if (onlyBookmarks)
|
|
// query.setFolders(folderRestrict, folderRestrict.length);
|
|
this.load([query], this.getBestOptions());
|
|
]]></body>
|
|
</method>
|
|
|
|
<property name="queryString">
|
|
<getter><![CDATA[
|
|
var result = this.getResult();
|
|
var queries = result.root.getQueries({ });
|
|
var options = result.root.queryOptions;
|
|
|
|
const NH = Ci.nsINavHistoryService;
|
|
return this._places.queriesToQueryString(queries, queries.length,
|
|
options);
|
|
]]></getter>
|
|
<setter><![CDATA[
|
|
var queries = { }, options = { };
|
|
this._places.queryStringToQueries(val, queries, { }, options);
|
|
if (!queries.value.length)
|
|
queries.value = [this._places.getNewQuery()];
|
|
this.load(queries.value, options.value);
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="loadFolder">
|
|
<parameter name="folderId"/>
|
|
<body><![CDATA[
|
|
var query = this._places.getNewQuery();
|
|
query.setFolders([folderId], 1);
|
|
|
|
var options = this._places.getNewQueryOptions();
|
|
options.setGroupingMode([Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], 1);
|
|
options.excludeItems = this.excludeItems;
|
|
options.expandQueries = false;
|
|
var result = this._places.executeQuery(query, options);
|
|
result.QueryInterface(Ci.nsITreeView);
|
|
this.view = result;
|
|
this._fireEvent("reloaded");
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="load">
|
|
<parameter name="queries"/>
|
|
<parameter name="options"/>
|
|
<body><![CDATA[
|
|
// override with our local options
|
|
options.excludeItems = this.excludeItems;
|
|
options.expandQueries = false;
|
|
var result = this._places.executeQueries(queries, queries.length,
|
|
options);
|
|
result.QueryInterface(Ci.nsITreeView);
|
|
this.view = result;
|
|
this._fireEvent("reloaded");
|
|
]]></body>
|
|
</method>
|
|
|
|
<property name="isBookmarks">
|
|
<getter><![CDATA[
|
|
return PlacesController.nodeIsFolder(this.getResult().root);
|
|
]]></getter>
|
|
</property>
|
|
|
|
<!-- AVI Method -->
|
|
<property name="hasSelection">
|
|
<getter><![CDATA[
|
|
return this.view.selection.count >= 1;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<!-- AVI Method -->
|
|
<property name="hasSingleSelection">
|
|
<getter><![CDATA[
|
|
return this.view.selection.count == 1;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<!-- AVI Method -->
|
|
<method name="getSelectionNodes">
|
|
<body><![CDATA[
|
|
var selection = this.view.selection;
|
|
var rc = selection.getRangeCount();
|
|
var nodes = [];
|
|
var result = this.getResult();
|
|
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(result.nodeForTreeIndex(j));
|
|
}
|
|
return nodes;
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- AVI Method -->
|
|
<method name="getCopyableSelection">
|
|
<body><![CDATA[
|
|
// XXXben implement me!
|
|
return this.getSelectionNodes();
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- AVI Method -->
|
|
<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.getResult().nodeForTreeIndex(min.value);
|
|
]]></getter>
|
|
</property>
|
|
|
|
<!-- AVI Method -->
|
|
<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.getResult().nodeForTreeIndex(min.value);
|
|
if (PlacesController.nodeIsURI(node))
|
|
return node.QueryInterface(Ci.nsINavHistoryURIResultNode);
|
|
return null;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<!-- AVI Method -->
|
|
<property name="insertionPoint">
|
|
<getter><![CDATA[
|
|
var selection = this.view.selection;
|
|
var rc = selection.getRangeCount();
|
|
var min = { }, max = { };
|
|
selection.getRangeAt(rc - 1, min, max);
|
|
|
|
const DROP_AFTER = Ci.nsINavHistoryResultViewObserver.DROP_AFTER;
|
|
const DROP_ON = Ci.nsINavHistoryResultViewObserver.DROP_ON;
|
|
var orientation = DROP_AFTER;
|
|
if (this.view.isContainer(max.value) &&
|
|
this.view.isContainerOpen(max.value))
|
|
orientation = DROP_ON;
|
|
return this._getInsertionPoint(max.value, 1, orientation);
|
|
]]></getter>
|
|
</property>
|
|
|
|
<method name="_getInsertionPoint">
|
|
<parameter name="index"/>
|
|
<parameter name="orientation"/>
|
|
<body><![CDATA[
|
|
const NHRVO = Ci.nsINavHistoryResultViewObserver;
|
|
var result = this.getResult();
|
|
var container = result.root;
|
|
// 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 = result.nodeForTreeIndex(index);
|
|
if (this.view.isContainer(index) &&
|
|
(this.view.isContainerOpen(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;
|
|
}
|
|
}
|
|
var folder = container.QueryInterface(Ci.nsINavHistoryFolderResultNode);
|
|
return new InsertionPoint(folder.folderId, index, orientation);
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- AVI Method -->
|
|
<property name="browserWindow" onget="return this._browserWindow"/>
|
|
|
|
<!-- AVI Method -->
|
|
<field name="firstDropIndex">0</field>
|
|
|
|
<!-- AVI Method -->
|
|
<field name="supportedDropTypes">null</field>
|
|
|
|
<!-- AVI Method -->
|
|
<field name="supportedDropOnTypes">null</field>
|
|
|
|
<!-- AVI Method -->
|
|
<field name="excludeQueries">null</field>
|
|
|
|
<!-- AVI Method -->
|
|
<field name="dropOnFilterOptions">null</field>
|
|
|
|
<!-- AVI Method -->
|
|
<method name="selectAll">
|
|
<body><![CDATA[
|
|
this.view.selection.selectAll();
|
|
]]></body>
|
|
</method>
|
|
|
|
<!-- nsDragAndDrop -->
|
|
<method name="onDragStart">
|
|
<parameter name="event"/>
|
|
<parameter name="xferData"/>
|
|
<parameter name="dragAction"/>
|
|
<body><![CDATA[
|
|
if (this.getAttribute("sortActive") == "true")
|
|
throw Components.results.NS_OK;
|
|
xferData.data = PlacesController.getTransferData();
|
|
// XXXben - the drag wrapper should do this automatically.
|
|
if (event.ctrlKey)
|
|
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
|
|
]]></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[ ]]></body>
|
|
</method>
|
|
|
|
<!-- nsDragAndDrop -->
|
|
<method name="getSupportedFlavours">
|
|
<body><![CDATA[
|
|
var flavorSet = new FlavourSet();
|
|
for (var i = 0; i < this.supportedDropTypes.length; ++i)
|
|
flavorSet.appendFlavour(this.supportedDropTypes[i]);
|
|
return flavorSet;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_getInsertionNode">
|
|
<parameter name="insertionPoint"/>
|
|
<parameter name="excludeItems"/>
|
|
<body><![CDATA[
|
|
var result =
|
|
PlacesController.getFolderContents(insertionPoint.folderId,
|
|
excludeItems, false);
|
|
var index = insertionPoint.index;
|
|
if (insertionPoint.index == 0)
|
|
index = 0;
|
|
else if (insertionPoint.index == -1 || insertionPoint.index >= result.root.childCount)
|
|
index = result.root.childCount - 1;
|
|
return index > -1 ? result.root.getChild(index) : null;
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="_getInsertionIndex">
|
|
<parameter name="insertionPoint"/>
|
|
<body><![CDATA[
|
|
const NHRVO = Ci.nsINavHistoryResultViewObserver;
|
|
// 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.
|
|
return this.getResult().treeIndexForNode(node);
|
|
]]></body>
|
|
</method>
|
|
|
|
<field name="_viewObserver"><![CDATA[({
|
|
_self: this,
|
|
|
|
canDrop: function VO_canDrop(index, orientation) {
|
|
var root = this._self.getResult();
|
|
var node = index != -1 ? root.nodeForTreeIndex(index) : root;
|
|
const DROP_ON = Ci.nsINavHistoryResultViewObserver.DROP_ON;
|
|
// Cannot drop before fixed items in the list.
|
|
if (node.parent == root &&
|
|
PlacesController.getIndexOfNode(node) < this._self.firstDropIndex &&
|
|
orientation != DROP_ON)
|
|
return false;
|
|
|
|
// Cannot drop into a read-only container
|
|
if (orientation == DROP_ON) {
|
|
if (node.containerReadOnly) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (node.parent && node.parent.containerReadOnly) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return PlacesControllerDragHelper.canDrop(this._self, orientation);
|
|
},
|
|
|
|
_adjustInsertionPoint: function VO__adjustInsertionPoint(insertionPoint) {
|
|
var index = this._self._getInsertionIndex(insertionPoint);
|
|
|
|
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;
|
|
},
|
|
|
|
onDrop: function VO_onDrop(index, orientation) {
|
|
LOG("VO: onDrop: " + index + ", orientation: " + orientation);
|
|
if (!this.canDrop(index, orientation))
|
|
return;
|
|
|
|
var dragService =
|
|
Cc["@mozilla.org/widget/dragservice;1"].
|
|
getService(Ci.nsIDragService);
|
|
var session = dragService.getCurrentSession();
|
|
var sourceView = session.sourceNode.wrappedJSObject;
|
|
while (sourceView && sourceView.localName != "tree")
|
|
sourceView = sourceView.parentNode;
|
|
|
|
// Determine how many items will be visible in the target view after
|
|
// the drop operation completes. This can be zero if all items are
|
|
// dropped into a closed folder.
|
|
var visibleInsertCount = session.numDropItems;
|
|
if (orientation == Ci.nsINavHistoryResultViewObserver.DROP_ON &&
|
|
this._self.view.isContainer(index) &&
|
|
!this._self.view.isContainerOpen(index))
|
|
visibleInsertCount = 0;
|
|
|
|
// 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 (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,
|
|
visibleInsertCount);
|
|
},
|
|
|
|
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">-1</field>
|
|
<method name="saveSelection">
|
|
<body><![CDATA[
|
|
var s = this.view.selection;
|
|
var rc = s.getRangeCount();
|
|
var min = { }, max = { };
|
|
s.getRangeAt(rc - 1, min, max);
|
|
var rowCount = this.view.rowCount;
|
|
if (max.value == (rowCount - 1) ||
|
|
this.view.getLevel(max.value + 1) != this.view.getLevel(max.value))
|
|
this._nextSelection = max.value - s.count;
|
|
else
|
|
this._nextSelection = max.value - s.count + 1;
|
|
LOG("NS = " + this._nextSelection);
|
|
]]></body>
|
|
</method>
|
|
|
|
<method name="restoreSelection">
|
|
<body><![CDATA[
|
|
LOG("RNS = " + this._nextSelection);
|
|
if (this._nextSelection != -1)
|
|
this.view.selection.select(this._nextSelection);
|
|
]]></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>
|
|
|