browser/components/places/content/menu.xml

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:b57634ad37a4
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="placesMenuBindings"
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-popup-base"
14 extends="chrome://global/content/bindings/popup.xml#popup">
15 <content>
16 <xul:hbox flex="1">
17 <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
18 <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
19 </xul:vbox>
20 <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
21 smoothscroll="false">
22 <children/>
23 </xul:arrowscrollbox>
24 </xul:hbox>
25 </content>
26
27 <implementation>
28
29 <field name="_indicatorBar">
30 document.getAnonymousElementByAttribute(this, "class",
31 "menupopup-drop-indicator-bar");
32 </field>
33
34 <field name="_scrollBox">
35 document.getAnonymousElementByAttribute(this, "class",
36 "popup-internal-box");
37 </field>
38
39 <!-- This is the view that manage the popup -->
40 <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
41
42 <!-- Check if we should hide the drop indicator for the target -->
43 <method name="_hideDropIndicator">
44 <parameter name="aEvent"/>
45 <body><![CDATA[
46 let target = aEvent.target;
47
48 // Don't draw the drop indicator outside of markers.
49 // The markers are hidden, since otherwise sometimes popups acquire
50 // scrollboxes on OS X, so we can't use them directly.
51 let firstChildTop = this._startMarker.nextSibling.boxObject.y;
52 let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
53 this._endMarker.previousSibling.boxObject.height;
54 let betweenMarkers = target.boxObject.y >= firstChildTop ||
55 target.boxObject.y <= lastChildBottom;
56
57 // Hide the dropmarker if current node is not a Places node.
58 return !(target && target._placesNode && betweenMarkers);
59 ]]></body>
60 </method>
61
62 <!-- This function returns information about where to drop when
63 dragging over this popup insertion point -->
64 <method name="_getDropPoint">
65 <parameter name="aEvent"/>
66 <body><![CDATA[
67 // Can't drop if the menu isn't a folder
68 let resultNode = this._placesNode;
69
70 if (!PlacesUtils.nodeIsFolder(resultNode) ||
71 PlacesControllerDragHelper.disallowInsertion(resultNode)) {
72 return null;
73 }
74
75 var dropPoint = { ip: null, folderElt: null };
76
77 // The element we are dragging over
78 let elt = aEvent.target;
79 if (elt.localName == "menupopup")
80 elt = elt.parentNode;
81
82 // Calculate positions taking care of arrowscrollbox
83 let scrollbox = this._scrollBox;
84 let eventY = aEvent.layerY + (scrollbox.boxObject.y - this.boxObject.y);
85 let scrollboxOffset = scrollbox.scrollBoxObject.y -
86 (scrollbox.boxObject.y - this.boxObject.y);
87 let eltY = elt.boxObject.y - scrollboxOffset;
88 let eltHeight = elt.boxObject.height;
89
90 if (!elt._placesNode) {
91 // If we are dragging over a non places node drop at the end.
92 dropPoint.ip = new InsertionPoint(
93 PlacesUtils.getConcreteItemId(resultNode),
94 -1,
95 Ci.nsITreeView.DROP_ON);
96 // We can set folderElt if we are dropping over a static menu that
97 // has an internal placespopup.
98 let isMenu = elt.localName == "menu" ||
99 (elt.localName == "toolbarbutton" &&
100 elt.getAttribute("type") == "menu");
101 if (isMenu && elt.lastChild &&
102 elt.lastChild.hasAttribute("placespopup"))
103 dropPoint.folderElt = elt;
104 return dropPoint;
105 }
106 else if ((PlacesUtils.nodeIsFolder(elt._placesNode) ||
107 PlacesUtils.nodeIsTagQuery(elt._placesNode)) &&
108 !PlacesUtils.nodeIsReadOnly(elt._placesNode)) {
109 // This is a folder or a tag container.
110 if (eventY - eltY < eltHeight * 0.20) {
111 // If mouse is in the top part of the element, drop above folder.
112 dropPoint.ip = new InsertionPoint(
113 PlacesUtils.getConcreteItemId(resultNode),
114 -1,
115 Ci.nsITreeView.DROP_BEFORE,
116 PlacesUtils.nodeIsTagQuery(elt._placesNode),
117 elt._placesNode.itemId);
118 return dropPoint;
119 }
120 else if (eventY - eltY < eltHeight * 0.80) {
121 // If mouse is in the middle of the element, drop inside folder.
122 dropPoint.ip = new InsertionPoint(
123 PlacesUtils.getConcreteItemId(elt._placesNode),
124 -1,
125 Ci.nsITreeView.DROP_ON,
126 PlacesUtils.nodeIsTagQuery(elt._placesNode));
127 dropPoint.folderElt = elt;
128 return dropPoint;
129 }
130 }
131 else if (eventY - eltY <= eltHeight / 2) {
132 // This is a non-folder node or a readonly folder.
133 // If the mouse is above the middle, drop above this item.
134 dropPoint.ip = new InsertionPoint(
135 PlacesUtils.getConcreteItemId(resultNode),
136 -1,
137 Ci.nsITreeView.DROP_BEFORE,
138 PlacesUtils.nodeIsTagQuery(elt._placesNode),
139 elt._placesNode.itemId);
140 return dropPoint;
141 }
142
143 // Drop below the item.
144 dropPoint.ip = new InsertionPoint(
145 PlacesUtils.getConcreteItemId(resultNode),
146 -1,
147 Ci.nsITreeView.DROP_AFTER,
148 PlacesUtils.nodeIsTagQuery(elt._placesNode),
149 elt._placesNode.itemId);
150 return dropPoint;
151 ]]></body>
152 </method>
153
154 <!-- Sub-menus should be opened when the mouse drags over them, and closed
155 when the mouse drags off. The overFolder object manages opening and
156 closing of folders when the mouse hovers. -->
157 <field name="_overFolder"><![CDATA[({
158 _self: this,
159 _folder: {elt: null,
160 openTimer: null,
161 hoverTime: 350,
162 closeTimer: null},
163 _closeMenuTimer: null,
164
165 get elt() {
166 return this._folder.elt;
167 },
168 set elt(val) {
169 return this._folder.elt = val;
170 },
171
172 get openTimer() {
173 return this._folder.openTimer;
174 },
175 set openTimer(val) {
176 return this._folder.openTimer = val;
177 },
178
179 get hoverTime() {
180 return this._folder.hoverTime;
181 },
182 set hoverTime(val) {
183 return this._folder.hoverTime = val;
184 },
185
186 get closeTimer() {
187 return this._folder.closeTimer;
188 },
189 set closeTimer(val) {
190 return this._folder.closeTimer = val;
191 },
192
193 get closeMenuTimer() {
194 return this._closeMenuTimer;
195 },
196 set closeMenuTimer(val) {
197 return this._closeMenuTimer = val;
198 },
199
200 setTimer: function OF__setTimer(aTime) {
201 var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
202 timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
203 return timer;
204 },
205
206 notify: function OF__notify(aTimer) {
207 // Function to process all timer notifications.
208
209 if (aTimer == this._folder.openTimer) {
210 // Timer to open a submenu that's being dragged over.
211 this._folder.elt.lastChild.setAttribute("autoopened", "true");
212 this._folder.elt.lastChild.showPopup(this._folder.elt);
213 this._folder.openTimer = null;
214 }
215
216 else if (aTimer == this._folder.closeTimer) {
217 // Timer to close a submenu that's been dragged off of.
218 // Only close the submenu if the mouse isn't being dragged over any
219 // of its child menus.
220 var draggingOverChild = PlacesControllerDragHelper
221 .draggingOverChildNode(this._folder.elt);
222 if (draggingOverChild)
223 this._folder.elt = null;
224 this.clear();
225
226 // Close any parent folders which aren't being dragged over.
227 // (This is necessary because of the above code that keeps a folder
228 // open while its children are being dragged over.)
229 if (!draggingOverChild)
230 this.closeParentMenus();
231 }
232
233 else if (aTimer == this.closeMenuTimer) {
234 // Timer to close this menu after the drag exit.
235 var popup = this._self;
236 // if we are no more dragging we can leave the menu open to allow
237 // for better D&D bookmark organization
238 if (PlacesControllerDragHelper.getSession() &&
239 !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
240 popup.hidePopup();
241 // Close any parent menus that aren't being dragged over;
242 // otherwise they'll stay open because they couldn't close
243 // while this menu was being dragged over.
244 this.closeParentMenus();
245 }
246 this._closeMenuTimer = null;
247 }
248 },
249
250 // Helper function to close all parent menus of this menu,
251 // as long as none of the parent's children are currently being
252 // dragged over.
253 closeParentMenus: function OF__closeParentMenus() {
254 var popup = this._self;
255 var parent = popup.parentNode;
256 while (parent) {
257 if (parent.localName == "menupopup" && parent._placesNode) {
258 if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode))
259 break;
260 parent.hidePopup();
261 }
262 parent = parent.parentNode;
263 }
264 },
265
266 // The mouse is no longer dragging over the stored menubutton.
267 // Close the menubutton, clear out drag styles, and clear all
268 // timers for opening/closing it.
269 clear: function OF__clear() {
270 if (this._folder.elt && this._folder.elt.lastChild) {
271 if (!this._folder.elt.lastChild.hasAttribute("dragover"))
272 this._folder.elt.lastChild.hidePopup();
273 // remove menuactive style
274 this._folder.elt.removeAttribute("_moz-menuactive");
275 this._folder.elt = null;
276 }
277 if (this._folder.openTimer) {
278 this._folder.openTimer.cancel();
279 this._folder.openTimer = null;
280 }
281 if (this._folder.closeTimer) {
282 this._folder.closeTimer.cancel();
283 this._folder.closeTimer = null;
284 }
285 }
286 })]]></field>
287
288 <method name="_cleanupDragDetails">
289 <body><![CDATA[
290 // Called on dragend and drop.
291 PlacesControllerDragHelper.currentDropTarget = null;
292 this._rootView._draggedElt = null;
293 this.removeAttribute("dragover");
294 this.removeAttribute("dragstart");
295 this._indicatorBar.hidden = true;
296 ]]></body>
297 </method>
298
299 </implementation>
300
301 <handlers>
302 <handler event="DOMMenuItemActive"><![CDATA[
303 let elt = event.target;
304 if (elt.parentNode != this)
305 return;
306
307 #ifdef XP_MACOSX
308 // XXX: The following check is a temporary hack until bug 420033 is
309 // resolved.
310 let parentElt = elt.parent;
311 while (parentElt) {
312 if (parentElt.id == "bookmarksMenuPopup" ||
313 parentElt.id == "goPopup")
314 return;
315
316 parentElt = parentElt.parentNode;
317 }
318 #endif
319
320 if (window.XULBrowserWindow) {
321 let elt = event.target;
322 let placesNode = elt._placesNode;
323
324 var linkURI;
325 if (placesNode && PlacesUtils.nodeIsURI(placesNode))
326 linkURI = placesNode.uri;
327 else if (elt.hasAttribute("targetURI"))
328 linkURI = elt.getAttribute("targetURI");
329
330 if (linkURI)
331 window.XULBrowserWindow.setOverLink(linkURI, null);
332 }
333 ]]></handler>
334
335 <handler event="DOMMenuItemInactive"><![CDATA[
336 let elt = event.target;
337 if (elt.parentNode != this)
338 return;
339
340 if (window.XULBrowserWindow)
341 window.XULBrowserWindow.setOverLink("", null);
342 ]]></handler>
343
344 <handler event="dragstart"><![CDATA[
345 if (!event.target._placesNode)
346 return;
347
348 let draggedElt = event.target._placesNode;
349
350 // Force a copy action if parent node is a query or we are dragging a
351 // not-removable node.
352 if (!PlacesControllerDragHelper.canMoveNode(draggedElt))
353 event.dataTransfer.effectAllowed = "copyLink";
354
355 // Activate the view and cache the dragged element.
356 this._rootView._draggedElt = draggedElt;
357 this._rootView.controller.setDataTransfer(event);
358 this.setAttribute("dragstart", "true");
359 event.stopPropagation();
360 ]]></handler>
361
362 <handler event="drop"><![CDATA[
363 PlacesControllerDragHelper.currentDropTarget = event.target;
364
365 let dropPoint = this._getDropPoint(event);
366 if (dropPoint && dropPoint.ip) {
367 PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer);
368 event.preventDefault();
369 }
370
371 this._cleanupDragDetails();
372 event.stopPropagation();
373 ]]></handler>
374
375 <handler event="dragover"><![CDATA[
376 PlacesControllerDragHelper.currentDropTarget = event.target;
377 let dt = event.dataTransfer;
378
379 let dropPoint = this._getDropPoint(event);
380 if (!dropPoint || !dropPoint.ip ||
381 !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
382 this._indicatorBar.hidden = true;
383 event.stopPropagation();
384 return;
385 }
386
387 // Mark this popup as being dragged over.
388 this.setAttribute("dragover", "true");
389
390 if (dropPoint.folderElt) {
391 // We are dragging over a folder.
392 // _overFolder should take the care of opening it on a timer.
393 if (this._overFolder.elt &&
394 this._overFolder.elt != dropPoint.folderElt) {
395 // We are dragging over a new folder, let's clear old values
396 this._overFolder.clear();
397 }
398 if (!this._overFolder.elt) {
399 this._overFolder.elt = dropPoint.folderElt;
400 // Create the timer to open this folder.
401 this._overFolder.openTimer = this._overFolder
402 .setTimer(this._overFolder.hoverTime);
403 }
404 // Since we are dropping into a folder set the corresponding style.
405 dropPoint.folderElt.setAttribute("_moz-menuactive", true);
406 }
407 else {
408 // We are not dragging over a folder.
409 // Clear out old _overFolder information.
410 this._overFolder.clear();
411 }
412
413 // Autoscroll the popup strip if we drag over the scroll buttons.
414 let anonid = event.originalTarget.getAttribute('anonid');
415 let scrollDir = anonid == "scrollbutton-up" ? -1 :
416 anonid == "scrollbutton-down" ? 1 : 0;
417 if (scrollDir != 0) {
418 this._scrollBox.scrollByIndex(scrollDir, false);
419 }
420
421 // Check if we should hide the drop indicator for this target.
422 if (dropPoint.folderElt || this._hideDropIndicator(event)) {
423 this._indicatorBar.hidden = true;
424 event.preventDefault();
425 event.stopPropagation();
426 return;
427 }
428
429 // We should display the drop indicator relative to the arrowscrollbox.
430 let sbo = this._scrollBox.scrollBoxObject;
431 let newMarginTop = 0;
432 if (scrollDir == 0) {
433 let elt = this.firstChild;
434 while (elt && event.screenY > elt.boxObject.screenY +
435 elt.boxObject.height / 2)
436 elt = elt.nextSibling;
437 newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
438 sbo.height;
439 }
440 else if (scrollDir == 1)
441 newMarginTop = sbo.height;
442
443 // Set the new marginTop based on arrowscrollbox.
444 newMarginTop += sbo.y - this._scrollBox.boxObject.y;
445 this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
446 this._indicatorBar.hidden = false;
447
448 event.preventDefault();
449 event.stopPropagation();
450 ]]></handler>
451
452 <handler event="dragexit"><![CDATA[
453 PlacesControllerDragHelper.currentDropTarget = null;
454 this.removeAttribute("dragover");
455
456 // If we have not moved to a valid new target clear the drop indicator
457 // this happens when moving out of the popup.
458 let target = event.relatedTarget;
459 if (!target || !this.contains(target))
460 this._indicatorBar.hidden = true;
461
462 // Close any folder being hovered over
463 if (this._overFolder.elt) {
464 this._overFolder.closeTimer = this._overFolder
465 .setTimer(this._overFolder.hoverTime);
466 }
467
468 // The autoopened attribute is set when this folder was automatically
469 // opened after the user dragged over it. If this attribute is set,
470 // auto-close the folder on drag exit.
471 // We should also try to close this popup if the drag has started
472 // from here, the timer will check if we are dragging over a child.
473 if (this.hasAttribute("autoopened") ||
474 this.hasAttribute("dragstart")) {
475 this._overFolder.closeMenuTimer = this._overFolder
476 .setTimer(this._overFolder.hoverTime);
477 }
478
479 event.stopPropagation();
480 ]]></handler>
481
482 <handler event="dragend"><![CDATA[
483 this._cleanupDragDetails();
484 ]]></handler>
485
486 </handlers>
487 </binding>
488
489 <!-- Most of this is copied from the arrowpanel binding in popup.xml -->
490 <binding id="places-popup-arrow"
491 extends="chrome://browser/content/places/menu.xml#places-popup-base">
492 <content flip="both" side="top" position="bottomcenter topright">
493 <xul:vbox anonid="container" class="panel-arrowcontainer" flex="1"
494 xbl:inherits="side,panelopen">
495 <xul:box anonid="arrowbox" class="panel-arrowbox">
496 <xul:image anonid="arrow" class="panel-arrow" xbl:inherits="side"/>
497 </xul:box>
498 <xul:box class="panel-arrowcontent" xbl:inherits="side,align,dir,orient,pack" flex="1">
499 <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
500 <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
501 </xul:vbox>
502 <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
503 smoothscroll="false">
504 <children/>
505 </xul:arrowscrollbox>
506 </xul:box>
507 </xul:vbox>
508 </content>
509
510 <implementation>
511 <constructor><![CDATA[
512 this.style.pointerEvents = 'none';
513 ]]></constructor>
514 <method name="adjustArrowPosition">
515 <body><![CDATA[
516 var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
517
518 var anchor = this.anchorNode;
519 if (!anchor) {
520 arrow.hidden = true;
521 return;
522 }
523
524 var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
525 var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
526
527 var position = this.alignmentPosition;
528 var offset = this.alignmentOffset;
529
530 // if this panel has a "sliding" arrow, we may have previously set margins...
531 arrowbox.style.removeProperty("transform");
532 if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
533 container.orient = "horizontal";
534 arrowbox.orient = "vertical";
535 if (position.indexOf("_after") > 0) {
536 arrowbox.pack = "end";
537 } else {
538 arrowbox.pack = "start";
539 }
540 arrowbox.style.transform = "translate(0, " + -offset + "px)";
541
542 // The assigned side stays the same regardless of direction.
543 var isRTL = (window.getComputedStyle(this).direction == "rtl");
544
545 if (position.indexOf("start_") == 0) {
546 container.dir = "reverse";
547 this.setAttribute("side", isRTL ? "left" : "right");
548 }
549 else {
550 container.dir = "";
551 this.setAttribute("side", isRTL ? "right" : "left");
552 }
553 }
554 else if (position.indexOf("before_") == 0 || position.indexOf("after_") == 0) {
555 container.orient = "";
556 arrowbox.orient = "";
557 if (position.indexOf("_end") > 0) {
558 arrowbox.pack = "end";
559 } else {
560 arrowbox.pack = "start";
561 }
562 arrowbox.style.transform = "translate(" + -offset + "px, 0)";
563
564 if (position.indexOf("before_") == 0) {
565 container.dir = "reverse";
566 this.setAttribute("side", "bottom");
567 }
568 else {
569 container.dir = "";
570 this.setAttribute("side", "top");
571 }
572 }
573
574 arrow.hidden = false;
575 ]]></body>
576 </method>
577 </implementation>
578
579 <handlers>
580 <handler event="popupshowing" phase="target"><![CDATA[
581 this.adjustArrowPosition();
582 ]]></handler>
583 <handler event="popupshown" phase="target"><![CDATA[
584 this.setAttribute("panelopen", "true");
585 let disablePointerEvents;
586 if (!this.hasAttribute("disablepointereventsfortransition")) {
587 let container = document.getAnonymousElementByAttribute(this, "anonid", "container");
588 let cs = getComputedStyle(container);
589 let transitionProp = cs.transitionProperty;
590 let transitionTime = parseFloat(cs.transitionDuration);
591 disablePointerEvents = (transitionProp.contains("transform") ||
592 transitionProp == "all") &&
593 transitionTime > 0;
594 this.setAttribute("disablepointereventsfortransition", disablePointerEvents);
595 } else {
596 disablePointerEvents = this.getAttribute("disablepointereventsfortransition") == "true";
597 }
598 if (!disablePointerEvents) {
599 this.style.removeProperty("pointer-events");
600 }
601 ]]></handler>
602 <handler event="transitionend"><![CDATA[
603 if (event.originalTarget.getAttribute("anonid") == "container" &&
604 event.propertyName == "transform") {
605 this.style.removeProperty("pointer-events");
606 }
607 ]]></handler>
608 <handler event="popuphidden" phase="target"><![CDATA[
609 this.removeAttribute("panelopen");
610 if (this.getAttribute("disablepointereventsfortransition") == "true") {
611 this.style.pointerEvents = 'none';
612 }
613 ]]></handler>
614 </handlers>
615 </binding>
616 </bindings>

mercurial