[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:
Nikos Kouremenos 2005-07-19 14:38:58 +00:00
parent cc7c233e0d
commit d6c9c7cbc6
5 changed files with 139 additions and 14 deletions

View File

@ -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 =\

View File

@ -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))

View File

@ -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">

View File

@ -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(

View File

@ -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)