1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/components/places/content/tree.xml Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,797 @@ 1.4 +<?xml version="1.0"?> 1.5 + 1.6 +<!-- This Source Code Form is subject to the terms of the Mozilla Public 1.7 + - License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 1.9 + 1.10 +<bindings id="placesTreeBindings" 1.11 + xmlns="http://www.mozilla.org/xbl" 1.12 + xmlns:xbl="http://www.mozilla.org/xbl" 1.13 + xmlns:html="http://www.w3.org/1999/xhtml" 1.14 + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 1.15 + 1.16 + <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree"> 1.17 + <implementation> 1.18 + <constructor><![CDATA[ 1.19 + // Force an initial build. 1.20 + if (this.place) 1.21 + this.place = this.place; 1.22 + ]]></constructor> 1.23 + 1.24 + <destructor><![CDATA[ 1.25 + // Break the treeviewer->result->treeviewer cycle. 1.26 + // Note: unsetting the result's viewer also unsets 1.27 + // the viewer's reference to our treeBoxObject. 1.28 + var result = this.result; 1.29 + if (result) { 1.30 + result.root.containerOpen = false; 1.31 + } 1.32 + 1.33 + // Unregister the controllber before unlinking the view, otherwise it 1.34 + // may still try to update commands on a view with a null result. 1.35 + if (this._controller) { 1.36 + this._controller.terminate(); 1.37 + this.controllers.removeController(this._controller); 1.38 + } 1.39 + 1.40 + this.view = null; 1.41 + ]]></destructor> 1.42 + 1.43 + <property name="controller" 1.44 + readonly="true" 1.45 + onget="return this._controller"/> 1.46 + 1.47 + <!-- overriding --> 1.48 + <property name="view"> 1.49 + <getter><![CDATA[ 1.50 + try { 1.51 + return this.treeBoxObject.view.wrappedJSObject || null; 1.52 + } 1.53 + catch(e) { 1.54 + return null; 1.55 + } 1.56 + ]]></getter> 1.57 + <setter><![CDATA[ 1.58 + return this.treeBoxObject.view = val; 1.59 + ]]></setter> 1.60 + </property> 1.61 + 1.62 + <property name="associatedElement" 1.63 + readonly="true" 1.64 + onget="return this"/> 1.65 + 1.66 + <method name="applyFilter"> 1.67 + <parameter name="filterString"/> 1.68 + <parameter name="folderRestrict"/> 1.69 + <parameter name="includeHidden"/> 1.70 + <body><![CDATA[ 1.71 + // preserve grouping 1.72 + var queryNode = PlacesUtils.asQuery(this.result.root); 1.73 + var options = queryNode.queryOptions.clone(); 1.74 + 1.75 + // Make sure we're getting uri results. 1.76 + // We do not yet support searching into grouped queries or into 1.77 + // tag containers, so we must fall to the default case. 1.78 + if (PlacesUtils.nodeIsHistoryContainer(queryNode) || 1.79 + options.resultType == options.RESULTS_AS_TAG_QUERY || 1.80 + options.resultType == options.RESULTS_AS_TAG_CONTENTS) 1.81 + options.resultType = options.RESULTS_AS_URI; 1.82 + 1.83 + var query = PlacesUtils.history.getNewQuery(); 1.84 + query.searchTerms = filterString; 1.85 + 1.86 + if (folderRestrict) { 1.87 + query.setFolders(folderRestrict, folderRestrict.length); 1.88 + options.queryType = options.QUERY_TYPE_BOOKMARKS; 1.89 + } 1.90 + 1.91 + options.includeHidden = !!includeHidden; 1.92 + 1.93 + this.load([query], options); 1.94 + ]]></body> 1.95 + </method> 1.96 + 1.97 + <method name="load"> 1.98 + <parameter name="queries"/> 1.99 + <parameter name="options"/> 1.100 + <body><![CDATA[ 1.101 + let result = PlacesUtils.history 1.102 + .executeQueries(queries, queries.length, 1.103 + options); 1.104 + let callback; 1.105 + if (this.flatList) { 1.106 + let onOpenFlatContainer = this.onOpenFlatContainer; 1.107 + if (onOpenFlatContainer) 1.108 + callback = new Function("aContainer", onOpenFlatContainer); 1.109 + } 1.110 + 1.111 + if (!this._controller) { 1.112 + this._controller = new PlacesController(this); 1.113 + this.controllers.appendController(this._controller); 1.114 + } 1.115 + 1.116 + let treeView = new PlacesTreeView(this.flatList, callback, this._controller); 1.117 + 1.118 + // Observer removal is done within the view itself. When the tree 1.119 + // goes away, treeboxobject calls view.setTree(null), which then 1.120 + // calls removeObserver. 1.121 + result.addObserver(treeView, false); 1.122 + this.view = treeView; 1.123 + 1.124 + if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) { 1.125 + treeView.selection.select(0); 1.126 + } 1.127 + 1.128 + this._cachedInsertionPoint = undefined; 1.129 + ]]></body> 1.130 + </method> 1.131 + 1.132 + <property name="flatList"> 1.133 + <getter><![CDATA[ 1.134 + return this.getAttribute("flatList") == "true"; 1.135 + ]]></getter> 1.136 + <setter><![CDATA[ 1.137 + if (this.flatList != val) { 1.138 + this.setAttribute("flatList", val); 1.139 + // reload with the last place set 1.140 + if (this.place) 1.141 + this.place = this.place; 1.142 + } 1.143 + return val; 1.144 + ]]></setter> 1.145 + </property> 1.146 + 1.147 + <property name="onOpenFlatContainer"> 1.148 + <getter><![CDATA[ 1.149 + return this.getAttribute("onopenflatcontainer"); 1.150 + ]]></getter> 1.151 + <setter><![CDATA[ 1.152 + if (this.onOpenFlatContainer != val) { 1.153 + this.setAttribute("onopenflatcontainer", val); 1.154 + // reload with the last place set 1.155 + if (this.place) 1.156 + this.place = this.place; 1.157 + } 1.158 + return val; 1.159 + ]]></setter> 1.160 + </property> 1.161 + 1.162 + <!-- 1.163 + Causes a particular node represented by the specified placeURI to be 1.164 + selected in the tree. All containers above the node in the hierarchy 1.165 + will be opened, so that the node is visible. 1.166 + --> 1.167 + <method name="selectPlaceURI"> 1.168 + <parameter name="placeURI"/> 1.169 + <body><![CDATA[ 1.170 + // Do nothing if a node matching the given uri is already selected 1.171 + if (this.hasSelection && this.selectedNode.uri == placeURI) 1.172 + return; 1.173 + 1.174 + function findNode(container, placeURI, nodesURIChecked) { 1.175 + var containerURI = container.uri; 1.176 + if (containerURI == placeURI) 1.177 + return container; 1.178 + if (nodesURIChecked.indexOf(containerURI) != -1) 1.179 + return null; 1.180 + 1.181 + // never check the contents of the same query 1.182 + nodesURIChecked.push(containerURI); 1.183 + 1.184 + var wasOpen = container.containerOpen; 1.185 + if (!wasOpen) 1.186 + container.containerOpen = true; 1.187 + for (var i = 0; i < container.childCount; ++i) { 1.188 + var child = container.getChild(i); 1.189 + var childURI = child.uri; 1.190 + if (childURI == placeURI) 1.191 + return child; 1.192 + else if (PlacesUtils.nodeIsContainer(child)) { 1.193 + var nested = findNode(PlacesUtils.asContainer(child), placeURI, nodesURIChecked); 1.194 + if (nested) 1.195 + return nested; 1.196 + } 1.197 + } 1.198 + 1.199 + if (!wasOpen) 1.200 + container.containerOpen = false; 1.201 + 1.202 + return null; 1.203 + } 1.204 + 1.205 + var container = this.result.root; 1.206 + NS_ASSERT(container, "No result, cannot select place URI!"); 1.207 + if (!container) 1.208 + return; 1.209 + 1.210 + var child = findNode(container, placeURI, []); 1.211 + if (child) 1.212 + this.selectNode(child); 1.213 + else { 1.214 + // If the specified child could not be located, clear the selection 1.215 + var selection = this.view.selection; 1.216 + selection.clearSelection(); 1.217 + } 1.218 + ]]></body> 1.219 + </method> 1.220 + 1.221 + <!-- 1.222 + Causes a particular node to be selected in the tree, resulting in all 1.223 + containers above the node in the hierarchy to be opened, so that the 1.224 + node is visible. 1.225 + --> 1.226 + <method name="selectNode"> 1.227 + <parameter name="node"/> 1.228 + <body><![CDATA[ 1.229 + var view = this.view; 1.230 + 1.231 + var parent = node.parent; 1.232 + if (parent && !parent.containerOpen) { 1.233 + // Build a list of all of the nodes that are the parent of this one 1.234 + // in the result. 1.235 + var parents = []; 1.236 + var root = this.result.root; 1.237 + while (parent && parent != root) { 1.238 + parents.push(parent); 1.239 + parent = parent.parent; 1.240 + } 1.241 + 1.242 + // Walk the list backwards (opening from the root of the hierarchy) 1.243 + // opening each folder as we go. 1.244 + for (var i = parents.length - 1; i >= 0; --i) { 1.245 + var index = view.treeIndexForNode(parents[i]); 1.246 + if (index != Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE && 1.247 + view.isContainer(index) && !view.isContainerOpen(index)) 1.248 + view.toggleOpenState(index); 1.249 + } 1.250 + // Select the specified node... 1.251 + } 1.252 + 1.253 + var index = view.treeIndexForNode(node); 1.254 + if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE) 1.255 + return; 1.256 + 1.257 + view.selection.select(index); 1.258 + // ... and ensure it's visible, not scrolled off somewhere. 1.259 + this.treeBoxObject.ensureRowIsVisible(index); 1.260 + ]]></body> 1.261 + </method> 1.262 + 1.263 + <!-- nsIPlacesView --> 1.264 + <property name="result"> 1.265 + <getter><![CDATA[ 1.266 + try { 1.267 + return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result; 1.268 + } 1.269 + catch (e) { 1.270 + return null; 1.271 + } 1.272 + ]]></getter> 1.273 + </property> 1.274 + 1.275 + <!-- nsIPlacesView --> 1.276 + <property name="place"> 1.277 + <getter><![CDATA[ 1.278 + return this.getAttribute("place"); 1.279 + ]]></getter> 1.280 + <setter><![CDATA[ 1.281 + this.setAttribute("place", val); 1.282 + 1.283 + var queriesRef = { }; 1.284 + var queryCountRef = { }; 1.285 + var optionsRef = { }; 1.286 + PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef); 1.287 + if (queryCountRef.value == 0) 1.288 + queriesRef.value = [PlacesUtils.history.getNewQuery()]; 1.289 + if (!optionsRef.value) 1.290 + optionsRef.value = PlacesUtils.history.getNewQueryOptions(); 1.291 + 1.292 + this.load(queriesRef.value, optionsRef.value); 1.293 + 1.294 + return val; 1.295 + ]]></setter> 1.296 + </property> 1.297 + 1.298 + <!-- nsIPlacesView --> 1.299 + <property name="hasSelection"> 1.300 + <getter><![CDATA[ 1.301 + return this.view && this.view.selection.count >= 1; 1.302 + ]]></getter> 1.303 + </property> 1.304 + 1.305 + <!-- nsIPlacesView --> 1.306 + <property name="selectedNodes"> 1.307 + <getter><![CDATA[ 1.308 + let nodes = []; 1.309 + if (!this.hasSelection) 1.310 + return nodes; 1.311 + 1.312 + let selection = this.view.selection; 1.313 + let rc = selection.getRangeCount(); 1.314 + let resultview = this.view; 1.315 + for (let i = 0; i < rc; ++i) { 1.316 + let min = { }, max = { }; 1.317 + selection.getRangeAt(i, min, max); 1.318 + for (let j = min.value; j <= max.value; ++j) { 1.319 + nodes.push(resultview.nodeForTreeIndex(j)); 1.320 + } 1.321 + } 1.322 + return nodes; 1.323 + ]]></getter> 1.324 + </property> 1.325 + 1.326 + <method name="toggleCutNode"> 1.327 + <parameter name="aNode"/> 1.328 + <parameter name="aValue"/> 1.329 + <body><![CDATA[ 1.330 + this.view.toggleCutNode(aNode, aValue); 1.331 + ]]></body> 1.332 + </method> 1.333 + 1.334 + <!-- nsIPlacesView --> 1.335 + <property name="removableSelectionRanges"> 1.336 + <getter><![CDATA[ 1.337 + // This property exists in addition to selectedNodes because it 1.338 + // encodes selection ranges (which only occur in list views) into 1.339 + // the return value. For each removed range, the index at which items 1.340 + // will be re-inserted upon the remove transaction being performed is 1.341 + // the first index of the range, so that the view updates correctly. 1.342 + // 1.343 + // For example, if we remove rows 2,3,4 and 7,8 from a list, when we 1.344 + // undo that operation, if we insert what was at row 3 at row 3 again, 1.345 + // it will show up _after_ the item that was at row 5. So we need to 1.346 + // insert all items at row 2, and the tree view will update correctly. 1.347 + // 1.348 + // Also, this function collapses the selection to remove redundant 1.349 + // data, e.g. when deleting this selection: 1.350 + // 1.351 + // http://www.foo.com/ 1.352 + // (-) Some Folder 1.353 + // http://www.bar.com/ 1.354 + // 1.355 + // ... returning http://www.bar.com/ as part of the selection is 1.356 + // redundant because it is implied by removing "Some Folder". We 1.357 + // filter out all such redundancies since some partial amount of 1.358 + // the folder's children may be selected. 1.359 + // 1.360 + let nodes = []; 1.361 + if (!this.hasSelection) 1.362 + return nodes; 1.363 + 1.364 + var selection = this.view.selection; 1.365 + var rc = selection.getRangeCount(); 1.366 + var resultview = this.view; 1.367 + // This list is kept independently of the range selected (i.e. OUTSIDE 1.368 + // the for loop) since the row index of a container is unique for the 1.369 + // entire view, and we could have some really wacky selection and we 1.370 + // don't want to blow up. 1.371 + var containers = { }; 1.372 + for (var i = 0; i < rc; ++i) { 1.373 + var range = []; 1.374 + var min = { }, max = { }; 1.375 + selection.getRangeAt(i, min, max); 1.376 + 1.377 + for (var j = min.value; j <= max.value; ++j) { 1.378 + if (this.view.isContainer(j)) 1.379 + containers[j] = true; 1.380 + if (!(this.view.getParentIndex(j) in containers)) 1.381 + range.push(resultview.nodeForTreeIndex(j)); 1.382 + } 1.383 + nodes.push(range); 1.384 + } 1.385 + return nodes; 1.386 + ]]></getter> 1.387 + </property> 1.388 + 1.389 + <!-- nsIPlacesView --> 1.390 + <property name="draggableSelection" 1.391 + onget="return this.selectedNodes"/> 1.392 + 1.393 + <!-- nsIPlacesView --> 1.394 + <property name="selectedNode"> 1.395 + <getter><![CDATA[ 1.396 + var view = this.view; 1.397 + if (!view || view.selection.count != 1) 1.398 + return null; 1.399 + 1.400 + var selection = view.selection; 1.401 + var min = { }, max = { }; 1.402 + selection.getRangeAt(0, min, max); 1.403 + 1.404 + return this.view.nodeForTreeIndex(min.value); 1.405 + ]]></getter> 1.406 + </property> 1.407 + 1.408 + <!-- nsIPlacesView --> 1.409 + <property name="insertionPoint"> 1.410 + <getter><![CDATA[ 1.411 + // invalidated on selection and focus changes 1.412 + if (this._cachedInsertionPoint !== undefined) 1.413 + return this._cachedInsertionPoint; 1.414 + 1.415 + // there is no insertion point for history queries 1.416 + // so bail out now and save a lot of work when updating commands 1.417 + var resultNode = this.result.root; 1.418 + if (PlacesUtils.nodeIsQuery(resultNode) && 1.419 + PlacesUtils.asQuery(resultNode).queryOptions.queryType == 1.420 + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) 1.421 + return this._cachedInsertionPoint = null; 1.422 + 1.423 + var orientation = Ci.nsITreeView.DROP_BEFORE; 1.424 + // If there is no selection, insert at the end of the container. 1.425 + if (!this.hasSelection) { 1.426 + var index = this.view.rowCount - 1; 1.427 + this._cachedInsertionPoint = 1.428 + this._getInsertionPoint(index, orientation); 1.429 + return this._cachedInsertionPoint; 1.430 + } 1.431 + 1.432 + // This is a two-part process. The first part is determining the drop 1.433 + // orientation. 1.434 + // * The default orientation is to drop _before_ the selected item. 1.435 + // * If the selected item is a container, the default orientation 1.436 + // is to drop _into_ that container. 1.437 + // 1.438 + // Warning: It may be tempting to use tree indexes in this code, but 1.439 + // you must not, since the tree is nested and as your tree 1.440 + // index may change when folders before you are opened and 1.441 + // closed. You must convert your tree index to a node, and 1.442 + // then use getChildIndex to find your absolute index in 1.443 + // the parent container instead. 1.444 + // 1.445 + var resultView = this.view; 1.446 + var selection = resultView.selection; 1.447 + var rc = selection.getRangeCount(); 1.448 + var min = { }, max = { }; 1.449 + selection.getRangeAt(rc - 1, min, max); 1.450 + 1.451 + // If the sole selection is a container, and we are not in 1.452 + // a flatlist, insert into it. 1.453 + // Note that this only applies to _single_ selections, 1.454 + // if the last element within a multi-selection is a 1.455 + // container, insert _adjacent_ to the selection. 1.456 + // 1.457 + // If the sole selection is the bookmarks toolbar folder, we insert 1.458 + // into it even if it is not opened 1.459 + if (selection.count == 1 && resultView.isContainer(max.value) && 1.460 + !this.flatList) 1.461 + orientation = Ci.nsITreeView.DROP_ON; 1.462 + 1.463 + this._cachedInsertionPoint = 1.464 + this._getInsertionPoint(max.value, orientation); 1.465 + return this._cachedInsertionPoint; 1.466 + ]]></getter> 1.467 + </property> 1.468 + 1.469 + <method name="_getInsertionPoint"> 1.470 + <parameter name="index"/> 1.471 + <parameter name="orientation"/> 1.472 + <body><![CDATA[ 1.473 + var result = this.result; 1.474 + var resultview = this.view; 1.475 + var container = result.root; 1.476 + var dropNearItemId = -1; 1.477 + NS_ASSERT(container, "null container"); 1.478 + // When there's no selection, assume the container is the container 1.479 + // the view is populated from (i.e. the result's itemId). 1.480 + if (index != -1) { 1.481 + var lastSelected = resultview.nodeForTreeIndex(index); 1.482 + if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) { 1.483 + // If the last selected item is an open container, append _into_ 1.484 + // it, rather than insert adjacent to it. 1.485 + container = lastSelected; 1.486 + index = -1; 1.487 + } 1.488 + else if (lastSelected.containerOpen && 1.489 + orientation == Ci.nsITreeView.DROP_AFTER && 1.490 + lastSelected.hasChildren) { 1.491 + // If the last selected item is an open container and the user is 1.492 + // trying to drag into it as a first item, really insert into it. 1.493 + container = lastSelected; 1.494 + orientation = Ci.nsITreeView.DROP_ON; 1.495 + index = 0; 1.496 + } 1.497 + else { 1.498 + // Use the last-selected node's container. 1.499 + container = lastSelected.parent; 1.500 + 1.501 + // See comment in the treeView.js's copy of this method 1.502 + if (!container || !container.containerOpen) 1.503 + return null; 1.504 + 1.505 + // Avoid the potentially expensive call to getChildIndex 1.506 + // if we know this container doesn't allow insertion 1.507 + if (PlacesControllerDragHelper.disallowInsertion(container)) 1.508 + return null; 1.509 + 1.510 + var queryOptions = PlacesUtils.asQuery(result.root).queryOptions; 1.511 + if (queryOptions.sortingMode != 1.512 + Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) { 1.513 + // If we are within a sorted view, insert at the end 1.514 + index = -1; 1.515 + } 1.516 + else if (queryOptions.excludeItems || 1.517 + queryOptions.excludeQueries || 1.518 + queryOptions.excludeReadOnlyFolders) { 1.519 + // Some item may be invisible, insert near last selected one. 1.520 + // We don't replace index here to avoid requests to the db, 1.521 + // instead it will be calculated later by the controller. 1.522 + index = -1; 1.523 + dropNearItemId = lastSelected.itemId; 1.524 + } 1.525 + else { 1.526 + var lsi = container.getChildIndex(lastSelected); 1.527 + index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1; 1.528 + } 1.529 + } 1.530 + } 1.531 + 1.532 + if (PlacesControllerDragHelper.disallowInsertion(container)) 1.533 + return null; 1.534 + 1.535 + return new InsertionPoint(PlacesUtils.getConcreteItemId(container), 1.536 + index, orientation, 1.537 + PlacesUtils.nodeIsTagQuery(container), 1.538 + dropNearItemId); 1.539 + ]]></body> 1.540 + </method> 1.541 + 1.542 + <!-- nsIPlacesView --> 1.543 + <method name="selectAll"> 1.544 + <body><![CDATA[ 1.545 + this.view.selection.selectAll(); 1.546 + ]]></body> 1.547 + </method> 1.548 + 1.549 + <!-- This method will select the first node in the tree that matches 1.550 + each given item id. It will open any folder nodes that it needs 1.551 + to in order to show the selected items. 1.552 + --> 1.553 + <method name="selectItems"> 1.554 + <parameter name="aIDs"/> 1.555 + <parameter name="aOpenContainers"/> 1.556 + <body><![CDATA[ 1.557 + // Never open containers in flat lists. 1.558 + if (this.flatList) 1.559 + aOpenContainers = false; 1.560 + // By default, we do search and select within containers which were 1.561 + // closed (note that containers in which nodes were not found are 1.562 + // closed). 1.563 + if (aOpenContainers === undefined) 1.564 + aOpenContainers = true; 1.565 + 1.566 + var ids = aIDs; // don't manipulate the caller's array 1.567 + 1.568 + // Array of nodes found by findNodes which are to be selected 1.569 + var nodes = []; 1.570 + 1.571 + // Array of nodes found by findNodes which should be opened 1.572 + var nodesToOpen = []; 1.573 + 1.574 + // A set of URIs of container-nodes that were previously searched, 1.575 + // and thus shouldn't be searched again. This is empty at the initial 1.576 + // start of the recursion and gets filled in as the recursion 1.577 + // progresses. 1.578 + var nodesURIChecked = []; 1.579 + 1.580 + /** 1.581 + * Recursively search through a node's children for items 1.582 + * with the given IDs. When a matching item is found, remove its ID 1.583 + * from the IDs array, and add the found node to the nodes dictionary. 1.584 + * 1.585 + * NOTE: This method will leave open any node that had matching items 1.586 + * in its subtree. 1.587 + */ 1.588 + function findNodes(node) { 1.589 + var foundOne = false; 1.590 + // See if node matches an ID we wanted; add to results. 1.591 + // For simple folder queries, check both itemId and the concrete 1.592 + // item id. 1.593 + var index = ids.indexOf(node.itemId); 1.594 + if (index == -1 && 1.595 + node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) 1.596 + index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId); 1.597 + 1.598 + if (index != -1) { 1.599 + nodes.push(node); 1.600 + foundOne = true; 1.601 + ids.splice(index, 1); 1.602 + } 1.603 + 1.604 + if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) || 1.605 + nodesURIChecked.indexOf(node.uri) != -1) 1.606 + return foundOne; 1.607 + 1.608 + // Don't try to open a query or a shurtcut, since it may return 1.609 + // any duplicate data and be infinitely nested. Though, if it has 1.610 + // been explicitly opened by the caller, search into it. 1.611 + let shouldOpen = aOpenContainers && 1.612 + node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER; 1.613 + PlacesUtils.asContainer(node); 1.614 + if (!node.containerOpen && !shouldOpen) 1.615 + return foundOne; 1.616 + 1.617 + nodesURIChecked.push(node.uri); 1.618 + 1.619 + // Remember the beginning state so that we can re-close 1.620 + // this node if we don't find any additional results here. 1.621 + var previousOpenness = node.containerOpen; 1.622 + node.containerOpen = true; 1.623 + for (var child = 0; child < node.childCount && ids.length > 0; 1.624 + child++) { 1.625 + var childNode = node.getChild(child); 1.626 + var found = findNodes(childNode); 1.627 + if (!foundOne) 1.628 + foundOne = found; 1.629 + } 1.630 + 1.631 + // If we didn't find any additional matches in this node's 1.632 + // subtree, revert the node to its previous openness. 1.633 + if (foundOne) 1.634 + nodesToOpen.unshift(node); 1.635 + node.containerOpen = previousOpenness; 1.636 + return foundOne; 1.637 + } 1.638 + 1.639 + // Disable notifications while looking for nodes. 1.640 + let result = this.result; 1.641 + let didSuppressNotifications = result.suppressNotifications; 1.642 + if (!didSuppressNotifications) 1.643 + result.suppressNotifications = true 1.644 + try { 1.645 + findNodes(this.result.root); 1.646 + } 1.647 + finally { 1.648 + if (!didSuppressNotifications) 1.649 + result.suppressNotifications = false; 1.650 + } 1.651 + 1.652 + // For all the nodes we've found, highlight the corresponding 1.653 + // index in the tree. 1.654 + var resultview = this.view; 1.655 + var selection = this.view.selection; 1.656 + selection.selectEventsSuppressed = true; 1.657 + selection.clearSelection(); 1.658 + // Open nodes containing found items 1.659 + for (var i = 0; i < nodesToOpen.length; i++) { 1.660 + nodesToOpen[i].containerOpen = true; 1.661 + } 1.662 + for (var i = 0; i < nodes.length; i++) { 1.663 + var index = resultview.treeIndexForNode(nodes[i]); 1.664 + if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE) 1.665 + continue; 1.666 + selection.rangedSelect(index, index, true); 1.667 + } 1.668 + selection.selectEventsSuppressed = false; 1.669 + ]]></body> 1.670 + </method> 1.671 + 1.672 + <field name="_contextMenuShown">false</field> 1.673 + 1.674 + <method name="buildContextMenu"> 1.675 + <parameter name="aPopup"/> 1.676 + <body><![CDATA[ 1.677 + this._contextMenuShown = true; 1.678 + return this.controller.buildContextMenu(aPopup); 1.679 + ]]></body> 1.680 + </method> 1.681 + 1.682 + <method name="destroyContextMenu"> 1.683 + <parameter name="aPopup"/> 1.684 + this._contextMenuShown = false; 1.685 + <body/> 1.686 + </method> 1.687 + 1.688 + <property name="ownerWindow" 1.689 + readonly="true" 1.690 + onget="return window;"/> 1.691 + 1.692 + <field name="_active">true</field> 1.693 + <property name="active" 1.694 + onget="return this._active" 1.695 + onset="return this._active = val"/> 1.696 + 1.697 + </implementation> 1.698 + <handlers> 1.699 + <handler event="focus"><![CDATA[ 1.700 + this._cachedInsertionPoint = undefined; 1.701 + 1.702 + // See select handler. We need the sidebar's places commandset to be 1.703 + // updated as well 1.704 + document.commandDispatcher.updateCommands("focus"); 1.705 + ]]></handler> 1.706 + <handler event="select"><![CDATA[ 1.707 + this._cachedInsertionPoint = undefined; 1.708 + 1.709 + // This additional complexity is here for the sidebars 1.710 + var win = window; 1.711 + while (true) { 1.712 + win.document.commandDispatcher.updateCommands("focus"); 1.713 + if (win == window.top) 1.714 + break; 1.715 + 1.716 + win = win.parent; 1.717 + } 1.718 + ]]></handler> 1.719 + 1.720 + <handler event="dragstart"><![CDATA[ 1.721 + if (event.target.localName != "treechildren") 1.722 + return; 1.723 + 1.724 + let nodes = this.selectedNodes; 1.725 + for (let i = 0; i < nodes.length; i++) { 1.726 + let node = nodes[i]; 1.727 + 1.728 + // Disallow dragging the root node of a tree. 1.729 + if (!node.parent) { 1.730 + event.preventDefault(); 1.731 + event.stopPropagation(); 1.732 + return; 1.733 + } 1.734 + 1.735 + // If this node is child of a readonly container (e.g. a livemark) 1.736 + // or cannot be moved, we must force a copy. 1.737 + if (!PlacesControllerDragHelper.canMoveNode(node)) { 1.738 + event.dataTransfer.effectAllowed = "copyLink"; 1.739 + break; 1.740 + } 1.741 + } 1.742 + 1.743 + this._controller.setDataTransfer(event); 1.744 + event.stopPropagation(); 1.745 + ]]></handler> 1.746 + 1.747 + <handler event="dragover"><![CDATA[ 1.748 + if (event.target.localName != "treechildren") 1.749 + return; 1.750 + 1.751 + let row = { }, col = { }, child = { }; 1.752 + this.treeBoxObject.getCellAt(event.clientX, event.clientY, 1.753 + row, col, child); 1.754 + let node = row.value != -1 ? 1.755 + this.view.nodeForTreeIndex(row.value) : 1.756 + this.result.root; 1.757 + // cache the dropTarget for the view 1.758 + PlacesControllerDragHelper.currentDropTarget = node; 1.759 + 1.760 + // We have to calculate the orientation since view.canDrop will use 1.761 + // it and we want to be consistent with the dropfeedback. 1.762 + let tbo = this.treeBoxObject; 1.763 + let rowHeight = tbo.rowHeight; 1.764 + let eventY = event.clientY - tbo.treeBody.boxObject.y - 1.765 + rowHeight * (row.value - tbo.getFirstVisibleRow()); 1.766 + 1.767 + let orientation = Ci.nsITreeView.DROP_BEFORE; 1.768 + 1.769 + if (row.value == -1) { 1.770 + // If the row is not valid we try to insert inside the resultNode. 1.771 + orientation = Ci.nsITreeView.DROP_ON; 1.772 + } 1.773 + else if (PlacesUtils.nodeIsContainer(node) && 1.774 + eventY > rowHeight * 0.75) { 1.775 + // If we are below the 75% of a container the treeview we try 1.776 + // to drop after the node. 1.777 + orientation = Ci.nsITreeView.DROP_AFTER; 1.778 + } 1.779 + else if (PlacesUtils.nodeIsContainer(node) && 1.780 + eventY > rowHeight * 0.25) { 1.781 + // If we are below the 25% of a container the treeview we try 1.782 + // to drop inside the node. 1.783 + orientation = Ci.nsITreeView.DROP_ON; 1.784 + } 1.785 + 1.786 + if (!this.view.canDrop(row.value, orientation, event.dataTransfer)) 1.787 + return; 1.788 + 1.789 + event.preventDefault(); 1.790 + event.stopPropagation(); 1.791 + ]]></handler> 1.792 + 1.793 + <handler event="dragend"><![CDATA[ 1.794 + PlacesControllerDragHelper.currentDropTarget = null; 1.795 + ]]></handler> 1.796 + 1.797 + </handlers> 1.798 + </binding> 1.799 + 1.800 +</bindings>