From c9c066ae336fd315d87de39894f06ce3aca3a2fc Mon Sep 17 00:00:00 2001
From: Florent Le Coz <louiz@louiz.org>
Date: Fri, 27 Jan 2012 19:13:04 +0100
Subject: [Link Mauve] Add the ability to send and receive Attention
 (XEP-0224).

---
 src/connection.py |  1 +
 src/core.py       | 20 ++++++++++++++++++
 src/tabs.py       | 62 +++++++++++++++++++++++++++++++++++++++++++++++++------
 src/theming.py    |  6 ++++--
 4 files changed, 81 insertions(+), 8 deletions(-)

diff --git a/src/connection.py b/src/connection.py
index 1079b5a6..74a21cdb 100644
--- a/src/connection.py
+++ b/src/connection.py
@@ -59,6 +59,7 @@ class Connection(sleekxmpp.ClientXMPP):
             self.register_plugin('xep_0092', pconfig=info)
         if config.get('send_time', 'true') == 'true':
             self.register_plugin('xep_0202')
+        self.register_plugin('xep_0224')
 
     def start(self):
         # TODO, try multiple servers
diff --git a/src/core.py b/src/core.py
index 9b74c1a5..4364734e 100644
--- a/src/core.py
+++ b/src/core.py
@@ -194,6 +194,7 @@ class Core(object):
         self.xmpp.add_event_handler("chatstate_paused", self.on_chatstate_paused)
         self.xmpp.add_event_handler("chatstate_gone", self.on_chatstate_gone)
         self.xmpp.add_event_handler("chatstate_inactive", self.on_chatstate_inactive)
+        self.xmpp.add_event_handler("attention", self.on_attention)
         self.xmpp.register_handler(Callback('ALL THE STANZAS', connection.MatchAll(None), self.incoming_stanza))
 
         self.timed_events = set()
@@ -503,6 +504,21 @@ class Core(object):
             tab.input.refresh()
             self.doupdate()
 
+    def on_attention(self, message):
+        jid_from = message['from']
+        self.information('%s requests your attention!' % jid_from, 'Info')
+        for tab in self.tabs:
+            if tab.get_name() == jid_from:
+                tab.state = 'attention'
+                self.refresh_tab_win()
+                return
+        for tab in self.tabs:
+            if tab.get_name() == jid_from.bare:
+                tab.state = 'attention'
+                self.refresh_tab_win()
+                return
+        self.information('%s tab not found.' % jid_from, 'Error')
+
     def open_new_form(self, form, on_cancel, on_send, **kwargs):
         """
         Open a new tab containing the form
@@ -1075,6 +1091,10 @@ class Core(object):
         - A Muc with an highlight
         - A Muc with any new message
         """
+        for tab in self.tabs:
+           if tab.state == 'attention':
+               self.command_win('%s' % tab.nb)
+               return
         for tab in self.tabs:
             if tab.state == 'private':
                 self.command_win('%s' % tab.nb)
