Chatstate notifications in tabs #672

This commit is contained in:
Travis Shirk 2005-09-04 23:30:40 +00:00
parent 447c5f617b
commit bb6c0d6a26
5 changed files with 112 additions and 34 deletions

View File

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

View File

@ -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' ],
}, {}),
}

View File

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

View File

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

View File

@ -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
@ -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':