browser/components/places/content/tree.xml

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 <?xml version="1.0"?>
michael@0 2
michael@0 3 <!-- This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 - License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
michael@0 6
michael@0 7 <bindings id="placesTreeBindings"
michael@0 8 xmlns="http://www.mozilla.org/xbl"
michael@0 9 xmlns:xbl="http://www.mozilla.org/xbl"
michael@0 10 xmlns:html="http://www.w3.org/1999/xhtml"
michael@0 11 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
michael@0 12
michael@0 13 <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree">
michael@0 14 <implementation>
michael@0 15 <constructor><![CDATA[
michael@0 16 // Force an initial build.
michael@0 17 if (this.place)
michael@0 18 this.place = this.place;
michael@0 19 ]]></constructor>
michael@0 20
michael@0 21 <destructor><![CDATA[
michael@0 22 // Break the treeviewer->result->treeviewer cycle.
michael@0 23 // Note: unsetting the result's viewer also unsets
michael@0 24 // the viewer's reference to our treeBoxObject.
michael@0 25 var result = this.result;
michael@0 26 if (result) {
michael@0 27 result.root.containerOpen = false;
michael@0 28 }
michael@0 29
michael@0 30 // Unregister the controllber before unlinking the view, otherwise it
michael@0 31 // may still try to update commands on a view with a null result.
michael@0 32 if (this._controller) {
michael@0 33 this._controller.terminate();
michael@0 34 this.controllers.removeController(this._controller);
michael@0 35 }
michael@0 36
michael@0 37 this.view = null;
michael@0 38 ]]></destructor>
michael@0 39
michael@0 40 <property name="controller"
michael@0 41 readonly="true"
michael@0 42 onget="return this._controller"/>
michael@0 43
michael@0 44 <!-- overriding -->
michael@0 45 <property name="view">
michael@0 46 <getter><![CDATA[
michael@0 47 try {
michael@0 48 return this.treeBoxObject.view.wrappedJSObject || null;
michael@0 49 }
michael@0 50 catch(e) {
michael@0 51 return null;
michael@0 52 }
michael@0 53 ]]></getter>
michael@0 54 <setter><![CDATA[
michael@0 55 return this.treeBoxObject.view = val;
michael@0 56 ]]></setter>
michael@0 57 </property>
michael@0 58
michael@0 59 <property name="associatedElement"
michael@0 60 readonly="true"
michael@0 61 onget="return this"/>
michael@0 62
michael@0 63 <method name="applyFilter">
michael@0 64 <parameter name="filterString"/>
michael@0 65 <parameter name="folderRestrict"/>
michael@0 66 <parameter name="includeHidden"/>
michael@0 67 <body><![CDATA[
michael@0 68 // preserve grouping
michael@0 69 var queryNode = PlacesUtils.asQuery(this.result.root);
michael@0 70 var options = queryNode.queryOptions.clone();
michael@0 71
michael@0 72 // Make sure we're getting uri results.
michael@0 73 // We do not yet support searching into grouped queries or into
michael@0 74 // tag containers, so we must fall to the default case.
michael@0 75 if (PlacesUtils.nodeIsHistoryContainer(queryNode) ||
michael@0 76 options.resultType == options.RESULTS_AS_TAG_QUERY ||
michael@0 77 options.resultType == options.RESULTS_AS_TAG_CONTENTS)
michael@0 78 options.resultType = options.RESULTS_AS_URI;
michael@0 79
michael@0 80 var query = PlacesUtils.history.getNewQuery();
michael@0 81 query.searchTerms = filterString;
michael@0 82
michael@0 83 if (folderRestrict) {
michael@0 84 query.setFolders(folderRestrict, folderRestrict.length);
michael@0 85 options.queryType = options.QUERY_TYPE_BOOKMARKS;
michael@0 86 }
michael@0 87
michael@0 88 options.includeHidden = !!includeHidden;
michael@0 89
michael@0 90 this.load([query], options);
michael@0 91 ]]></body>
michael@0 92 </method>
michael@0 93
michael@0 94 <method name="load">
michael@0 95 <parameter name="queries"/>
michael@0 96 <parameter name="options"/>
michael@0 97 <body><![CDATA[
michael@0 98 let result = PlacesUtils.history
michael@0 99 .executeQueries(queries, queries.length,
michael@0 100 options);
michael@0 101 let callback;
michael@0 102 if (this.flatList) {
michael@0 103 let onOpenFlatContainer = this.onOpenFlatContainer;
michael@0 104 if (onOpenFlatContainer)
michael@0 105 callback = new Function("aContainer", onOpenFlatContainer);
michael@0 106 }
michael@0 107
michael@0 108 if (!this._controller) {
michael@0 109 this._controller = new PlacesController(this);
michael@0 110 this.controllers.appendController(this._controller);
michael@0 111 }
michael@0 112
michael@0 113 let treeView = new PlacesTreeView(this.flatList, callback, this._controller);
michael@0 114
michael@0 115 // Observer removal is done within the view itself. When the tree
michael@0 116 // goes away, treeboxobject calls view.setTree(null), which then
michael@0 117 // calls removeObserver.
michael@0 118 result.addObserver(treeView, false);
michael@0 119 this.view = treeView;
michael@0 120
michael@0 121 if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) {
michael@0 122 treeView.selection.select(0);
michael@0 123 }
michael@0 124
michael@0 125 this._cachedInsertionPoint = undefined;
michael@0 126 ]]></body>
michael@0 127 </method>
michael@0 128
michael@0 129 <property name="flatList">
michael@0 130 <getter><![CDATA[
michael@0 131 return this.getAttribute("flatList") == "true";
michael@0 132 ]]></getter>
michael@0 133 <setter><![CDATA[
michael@0 134 if (this.flatList != val) {
michael@0 135 this.setAttribute("flatList", val);
michael@0 136 // reload with the last place set
michael@0 137 if (this.place)
michael@0 138 this.place = this.place;
michael@0 139 }
michael@0 140 return val;
michael@0 141 ]]></setter>
michael@0 142 </property>
michael@0 143
michael@0 144 <property name="onOpenFlatContainer">
michael@0 145 <getter><![CDATA[
michael@0 146 return this.getAttribute("onopenflatcontainer");
michael@0 147 ]]></getter>
michael@0 148 <setter><![CDATA[
michael@0 149 if (this.onOpenFlatContainer != val) {
michael@0 150 this.setAttribute("onopenflatcontainer", val);
michael@0 151 // reload with the last place set
michael@0 152 if (this.place)
michael@0 153 this.place = this.place;
michael@0 154 }
michael@0 155 return val;
michael@0 156 ]]></setter>
michael@0 157 </property>
michael@0 158
michael@0 159 <!--
michael@0 160 Causes a particular node represented by the specified placeURI to be
michael@0 161 selected in the tree. All containers above the node in the hierarchy
michael@0 162 will be opened, so that the node is visible.
michael@0 163 -->
michael@0 164 <method name="selectPlaceURI">
michael@0 165 <parameter name="placeURI"/>
michael@0 166 <body><![CDATA[
michael@0 167 // Do nothing if a node matching the given uri is already selected
michael@0 168 if (this.hasSelection && this.selectedNode.uri == placeURI)
michael@0 169 return;
michael@0 170
michael@0 171 function findNode(container, placeURI, nodesURIChecked) {
michael@0 172 var containerURI = container.uri;
michael@0 173 if (containerURI == placeURI)
michael@0 174 return container;
michael@0 175 if (nodesURIChecked.indexOf(containerURI) != -1)
michael@0 176 return null;
michael@0 177
michael@0 178 // never check the contents of the same query
michael@0 179 nodesURIChecked.push(containerURI);
michael@0 180
michael@0 181 var wasOpen = container.containerOpen;
michael@0 182 if (!wasOpen)
michael@0 183 container.containerOpen = true;
michael@0 184 for (var i = 0; i < container.childCount; ++i) {
michael@0 185 var child = container.getChild(i);
michael@0 186 var childURI = child.uri;
michael@0 187 if (childURI == placeURI)
michael@0 188 return child;
michael@0 189 else if (PlacesUtils.nodeIsContainer(child)) {
michael@0 190 var nested = findNode(PlacesUtils.asContainer(child), placeURI, nodesURIChecked);
michael@0 191 if (nested)
michael@0 192 return nested;
michael@0 193 }
michael@0 194 }
michael@0 195
michael@0 196 if (!wasOpen)
michael@0 197 container.containerOpen = false;
michael@0 198
michael@0 199 return null;
michael@0 200 }
michael@0 201
michael@0 202 var container = this.result.root;
michael@0 203 NS_ASSERT(container, "No result, cannot select place URI!");
michael@0 204 if (!container)
michael@0 205 return;
michael@0 206
michael@0 207 var child = findNode(container, placeURI, []);
michael@0 208 if (child)
michael@0 209 this.selectNode(child);
michael@0 210 else {
michael@0 211 // If the specified child could not be located, clear the selection
michael@0 212 var selection = this.view.selection;
michael@0 213 selection.clearSelection();
michael@0 214 }
michael@0 215 ]]></body>
michael@0 216 </method>
michael@0 217
michael@0 218 <!--
michael@0 219 Causes a particular node to be selected in the tree, resulting in all
michael@0 220 containers above the node in the hierarchy to be opened, so that the
michael@0 221 node is visible.
michael@0 222 -->
michael@0 223 <method name="selectNode">
michael@0 224 <parameter name="node"/>
michael@0 225 <body><![CDATA[
michael@0 226 var view = this.view;
michael@0 227
michael@0 228 var parent = node.parent;
michael@0 229 if (parent && !parent.containerOpen) {
michael@0 230 // Build a list of all of the nodes that are the parent of this one
michael@0 231 // in the result.
michael@0 232 var parents = [];
michael@0 233 var root = this.result.root;
michael@0 234 while (parent && parent != root) {
michael@0 235 parents.push(parent);
michael@0 236 parent = parent.parent;
michael@0 237 }
michael@0 238
michael@0 239 // Walk the list backwards (opening from the root of the hierarchy)
michael@0 240 // opening each folder as we go.
michael@0 241 for (var i = parents.length - 1; i >= 0; --i) {
michael@0 242 var index = view.treeIndexForNode(parents[i]);
michael@0 243 if (index != Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE &&
michael@0 244 view.isContainer(index) && !view.isContainerOpen(index))
michael@0 245 view.toggleOpenState(index);
michael@0 246 }
michael@0 247 // Select the specified node...
michael@0 248 }
michael@0 249
michael@0 250 var index = view.treeIndexForNode(node);
michael@0 251 if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
michael@0 252 return;
michael@0 253
michael@0 254 view.selection.select(index);
michael@0 255 // ... and ensure it's visible, not scrolled off somewhere.
michael@0 256 this.treeBoxObject.ensureRowIsVisible(index);
michael@0 257 ]]></body>
michael@0 258 </method>
michael@0 259
michael@0 260 <!-- nsIPlacesView -->
michael@0 261 <property name="result">
michael@0 262 <getter><![CDATA[
michael@0 263 try {
michael@0 264 return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
michael@0 265 }
michael@0 266 catch (e) {
michael@0 267 return null;
michael@0 268 }
michael@0 269 ]]></getter>
michael@0 270 </property>
michael@0 271
michael@0 272 <!-- nsIPlacesView -->
michael@0 273 <property name="place">
michael@0 274 <getter><![CDATA[
michael@0 275 return this.getAttribute("place");
michael@0 276 ]]></getter>
michael@0 277 <setter><![CDATA[
michael@0 278 this.setAttribute("place", val);
michael@0 279
michael@0 280 var queriesRef = { };
michael@0 281 var queryCountRef = { };
michael@0 282 var optionsRef = { };
michael@0 283 PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
michael@0 284 if (queryCountRef.value == 0)
michael@0 285 queriesRef.value = [PlacesUtils.history.getNewQuery()];
michael@0 286 if (!optionsRef.value)
michael@0 287 optionsRef.value = PlacesUtils.history.getNewQueryOptions();
michael@0 288
michael@0 289 this.load(queriesRef.value, optionsRef.value);
michael@0 290
michael@0 291 return val;
michael@0 292 ]]></setter>
michael@0 293 </property>
michael@0 294
michael@0 295 <!-- nsIPlacesView -->
michael@0 296 <property name="hasSelection">
michael@0 297 <getter><![CDATA[
michael@0 298 return this.view && this.view.selection.count >= 1;
michael@0 299 ]]></getter>
michael@0 300 </property>
michael@0 301
michael@0 302 <!-- nsIPlacesView -->
michael@0 303 <property name="selectedNodes">
michael@0 304 <getter><![CDATA[
michael@0 305 let nodes = [];
michael@0 306 if (!this.hasSelection)
michael@0 307 return nodes;
michael@0 308
michael@0 309 let selection = this.view.selection;
michael@0 310 let rc = selection.getRangeCount();
michael@0 311 let resultview = this.view;
michael@0 312 for (let i = 0; i < rc; ++i) {
michael@0 313 let min = { }, max = { };
michael@0 314 selection.getRangeAt(i, min, max);
michael@0 315 for (let j = min.value; j <= max.value; ++j) {
michael@0 316 nodes.push(resultview.nodeForTreeIndex(j));
michael@0 317 }
michael@0 318 }
michael@0 319 return nodes;
michael@0 320 ]]></getter>
michael@0 321 </property>
michael@0 322
michael@0 323 <method name="toggleCutNode">
michael@0 324 <parameter name="aNode"/>
michael@0 325 <parameter name="aValue"/>
michael@0 326 <body><![CDATA[
michael@0 327 this.view.toggleCutNode(aNode, aValue);
michael@0 328 ]]></body>
michael@0 329 </method>
michael@0 330
michael@0 331 <!-- nsIPlacesView -->
michael@0 332 <property name="removableSelectionRanges">
michael@0 333 <getter><![CDATA[
michael@0 334 // This property exists in addition to selectedNodes because it
michael@0 335 // encodes selection ranges (which only occur in list views) into
michael@0 336 // the return value. For each removed range, the index at which items
michael@0 337 // will be re-inserted upon the remove transaction being performed is
michael@0 338 // the first index of the range, so that the view updates correctly.
michael@0 339 //
michael@0 340 // For example, if we remove rows 2,3,4 and 7,8 from a list, when we
michael@0 341 // undo that operation, if we insert what was at row 3 at row 3 again,
michael@0 342 // it will show up _after_ the item that was at row 5. So we need to
michael@0 343 // insert all items at row 2, and the tree view will update correctly.
michael@0 344 //
michael@0 345 // Also, this function collapses the selection to remove redundant
michael@0 346 // data, e.g. when deleting this selection:
michael@0 347 //
michael@0 348 // http://www.foo.com/
michael@0 349 // (-) Some Folder
michael@0 350 // http://www.bar.com/
michael@0 351 //
michael@0 352 // ... returning http://www.bar.com/ as part of the selection is
michael@0 353 // redundant because it is implied by removing "Some Folder". We
michael@0 354 // filter out all such redundancies since some partial amount of
michael@0 355 // the folder's children may be selected.
michael@0 356 //
michael@0 357 let nodes = [];
michael@0 358 if (!this.hasSelection)
michael@0 359 return nodes;
michael@0 360
michael@0 361 var selection = this.view.selection;
michael@0 362 var rc = selection.getRangeCount();
michael@0 363 var resultview = this.view;
michael@0 364 // This list is kept independently of the range selected (i.e. OUTSIDE
michael@0 365 // the for loop) since the row index of a container is unique for the
michael@0 366 // entire view, and we could have some really wacky selection and we
michael@0 367 // don't want to blow up.
michael@0 368 var containers = { };
michael@0 369 for (var i = 0; i < rc; ++i) {
michael@0 370 var range = [];
michael@0 371 var min = { }, max = { };
michael@0 372 selection.getRangeAt(i, min, max);
michael@0 373
michael@0 374 for (var j = min.value; j <= max.value; ++j) {
michael@0 375 if (this.view.isContainer(j))
michael@0 376 containers[j] = true;
michael@0 377 if (!(this.view.getParentIndex(j) in containers))
michael@0 378 range.push(resultview.nodeForTreeIndex(j));
michael@0 379 }
michael@0 380 nodes.push(range);
michael@0 381 }
michael@0 382 return nodes;
michael@0 383 ]]></getter>
michael@0 384 </property>
michael@0 385
michael@0 386 <!-- nsIPlacesView -->
michael@0 387 <property name="draggableSelection"
michael@0 388 onget="return this.selectedNodes"/>
michael@0 389
michael@0 390 <!-- nsIPlacesView -->
michael@0 391 <property name="selectedNode">
michael@0 392 <getter><![CDATA[
michael@0 393 var view = this.view;
michael@0 394 if (!view || view.selection.count != 1)
michael@0 395 return null;
michael@0 396
michael@0 397 var selection = view.selection;
michael@0 398 var min = { }, max = { };
michael@0 399 selection.getRangeAt(0, min, max);
michael@0 400
michael@0 401 return this.view.nodeForTreeIndex(min.value);
michael@0 402 ]]></getter>
michael@0 403 </property>
michael@0 404
michael@0 405 <!-- nsIPlacesView -->
michael@0 406 <property name="insertionPoint">
michael@0 407 <getter><![CDATA[
michael@0 408 // invalidated on selection and focus changes
michael@0 409 if (this._cachedInsertionPoint !== undefined)
michael@0 410 return this._cachedInsertionPoint;
michael@0 411
michael@0 412 // there is no insertion point for history queries
michael@0 413 // so bail out now and save a lot of work when updating commands
michael@0 414 var resultNode = this.result.root;
michael@0 415 if (PlacesUtils.nodeIsQuery(resultNode) &&
michael@0 416 PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
michael@0 417 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
michael@0 418 return this._cachedInsertionPoint = null;
michael@0 419
michael@0 420 var orientation = Ci.nsITreeView.DROP_BEFORE;
michael@0 421 // If there is no selection, insert at the end of the container.
michael@0 422 if (!this.hasSelection) {
michael@0 423 var index = this.view.rowCount - 1;
michael@0 424 this._cachedInsertionPoint =
michael@0 425 this._getInsertionPoint(index, orientation);
michael@0 426 return this._cachedInsertionPoint;
michael@0 427 }
michael@0 428
michael@0 429 // This is a two-part process. The first part is determining the drop
michael@0 430 // orientation.
michael@0 431 // * The default orientation is to drop _before_ the selected item.
michael@0 432 // * If the selected item is a container, the default orientation
michael@0 433 // is to drop _into_ that container.
michael@0 434 //
michael@0 435 // Warning: It may be tempting to use tree indexes in this code, but
michael@0 436 // you must not, since the tree is nested and as your tree
michael@0 437 // index may change when folders before you are opened and
michael@0 438 // closed. You must convert your tree index to a node, and
michael@0 439 // then use getChildIndex to find your absolute index in
michael@0 440 // the parent container instead.
michael@0 441 //
michael@0 442 var resultView = this.view;
michael@0 443 var selection = resultView.selection;
michael@0 444 var rc = selection.getRangeCount();
michael@0 445 var min = { }, max = { };
michael@0 446 selection.getRangeAt(rc - 1, min, max);
michael@0 447
michael@0 448 // If the sole selection is a container, and we are not in
michael@0 449 // a flatlist, insert into it.
michael@0 450 // Note that this only applies to _single_ selections,
michael@0 451 // if the last element within a multi-selection is a
michael@0 452 // container, insert _adjacent_ to the selection.
michael@0 453 //
michael@0 454 // If the sole selection is the bookmarks toolbar folder, we insert
michael@0 455 // into it even if it is not opened
michael@0 456 if (selection.count == 1 && resultView.isContainer(max.value) &&
michael@0 457 !this.flatList)
michael@0 458 orientation = Ci.nsITreeView.DROP_ON;
michael@0 459
michael@0 460 this._cachedInsertionPoint =
michael@0 461 this._getInsertionPoint(max.value, orientation);
michael@0 462 return this._cachedInsertionPoint;
michael@0 463 ]]></getter>
michael@0 464 </property>
michael@0 465
michael@0 466 <method name="_getInsertionPoint">
michael@0 467 <parameter name="index"/>
michael@0 468 <parameter name="orientation"/>
michael@0 469 <body><![CDATA[
michael@0 470 var result = this.result;
michael@0 471 var resultview = this.view;
michael@0 472 var container = result.root;
michael@0 473 var dropNearItemId = -1;
michael@0 474 NS_ASSERT(container, "null container");
michael@0 475 // When there's no selection, assume the container is the container
michael@0 476 // the view is populated from (i.e. the result's itemId).
michael@0 477 if (index != -1) {
michael@0 478 var lastSelected = resultview.nodeForTreeIndex(index);
michael@0 479 if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
michael@0 480 // If the last selected item is an open container, append _into_
michael@0 481 // it, rather than insert adjacent to it.
michael@0 482 container = lastSelected;
michael@0 483 index = -1;
michael@0 484 }
michael@0 485 else if (lastSelected.containerOpen &&
michael@0 486 orientation == Ci.nsITreeView.DROP_AFTER &&
michael@0 487 lastSelected.hasChildren) {
michael@0 488 // If the last selected item is an open container and the user is
michael@0 489 // trying to drag into it as a first item, really insert into it.
michael@0 490 container = lastSelected;
michael@0 491 orientation = Ci.nsITreeView.DROP_ON;
michael@0 492 index = 0;
michael@0 493 }
michael@0 494 else {
michael@0 495 // Use the last-selected node's container.
michael@0 496 container = lastSelected.parent;
michael@0 497
michael@0 498 // See comment in the treeView.js's copy of this method
michael@0 499 if (!container || !container.containerOpen)
michael@0 500 return null;
michael@0 501
michael@0 502 // Avoid the potentially expensive call to getChildIndex
michael@0 503 // if we know this container doesn't allow insertion
michael@0 504 if (PlacesControllerDragHelper.disallowInsertion(container))
michael@0 505 return null;
michael@0 506
michael@0 507 var queryOptions = PlacesUtils.asQuery(result.root).queryOptions;
michael@0 508 if (queryOptions.sortingMode !=
michael@0 509 Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
michael@0 510 // If we are within a sorted view, insert at the end
michael@0 511 index = -1;
michael@0 512 }
michael@0 513 else if (queryOptions.excludeItems ||
michael@0 514 queryOptions.excludeQueries ||
michael@0 515 queryOptions.excludeReadOnlyFolders) {
michael@0 516 // Some item may be invisible, insert near last selected one.
michael@0 517 // We don't replace index here to avoid requests to the db,
michael@0 518 // instead it will be calculated later by the controller.
michael@0 519 index = -1;
michael@0 520 dropNearItemId = lastSelected.itemId;
michael@0 521 }
michael@0 522 else {
michael@0 523 var lsi = container.getChildIndex(lastSelected);
michael@0 524 index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
michael@0 525 }
michael@0 526 }
michael@0 527 }
michael@0 528
michael@0 529 if (PlacesControllerDragHelper.disallowInsertion(container))
michael@0 530 return null;
michael@0 531
michael@0 532 return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
michael@0 533 index, orientation,
michael@0 534 PlacesUtils.nodeIsTagQuery(container),
michael@0 535 dropNearItemId);
michael@0 536 ]]></body>
michael@0 537 </method>
michael@0 538
michael@0 539 <!-- nsIPlacesView -->
michael@0 540 <method name="selectAll">
michael@0 541 <body><![CDATA[
michael@0 542 this.view.selection.selectAll();
michael@0 543 ]]></body>
michael@0 544 </method>
michael@0 545
michael@0 546 <!-- This method will select the first node in the tree that matches
michael@0 547 each given item id. It will open any folder nodes that it needs
michael@0 548 to in order to show the selected items.
michael@0 549 -->
michael@0 550 <method name="selectItems">
michael@0 551 <parameter name="aIDs"/>
michael@0 552 <parameter name="aOpenContainers"/>
michael@0 553 <body><![CDATA[
michael@0 554 // Never open containers in flat lists.
michael@0 555 if (this.flatList)
michael@0 556 aOpenContainers = false;
michael@0 557 // By default, we do search and select within containers which were
michael@0 558 // closed (note that containers in which nodes were not found are
michael@0 559 // closed).
michael@0 560 if (aOpenContainers === undefined)
michael@0 561 aOpenContainers = true;
michael@0 562
michael@0 563 var ids = aIDs; // don't manipulate the caller's array
michael@0 564
michael@0 565 // Array of nodes found by findNodes which are to be selected
michael@0 566 var nodes = [];
michael@0 567
michael@0 568 // Array of nodes found by findNodes which should be opened
michael@0 569 var nodesToOpen = [];
michael@0 570
michael@0 571 // A set of URIs of container-nodes that were previously searched,
michael@0 572 // and thus shouldn't be searched again. This is empty at the initial
michael@0 573 // start of the recursion and gets filled in as the recursion
michael@0 574 // progresses.
michael@0 575 var nodesURIChecked = [];
michael@0 576
michael@0 577 /**
michael@0 578 * Recursively search through a node's children for items
michael@0 579 * with the given IDs. When a matching item is found, remove its ID
michael@0 580 * from the IDs array, and add the found node to the nodes dictionary.
michael@0 581 *
michael@0 582 * NOTE: This method will leave open any node that had matching items
michael@0 583 * in its subtree.
michael@0 584 */
michael@0 585 function findNodes(node) {
michael@0 586 var foundOne = false;
michael@0 587 // See if node matches an ID we wanted; add to results.
michael@0 588 // For simple folder queries, check both itemId and the concrete
michael@0 589 // item id.
michael@0 590 var index = ids.indexOf(node.itemId);
michael@0 591 if (index == -1 &&
michael@0 592 node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
michael@0 593 index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId);
michael@0 594
michael@0 595 if (index != -1) {
michael@0 596 nodes.push(node);
michael@0 597 foundOne = true;
michael@0 598 ids.splice(index, 1);
michael@0 599 }
michael@0 600
michael@0 601 if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
michael@0 602 nodesURIChecked.indexOf(node.uri) != -1)
michael@0 603 return foundOne;
michael@0 604
michael@0 605 // Don't try to open a query or a shurtcut, since it may return
michael@0 606 // any duplicate data and be infinitely nested. Though, if it has
michael@0 607 // been explicitly opened by the caller, search into it.
michael@0 608 let shouldOpen = aOpenContainers &&
michael@0 609 node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;
michael@0 610 PlacesUtils.asContainer(node);
michael@0 611 if (!node.containerOpen && !shouldOpen)
michael@0 612 return foundOne;
michael@0 613
michael@0 614 nodesURIChecked.push(node.uri);
michael@0 615
michael@0 616 // Remember the beginning state so that we can re-close
michael@0 617 // this node if we don't find any additional results here.
michael@0 618 var previousOpenness = node.containerOpen;
michael@0 619 node.containerOpen = true;
michael@0 620 for (var child = 0; child < node.childCount && ids.length > 0;
michael@0 621 child++) {
michael@0 622 var childNode = node.getChild(child);
michael@0 623 var found = findNodes(childNode);
michael@0 624 if (!foundOne)
michael@0 625 foundOne = found;
michael@0 626 }
michael@0 627
michael@0 628 // If we didn't find any additional matches in this node's
michael@0 629 // subtree, revert the node to its previous openness.
michael@0 630 if (foundOne)
michael@0 631 nodesToOpen.unshift(node);
michael@0 632 node.containerOpen = previousOpenness;
michael@0 633 return foundOne;
michael@0 634 }
michael@0 635
michael@0 636 // Disable notifications while looking for nodes.
michael@0 637 let result = this.result;
michael@0 638 let didSuppressNotifications = result.suppressNotifications;
michael@0 639 if (!didSuppressNotifications)
michael@0 640 result.suppressNotifications = true
michael@0 641 try {
michael@0 642 findNodes(this.result.root);
michael@0 643 }
michael@0 644 finally {
michael@0 645 if (!didSuppressNotifications)
michael@0 646 result.suppressNotifications = false;
michael@0 647 }
michael@0 648
michael@0 649 // For all the nodes we've found, highlight the corresponding
michael@0 650 // index in the tree.
michael@0 651 var resultview = this.view;
michael@0 652 var selection = this.view.selection;
michael@0 653 selection.selectEventsSuppressed = true;
michael@0 654 selection.clearSelection();
michael@0 655 // Open nodes containing found items
michael@0 656 for (var i = 0; i < nodesToOpen.length; i++) {
michael@0 657 nodesToOpen[i].containerOpen = true;
michael@0 658 }
michael@0 659 for (var i = 0; i < nodes.length; i++) {
michael@0 660 var index = resultview.treeIndexForNode(nodes[i]);
michael@0 661 if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
michael@0 662 continue;
michael@0 663 selection.rangedSelect(index, index, true);
michael@0 664 }
michael@0 665 selection.selectEventsSuppressed = false;
michael@0 666 ]]></body>
michael@0 667 </method>
michael@0 668
michael@0 669 <field name="_contextMenuShown">false</field>
michael@0 670
michael@0 671 <method name="buildContextMenu">
michael@0 672 <parameter name="aPopup"/>
michael@0 673 <body><![CDATA[
michael@0 674 this._contextMenuShown = true;
michael@0 675 return this.controller.buildContextMenu(aPopup);
michael@0 676 ]]></body>
michael@0 677 </method>
michael@0 678
michael@0 679 <method name="destroyContextMenu">
michael@0 680 <parameter name="aPopup"/>
michael@0 681 this._contextMenuShown = false;
michael@0 682 <body/>
michael@0 683 </method>
michael@0 684
michael@0 685 <property name="ownerWindow"
michael@0 686 readonly="true"
michael@0 687 onget="return window;"/>
michael@0 688
michael@0 689 <field name="_active">true</field>
michael@0 690 <property name="active"
michael@0 691 onget="return this._active"
michael@0 692 onset="return this._active = val"/>
michael@0 693
michael@0 694 </implementation>
michael@0 695 <handlers>
michael@0 696 <handler event="focus"><![CDATA[
michael@0 697 this._cachedInsertionPoint = undefined;
michael@0 698
michael@0 699 // See select handler. We need the sidebar's places commandset to be
michael@0 700 // updated as well
michael@0 701 document.commandDispatcher.updateCommands("focus");
michael@0 702 ]]></handler>
michael@0 703 <handler event="select"><![CDATA[
michael@0 704 this._cachedInsertionPoint = undefined;
michael@0 705
michael@0 706 // This additional complexity is here for the sidebars
michael@0 707 var win = window;
michael@0 708 while (true) {
michael@0 709 win.document.commandDispatcher.updateCommands("focus");
michael@0 710 if (win == window.top)
michael@0 711 break;
michael@0 712
michael@0 713 win = win.parent;
michael@0 714 }
michael@0 715 ]]></handler>
michael@0 716
michael@0 717 <handler event="dragstart"><![CDATA[
michael@0 718 if (event.target.localName != "treechildren")
michael@0 719 return;
michael@0 720
michael@0 721 let nodes = this.selectedNodes;
michael@0 722 for (let i = 0; i < nodes.length; i++) {
michael@0 723 let node = nodes[i];
michael@0 724
michael@0 725 // Disallow dragging the root node of a tree.
michael@0 726 if (!node.parent) {
michael@0 727 event.preventDefault();
michael@0 728 event.stopPropagation();
michael@0 729 return;
michael@0 730 }
michael@0 731
michael@0 732 // If this node is child of a readonly container (e.g. a livemark)
michael@0 733 // or cannot be moved, we must force a copy.
michael@0 734 if (!PlacesControllerDragHelper.canMoveNode(node)) {
michael@0 735 event.dataTransfer.effectAllowed = "copyLink";
michael@0 736 break;
michael@0 737 }
michael@0 738 }
michael@0 739
michael@0 740 this._controller.setDataTransfer(event);
michael@0 741 event.stopPropagation();
michael@0 742 ]]></handler>
michael@0 743
michael@0 744 <handler event="dragover"><![CDATA[
michael@0 745 if (event.target.localName != "treechildren")
michael@0 746 return;
michael@0 747
michael@0 748 let row = { }, col = { }, child = { };
michael@0 749 this.treeBoxObject.getCellAt(event.clientX, event.clientY,
michael@0 750 row, col, child);
michael@0 751 let node = row.value != -1 ?
michael@0 752 this.view.nodeForTreeIndex(row.value) :
michael@0 753 this.result.root;
michael@0 754 // cache the dropTarget for the view
michael@0 755 PlacesControllerDragHelper.currentDropTarget = node;
michael@0 756
michael@0 757 // We have to calculate the orientation since view.canDrop will use
michael@0 758 // it and we want to be consistent with the dropfeedback.
michael@0 759 let tbo = this.treeBoxObject;
michael@0 760 let rowHeight = tbo.rowHeight;
michael@0 761 let eventY = event.clientY - tbo.treeBody.boxObject.y -
michael@0 762 rowHeight * (row.value - tbo.getFirstVisibleRow());
michael@0 763
michael@0 764 let orientation = Ci.nsITreeView.DROP_BEFORE;
michael@0 765
michael@0 766 if (row.value == -1) {
michael@0 767 // If the row is not valid we try to insert inside the resultNode.
michael@0 768 orientation = Ci.nsITreeView.DROP_ON;
michael@0 769 }
michael@0 770 else if (PlacesUtils.nodeIsContainer(node) &&
michael@0 771 eventY > rowHeight * 0.75) {
michael@0 772 // If we are below the 75% of a container the treeview we try
michael@0 773 // to drop after the node.
michael@0 774 orientation = Ci.nsITreeView.DROP_AFTER;
michael@0 775 }
michael@0 776 else if (PlacesUtils.nodeIsContainer(node) &&
michael@0 777 eventY > rowHeight * 0.25) {
michael@0 778 // If we are below the 25% of a container the treeview we try
michael@0 779 // to drop inside the node.
michael@0 780 orientation = Ci.nsITreeView.DROP_ON;
michael@0 781 }
michael@0 782
michael@0 783 if (!this.view.canDrop(row.value, orientation, event.dataTransfer))
michael@0 784 return;
michael@0 785
michael@0 786 event.preventDefault();
michael@0 787 event.stopPropagation();
michael@0 788 ]]></handler>
michael@0 789
michael@0 790 <handler event="dragend"><![CDATA[
michael@0 791 PlacesControllerDragHelper.currentDropTarget = null;
michael@0 792 ]]></handler>
michael@0 793
michael@0 794 </handlers>
michael@0 795 </binding>
michael@0 796
michael@0 797 </bindings>

mercurial