[peralta] Chat State Notifications inital patch. I assume that every wm will focus-out before iconify. If that is not the case I am going to use window-state-changed too [to be tested]
This commit is contained in:
parent
cc7c233e0d
commit
d6c9c7cbc6
|
@ -218,6 +218,7 @@ class Connection:
|
||||||
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
|
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
|
||||||
tim = time.localtime(timegm(tim))
|
tim = time.localtime(timegm(tim))
|
||||||
encrypted = False
|
encrypted = False
|
||||||
|
chatstate_tag = None
|
||||||
xtags = msg.getTags('x')
|
xtags = msg.getTags('x')
|
||||||
encTag = None
|
encTag = None
|
||||||
decmsg = ''
|
decmsg = ''
|
||||||
|
@ -225,6 +226,14 @@ class Connection:
|
||||||
if xtag.getNamespace() == common.xmpp.NS_ENCRYPTED:
|
if xtag.getNamespace() == common.xmpp.NS_ENCRYPTED:
|
||||||
encTag = xtag
|
encTag = xtag
|
||||||
break
|
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:
|
if encTag and USE_GPG:
|
||||||
#decrypt
|
#decrypt
|
||||||
encmsg = encTag.getData()
|
encmsg = encTag.getData()
|
||||||
|
@ -253,9 +262,9 @@ class Connection:
|
||||||
gajim.logger.write('incoming', log_msgtxt, str(msg.getFrom()),
|
gajim.logger.write('incoming', log_msgtxt, str(msg.getFrom()),
|
||||||
tim = tim)
|
tim = tim)
|
||||||
self.dispatch('MSG', (str(msg.getFrom()), msgtxt, tim, encrypted,
|
self.dispatch('MSG', (str(msg.getFrom()), msgtxt, tim, encrypted,
|
||||||
mtype, subject))
|
mtype, subject, None))
|
||||||
else: # it's type 'chat'
|
else: # it's type 'chat'
|
||||||
if not msg.getTag('body'): #no <body>
|
if not msg.getTag('body') and chatstate_tag is None: #no <body>
|
||||||
return
|
return
|
||||||
log_msgtxt = msgtxt
|
log_msgtxt = msgtxt
|
||||||
if subject:
|
if subject:
|
||||||
|
@ -263,7 +272,7 @@ class Connection:
|
||||||
gajim.logger.write('incoming', log_msgtxt, str(msg.getFrom()),
|
gajim.logger.write('incoming', log_msgtxt, str(msg.getFrom()),
|
||||||
tim = tim)
|
tim = tim)
|
||||||
self.dispatch('MSG', (str(msg.getFrom()), msgtxt, tim, encrypted,
|
self.dispatch('MSG', (str(msg.getFrom()), msgtxt, tim, encrypted,
|
||||||
mtype, subject))
|
mtype, subject, chatstate_tag))
|
||||||
# END messageCB
|
# END messageCB
|
||||||
|
|
||||||
def _presenceCB(self, con, prs):
|
def _presenceCB(self, con, prs):
|
||||||
|
@ -859,10 +868,10 @@ class Connection:
|
||||||
self.connection.send(p)
|
self.connection.send(p)
|
||||||
self.dispatch('STATUS', show)
|
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:
|
if not self.connection:
|
||||||
return
|
return
|
||||||
if not msg:
|
if not msg and chatstate is None:
|
||||||
return
|
return
|
||||||
msgtxt = msg
|
msgtxt = msg
|
||||||
msgenc = ''
|
msgenc = ''
|
||||||
|
@ -886,6 +895,12 @@ class Connection:
|
||||||
typ = 'normal')
|
typ = 'normal')
|
||||||
if msgenc:
|
if msgenc:
|
||||||
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(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 <body> 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)
|
self.to_be_sent.append(msg_iq)
|
||||||
gajim.logger.write('outgoing', msg, jid)
|
gajim.logger.write('outgoing', msg, jid)
|
||||||
self.dispatch('MSGSENT', (jid, msg, keyID))
|
self.dispatch('MSGSENT', (jid, msg, keyID))
|
||||||
|
@ -939,7 +954,7 @@ class Connection:
|
||||||
{'agent': agent})
|
{'agent': agent})
|
||||||
return
|
return
|
||||||
|
|
||||||
def update_user(self, jid, name, groups):
|
def update_contact(self, jid, name, groups):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
self.connection.getRoster().setItem(jid = jid, name = name,
|
self.connection.getRoster().setItem(jid = jid, name = name,
|
||||||
groups = groups)
|
groups = groups)
|
||||||
|
@ -1150,6 +1165,7 @@ class Connection:
|
||||||
typ = ptype, show = show, status = status))
|
typ = ptype, show = show, status = status))
|
||||||
|
|
||||||
def gc_set_role(self, room_jid, nick, role, reason = ''):
|
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:
|
if not self.connection:
|
||||||
return
|
return
|
||||||
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
|
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
|
||||||
|
@ -1162,6 +1178,7 @@ class Connection:
|
||||||
self.to_be_sent.append(iq)
|
self.to_be_sent.append(iq)
|
||||||
|
|
||||||
def gc_set_affiliation(self, room_jid, jid, affiliation, reason = ''):
|
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:
|
if not self.connection:
|
||||||
return
|
return
|
||||||
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
|
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
|
||||||
|
|
14
src/gajim.py
14
src/gajim.py
|
@ -344,9 +344,10 @@ class Interface:
|
||||||
self.remote.raise_signal('GCPresence', (account, array))
|
self.remote.raise_signal('GCPresence', (account, array))
|
||||||
|
|
||||||
def handle_event_msg(self, 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]
|
jid = array[0].split('/')[0]
|
||||||
msg_type = array[4]
|
msg_type = array[4]
|
||||||
|
chatstate_tag = array[6]
|
||||||
if jid.find('@') <= 0:
|
if jid.find('@') <= 0:
|
||||||
jid = jid.replace('@', '')
|
jid = jid.replace('@', '')
|
||||||
|
|
||||||
|
@ -406,6 +407,17 @@ class Interface:
|
||||||
self.play_sound('next_message_received')
|
self.play_sound('next_message_received')
|
||||||
if self.remote:
|
if self.remote:
|
||||||
self.remote.raise_signal('NewMessage', (account, array))
|
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):
|
def handle_event_msgerror(self, account, array):
|
||||||
#('MSGERROR', account, (jid, error_code, error_msg, msg, time))
|
#('MSGERROR', account, (jid, error_code, error_msg, msg, time))
|
||||||
|
|
|
@ -9932,7 +9932,6 @@ Custom</property>
|
||||||
<signal name="event" handler="on_tabbed_chat_window_event"/>
|
<signal name="event" handler="on_tabbed_chat_window_event"/>
|
||||||
<signal name="button_press_event" handler="on_tabbed_chat_window_button_press_event" last_modification_time="Fri, 24 Jun 2005 23:54:20 GMT"/>
|
<signal name="button_press_event" handler="on_tabbed_chat_window_button_press_event" last_modification_time="Fri, 24 Jun 2005 23:54:20 GMT"/>
|
||||||
<signal name="focus_out_event" handler="on_tabbed_chat_window_focus_out_event" last_modification_time="Mon, 18 Jul 2005 22:07:30 GMT"/>
|
<signal name="focus_out_event" handler="on_tabbed_chat_window_focus_out_event" last_modification_time="Mon, 18 Jul 2005 22:07:30 GMT"/>
|
||||||
<signal name="window_state_event" handler="on_tabbed_chat_window_state_changed" last_modification_time="Mon, 18 Jul 2005 22:21:37 GMT"/>
|
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<widget class="GtkNotebook" id="chat_notebook">
|
<widget class="GtkNotebook" id="chat_notebook">
|
||||||
|
|
|
@ -310,8 +310,29 @@ class RosterWindow:
|
||||||
profile_avatar_menuitem = self.xml.get_widget('profile_avatar_menuitem')
|
profile_avatar_menuitem = self.xml.get_widget('profile_avatar_menuitem')
|
||||||
|
|
||||||
|
|
||||||
xm = gtk.glade.XML(GTKGUI_GLADE, 'advanced_menuitem_menu', APP)
|
ui_description = """
|
||||||
advanced_menuitem_menu = xm.get_widget('advanced_menuitem_menu')
|
<ui>
|
||||||
|
<menu action='advanced_menuitem_menu'>
|
||||||
|
<menu action='A'>
|
||||||
|
%(menu)s
|
||||||
|
</menu>
|
||||||
|
<menu action='B'>
|
||||||
|
%(menu)s
|
||||||
|
</menu>
|
||||||
|
<menu action='C'>
|
||||||
|
%(menu)s
|
||||||
|
</menu>
|
||||||
|
<menuitem action="Quit" />
|
||||||
|
</menu>
|
||||||
|
</ui>"""
|
||||||
|
|
||||||
|
menu_description = """
|
||||||
|
<menuitem action='Send Single Message' />
|
||||||
|
<menuitem action='XML Console' />
|
||||||
|
<separator />
|
||||||
|
<menuitem action='Administrator' />
|
||||||
|
<menuitem action='Test5' />
|
||||||
|
"""
|
||||||
|
|
||||||
if self.add_new_contact_handler_id:
|
if self.add_new_contact_handler_id:
|
||||||
add_new_contact_menuitem.handler_disconnect(
|
add_new_contact_menuitem.handler_disconnect(
|
||||||
|
|
|
@ -44,6 +44,7 @@ class TabbedChatWindow(chat.Chat):
|
||||||
def __init__(self, user, plugin, account):
|
def __init__(self, user, plugin, account):
|
||||||
chat.Chat.__init__(self, plugin, account, 'tabbed_chat_window')
|
chat.Chat.__init__(self, plugin, account, 'tabbed_chat_window')
|
||||||
self.users = {}
|
self.users = {}
|
||||||
|
self.chatstates = {}
|
||||||
self.new_user(user)
|
self.new_user(user)
|
||||||
self.show_title()
|
self.show_title()
|
||||||
self.xml.signal_connect('on_tabbed_chat_window_destroy',
|
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.on_tabbed_chat_window_delete_event)
|
||||||
self.xml.signal_connect('on_tabbed_chat_window_focus_in_event',
|
self.xml.signal_connect('on_tabbed_chat_window_focus_in_event',
|
||||||
self.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.xml.signal_connect('on_tabbed_chat_window_button_press_event',
|
||||||
self.on_chat_window_button_press_event)
|
self.on_chat_window_button_press_event)
|
||||||
self.xml.signal_connect('on_chat_notebook_key_press_event',
|
self.xml.signal_connect('on_chat_notebook_key_press_event',
|
||||||
|
@ -207,10 +210,28 @@ class TabbedChatWindow(chat.Chat):
|
||||||
|
|
||||||
def on_tabbed_chat_window_destroy(self, widget):
|
def on_tabbed_chat_window_destroy(self, widget):
|
||||||
#clean self.plugin.windows[self.account]['chats']
|
#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')
|
chat.Chat.on_window_destroy(self, widget, 'chats')
|
||||||
|
|
||||||
def on_tabbed_chat_window_focus_in_event(self, widget, event):
|
def on_tabbed_chat_window_focus_in_event(self, widget, event):
|
||||||
chat.Chat.on_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):
|
def on_chat_notebook_key_press_event(self, widget, event):
|
||||||
chat.Chat.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:
|
if dialog.get_response() != gtk.RESPONSE_OK:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# chatstates - window is destroyed, send gone
|
||||||
|
self.send_chatstate('gone')
|
||||||
|
|
||||||
chat.Chat.remove_tab(self, jid, 'chats')
|
chat.Chat.remove_tab(self, jid, 'chats')
|
||||||
if len(self.xmls) > 0:
|
if len(self.xmls) > 0:
|
||||||
del self.users[jid]
|
del self.users[jid]
|
||||||
|
@ -275,6 +299,9 @@ class TabbedChatWindow(chat.Chat):
|
||||||
gajim.connections[self.account].request_vcard(user.jid)
|
gajim.connections[self.account].request_vcard(user.jid)
|
||||||
self.childs[user.jid].show_all()
|
self.childs[user.jid].show_all()
|
||||||
|
|
||||||
|
# chatstates
|
||||||
|
self.chatstates[user.jid] = None
|
||||||
|
|
||||||
def on_message_textview_key_press_event(self, widget, event):
|
def on_message_textview_key_press_event(self, widget, event):
|
||||||
"""When a key is pressed:
|
"""When a key is pressed:
|
||||||
if enter is pressed without the shift key, message (if not empty) is sent
|
if enter is pressed without the shift key, message (if not empty) is sent
|
||||||
|
@ -327,6 +354,44 @@ class TabbedChatWindow(chat.Chat):
|
||||||
self.sent_messages_scroll(jid, 'down', widget.get_buffer())
|
self.sent_messages_scroll(jid, 'down', widget.get_buffer())
|
||||||
return True # override the default gtk+ thing for ctrl+down
|
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):
|
def send_message(self, message):
|
||||||
"""Send the message given to the active tab"""
|
"""Send the message given to the active tab"""
|
||||||
if not message:
|
if not message:
|
||||||
|
@ -350,7 +415,18 @@ class TabbedChatWindow(chat.Chat):
|
||||||
if self.xmls[jid].get_widget('gpg_togglebutton').get_active():
|
if self.xmls[jid].get_widget('gpg_togglebutton').get_active():
|
||||||
keyID = self.users[jid].keyID
|
keyID = self.users[jid].keyID
|
||||||
encrypted = True
|
encrypted = True
|
||||||
|
|
||||||
|
# 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)
|
gajim.connections[self.account].send_message(jid, message, keyID)
|
||||||
|
print self.chatstates[jid]
|
||||||
message_buffer.set_text('', -1)
|
message_buffer.set_text('', -1)
|
||||||
self.print_conversation(message, jid, jid, encrypted = encrypted)
|
self.print_conversation(message, jid, jid, encrypted = encrypted)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue