diff --git a/src/common/connection.py b/src/common/connection.py
index 858f5f4fc..3e466c4d2 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -218,6 +218,7 @@ class Connection:
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
tim = time.localtime(timegm(tim))
encrypted = False
+ chatstate_tag = None
xtags = msg.getTags('x')
encTag = None
decmsg = ''
@@ -225,6 +226,14 @@ class Connection:
if xtag.getNamespace() == common.xmpp.NS_ENCRYPTED:
encTag = xtag
break
+
+ # chatstates - look for chatstate tags in a message
+ children = msg.getChildren()
+ for child in children:
+ if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
+ chatstate_tag = child.getName()
+ break
+
if encTag and USE_GPG:
#decrypt
encmsg = encTag.getData()
@@ -253,9 +262,9 @@ class Connection:
gajim.logger.write('incoming', log_msgtxt, str(msg.getFrom()),
tim = tim)
self.dispatch('MSG', (str(msg.getFrom()), msgtxt, tim, encrypted,
- mtype, subject))
+ mtype, subject, None))
else: # it's type 'chat'
- if not msg.getTag('body'): #no
+ if not msg.getTag('body') and chatstate_tag is None: #no
return
log_msgtxt = msgtxt
if subject:
@@ -263,7 +272,7 @@ class Connection:
gajim.logger.write('incoming', log_msgtxt, str(msg.getFrom()),
tim = tim)
self.dispatch('MSG', (str(msg.getFrom()), msgtxt, tim, encrypted,
- mtype, subject))
+ mtype, subject, chatstate_tag))
# END messageCB
def _presenceCB(self, con, prs):
@@ -859,10 +868,10 @@ class Connection:
self.connection.send(p)
self.dispatch('STATUS', show)
- def send_message(self, jid, msg, keyID, type = 'chat', subject=''):
+ def send_message(self, jid, msg, keyID, type = 'chat', subject='', chatstate = None):
if not self.connection:
return
- if not msg:
+ if not msg and chatstate is None:
return
msgtxt = msg
msgenc = ''
@@ -886,6 +895,12 @@ class Connection:
typ = 'normal')
if msgenc:
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
+
+ # chatstates - if peer supports jep85, send chatstates
+ # please note that the only valid tag inside a message containing a tag is the active event
+ if chatstate != None:
+ msg_iq.setTag(chatstate, {}, namespace='http://jabber.org/protocol/chatstates')
+
self.to_be_sent.append(msg_iq)
gajim.logger.write('outgoing', msg, jid)
self.dispatch('MSGSENT', (jid, msg, keyID))
@@ -939,7 +954,7 @@ class Connection:
{'agent': agent})
return
- def update_user(self, jid, name, groups):
+ def update_contact(self, jid, name, groups):
if self.connection:
self.connection.getRoster().setItem(jid = jid, name = name,
groups = groups)
@@ -1150,6 +1165,7 @@ class Connection:
typ = ptype, show = show, status = status))
def gc_set_role(self, room_jid, nick, role, reason = ''):
+ '''role is for all the life of the room so it's based on nick'''
if not self.connection:
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
@@ -1162,6 +1178,7 @@ class Connection:
self.to_be_sent.append(iq)
def gc_set_affiliation(self, room_jid, jid, affiliation, reason = ''):
+ '''affiliation is for all the life of the room so it's based on jid'''
if not self.connection:
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
diff --git a/src/gajim.py b/src/gajim.py
index 1464ff6c5..dc8266bd6 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -344,9 +344,10 @@ class Interface:
self.remote.raise_signal('GCPresence', (account, array))
def handle_event_msg(self, account, array):
- #('MSG', account, (contact, msg, time, encrypted, msg_type, subject))
+ #('MSG', account, (contact, msg, time, encrypted, msg_type, subject, chatstate_tag))
jid = array[0].split('/')[0]
msg_type = array[4]
+ chatstate_tag = array[6]
if jid.find('@') <= 0:
jid = jid.replace('@', '')
@@ -406,6 +407,17 @@ class Interface:
self.play_sound('next_message_received')
if self.remote:
self.remote.raise_signal('NewMessage', (account, array))
+ if self.windows[account]['chats'].has_key(jid):
+ chat_win = self.windows[account]['chats'][jid]
+ # chatstates - display jep85 events in window
+ if chatstate_tag != None:
+ if chat_win.chatstates[jid] == 'ask':
+ chat_win.chatstates[jid] = 'active'
+ chat_win.print_conversation(jid + ' is now ' + chatstate_tag, jid, 'status', tim = array[2])
+ else:
+ # got no valid jep85 answer, peer does not support it
+ chat_win.chatstates[jid] = -1
+
def handle_event_msgerror(self, account, array):
#('MSGERROR', account, (jid, error_code, error_msg, msg, time))
diff --git a/src/gtkgui.glade b/src/gtkgui.glade
index c47132b41..eaa2b396b 100644
--- a/src/gtkgui.glade
+++ b/src/gtkgui.glade
@@ -9932,7 +9932,6 @@ Custom
-
diff --git a/src/roster_window.py b/src/roster_window.py
index 88373b7d3..5b36d2a3c 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -309,9 +309,30 @@ class RosterWindow:
'show_offline_contacts_menuitem')
profile_avatar_menuitem = self.xml.get_widget('profile_avatar_menuitem')
-
- xm = gtk.glade.XML(GTKGUI_GLADE, 'advanced_menuitem_menu', APP)
- advanced_menuitem_menu = xm.get_widget('advanced_menuitem_menu')
+
+ ui_description = """
+
+
+"""
+
+ menu_description = """
+
+
+
+
+
+"""
if self.add_new_contact_handler_id:
add_new_contact_menuitem.handler_disconnect(
diff --git a/src/tabbed_chat_window.py b/src/tabbed_chat_window.py
index e1c8ee534..2a71e22eb 100644
--- a/src/tabbed_chat_window.py
+++ b/src/tabbed_chat_window.py
@@ -44,6 +44,7 @@ class TabbedChatWindow(chat.Chat):
def __init__(self, user, plugin, account):
chat.Chat.__init__(self, plugin, account, 'tabbed_chat_window')
self.users = {}
+ self.chatstates = {}
self.new_user(user)
self.show_title()
self.xml.signal_connect('on_tabbed_chat_window_destroy',
@@ -52,6 +53,8 @@ class TabbedChatWindow(chat.Chat):
self.on_tabbed_chat_window_delete_event)
self.xml.signal_connect('on_tabbed_chat_window_focus_in_event',
self.on_tabbed_chat_window_focus_in_event)
+ self.xml.signal_connect('on_tabbed_chat_window_focus_out_event',
+ self.on_tabbed_chat_window_focus_out_event)
self.xml.signal_connect('on_tabbed_chat_window_button_press_event',
self.on_chat_window_button_press_event)
self.xml.signal_connect('on_chat_notebook_key_press_event',
@@ -60,7 +63,7 @@ class TabbedChatWindow(chat.Chat):
self.on_chat_notebook_switch_page)
if gajim.config.get('saveposition'):
- # get window position and size from config
+ # get window position and size from config
self.window.move(gajim.config.get('chat-x-position'),
gajim.config.get('chat-y-position'))
self.window.resize(gajim.config.get('chat-width'),
@@ -197,7 +200,7 @@ class TabbedChatWindow(chat.Chat):
return True #stop the propagation of the event
if gajim.config.get('saveposition'):
- # save the window size and position
+ # save the window size and position
x, y = self.window.get_position()
gajim.config.set('chat-x-position', x)
gajim.config.set('chat-y-position', y)
@@ -207,10 +210,28 @@ class TabbedChatWindow(chat.Chat):
def on_tabbed_chat_window_destroy(self, widget):
#clean self.plugin.windows[self.account]['chats']
+ # on window destroy, send 'gone' chatstate
+ self.send_chatstate('gone')
chat.Chat.on_window_destroy(self, widget, 'chats')
def on_tabbed_chat_window_focus_in_event(self, widget, event):
chat.Chat.on_chat_window_focus_in_event(self, widget, event)
+ # on focus in, send 'active' chatstate
+ self.send_chatstate('active')
+
+ def on_tabbed_chat_window_focus_out_event(self, widget, event):
+ gobject.timeout_add(500, self.check_window_state, widget)
+
+ def check_window_state(self, widget):
+ ''' we want: "minimized" or "focus-out"
+ not "focus-out, minimized" or "focus-out" '''
+ new_state = widget.window.get_state()
+ if new_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
+ print 'iconify'
+ self.send_chatstate('inactive')
+ else:
+ print 'just focus-out'
+ self.send_chatstate('paused')
def on_chat_notebook_key_press_event(self, widget, event):
chat.Chat.on_chat_notebook_key_press_event(self, widget, event)
@@ -237,6 +258,9 @@ class TabbedChatWindow(chat.Chat):
if dialog.get_response() != gtk.RESPONSE_OK:
return
+ # chatstates - window is destroyed, send gone
+ self.send_chatstate('gone')
+
chat.Chat.remove_tab(self, jid, 'chats')
if len(self.xmls) > 0:
del self.users[jid]
@@ -275,6 +299,9 @@ class TabbedChatWindow(chat.Chat):
gajim.connections[self.account].request_vcard(user.jid)
self.childs[user.jid].show_all()
+ # chatstates
+ self.chatstates[user.jid] = None
+
def on_message_textview_key_press_event(self, widget, event):
"""When a key is pressed:
if enter is pressed without the shift key, message (if not empty) is sent
@@ -326,7 +353,45 @@ class TabbedChatWindow(chat.Chat):
if event.state & gtk.gdk.CONTROL_MASK: #Ctrl+Down
self.sent_messages_scroll(jid, 'down', widget.get_buffer())
return True # override the default gtk+ thing for ctrl+down
+
+ else:
+ # chatstates
+ # if composing, send chatstate
+ self.send_chatstate('composing')
+ def send_chatstate(self, state):
+ # please read jep-85 to get an idea of this
+ # we keep track of jep85 support by the peer by three extra states: None, -1 and 'ask'
+ # None if no info about peer
+ # -1 if peer does not support jep85
+ # 'ask' if we sent 'active' chatstate and are waiting for reply
+
+ jid = self.get_active_jid()
+
+ # print jid, self.chatstates[jid], state
+ if self.chatstates[jid] == -1:
+ return
+
+ # if current state equals last state, return
+ if self.chatstates[jid] == state:
+ return
+
+ if self.chatstates[jid] is None:
+ # state = 'ask'
+ # send and return
+ return
+
+ if self.chatstates[jid] == 'ask':
+ return
+
+ # if last state was composing, don't send active
+ if self.chatstates[jid] == 'composing' and state == 'active':
+ return
+
+ self.chatstates[jid] = state
+ gajim.connections[self.account].send_message(jid, None, None, chatstate = state)
+
+
def send_message(self, message):
"""Send the message given to the active tab"""
if not message:
@@ -350,7 +415,18 @@ class TabbedChatWindow(chat.Chat):
if self.xmls[jid].get_widget('gpg_togglebutton').get_active():
keyID = self.users[jid].keyID
encrypted = True
- gajim.connections[self.account].send_message(jid, message, keyID)
+
+ # chatstates - if no info about peer, discover
+ if self.chatstates[jid] is None:
+
+ gajim.connections[self.account].send_message(jid, message, keyID, chatstate = 'active')
+ self.chatstates[jid] = 'ask'
+ # if peer supports jep85, send 'active'
+ elif self.chatstates[jid] != -1:
+ gajim.connections[self.account].send_message(jid, message, keyID, chatstate = 'active')
+ else:
+ gajim.connections[self.account].send_message(jid, message, keyID)
+ print self.chatstates[jid]
message_buffer.set_text('', -1)
self.print_conversation(message, jid, jid, encrypted = encrypted)