<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<!DOCTYPE bindings [
  <!ENTITY % chatDTD SYSTEM "chrome://messenger/locale/chat.dtd" >
  %chatDTD;
]>

<bindings id="convBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl"
          xmlns:html="http://www.w3.org/1999/xhtml">

  <binding id="conv" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
    <content>
      <xul:stack class="prplBuddyIcon" mousethrough="always">
        <xul:image class="protoIcon" xbl:inherits="src=iconPrpl,status"/>
        <xul:image class="statusIcon" xbl:inherits="status"/>
      </xul:stack>
      <xul:hbox flex="1" class="conv-hbox" mousethrough="always">
        <xul:label crop="end" flex="1" mousethrough="always"
                   anonid="displayname" class="convDisplayName blistDisplayName"
                   xbl:inherits="value=displayname,status"/>
        <xul:label anonid="unreadCount" class="convUnreadCount"
                   crop="end" mousethrough="never" flex="1000000"
                   xbl:inherits="value=unreadCount"/>
        <xul:button anonid="closeConversationButton" class="closeConversationButton"
                    tooltiptext="&closeConversationButton.tooltip;"/>
      </xul:hbox>
    </content>
    <implementation implements="nsIObserver">

     <constructor>
      <![CDATA[
        new MutationObserver(aMutations => {
          if (!this.convView || !this.convView.loaded)
            return;
          if (this.hasAttribute("selected"))
            this.convView.switchingToPanel();
          else
            this.convView.switchingAwayFromPanel();
        }).observe(this, {attributes: true, attributeFilter: ["selected"]});
      ]]>
     </constructor>

     <destructor>
      <![CDATA[
        if (this.conv) {
          this.conv.removeObserver(this);
          delete this.conv;
        }
      ]]>
     </destructor>

     <field name="convView">null</field>
     <method name="build">
      <parameter name="aConv"/>
      <body>
      <![CDATA[
        this.conv = aConv;
        this.conv.addObserver(this);
        this.update();
      ]]>
      </body>
     </method>

     <property name="displayName"
               onget="return this.conv.title;"/>

     <!-- for compatibility with the  imgroup sortComparator -->
     <property name="contact"
               onget="return this.conv;"/>

     <!-- nsIObserver implementation -->
     <method name="observe">
       <parameter name="aSubject"/>
       <parameter name="aTopic"/>
       <parameter name="aData"/>
       <body>
       <![CDATA[
         if (aTopic == "target-prpl-conversation-changed" ||
             aTopic == "unread-message-count-changed" ||
             aTopic == "update-conv-title" ||
             aTopic == "update-buddy-status" ||
             aTopic == "update-buddy-status" ||
             aTopic == "update-conv-chatleft" ||
             aTopic == "update-conv-chatjoining" ||
             aTopic == "chat-update-topic")
           this.update();
         if (aTopic == "update-conv-title")
           this.group.updateContactPosition(this.conv);
       ]]>
       </body>
     </method>

     <property name="selected">
       <getter><![CDATA[
           return gChatTab && gChatTab.tabNode.selected &&
                  this.getAttribute("selected") == "true";
         ]]></getter>
       <setter><![CDATA[
         if (val)
           this.setAttribute("selected", "true");
         else
           this.removeAttribute("selected");

         return val;
         ]]></setter>
     </property>

     <field name="directedUnreadCount">0</field>
     <method name="update">
      <body>
      <![CDATA[
        this.setAttribute("displayname", this.displayName);
        if (this.selected && document.hasFocus()) {
          if (this.convView && this.convView.loaded) {
            this.conv.markAsRead();
            this.directedUnreadCount = 0;
            chatHandler.updateTitle();
            chatHandler.updateChatButtonState();
          }
          this.setAttribute("unreadCount", "");
          this.removeAttribute("unread");
          this.removeAttribute("attention");
        }
        else {
          let unreadCount = this.conv.unreadIncomingMessageCount;
          if (!unreadCount)
            unreadCount = "";
          else {
            this.setAttribute("unread", "true");
            let directedMessages = unreadCount;
            if (this.conv.isChat) {
              directedMessages = this.conv.unreadTargetedMessageCount;
              if (directedMessages)
                this.setAttribute("attention", "true");
              unreadCount = directedMessages + "/" + unreadCount;
            }
            unreadCount = "(" + unreadCount + ")";
            if (directedMessages > this.directedUnreadCount)
              this.directedUnreadCount = directedMessages;
          }
          this.setAttribute("unreadCount", unreadCount);
          chatHandler.updateTitle();
        }

        if (this.conv.isChat) {
          if (this.conv.joining)
            this.setAttribute("status", "joining");
          else if (!this.conv.account.connected || this.conv.left)
            this.setAttribute("status", "left");
          else
            this.removeAttribute("status");
        }
        else {
          let statusType = Ci.imIStatusInfo.STATUS_UNKNOWN;
          let buddy = this.conv.buddy;
          if (buddy && buddy.account.connected)
            statusType = buddy.statusType;
          if (!("Status" in window))
            Components.utils.import("resource:///modules/imStatusUtils.jsm");
          this.setAttribute("status", Status.toAttribute(statusType));
        }

        this.setAttribute("iconPrpl",
                          this.conv.account.protocol.iconBaseURI + "icon.png");
      ]]>
      </body>
     </method>

     <method name="destroy">
      <body>
      <![CDATA[
        if (this.conv)
          this.conv.removeObserver(this);
        if (this.convView) {
          this.convView.destroy();
          this.convView.remove();
        }

        // If the conversation we are destroying was selected, we should
        // select something else, but the 'select' event handler of
        // the listbox will choke while updating the Chat tab title if
        // there are conversation nodes associated with a conversation
        // that no longer exists from the chat core's point of view, so
        // we do the actual selection change only after this conversation
        // item is fully destroyed and removed from the list.
        let newSelectedItem;
        let list = this.parentNode;
        if (list.selectedItem == this)
          newSelectedItem = this.previousSibling;

        if (this.log) {
          this.hidden = true;
          delete this.log;
        }
        else {
          this.remove();
          delete this.conv;
        }
        if (newSelectedItem)
          list.selectedItem = newSelectedItem;
      ]]>
      </body>
     </method>

     <method name="closeConversation">
      <body>
       <![CDATA[
         if (this.conv)
           this.conv.close();
         else
           this.destroy();
       ]]>
      </body>
     </method>

     <method name="keyPress">
      <parameter name="aEvent"/>
      <body>
      <![CDATA[
        // If Enter or Return is pressed, focus the input box.
        if (aEvent.keyCode == aEvent.DOM_VK_RETURN) {
          this.convView.focus();
          return;
        }

        let isMac = "nsILocalFileMac" in Components.interfaces;
        let accelKeyPressed = isMac ? aEvent.metaKey : aEvent.ctrlKey;
        // If a character was typed or the accel+v copy shortcut was used,
        // focus the input box and resend the key event.
        if (aEvent.charCode != 0 && !aEvent.altKey &&
            (accelKeyPressed && aEvent.charCode == 'v'.charCodeAt(0) ||
             !aEvent.ctrlKey && !aEvent.metaKey)) {
          this.convView.focus();
          const masks = Components.interfaces.nsIDOMNSEvent;
          let modifiers = 0;
          if (aEvent.shiftKey)
            modifiers |= masks.SHIFT_MASK;
          if (aEvent.ctrlKey)
            modifiers |= masks.CONTROL_MASK;
          if (aEvent.metaKey)
            modifiers |= masks.META_MASK;
          window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                .getInterface(Components.interfaces.nsIDOMWindowUtils)
                .sendKeyEvent(aEvent.type, aEvent.keyCode, aEvent.charCode,
                              modifiers);
          aEvent.preventDefault();
        }
      ]]>
      </body>
     </method>
    </implementation>
    <handlers>
     <handler event="mousedown" phase="capturing">
       <![CDATA[
         let anonid = event.originalTarget.getAttribute("anonid");
         if (anonid == "closeConversationButton") {
           closeConversation();
           event.stopPropagation();
           event.preventDefault();
         }
       ]]>
     </handler>
    </handlers>
  </binding>
</bindings>
