Chatstate notifications in tabs #672
This commit is contained in:
parent
447c5f617b
commit
bb6c0d6a26
64
src/chat.py
64
src/chat.py
|
@ -160,19 +160,59 @@ class Chat:
|
|||
self.window.set_title(title)
|
||||
gtkgui_helpers.set_unset_urgency_hint(self.window, unread)
|
||||
|
||||
def redraw_tab(self, jid):
|
||||
"""redraw the label of the tab"""
|
||||
start = ''
|
||||
def redraw_tab(self, contact, chatstate = None):
|
||||
'''redraw the label of the tab
|
||||
if chatstate is given that means we have HE SENT US a chatstate'''
|
||||
if isinstance(contact, unicode):
|
||||
jid = contact
|
||||
else:
|
||||
jid = contact.jid
|
||||
|
||||
unread = ''
|
||||
if self.nb_unread[jid] > 1:
|
||||
start = '[' + unicode(self.nb_unread[jid]) + '] '
|
||||
elif self.nb_unread[jid] == 1:
|
||||
start = '* '
|
||||
unread = '[' + unicode(self.nb_unread[jid]) + '] '
|
||||
# Update status images
|
||||
self.set_state_image(jid)
|
||||
|
||||
child = self.childs[jid]
|
||||
hb = self.notebook.get_tab_label(child).get_children()[0]
|
||||
if self.widget_name == 'tabbed_chat_window':
|
||||
nickname = hb.get_children()[1]
|
||||
close_button = hb.get_children()[2]
|
||||
|
||||
# Draw tab label using chatstate
|
||||
theme = gajim.config.get('roster_theme')
|
||||
color = None
|
||||
if unread and chatstate == 'active':
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_unread_color')
|
||||
elif chatstate is not None:
|
||||
if chatstate == 'composing':
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_composing_color')
|
||||
elif unread and self.has_focus:
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_active_color')
|
||||
elif unread:
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_unread_color')
|
||||
elif chatstate == 'inactive':
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_inactive_color')
|
||||
elif chatstate == 'gone':
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_gone_color')
|
||||
elif chatstate == 'paused':
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_paused_color')
|
||||
else:
|
||||
color = gajim.config.get_per('themes', theme,
|
||||
'state_active_color')
|
||||
if color:
|
||||
# FIXME: When tabs are in the "background" the color change has
|
||||
# no affect
|
||||
#print jid, chatstate, color
|
||||
nickname.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
|
||||
elif self.widget_name == 'groupchat_window':
|
||||
nickname = hb.get_children()[0]
|
||||
close_button = hb.get_children()[1]
|
||||
|
@ -183,7 +223,7 @@ class Chat:
|
|||
close_button.hide()
|
||||
|
||||
nickname.set_max_width_chars(10)
|
||||
nickname.set_text(start + self.names[jid])
|
||||
nickname.set_text(unread + self.names[jid])
|
||||
|
||||
|
||||
def on_window_destroy(self, widget, kind): #kind is 'chats' or 'gc'
|
||||
|
@ -227,9 +267,14 @@ class Chat:
|
|||
self.plugin.windows['logs'][jid] = history_window.HistoryWindow(
|
||||
self.plugin, jid, self.account)
|
||||
|
||||
def on_chat_window_focus_out_event(self, widget, event):
|
||||
self.has_focus = False
|
||||
|
||||
def on_chat_window_focus_in_event(self, widget, event):
|
||||
"""When window gets focus"""
|
||||
self.has_focus = True
|
||||
jid = self.get_active_jid()
|
||||
|
||||
textview = self.xmls[jid].get_widget('conversation_textview')
|
||||
buffer = textview.get_buffer()
|
||||
end_iter = buffer.get_end_iter()
|
||||
|
@ -239,7 +284,6 @@ class Chat:
|
|||
#we are at the end
|
||||
if self.nb_unread[jid] > 0:
|
||||
self.nb_unread[jid] = 0
|
||||
self.redraw_tab(jid)
|
||||
self.show_title()
|
||||
if self.plugin.systray_enabled:
|
||||
self.plugin.systray.remove_jid(jid, self.account)
|
||||
|
@ -250,6 +294,8 @@ class Chat:
|
|||
if gtk.gtk_version >= (2, 8, 0) and gtk.pygtk_version >= (2, 8, 0):
|
||||
if widget.props.urgency_hint:
|
||||
widget.props.urgency_hint = False
|
||||
# Undo "unread" state display, etc. But note, there is not chatstate past here
|
||||
self.redraw_tab(jid)
|
||||
|
||||
def on_compact_view_menuitem_activate(self, widget):
|
||||
isactive = widget.get_active()
|
||||
|
@ -1108,6 +1154,8 @@ class Chat:
|
|||
|
||||
# FIXME: who gives us text that is not a string?
|
||||
if not text:
|
||||
# FIXME: Let's find out...
|
||||
assert(False)
|
||||
text = ''
|
||||
|
||||
if buffer.get_char_count() > 0:
|
||||
|
|
|
@ -215,6 +215,13 @@ class Config:
|
|||
'contactfont': [ opt_str, 'Sans 10' ],
|
||||
'bannertextcolor': [ opt_color, '#ffffff' ],
|
||||
'bannerbgcolor': [ opt_color, '#000000' ],
|
||||
|
||||
'state_unread_color': [ opt_color, '#000000' ],
|
||||
'state_active_color': [ opt_color, '#000000' ],
|
||||
'state_inactive_color': [ opt_color, '#9e9e9e' ],
|
||||
'state_composing_color': [ opt_color, '#008b00' ],
|
||||
'state_paused_color': [ opt_color, '#0000cd' ],
|
||||
'state_gone_color': [ opt_color, '#bebebe' ],
|
||||
}, {}),
|
||||
}
|
||||
|
||||
|
|
|
@ -382,9 +382,10 @@ class Interface:
|
|||
not gajim.contacts[account].has_key(jid):
|
||||
return
|
||||
|
||||
# Handle chat states
|
||||
contact = gajim.get_first_contact_instance_from_jid(account, jid)
|
||||
if self.windows[account]['chats'].has_key(jid):
|
||||
chat_win = self.windows[account]['chats'][jid]
|
||||
contact = gajim.get_first_contact_instance_from_jid(account, jid)
|
||||
if chatstate is not None: # he sent us reply, so he supports jep85
|
||||
if contact.chatstate == 'ask': # we were jep85 disco?
|
||||
contact.chatstate = 'active' # no more
|
||||
|
@ -393,6 +394,10 @@ class Interface:
|
|||
else:
|
||||
# got no valid jep85 answer, peer does not support it
|
||||
contact.chatstate = False
|
||||
else:
|
||||
# Brand new message, incoming.
|
||||
if chatstate == 'active':
|
||||
contact.chatstate = chatstate
|
||||
|
||||
if not array[1]: #empty message text
|
||||
return
|
||||
|
|
|
@ -1073,6 +1073,11 @@ class GroupchatWindow(chat.Chat):
|
|||
self.got_disconnected(room_jid) #init some variables
|
||||
conversation_textview.grab_focus()
|
||||
self.childs[room_jid].show_all()
|
||||
|
||||
def set_state_image(self, jid):
|
||||
# FIXME: Tab notifications?
|
||||
pass
|
||||
|
||||
def on_list_treeview_motion_notify_event(self, widget, event):
|
||||
model = widget.get_model()
|
||||
props = widget.get_path_at_pos(int(event.x), int(event.y))
|
||||
|
|
|
@ -46,7 +46,6 @@ class TabbedChatWindow(chat.Chat):
|
|||
def __init__(self, user, plugin, account):
|
||||
chat.Chat.__init__(self, plugin, account, 'tabbed_chat_window')
|
||||
self.contacts = {}
|
||||
self.chatstates = {}
|
||||
# keep check for possible paused timeouts per jid
|
||||
self.possible_paused_timeout_id = {}
|
||||
# keep check for possible inactive timeouts per jid
|
||||
|
@ -245,6 +244,9 @@ timestamp, contact):
|
|||
hb = self.notebook.get_tab_label(child).get_children()[0]
|
||||
status_image = hb.get_children()[0]
|
||||
state_images = self.plugin.roster.get_appropriate_state_images(jid)
|
||||
# If messages are unread show the 'message' image
|
||||
if self.nb_unread[jid]:
|
||||
show = 'message'
|
||||
image = state_images[show]
|
||||
banner_status_image = self.xmls[jid].get_widget('banner_status_image')
|
||||
|
||||
|
@ -296,18 +298,11 @@ timestamp, contact):
|
|||
def on_tabbed_chat_window_focus_out_event(self, widget, event):
|
||||
'''catch focus out and minimized and send inactive chatstate;
|
||||
minimize action also focuses out first so it's catched here'''
|
||||
chat.Chat.on_chat_window_focus_out_event(self, widget, event)
|
||||
window_state = widget.window.get_state()
|
||||
if window_state is None:
|
||||
return
|
||||
|
||||
# focus-out is also emitted by showing context menu
|
||||
# so check to see if we're really not paying attention to window/tab
|
||||
# NOTE: if the user changes tab, switch-tab sends inactive to the tab
|
||||
# we are leaving so we just send to active tab here
|
||||
if self.popup_is_shown is False: # we are outside of the window
|
||||
# so no context menu, so send 'inactive' to active tab
|
||||
self.send_chatstate('inactive')
|
||||
|
||||
def on_chat_notebook_key_press_event(self, widget, event):
|
||||
chat.Chat.on_chat_notebook_key_press_event(self, widget, event)
|
||||
|
||||
|
@ -367,6 +362,8 @@ timestamp, contact):
|
|||
message_tv_buffer = message_textview.get_buffer()
|
||||
message_tv_buffer.connect('insert-text',
|
||||
self.on_message_tv_buffer_insert_text, contact.jid)
|
||||
message_tv_buffer.connect('changed',
|
||||
self.on_message_tv_buffer_changed, contact)
|
||||
|
||||
if contact.jid in gajim.encrypted_chats[self.account]:
|
||||
self.xmls[contact.jid].get_widget('gpg_togglebutton').set_active(True)
|
||||
|
@ -376,13 +373,12 @@ timestamp, contact):
|
|||
self.tabbed_chat_popup_menu = xm.get_widget('tabbed_chat_popup_menu')
|
||||
|
||||
chat.Chat.new_tab(self, contact.jid)
|
||||
self.redraw_tab(contact.jid)
|
||||
self.redraw_tab(contact)
|
||||
self.draw_widgets(contact)
|
||||
|
||||
# restore previous conversation
|
||||
self.restore_conversation(contact.jid)
|
||||
|
||||
# print queued messages
|
||||
if gajim.awaiting_messages[self.account].has_key(contact.jid):
|
||||
self.read_queue(contact.jid)
|
||||
|
||||
|
@ -392,7 +388,6 @@ timestamp, contact):
|
|||
# chatstates
|
||||
self.reset_kbd_mouse_timeout_vars()
|
||||
|
||||
self.chatstates[contact.jid] = None # OUR current chatstate with contact
|
||||
self.possible_paused_timeout_id[contact.jid] =\
|
||||
gobject.timeout_add(5000, self.check_for_possible_paused_chatstate,
|
||||
contact.jid)
|
||||
|
@ -404,25 +399,33 @@ timestamp, contact):
|
|||
''' handle incoming chatstate that jid SENT TO us '''
|
||||
contact = gajim.get_first_contact_instance_from_jid(account, jid)
|
||||
self.draw_name_banner(contact, chatstate)
|
||||
# update chatstate in tab for this chat
|
||||
self.redraw_tab(contact, chatstate)
|
||||
|
||||
def check_for_possible_paused_chatstate(self, jid):
|
||||
''' did we move mouse of that window or wrote something in message textview
|
||||
''' did we move mouse of that window or write something in message
|
||||
textview
|
||||
in the last 5 seconds?
|
||||
if yes we go active for mouse, composing for kbd
|
||||
if no we go paused if we were previously composing '''
|
||||
if jid not in self.xmls:
|
||||
# the tab with jid is no longer open. stop timer
|
||||
return False # stop looping
|
||||
current_state = self.chatstates[jid]
|
||||
|
||||
# FIXME: Why don't we just pass contact?
|
||||
contact = gajim.get_first_contact_instance_from_jid(self.account, jid)
|
||||
current_state = contact.chatstate
|
||||
if current_state is False: # jid doesn't support chatstates
|
||||
return False # stop looping
|
||||
|
||||
if self.mouse_over_in_last_5_secs:
|
||||
self.send_chatstate('active', jid)
|
||||
elif self.kbd_activity_in_last_5_secs:
|
||||
message_buffer = self.xmls[jid].get_widget('message_textview').get_buffer()
|
||||
if self.kbd_activity_in_last_5_secs and message_buffer.get_char_count():
|
||||
# Only composing if the keyboard activity was in text entry
|
||||
self.send_chatstate('composing', jid)
|
||||
elif self.mouse_over_in_last_5_secs:
|
||||
self.send_chatstate('active', jid)
|
||||
else:
|
||||
if self.chatstates[jid] == 'composing':
|
||||
if current_state == 'composing':
|
||||
self.send_chatstate('paused', jid) # pause composing
|
||||
|
||||
# assume no activity and let the motion-notify or 'insert-text' make them True
|
||||
|
@ -431,7 +434,8 @@ timestamp, contact):
|
|||
return True # loop forever
|
||||
|
||||
def check_for_possible_inactive_chatstate(self, jid):
|
||||
''' did we move mouse over that window or wrote something in message textview
|
||||
''' did we move mouse over that window or wrote something in message
|
||||
textview
|
||||
in the last 30 seconds?
|
||||
if yes we go active
|
||||
if no we go inactive '''
|
||||
|
@ -439,7 +443,9 @@ timestamp, contact):
|
|||
# the tab with jid is no longer open. stop timer
|
||||
return False # stop looping
|
||||
|
||||
current_state = self.chatstates[jid]
|
||||
# FIXME: Why don't we just pass contact?
|
||||
contact = gajim.get_first_contact_instance_from_jid(self.account, jid)
|
||||
current_state = contact.chatstate
|
||||
if current_state is False: # jid doesn't support chatstates
|
||||
return False # stop looping
|
||||
|
||||
|
@ -447,7 +453,7 @@ timestamp, contact):
|
|||
return True # loop forever
|
||||
|
||||
if not (self.mouse_over_in_last_30_secs or\
|
||||
self.kbd_activity_in_last_30_secs):
|
||||
self.kbd_activity_in_last_30_secs):
|
||||
self.send_chatstate('inactive', jid)
|
||||
|
||||
# assume no activity and let the motion-notify or 'insert-text' make them True
|
||||
|
@ -456,12 +462,19 @@ timestamp, contact):
|
|||
|
||||
return True # loop forever
|
||||
|
||||
def on_message_tv_buffer_insert_text(self, textbuffer, textiter, text,
|
||||
length, jid):
|
||||
def on_message_tv_buffer_insert_text(self, textbuffer, textiter, text, length, jid):
|
||||
self.kbd_activity_in_last_5_secs = True
|
||||
self.kbd_activity_in_last_30_secs = True
|
||||
# XXX: only send the event every N'th character after the first N... optimization
|
||||
self.send_chatstate('composing', jid)
|
||||
|
||||
def on_message_tv_buffer_changed(self, textbuffer, contact):
|
||||
self.kbd_activity_in_last_5_secs = True
|
||||
self.kbd_activity_in_last_30_secs = True
|
||||
# All characters have been erased, undo composing
|
||||
if textbuffer.get_char_count() == 0:
|
||||
self.send_chatstate('active', contact.jid)
|
||||
|
||||
def reset_kbd_mouse_timeout_vars(self):
|
||||
self.kbd_activity_in_last_5_secs = False
|
||||
self.mouse_over_in_last_5_secs = False
|
||||
|
@ -563,8 +576,8 @@ timestamp, contact):
|
|||
# NOTE:
|
||||
# send 'active', set current state to 'ask' and return is done
|
||||
# in self.send_message() because we need REAL message (with <body>)
|
||||
# for that procedure so return to make sure we send only once 'active'
|
||||
# until we know peer supports jep85
|
||||
# for that procedure so return to make sure we send only once
|
||||
# 'active' until we know peer supports jep85
|
||||
return
|
||||
|
||||
if contact.chatstate == 'ask':
|
||||
|
|
Loading…
Reference in New Issue