diff --git a/src/tabs.py b/src/tabs.py
index 7df200b0..9bc6e24c 100644
--- a/src/tabs.py
+++ b/src/tabs.py
@@ -67,7 +67,7 @@ STATE_COLORS = {
         'private': lambda: get_theme().COLOR_TAB_PRIVATE,
         'normal': lambda: get_theme().COLOR_TAB_NORMAL,
         'current': lambda: get_theme().COLOR_TAB_CURRENT,
-#        'attention': lambda: get_theme().COLOR_TAB_ATTENTION,
+        'attention': lambda: get_theme().COLOR_TAB_ATTENTION,
     }
 
 VERTICAL_STATE_COLORS = {
@@ -77,7 +77,7 @@ VERTICAL_STATE_COLORS = {
         'private': lambda: get_theme().COLOR_VERTICAL_TAB_PRIVATE,
         'normal': lambda: get_theme().COLOR_VERTICAL_TAB_NORMAL,
         'current': lambda: get_theme().COLOR_VERTICAL_TAB_CURRENT,
-#        'attention': lambda: get_theme().COLOR_VERTICAL_TAB_ATTENTION,
+        'attention': lambda: get_theme().COLOR_VERTICAL_TAB_ATTENTION,
     }
 
 
@@ -88,7 +88,7 @@ STATE_PRIORITY = {
         'message': 1,
         'highlight': 2,
         'private': 2,
-#        'attention': 3
+        'attention': 3
     }
 
 class Tab(object):
@@ -156,7 +156,7 @@ class Tab(object):
             log.debug("Invalid value for tab state: %s", value)
         elif STATE_PRIORITY[value] < STATE_PRIORITY[self._state] and \
                 value != 'current':
-            log.debug("Did not set status because of lower priority, asked: %s, kept: %s", (value, self.state))
+            log.debug("Did not set status because of lower priority, asked: %s, kept: %s", (value, self._state))
         else:
             self._state = value
 
@@ -349,6 +349,7 @@ class ChatTab(Tab):
         # if that’s None, then no paused chatstate was sent recently
         # if that’s a weakref returning None, then a paused chatstate was sent
         # since the last input
+        self.remote_supports_attention = False
         self.key_func['M-v'] = self.move_separator
         self.key_func['M-/'] = self.last_words_completion
         self.key_func['^M'] = self.on_enter
@@ -495,6 +496,7 @@ class ChatTab(Tab):
     def command_say(self, line):
         raise NotImplementedError
 
+
 class MucTab(ChatTab):
     """
     The tab containing a multi-user-chat room.
@@ -1359,6 +1361,7 @@ class PrivateTab(ChatTab):
         self._text_buffer.add_window(self.text_win)
         self.info_header = windows.PrivateInfoWin()
         self.input = windows.MessageInput()
+        self.check_attention()
         # keys
         self.key_func['^I'] = self.completion
         # commands
@@ -1379,7 +1382,7 @@ class PrivateTab(ChatTab):
     def completion(self):
         self.complete_commands(self.input)
 
-    def command_say(self, line):
+    def command_say(self, line, attention=False):
         if not self.on:
             return
         msg = self.core.xmpp.make_message(self.get_name())
@@ -1396,12 +1399,35 @@ class PrivateTab(ChatTab):
         if config.get_by_tabname('send_chat_states', 'true', self.general_jid, True) == 'true' and self.remote_wants_chatstates is not False:
             needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
             msg['chat_state'] = needed
+        if attention and self.remote_supports_attention:
+            msg['attention'] = True
         self.core.events.trigger('private_say_after', msg, self)
         msg.send()
         self.cancel_paused_delay()
         self.text_win.refresh()
         self.input.refresh()
 
+    def command_attention(self, message=''):
+        if message is not '':
+            self.command_say(message, attention=True)
+        else:
+            msg = self.core.xmpp.make_message(self.get_name())
+            msg['type'] = 'chat'
+            msg['attention'] = True
+            msg.send()
+
+    def check_attention(self):
+        self.core.xmpp.plugin['xep_0030'].get_info(jid=self.get_name(), block=False, timeout=5, callback=self.on_attention_checked)
+
+    def on_attention_checked(self, iq):
+        if 'urn:xmpp:attention:0' in iq['disco_info'].get_features():
+            self.core.information('Attention is supported!', 'Info')
+            self.remote_supports_attention = True
+            self.commands['attention'] =  (self.command_attention, _('Usage: /attention [message]\nAttention: Require the attention of the contact. Can also send a message along with the attention.'), None)
+        else:
+            self.core.information('Attention is not supported. :(', 'Info')
+            self.remote_supports_attention = False
+
     def command_unquery(self, arg):
         """
         /unquery
@@ -2096,6 +2122,7 @@ class ConversationTab(ChatTab):
         self.upper_bar = windows.ConversationStatusMessageWin()
         self.info_header = windows.ConversationInfoWin()
         self.input = windows.MessageInput()
+        self.check_attention()
         # keys
         self.key_func['^I'] = self.completion
         # commands
@@ -2125,7 +2152,7 @@ class ConversationTab(ChatTab):
     def completion(self):
         self.complete_commands(self.input)
 
-    def command_say(self, line):
+    def command_say(self, line, attention=False):
         msg = self.core.xmpp.make_message(self.get_name())
         msg['type'] = 'chat'
         msg['body'] = line
@@ -2141,6 +2168,8 @@ class ConversationTab(ChatTab):
         if config.get_by_tabname('send_chat_states', 'true', self.general_jid, True) == 'true' and self.remote_wants_chatstates is not False:
             needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
             msg['chat_state'] = needed
+        if attention and self.remote_supports_attention:
+            msg['attention'] = True
         self.core.events.trigger('conversation_say_after', msg, self)
         msg.send()
         logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line)
@@ -2160,6 +2189,27 @@ class ConversationTab(ChatTab):
             self.refresh()
             self.core.doupdate()
 
+    def command_attention(self, message=''):
+        if message is not '':
+            self.command_say(message, attention=True)
+        else:
+            msg = self.core.xmpp.make_message(self.get_name())
+            msg['type'] = 'chat'
+            msg['attention'] = True
+            msg.send()
+
+    def check_attention(self):
+        self.core.xmpp.plugin['xep_0030'].get_info(jid=self.get_name(), block=False, timeout=5, callback=self.on_attention_checked)
+
+    def on_attention_checked(self, iq):
+        if 'urn:xmpp:attention:0' in iq['disco_info'].get_features():
+            self.core.information('Attention is supported!', 'Info')
+            self.remote_supports_attention = True
+            self.commands['attention'] =  (self.command_attention, _('Usage: /attention [message]\nAttention: Require the attention of the contact. Can also send a message along with the attention.'), None)
+        else:
+            self.core.information('Attention is not supported. :(', 'Info')
+            self.remote_supports_attention = False
+
     def command_unquery(self, arg):
         self.core.close_tab()
 
diff --git a/src/theming.py b/src/theming.py
index 25188f81..fba33366 100644
--- a/src/theming.py
+++ b/src/theming.py
@@ -124,15 +124,17 @@ class Theme(object):
     COLOR_TAB_NORMAL = (7, 4)
     COLOR_TAB_CURRENT = (7, 6)
     COLOR_TAB_NEW_MESSAGE = (7, 5)
-    COLOR_TAB_HIGHLIGHT = (7, 1)
+    COLOR_TAB_HIGHLIGHT = (7, 3)
     COLOR_TAB_PRIVATE = (7, 2)
+    COLOR_TAB_ATTENTION = (7, 1)
     COLOR_TAB_DISCONNECTED = (7, 8)
 
     COLOR_VERTICAL_TAB_NORMAL = (4, -1)
     COLOR_VERTICAL_TAB_CURRENT = (7, 4)
     COLOR_VERTICAL_TAB_NEW_MESSAGE = (5, -1)
-    COLOR_VERTICAL_TAB_HIGHLIGHT = (1, -1)
+    COLOR_VERTICAL_TAB_HIGHLIGHT = (3, -1)
     COLOR_VERTICAL_TAB_PRIVATE = (2, -1)
+    COLOR_VERTICAL_TAB_ATTENTION = (1, -1)
     COLOR_VERTICAL_TAB_DISCONNECTED = (8, -1)
 
     # Nickname colors
-- 
cgit v1.2.3