!import
1 <?xml version="1.0"?>
2
3 <!DOCTYPE bindings [
4 <!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
5 %messengerDTD;
6 <!ENTITY % tabMailDTD SYSTEM "chrome://messenger/locale/tabmail.dtd" >
7 %tabMailDTD;
8 <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
9 %globalDTD;
10 ]>
11
12 <bindings id="tabmailBindings"
13 xmlns="http://www.mozilla.org/xbl"
14 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
15 xmlns:xbl="http://www.mozilla.org/xbl">
16
17 <binding id="tabmail">
18 <resources>
19 <stylesheet src="chrome://messenger/skin/tabmail.css"/>
20 </resources>
21 <content>
22 <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
23 onselect="if (!('updateCurrentMailTab' in this.parentNode) || event.target.localName != 'tabs')
24 return; this.parentNode.updateCurrentMailTab();">
25 <xul:hbox class="tab-drop-indicator-bar">
26 <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
27 </xul:hbox>
28 <xul:hbox class="tabmail-strip" collapsed="true" tooltip="_child" context="_child"
29 anonid="strip"
30 ondraggesture="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
31 ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
32 ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
33 ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
34 <xul:tooltip onpopupshowing="return CreateToolbarTooltip(document, event);"/>
35 <xul:menupopup anonid="tabContextMenu">
36 <xul:menuitem label="&closeTabCmd.label;" accesskey="&closeTabCmd.accesskey;"
37 oncommand="var tabmail = this.parentNode.parentNode.parentNode.parentNode;
38 tabmail.removeTab(document.popupNode);"/>
39 </xul:menupopup>
40 <xul:tabs class="tabmail-tabs" flex="1"
41 anonid="tabcontainer"
42 setfocus="false"
43 onclick="this.parentNode.parentNode.parentNode.onTabClick(event);">
44 <xul:tab selected="true" validate="never" type="folder"
45 maxwidth="250" width="0" minwidth="100" flex="100"
46 class="tabmail-tab tabmail-tab" crop="end"/>
47 </xul:tabs>
48 </xul:hbox>
49 <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
50 <children/>
51 </xul:tabpanels>
52 </xul:tabbox>
53 </content>
54
55 <implementation>
field_currentTabOwner
56 <field name="currentTabOwner">
57 0;
58 </field>
field_tabOwners
59 <field name="tabOwners" readonly="true">
60 new Array();
61 </field>
field_tabStrip
62 <field name="tabStrip" readonly="true">
63 document.getAnonymousElementByAttribute(this, "anonid", "strip");
64 </field>
field_tabContainer
65 <field name="tabContainer" readonly="true">
66 document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
67 </field>
68 <method name="addTab">
69 <parameter name="aTabOwner"/>
addTab
70 <body>
71 <![CDATA[
72 var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
73 "tab");
74 t.setAttribute("crop", "end");
75 t.maxWidth = 250;
76 // t.minWidth = this.tabContainer.mTabMinWidth;
77 t.width = 0;
78 t.setAttribute("flex", "100");
79 t.setAttribute("validate", "never");
80 t.className = "tabmail-tab tabmail-tab";
81 if (!this.tabOwners.length)
82 {
83 // set up the first tab, which was previously invisible.
84 this.tabOwners[0] = new folderTabOwner();
85 this.setTabTitle(this.tabContainer.firstChild);
86 }
87 this.tabContainer.appendChild(t);
88 if (this.tabStrip.collapsed)
89 {
90 this.tabStrip.collapsed = false;
91 this.tabStrip.setAttribute("closebuttons", "alltabs");
92 }
93 this.saveCurrentTabInfo(); // save off the state of the old tab
94
95 // the order of the following statements is important
96 this.currentTabOwner = aTabOwner;
97 this.tabOwners[this.tabContainer.childNodes.length - 1] = aTabOwner;
98 this.tabContainer.selectedIndex = this.tabContainer.childNodes.length - 1; // this has a side effect of calling updateCurrentMailTab
99 this.setTabTitle(t);
100 // for styling purposes, apply the type to the tab...
101 t.setAttribute('type', aTabOwner.type);
102 this.currentTabOwner.open();
103 ]]>
104 </body>
105 </method>
106 <method name="closeTabs">
closeTabs
107 <body>
108 <![CDATA[
109 for (var i = 0; i < this.tabOwners.length; i++)
110 this.tabOwners[i].close();
111 ]]>
112 </body>
113 </method>
114 <method name="removeTab">
115 <parameter name="aTab"/>
removeTab
116 <body>
117 <![CDATA[
118 var numTabs = this.tabContainer.childNodes.length;
119 if (numTabs < 3)
120 {
121 // hide the tab bar
122 this.tabStrip.collapsed = true;
123 if (numTabs == 1) // can this happen?
124 return;
125 }
126 var i;
127 // Find and locate the tab in our list.
128 for (i = 0; i < numTabs; i++)
129 if (this.tabContainer.childNodes[i] == aTab)
130 break;
131 var tabOwner = this.tabOwners[i];
132 tabOwner.close(); // inform the owner the tab is being closed
133 this.tabOwners.splice(i, 1);
134 this.tabContainer.removeChild(aTab);
135 if (this.tabContainer.selectedIndex == -1)
136 this.tabContainer.selectedIndex = (i == --numTabs) ? i - 1 : i;
137 if (this.currentTabOwner == tabOwner)
138 this.updateCurrentMailTab();
139 ]]>
140 </body>
141 </method>
142 <!-- UpdateCurrentMailTab - called in response to changing the current tab -->
143 <method name="updateCurrentMailTab">
updateCurrentMailTab
144 <body>
145 <![CDATA[
146 if (this.currentTabOwner != this.tabOwners[this.tabContainer.selectedIndex])
147 {
148 this.saveCurrentTabInfo(); // save the old tab state before we change the current tab
149 // if this isn't set, then this is the first time we've switched tabs, so the
150 // old tab must be the 0th tab.
151 // the tab owner is responsible for actually setting up the UI for the tab.
152 var oldTabOwner = this.currentTabOwner;
153 this.currentTabOwner = this.tabOwners[this.tabContainer.selectedIndex];
154 this.currentTabOwner.onSelect(oldTabOwner);
155 }
156 ]]>
157 </body>
158 </method>
159 <method name="saveCurrentTabInfo">
saveCurrentTabInfo
160 <body>
161 <![CDATA[
162 if (!this.currentTabOwner)
163 this.currentTabOwner = this.tabOwners[0];
164 this.currentTabOwner.saveCurrentInfo();
165 ]]>
166 </body>
167 </method>
168 <method name="onTabClick">
169 <parameter name="event"/>
onTabClick
170 <body>
171 <![CDATA[
172 // a middle mouse button click on a tab is a short cut for closing a tab
173 if (event.button != 1 || event.target.localName != 'tab')
174 return;
175 this.removeTab(event.target);
176 event.stopPropagation();
177 ]]>
178 </body>
179 </method>
180 <method name="setTabTitle">
181 <parameter name="aTab"/>
setTabTitle
182 <body>
183 <![CDATA[
184 if (!aTab)
185 aTab = this.tabContainer.childNodes[this.tabContainer.selectedIndex];
186 // get the owner for the tab...
187 var i;
188 var numTabs = this.tabContainer.childNodes.length;
189 for (i = 0; i < numTabs; i++)
190 if (this.tabContainer.childNodes[i] == aTab)
191 break;
192 // on startup, we may not have a tab...
193 if (this.tabOwners[i])
194 {
195 aTab.setAttribute("label", this.tabOwners[i].title);
196 this.tabOwners[i].onTitleChanged(aTab);
197 }
198 ]]>
199 </body>
200 </method>
201 </implementation>
202 </binding>
203
204 <binding id="tabmail-tab" display="xul:box"
205 extends="chrome://global/content/bindings/tabbox.xml#tab">
206 <content chromedir="&locale.dir;"
207 closetabtext="&closeTab.label;">
208 <xul:hbox class="tab-middle box-inherit" xbl:inherits="align,dir,pack,orient,selected" flex="1">
209 <xul:image class="tab-icon" xbl:inherits="validate,src=image"/>
210 <xul:label class="tab-text" xbl:inherits="value=label,accesskey,crop,disabled" flex="1"/>
211 </xul:hbox>
212 <xul:toolbarbutton anonid="close-button" tabindex="-1" class="tab-close-button"/>
213 </content>
214
215 <implementation>
field_mOverCloseButton
216 <field name="mOverCloseButton">false</field>
217 <field name="mCorrespondingMenuitem">null</field>
218 </implementation>
219
220 <handlers>
onmouseover
221 <handler event="mouseover">
222 var anonid = event.originalTarget.getAttribute("anonid");
223 if (anonid == "close-button")
224 this.mOverCloseButton = true;
225 </handler>
onmouseout
226 <handler event="mouseout">
227 var anonid = event.originalTarget.getAttribute("anonid");
228 if (anonid == "close-button")
229 this.mOverCloseButton = false;
230 </handler>
onmousedown
231 <handler event="mousedown" button="0" phase="capturing">
232 <![CDATA[
233 if (this.mOverCloseButton)
234 event.stopPropagation();
235 ]]>
236 </handler>
237 </handlers>
238 </binding>
239
240 <binding id="tabmail-arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#arrowscrollbox-clicktoscroll">
241 <content>
242 <xul:toolbarbutton class="scrollbutton-up" collapsed="true"
243 xbl:inherits="orient"
244 anonid="scrollbutton-up"
245 onmousedown="_startScroll(-1);"
246 onmouseup="_stopScroll();"
247 onmouseout="_stopScroll();"
248 chromedir="&locale.dir;"/>
249 <xul:scrollbox xbl:inherits="orient,align,pack,dir" flex="1" anonid="scrollbox">
250 <children/>
251 </xul:scrollbox>
252 <xul:stack align="center" pack="end" class="scrollbutton-down-stack">
253 <xul:hbox flex="1" class="scrollbutton-down-box"
254 collapsed="true" anonid="down-box"/>
255 <xul:hbox flex="1" class="scrollbutton-down-box-animate"
256 collapsed="true" anonid="down-box-animate"/>
257 <xul:toolbarbutton class="scrollbutton-down" collapsed="true"
258 xbl:inherits="orient"
259 anonid="scrollbutton-down"
260 onmousedown="_startScroll(1);"
261 onmouseup="_stopScroll();"
262 onmouseout="_stopScroll();"
263 chromedir="&locale.dir;"/>
264 </xul:stack>
265 </content>
266 <implementation>
field__scrollButtonDownBox
267 <field name="_scrollButtonDownBox">
268 document.getAnonymousElementByAttribute(this, "anonid", "down-box");
269 </field>
field__scrollButtonDownBoxAnimate
270 <field name="_scrollButtonDownBoxAnimate">
271 document.getAnonymousElementByAttribute(this, "anonid", "down-box-animate");
272 </field>
273 </implementation>
274 <handlers>
onunderflow
275 <handler event="underflow"><![CDATA[
276 // filter underflow events which were dispatched on nested scrollboxes
277 if (event.target != this)
278 return;
279
280 // Ignore vertical events.
281 if (event.detail == 0) {
282 return;
283 }
284
285 this._scrollButtonDownBox.collapsed = true;
286 this._scrollButtonDownBoxAnimate.collapsed = true;
287 ]]></handler>
288
onoverflow
289 <handler event="overflow"><![CDATA[
290 // filter underflow events which were dispatched on nested scrollboxes
291 if (event.target != this)
292 return;
293
294 // Ignore vertical events.
295 if (event.detail == 0) {
296 return;
297 }
298
299 this._scrollButtonDownBox.collapsed = false;
300 this._scrollButtonDownBoxAnimate.collapsed = false;
301 ]]></handler>
302
onUpdatedScrollButtonsDisabledState
303 <handler event="UpdatedScrollButtonsDisabledState"><![CDATA[
304 // filter underflow events which were dispatched on nested scrollboxes
305 if (event.target != this)
306 return;
307
308 // fix for bug #352353
309 // unlike the scrollup button on the tab strip (which is a
310 // simple toolbarbutton) the scrolldown button is
311 // a more complicated stack of boxes and a toolbarbutton
312 // so that we can animate when a tab is opened offscreen.
313 // in order to style the box with the actual background image
314 // we need to manually set the disable state to match the
315 // disable state of the toolbarbutton.
316 this._scrollButtonDownBox
317 .setAttribute("disabled", this._scrollButtonDown.disabled);
318 ]]></handler>
319
320 </handlers>
321 </binding>
322
323 <binding id="tabmail-tabs"
324 extends="chrome://global/content/bindings/tabbox.xml#tabs">
325 <content>
326 <xul:arrowscrollbox anonid="arrowscrollbox" class="tabmail-arrowscrollbox" flex="1"
327 xbl:inherits="smoothscroll" orient="horizontal" style="min-width: 1px;">
328 <children includes="tab"/>
329 </xul:arrowscrollbox>
330 <xul:stack align="center" pack="end" class="tabs-alltabs-stack">
331 <xul:hbox flex="1" class="tabs-alltabs-box" anonid="alltabs-box"/>
332 <xul:hbox flex="1" class="tabs-alltabs-box-animate"
333 anonid="alltabs-box-animate"/>
334 <xul:toolbarbutton class="tabs-alltabs-button" type="menu"
335 anonid="alltabs-button"
336 tooltipstring="&listAllTabs.label;">
337 <xul:menupopup class="tabs-alltabs-popup"
338 anonid="alltabs-popup"
339 position="after_end"/>
340 </xul:toolbarbutton>
341 </xul:stack>
342 <xul:hbox class="tabs-closebutton-box" align="center" pack="end" anonid="tabstrip-closebutton">
343 <xul:toolbarbutton class="close-button tabs-closebutton"/>
344 </xul:hbox>
345 </content>
346 <implementation implements="nsITimerCallback, nsIDOMEventListener">
constructor
347 <constructor>
348 <![CDATA[
349 var pb2 =
350 Components.classes['@mozilla.org/preferences-service;1'].
351 getService(Components.interfaces.nsIPrefBranch2);
352
353 try {
354 this.mTabMinWidth = pb2.getIntPref("mail.tabs.tabMinWidth");
355 } catch (e) {
356 }
357 try {
358 this.mTabMaxWidth = pb2.getIntPref("mail.tabs.tabMaxWidth");
359 } catch (e) {
360 }
361 try {
362 this.mTabClipWidth = pb2.getIntPref("mail.tabs.tabClipWidth");
363 } catch (e) {
364 }
365 try {
366 this.mCloseButtons = pb2.getIntPref("mail.tabs.closeButtons");
367 } catch (e) {
368 }
369
370 this.firstChild.minWidth = this.mTabMinWidth;
371 this.firstChild.maxWidth = this.mTabMaxWidth;
372 this.adjustTabstrip();
373
374 pb2.addObserver("mail.tabs.closeButtons",
375 this._prefObserver, false);
376
377 window.addEventListener("resize", this, false);
378
379 // Listen to overflow/underflow events on the tabstrip,
380 // we cannot put these as xbl handlers on the entire binding because
381 // they would also get called for the all-tabs popup scrollbox.
382 // Also, we can't rely on event.target becuase these are all
383 // anonymous nodes.
384 this.mTabstrip.addEventListener("overflow", this, false);
385 this.mTabstrip.addEventListener("underflow", this, false);
386 ]]>
387 </constructor>
388
destructor
389 <destructor>
390 <![CDATA[
391 var pb2 =
392 Components.classes['@mozilla.org/preferences-service;1'].
393 getService(Components.interfaces.nsIPrefBranch2);
394 pb2.removeObserver("mail.tabs.closeButtons", this._prefObserver);
395
396 // Release timer to avoid reference cycles.
397 if (this._animateTimer) {
398 this._animateTimer.cancel();
399 this._animateTimer = null;
400 }
401
402 this.mTabstrip.removeEventListener("overflow", this, false);
403 this.mTabstrip.removeEventListener("underflow", this, false);
404 ]]>
405 </destructor>
406
field_mTabstripWidth
407 <field name="mTabstripWidth">0</field>
408
field_mTabstrip
409 <field name="mTabstrip">
410 document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
411 </field>
412
field_mTabstripClosebutton
413 <field name="mTabstripClosebutton">
414 document.getAnonymousElementByAttribute(this, "anonid", "tabstrip-closebutton");
415 </field>
416
field__prefObserver
417 <field name="_prefObserver">({
418 tabbox: this,
419
observe
420 observe: function(subject, topic, data)
421 {
422 if (topic == "nsPref:changed") {
423 switch (data) {
424 case "mail.tabs.closeButtons":
425 subject.QueryInterface(Components.interfaces.nsIPrefBranch);
426 this.tabbox.mCloseButtons = subject.getIntPref("mail.tabs.closeButtons");
427 this.tabbox.adjustTabstrip();
428 break;
429 }
430 }
431 },
432
QueryInterface
433 QueryInterface: function(aIID)
434 {
435 if (aIID.equals(Components.interfaces.nsIObserver) ||
436 aIID.equals(Components.interfaces.nsISupports))
437 return this;
438 throw Components.results.NS_NOINTERFACE;
439 }
440 });
441 </field>
field_mTabMinWidth
442 <field name="mTabMinWidth">100</field>
field_mTabMaxWidth
443 <field name="mTabMaxWidth">250</field>
field_mTabClipWidth
444 <field name="mTabClipWidth">140</field>
field_mCloseButtons
445 <field name="mCloseButtons">1</field>
446
447 <method name="adjustTabstrip">
adjustTabstrip
448 <body><![CDATA[
449 // modes for tabstrip
450 // 0 - activetab = close button on active tab only
451 // 1 - alltabs = close buttons on all tabs
452 // 2 - noclose = no close buttons at all
453 // 3 - closeatend = close button at the end of the tabstrip
454 switch (this.mCloseButtons) {
455 case 0:
456 this.setAttribute("closebuttons", "activetab");
457 break;
458 case 1:
459 var width = this.firstChild.boxObject.width;
460 // 0 width is an invalid value and indicates
461 // an item without display, so ignore.
462 if (width > this.mTabClipWidth || width == 0)
463 this.setAttribute("closebuttons", "alltabs");
464 else
465 this.setAttribute("closebuttons", "activetab");
466 break;
467 case 2:
468 case 3:
469 this.setAttribute("closebuttons", "noclose");
470 break;
471 }
472 this.mTabstripClosebutton.collapsed = this.mCloseButtons != 3;
473 ]]></body>
474 </method>
475
field__mPrefs
476 <field name="_mPrefs">null</field>
477 <property name="mPrefs" readonly="true">
get_mPrefs
478 <getter>
479 <![CDATA[
480 if (!this._mPrefs) {
481 this._mPrefs =
482 Components.classes['@mozilla.org/preferences-service;1'].
483 getService(Components.interfaces.nsIPrefBranch2);
484 }
485 return this._mPrefs;
486 ]]>
487 </getter>
488 </property>
489
490 <method name="_handleTabSelect">
_handleTabSelect
491 <body><![CDATA[
492 this.mTabstrip.ensureElementIsVisible(this.selectedItem);
493 ]]></body>
494 </method>
495
496 <method name="handleEvent">
497 <parameter name="aEvent"/>
handleEvent
498 <body><![CDATA[
499 switch (aEvent.type) {
500 case "overflow":
501 this.setAttribute("overflow", "true");
502 this.mTabstrip.scrollBoxObject
503 .ensureElementIsVisible(this.selectedItem);
504 break;
505 case "underflow":
506 this.removeAttribute("overflow");
507 break;
508 case "resize":
509 var width = this.mTabstrip.boxObject.width;
510 if (width != this.mTabstripWidth) {
511 this.adjustTabstrip();
512 // XXX without this line the tab bar won't budge
513 this.mTabstrip.scrollByPixels(1);
514 this._handleTabSelect();
515 this.mTabstripWidth = width;
516 }
517 break;
518 }
519 ]]></body>
520 </method>
521
522 <field name="mAllTabsPopup">
523 document.getAnonymousElementByAttribute(this,
524 "anonid", "alltabs-popup");
525 </field>
526
field_mAllTabsBoxAnimate
527 <field name="mAllTabsBoxAnimate">
528 document.getAnonymousElementByAttribute(this,
529 "anonid",
530 "alltabs-box-animate");
531 </field>
532
field_mDownBoxAnimate
533 <field name="mDownBoxAnimate">
534 this.mTabstrip._scrollButtonDownBoxAnimate;
535 </field>
536
field_mAllTabsButton
537 <field name="mAllTabsButton">
538 document.getAnonymousElementByAttribute(this,
539 "anonid", "alltabs-button");
540 </field>
541
field__animateTimer
542 <field name="_animateTimer">null</field>
field__animateStep
543 <field name="_animateStep">-1</field>
field__animateDelay
544 <field name="_animateDelay">25</field>
field__animatePercents
545 <field name="_animatePercents">
546 [1.00, 0.85, 0.80, 0.75, 0.71, 0.68, 0.65, 0.62, 0.59, 0.57,
547 0.54, 0.52, 0.50, 0.47, 0.45, 0.44, 0.42, 0.40, 0.38, 0.37,
548 0.35, 0.34, 0.32, 0.31, 0.30, 0.29, 0.28, 0.27, 0.26, 0.25,
549 0.24, 0.23, 0.23, 0.22, 0.22, 0.21, 0.21, 0.21, 0.20, 0.20,
550 0.20, 0.20, 0.20, 0.20, 0.20, 0.20, 0.19, 0.19, 0.19, 0.18,
551 0.18, 0.17, 0.17, 0.16, 0.15, 0.14, 0.13, 0.11, 0.09, 0.06]
552 </field>
553
554 <method name="_stopAnimation">
_stopAnimation
555 <body><![CDATA[
556 if (this._animateStep != -1) {
557 if (this._animateTimer)
558 this._animateTimer.cancel();
559
560 this._animateStep = -1;
561 this.mAllTabsBoxAnimate.style.opacity = 0.0;
562 this.mDownBoxAnimate.style.opacity = 0.0;
563 }
564 ]]></body>
565 </method>
566
567 <method name="_notifyBackgroundTab">
568 <parameter name="aTab"/>
_notifyBackgroundTab
569 <body><![CDATA[
570 var tsbo = this.mTabstrip.scrollBoxObject;
571 var tsboStart = tsbo.screenX;
572 var tsboEnd = tsboStart + tsbo.width;
573
574 var ctbo = aTab.boxObject;
575 var ctboStart = ctbo.screenX;
576 var ctboEnd = ctboStart + ctbo.width;
577
578 // only start the flash timer if the new tab (which was loaded in
579 // the background) is not completely visible
580 if (tsboStart > ctboStart || ctboEnd > tsboEnd) {
581 this._animateStep = 0;
582
583 if (!this._animateTimer)
584 this._animateTimer =
585 Components.classes["@mozilla.org/timer;1"]
586 .createInstance(Components.interfaces.nsITimer);
587 else
588 this._animateTimer.cancel();
589
590 this._animateTimer.initWithCallback(this,
591 this._animateDelay,
592 Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
593 }
594 ]]></body>
595 </method>
596
597 <method name="notify">
598 <parameter name="aTimer"/>
notify
599 <body><![CDATA[
600 if (!document)
601 aTimer.cancel();
602
603 var percent = this._animatePercents[this._animateStep];
604 this.mAllTabsBoxAnimate.style.opacity = percent;
605 this.mDownBoxAnimate.style.opacity = percent;
606
607 if (this._animateStep < (this._animatePercents.length - 1))
608 this._animateStep++;
609 else
610 this._stopAnimation();
611 ]]></body>
612 </method>
613 </implementation>
614 <handlers>
onTabSelect
615 <handler event="TabSelect" action="this._handleTabSelect();"/>
onmouseover
616 <handler event="mouseover"><![CDATA[
617 if (event.originalTarget == this.mAllTabsButton) {
618 this.mAllTabsButton
619 .setAttribute("tooltiptext",
620 this.mAllTabsButton.getAttribute("tooltipstring"));
621 }
622 else
623 this.mAllTabsButton.removeAttribute("tooltiptext");
624 ]]></handler>
625 </handlers>
626 </binding>
627
628 <!-- alltabs-popup binding
629 This binding relies on the structure of the tabbrowser binding.
630 Therefore it should only be used as a child of the tabs element.
631 This binding is exposed as a pseudo-public-API so themes can customize
632 the tabbar appearance without having to be scriptable
633 (see globalBindings.xml in Pinstripe for example).
634 -->
635 <binding id="tabmail-alltabs-popup"
636 extends="chrome://global/content/bindings/popup.xml#popup">
637 <implementation implements="nsIDOMEventListener">
field__xulWindow
638 <field name="_xulWindow">
639 null
640 </field>
641
constructor
642 <constructor><![CDATA[
643 // We cannot cache the XULBrowserWindow object itself since it might
644 // be set after this binding is constructed.
645 try {
646 this._xulWindow =
647 window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
648 .getInterface(Components.interfaces.nsIWebNavigation)
649 .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
650 .treeOwner
651 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
652 .getInterface(Components.interfaces.nsIXULWindow);
653 }
654 catch(ex) { }
655 ]]></constructor>
656
657 <method name="_menuItemOnCommand">
658 <parameter name="aEvent"/>
_menuItemOnCommand
659 <body><![CDATA[
660 var tabcontainer = document.getBindingParent(this);
661 tabcontainer.selectedItem = aEvent.target.tab;
662 ]]></body>
663 </method>
664
665 <method name="_tabOnAttrModified">
666 <parameter name="aEvent"/>
_tabOnAttrModified
667 <body><![CDATA[
668 var menuItem = aEvent.target.mCorrespondingMenuitem;
669 if (menuItem) {
670 var attrName = aEvent.attrName;
671 switch (attrName) {
672 case "label":
673 case "crop":
674 case "busy":
675 case "image":
676 case "selected":
677 if (aEvent.attrChange == aEvent.REMOVAL)
678 menuItem.removeAttribute(attrName);
679 else
680 menuItem.setAttribute(attrName, aEvent.newValue);
681 }
682 }
683 ]]></body>
684 </method>
685
686 <method name="_tabOnTabClose">
687 <parameter name="aEvent"/>
_tabOnTabClose
688 <body><![CDATA[
689 var menuItem = aEvent.target.mCorrespondingMenuitem;
690 if (menuItem)
691 this.removeChild(menuItem);
692 ]]></body>
693 </method>
694
695 <method name="handleEvent">
696 <parameter name="aEvent"/>
handleEvent
697 <body><![CDATA[
698 if (!aEvent.isTrusted)
699 return;
700
701 switch (aEvent.type) {
702 case "command":
703 this._menuItemOnCommand(aEvent);
704 break;
705 case "DOMAttrModified":
706 this._tabOnAttrModified(aEvent);
707 break;
708 case "TabClose":
709 this._tabOnTabClose(aEvent);
710 break;
711 case "TabOpen":
712 this._createTabMenuItem(aEvent.originalTarget);
713 break;
714 case "scroll":
715 this._updateTabsVisibilityStatus();
716 break;
717 }
718 ]]></body>
719 </method>
720
721 <method name="_updateTabsVisibilityStatus">
_updateTabsVisibilityStatus
722 <body><![CDATA[
723 var tabContainer = document.getBindingParent(this);
724 // We don't want menu item decoration unless there is overflow.
725 if (tabContainer.getAttribute("overflow") != "true")
726 return;
727
728 var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
729 for (var i = 0; i < this.childNodes.length; i++) {
730 var curTabBO = this.childNodes[i].tab.boxObject;
731 if (curTabBO.screenX >= tabstripBO.screenX &&
732 curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
733 this.childNodes[i].setAttribute("tabIsVisible", "true");
734 else
735 this.childNodes[i].removeAttribute("tabIsVisible");
736 }
737 ]]></body>
738 </method>
739
740 <method name="_createTabMenuItem">
741 <parameter name="aTab"/>
742 <body><![CDATA[
743 var menuItem = document.createElementNS(
744 "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
745 "menuitem");
746
747 menuItem.setAttribute("class", "menuitem-iconic alltabs-item");
748
749 menuItem.setAttribute("label", aTab.label);
750 menuItem.setAttribute("crop", aTab.getAttribute("crop"));
751 menuItem.setAttribute("image", aTab.getAttribute("image"));
752
753 if (aTab.hasAttribute("busy"))
754 menuItem.setAttribute("busy", aTab.getAttribute("busy"));
755 if (aTab.selected)
756 menuItem.setAttribute("selected", "true");
757
758 // Keep some attributes of the menuitem in sync with its
759 // corresponding tab (e.g. the tab label)
760 aTab.mCorrespondingMenuitem = menuItem;
761 aTab.addEventListener("DOMAttrModified", this, false);
762 aTab.addEventListener("TabClose", this, false);
763 menuItem.tab = aTab;
764 menuItem.addEventListener("command", this, false);
765
766 this.appendChild(menuItem);
767 return menuItem;
768 ]]></body>
769 </method>
770 </implementation>
771
772 <handlers>
773 <handler event="popupshowing">
774 <![CDATA[
775 // set up the menu popup
776 var tabcontainer = document.getBindingParent(this);
777 var tabs = tabcontainer.childNodes;
778
779 // Listen for changes in the tab bar.
780 var tabbrowser = document.getBindingParent(tabcontainer);
781 tabbrowser.addEventListener("TabOpen", this, false);
782 tabcontainer.mTabstrip.addEventListener("scroll", this, false);
783
784 // if an animation is in progress and the user
785 // clicks on the "all tabs" button, stop the animation
786 tabcontainer._stopAnimation();
787
788 for (var i = 0; i < tabs.length; i++) {
789 this._createTabMenuItem(tabs[i]);
790 }
791 this._updateTabsVisibilityStatus();
792 ]]></handler>
793
794 <handler event="popuphiding">
795 <![CDATA[
796 // clear out the menu popup and remove the listeners
797 while (this.hasChildNodes()) {
798 var menuItem = this.lastChild;
799 menuItem.removeEventListener("command", this, false);
800 menuItem.tab.removeEventListener("DOMAttrModified", this, false);
801 menuItem.tab.removeEventListener("TabClose", this, false);
802 menuItem.tab.mCorrespondingMenuitem = null;
803 this.removeChild(menuItem);
804 }
805 var tabcontainer = document.getBindingParent(this);
806 tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
807 document.getBindingParent(tabcontainer).removeEventListener("TabOpen", this, false);
808 ]]></handler>
809
810 <handler event="DOMMenuItemActive">
811 <![CDATA[
812 if (!this._xulWindow || !this._xulWindow.XULBrowserWindow)
813 return;
814
815 var tab = event.target.tab;
816 if (tab) {
817 var statusText = tab.linkedBrowser.currentURI.spec;
818 if (statusText == "about:blank") {
819 // XXXhack: Passing a space here (and not "")
820 // to make sure the the browser implementation would
821 // still consider it a hovered link.
822 statusText = " ";
823 }
824
825 this._xulWindow.XULBrowserWindow.setOverLink(statusText, null);
826 }
827 ]]></handler>
828
829 <handler event="DOMMenuItemInactive">
830 <![CDATA[
831 if (!this._xulWindow || !this._xulWindow.XULBrowserWindow)
832 return;
833
834 this._xulWindow.XULBrowserWindow.setOverLink("", null);
835 ]]></handler>
836 </handlers>
837 </binding>
838 <!-- close-tab-button binding
839 This binding relies on the structure of the tabbrowser binding.
840 Therefore it should only be used as a child of the tab or the tabs
841 element (in both cases, when they are anonymous nodes of <tabbrowser>).
842 This binding is exposed as a pseudo-public-API so themes can customize
843 the tabbar appearance without having to be scriptable
844 (see globalBindings.xml in Pinstripe for example).
845 -->
846 <binding id="tabmail-close-tab-button"
847 extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton-image">
848 <handlers>
onclick
849 <handler event="click" button="0"><![CDATA[
850 var bindingParent = document.getBindingParent(this);
851 if (bindingParent) {
852 var tabbedBrowser = document.getBindingParent(bindingParent);
853 if (bindingParent.localName == "tab") {
854 /* The only sequence in which a second click event (i.e. dblclik)
855 * can be dispatched on an in-tab close button is when it is shown
856 * after the first click (i.e. the first click event was dispatched
857 * on the tab). This happens when we show the close button only on
858 * the active tab. (bug 352021)
859 * The only sequence in which a third click event can be dispatched
860 * on an in-tab close button is when the tab was opened with a
861 * double click on the tabbar. (bug 378344)
862 * In both cases, it is most likely that the close button area has
863 * been accidentally clicked, therefore we do not close the tab.
864 */
865 if (event.detail > 1)
866 return;
867
868 tabbedBrowser.removeTab(bindingParent);
869 tabbedBrowser._blockDblClick = true;
870
871 /* XXXmano hack (see bug 343628):
872 * Since we're removing the event target, if the user
873 * double-clicks this button, the dblclick event will be dispatched
874 * with the tabbar as its event target (and explicit/originalTarget),
875 * which treats that as a mouse gesture for opening a new tab.
876 * In this context, we're manually blocking the dblclick event
877 * (see onTabBarDblClick).
878 */
879 var clickedOnce = false;
enableDblClick
880 function enableDblClick(event) {
881 if (event.detail == 1 && !clickedOnce) {
882 clickedOnce = true;
883 return;
884 }
anon:885:25
885 setTimeout(function() {
886 tabbedBrowser._blockDblClick = false;
887 }, 0);
888 tabbedBrowser.removeEventListener("click", enableDblClick, false);
889 }
890 tabbedBrowser.addEventListener("click", enableDblClick, false);
891 }
892 else // "tabs"
893 tabbedBrowser.removeCurrentTab();
894 }
895 ]]></handler>
ondblclick
896 <handler event="dblclick" button="0" phase="capturing">
897 // for the one-close-button case
898 event.stopPropagation();
899 </handler>
900 </handlers>
901 </binding>
902
903 </bindings>