browser/components/places/content/tree.xml

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

mercurial