multiple chat session windows per contact. groupchat and pms broken.

This commit is contained in:
Brendan Taylor 2008-04-30 02:55:13 +00:00
parent 2648aa4a9f
commit d4a766963f
5 changed files with 87 additions and 60 deletions

View file

@ -385,7 +385,7 @@ class ChatControlBase(MessageControl):
if found: if found:
widget.disconnect(id) widget.disconnect(id)
del self.handlers[id] del self.handlers[id]
def connect_style_event(self, widget, set_fg = False, set_bg = False): def connect_style_event(self, widget, set_fg = False, set_bg = False):
self.disconnect_style_event(widget) self.disconnect_style_event(widget)
id = widget.connect('style-set', self._on_style_set_event, set_fg, set_bg) id = widget.connect('style-set', self._on_style_set_event, set_fg, set_bg)
@ -709,11 +709,10 @@ class ChatControlBase(MessageControl):
not self.parent_win.is_active() or not end) and \ not self.parent_win.is_active() or not end) and \
kind in ('incoming', 'incoming_queue'): kind in ('incoming', 'incoming_queue'):
self.parent_win.redraw_tab(self) self.parent_win.redraw_tab(self)
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid, self.account)
if not self.parent_win.is_active(): if not self.parent_win.is_active():
self.parent_win.show_title(True, ctrl) # Enabled Urgent hint self.parent_win.show_title(True, self) # Enabled Urgent hint
else: else:
self.parent_win.show_title(False, ctrl) # Disabled Urgent hint self.parent_win.show_title(False, self) # Disabled Urgent hint
def toggle_emoticons(self): def toggle_emoticons(self):
'''hide show emoticons_button and make sure emoticons_menu is always there '''hide show emoticons_button and make sure emoticons_menu is always there
@ -905,8 +904,7 @@ class ChatControlBase(MessageControl):
self.parent_win.get_active_control() == self and \ self.parent_win.get_active_control() == self and \
self.parent_win.window.is_active(): self.parent_win.window.is_active():
# we are at the end # we are at the end
if not gajim.events.remove_events(self.account, self.get_full_jid(), if not self.session.remove_events(['printed_' + type_, type_]):
types = ['printed_' + type_, type_]):
# There were events to remove # There were events to remove
self.redraw_after_event_removed(jid) self.redraw_after_event_removed(jid)
@ -1019,11 +1017,11 @@ class ChatControl(ChatControlBase):
self.chat_buttons_set_visible(compact_view) self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_widget('banner_eventbox'), self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
gajim.config.get('hide_chat_banner')) gajim.config.get('hide_chat_banner'))
# Add lock image to show chat encryption # Add lock image to show chat encryption
self.lock_image = self.xml.get_widget('lock_image') self.lock_image = self.xml.get_widget('lock_image')
self.lock_tooltip = gtk.Tooltips() self.lock_tooltip = gtk.Tooltips()
# keep timeout id and window obj for possible big avatar # keep timeout id and window obj for possible big avatar
# it is on enter-notify and leave-notify so no need to be per jid # it is on enter-notify and leave-notify so no need to be per jid
self.show_bigger_avatar_timeout_id = None self.show_bigger_avatar_timeout_id = None
@ -1069,12 +1067,12 @@ class ChatControl(ChatControlBase):
gajim.encrypted_chats[self.account].append(contact.jid) gajim.encrypted_chats[self.account].append(contact.jid)
msg = _('GPG encryption enabled') msg = _('GPG encryption enabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None) ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session: if self.session:
self.session.loggable = gajim.config.get('log_encrypted_sessions') self.session.loggable = gajim.config.get('log_encrypted_sessions')
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \ self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
self.session.is_loggable()) self.session.is_loggable())
self.status_tooltip = gtk.Tooltips() self.status_tooltip = gtk.Tooltips()
self.update_ui() self.update_ui()
# restore previous conversation # restore previous conversation
@ -1093,17 +1091,17 @@ class ChatControl(ChatControlBase):
return return
avatar_w = avatar_pixbuf.get_width() avatar_w = avatar_pixbuf.get_width()
avatar_h = avatar_pixbuf.get_height() avatar_h = avatar_pixbuf.get_height()
scaled_buf = self.xml.get_widget('avatar_image').get_pixbuf() scaled_buf = self.xml.get_widget('avatar_image').get_pixbuf()
scaled_buf_w = scaled_buf.get_width() scaled_buf_w = scaled_buf.get_width()
scaled_buf_h = scaled_buf.get_height() scaled_buf_h = scaled_buf.get_height()
# do we have something bigger to show? # do we have something bigger to show?
if avatar_w > scaled_buf_w or avatar_h > scaled_buf_h: if avatar_w > scaled_buf_w or avatar_h > scaled_buf_h:
# wait for 0.5 sec in case we leave earlier # wait for 0.5 sec in case we leave earlier
self.show_bigger_avatar_timeout_id = gobject.timeout_add(500, self.show_bigger_avatar_timeout_id = gobject.timeout_add(500,
self.show_bigger_avatar, widget) self.show_bigger_avatar, widget)
def on_avatar_eventbox_leave_notify_event(self, widget, event): def on_avatar_eventbox_leave_notify_event(self, widget, event):
'''we left the eventbox area that holds the avatar img''' '''we left the eventbox area that holds the avatar img'''
# did we add a timeout? if yes remove it # did we add a timeout? if yes remove it
@ -1122,7 +1120,7 @@ class ChatControl(ChatControlBase):
self.handlers[id] = menuitem self.handlers[id] = menuitem
menu.append(menuitem) menu.append(menuitem)
menu.show_all() menu.show_all()
menu.connect('selection-done', lambda w:w.destroy()) menu.connect('selection-done', lambda w:w.destroy())
# show the menu # show the menu
menu.show_all() menu.show_all()
menu.popup(None, None, None, event.button, event.time) menu.popup(None, None, None, event.button, event.time)
@ -1189,7 +1187,7 @@ class ChatControl(ChatControlBase):
banner_name_label = self.xml.get_widget('banner_name_label') banner_name_label = self.xml.get_widget('banner_name_label')
banner_eventbox = self.xml.get_widget('banner_eventbox') banner_eventbox = self.xml.get_widget('banner_eventbox')
name = contact.get_shown_name() name = contact.get_shown_name()
if self.resource: if self.resource:
name += '/' + self.resource name += '/' + self.resource
@ -1275,7 +1273,7 @@ class ChatControl(ChatControlBase):
ec.remove(self.contact.jid) ec.remove(self.contact.jid)
self.gpg_is_active = False self.gpg_is_active = False
msg = _('GPG encryption disabled') msg = _('GPG encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None) ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session: if self.session:
self.session.loggable = True self.session.loggable = True
@ -1300,10 +1298,10 @@ class ChatControl(ChatControlBase):
gajim.config.add_per('contacts', self.contact.jid) gajim.config.add_per('contacts', self.contact.jid)
gajim.config.set_per('contacts', self.contact.jid, 'gpg_enabled', gajim.config.set_per('contacts', self.contact.jid, 'gpg_enabled',
self.gpg_is_active) self.gpg_is_active)
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \ self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
self.session.is_loggable()) self.session.is_loggable())
def _show_lock_image(self, visible, enc_type = '', enc_enabled = False, chat_logged = False): def _show_lock_image(self, visible, enc_type = '', enc_enabled = False, chat_logged = False):
'''Set lock icon visibiity and create tooltip''' '''Set lock icon visibiity and create tooltip'''
status_string = enc_enabled and 'is' or 'is NOT' status_string = enc_enabled and 'is' or 'is NOT'
@ -1467,7 +1465,7 @@ class ChatControl(ChatControlBase):
# assume no activity and let the motion-notify or 'insert-text' make them # assume no activity and let the motion-notify or 'insert-text' make them
# True refresh 30 seconds vars too or else it's 30 - 5 = 25 seconds! # True refresh 30 seconds vars too or else it's 30 - 5 = 25 seconds!
self.reset_kbd_mouse_timeout_vars() self.reset_kbd_mouse_timeout_vars()
return True # loop forever return True # loop forever
def check_for_possible_inactive_chatstate(self, arg): def check_for_possible_inactive_chatstate(self, arg):
''' did we move mouse over that window or wrote something in message ''' did we move mouse over that window or wrote something in message
@ -1617,7 +1615,7 @@ class ChatControl(ChatControlBase):
color = self.lighten_color(color) color = self.lighten_color(color)
else: # active or not chatstate, get color from gtk else: # active or not chatstate, get color from gtk
color = self.parent_win.notebook.style.fg[gtk.STATE_ACTIVE] color = self.parent_win.notebook.style.fg[gtk.STATE_ACTIVE]
name = self.contact.get_shown_name() name = self.contact.get_shown_name()
if self.resource: if self.resource:
@ -1636,7 +1634,7 @@ class ChatControl(ChatControlBase):
['printed_' + self.type_id, self.type_id])) ['printed_' + self.type_id, self.type_id]))
# Set tab image (always 16x16); unread messages show the 'event' image # Set tab image (always 16x16); unread messages show the 'event' image
tab_img = None tab_img = None
if num_unread and gajim.config.get('show_unread_tab_icon'): if num_unread and gajim.config.get('show_unread_tab_icon'):
img_16 = gajim.interface.roster.get_appropriate_state_images( img_16 = gajim.interface.roster.get_appropriate_state_images(
self.contact.jid, icon_name = 'event') self.contact.jid, icon_name = 'event')
@ -1672,7 +1670,7 @@ class ChatControl(ChatControlBase):
convert_to_gc_menuitem = xml.get_widget('convert_to_groupchat') convert_to_gc_menuitem = xml.get_widget('convert_to_groupchat')
muc_icon = gajim.interface.roster.load_icon('muc_active') muc_icon = gajim.interface.roster.load_icon('muc_active')
if muc_icon: if muc_icon:
convert_to_gc_menuitem.set_image(muc_icon) convert_to_gc_menuitem.set_image(muc_icon)
ag = gtk.accel_groups_from_object(self.parent_win.window)[0] ag = gtk.accel_groups_from_object(self.parent_win.window)[0]
history_menuitem.add_accelerator('activate', ag, gtk.keysyms.h, gtk.gdk.CONTROL_MASK, history_menuitem.add_accelerator('activate', ag, gtk.keysyms.h, gtk.gdk.CONTROL_MASK,
@ -1682,7 +1680,7 @@ class ChatControl(ChatControlBase):
contact = self.parent_win.get_active_contact() contact = self.parent_win.get_active_contact()
jid = contact.jid jid = contact.jid
# check if we support and use gpg # check if we support and use gpg
if not gajim.config.get_per('accounts', self.account, 'keyid') or\ if not gajim.config.get_per('accounts', self.account, 'keyid') or\
not gajim.connections[self.account].USE_GPG or\ not gajim.connections[self.account].USE_GPG or\
@ -1765,7 +1763,7 @@ class ChatControl(ChatControlBase):
# JEP 85 does not allow resending the same chatstate # JEP 85 does not allow resending the same chatstate
# this function checks for that and just returns so it's safe to call it # this function checks for that and just returns so it's safe to call it
# with same state. # with same state.
# This functions also checks for violation in state transitions # This functions also checks for violation in state transitions
# and raises RuntimeException with appropriate message # and raises RuntimeException with appropriate message
# more on that http://www.jabber.org/jeps/jep-0085.html#statechart # more on that http://www.jabber.org/jeps/jep-0085.html#statechart
@ -1794,7 +1792,7 @@ class ChatControl(ChatControlBase):
if contact.composing_xep is False: # jid cannot do xep85 nor xep22 if contact.composing_xep is False: # jid cannot do xep85 nor xep22
return return
# if the new state we wanna send (state) equals # if the new state we wanna send (state) equals
# the current state (contact.our_chatstate) then return # the current state (contact.our_chatstate) then return
if contact.our_chatstate == state: if contact.our_chatstate == state:
return return
@ -1806,7 +1804,7 @@ class ChatControl(ChatControlBase):
# in self.send_message() because we need REAL message (with <body>) # in self.send_message() because we need REAL message (with <body>)
# for that procedure so return to make sure we send only once # for that procedure so return to make sure we send only once
# 'active' until we know peer supports jep85 # 'active' until we know peer supports jep85
return return
if contact.our_chatstate == 'ask': if contact.our_chatstate == 'ask':
return return
@ -1826,7 +1824,7 @@ class ChatControl(ChatControlBase):
MessageControl.send_message(self, None, chatstate = 'active') MessageControl.send_message(self, None, chatstate = 'active')
contact.our_chatstate = 'active' contact.our_chatstate = 'active'
self.reset_kbd_mouse_timeout_vars() self.reset_kbd_mouse_timeout_vars()
# if we're inactive prevent composing (JEP violation) # if we're inactive prevent composing (JEP violation)
elif contact.our_chatstate == 'inactive' and state == 'composing': elif contact.our_chatstate == 'inactive' and state == 'composing':
# go active before # go active before
@ -2052,12 +2050,6 @@ class ChatControl(ChatControlBase):
if hasattr(self, 'session') and self.session and self.session.enable_encryption: if hasattr(self, 'session') and self.session and self.session.enable_encryption:
self.print_esession_details() self.print_esession_details()
# Is it a pm ?
is_pm = False
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
control = gajim.interface.msg_win_mgr.get_control(room_jid, self.account)
if control and control.type_id == message_control.TYPE_GC:
is_pm = True
# list of message ids which should be marked as read # list of message ids which should be marked as read
message_ids = [] message_ids = []
for event in events: for event in events:
@ -2082,11 +2074,16 @@ class ChatControl(ChatControlBase):
types = [self.type_id]) types = [self.type_id])
typ = 'chat' # Is it a normal chat or a pm ? typ = 'chat' # Is it a normal chat or a pm ?
# reset to status image in gc if it is a pm # reset to status image in gc if it is a pm
if is_pm: # Is it a pm ?
control.update_ui() room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
control.parent_win.show_title() # XXX fixme somehow
typ = 'pm' # control = gajim.interface.msg_win_mgr.get_control(room_jid, self.account)
# if control and control.type_id == message_control.TYPE_GC:
# control.update_ui()
# control.parent_win.show_title()
# typ = 'pm'
self.redraw_after_event_removed(jid) self.redraw_after_event_removed(jid)
if (self.contact.show in ('offline', 'error')): if (self.contact.show in ('offline', 'error')):
@ -2097,8 +2094,8 @@ class ChatControl(ChatControlBase):
len(gajim.contacts.get_contacts(self.account, jid)) < 2): len(gajim.contacts.get_contacts(self.account, jid)) < 2):
gajim.interface.roster.really_remove_contact(self.contact, gajim.interface.roster.really_remove_contact(self.contact,
self.account) self.account)
elif typ == 'pm': # elif typ == 'pm':
control.remove_contact(nick) # control.remove_contact(nick)
def show_bigger_avatar(self, small_avatar): def show_bigger_avatar(self, small_avatar):
'''resizes the avatar, if needed, so it has at max half the screen size '''resizes the avatar, if needed, so it has at max half the screen size

View file

@ -116,7 +116,7 @@ class Events:
def remove_events(self, account, jid, event = None, types = []): def remove_events(self, account, jid, event = None, types = []):
'''if event is not specified, remove all events from this jid, '''if event is not specified, remove all events from this jid,
optionnaly only from given type optionally only from given type
return True if no such event found''' return True if no such event found'''
if not self._events.has_key(account): if not self._events.has_key(account):
return True return True

View file

@ -152,8 +152,9 @@ class MessageWindow(object):
def get_num_controls(self): def get_num_controls(self):
n = 0 n = 0
for dict in self._controls.values(): for jid_dict in self._controls.values():
n += len(dict) for dict in jid_dict.values():
n += len(dict)
return n return n
def resize(self, width, height): def resize(self, width, height):
@ -206,7 +207,11 @@ class MessageWindow(object):
if not self._controls.has_key(control.account): if not self._controls.has_key(control.account):
self._controls[control.account] = {} self._controls[control.account] = {}
fjid = control.get_full_jid() fjid = control.get_full_jid()
self._controls[control.account][fjid] = control
if not self._controls[control.account].has_key(fjid):
self._controls[control.account][fjid] = {}
self._controls[control.account][fjid][control.session.thread_id] = control
if self.get_num_controls() == 2: if self.get_num_controls() == 2:
# is first conversation_textview scrolled down ? # is first conversation_textview scrolled down ?
@ -391,8 +396,8 @@ class MessageWindow(object):
else: else:
gtkgui_helpers.set_unset_urgency_hint(self.window, False) gtkgui_helpers.set_unset_urgency_hint(self.window, False)
def set_active_tab(self, jid, acct): def set_active_tab(self, session):
ctrl = self._controls[acct][jid] ctrl = self._controls[session.conn.name][session.jid][session.thread_id]
ctrl_page = self.notebook.page_num(ctrl.widget) ctrl_page = self.notebook.page_num(ctrl.widget)
self.notebook.set_current_page(ctrl_page) self.notebook.set_current_page(ctrl_page)
@ -424,7 +429,12 @@ class MessageWindow(object):
self.notebook.remove_page(self.notebook.page_num(ctrl.widget)) self.notebook.remove_page(self.notebook.page_num(ctrl.widget))
fjid = ctrl.get_full_jid() fjid = ctrl.get_full_jid()
del self._controls[ctrl.account][fjid] thread_id = ctrl.session.thread_id
del self._controls[ctrl.account][fjid][thread_id]
if len(self._controls[ctrl.account][fjid]) == 0:
del self._controls[ctrl.account][fjid]
if len(self._controls[ctrl.account]) == 0: if len(self._controls[ctrl.account]) == 0:
del self._controls[ctrl.account] del self._controls[ctrl.account]
@ -563,9 +573,10 @@ class MessageWindow(object):
del gajim.last_message_time[acct][old_jid] del gajim.last_message_time[acct][old_jid]
def controls(self): def controls(self):
for ctrl_dict in self._controls.values(): for jid_dict in self._controls.values():
for ctrl in ctrl_dict.values(): for ctrl_dict in jid_dict.values():
yield ctrl for ctrl in ctrl_dict.values():
yield ctrl
def move_to_next_unread_tab(self, forward): def move_to_next_unread_tab(self, forward):
ind = self.notebook.get_current_page() ind = self.notebook.get_current_page()

View file

@ -4312,18 +4312,18 @@ class RosterWindow:
if not session: if not session:
session = conn.get_session(fjid, None, 'chat') session = conn.get_session(fjid, None, 'chat')
win = gajim.interface.msg_win_mgr.get_window(fjid, account) if not session.control:
if not win:
session.control = self.new_chat(session, contact, account, resource=resource) session.control = self.new_chat(session, contact, account, resource=resource)
if len(gajim.events.get_events(account, fjid)): if len(gajim.events.get_events(account, fjid)):
session.control.read_queue() session.control.read_queue()
win = gajim.interface.msg_win_mgr.get_window(fjid, account)
# last message is long time ago # last message is long time ago
gajim.last_message_time[account][session.control.get_full_jid()] = 0 gajim.last_message_time[account][session.control.get_full_jid()] = 0
win.set_active_tab(fjid, account) win = session.control.parent_win
win.set_active_tab(session)
if conn.is_zeroconf and conn.status in ('offline', 'invisible'): if conn.is_zeroconf and conn.status in ('offline', 'invisible'):
win.get_control(fjid, account).got_disconnected() win.get_control(fjid, account).got_disconnected()
@ -5204,6 +5204,7 @@ class RosterWindow:
start = '[' + str(nb_unread) + '] ' start = '[' + str(nb_unread) + '] '
elif nb_unread == 1: elif nb_unread == 1:
start = '* ' start = '* '
self.window.set_title(start + 'Gajim') self.window.set_title(start + 'Gajim')
gtkgui_helpers.set_unset_urgency_hint(self.window, nb_unread) gtkgui_helpers.set_unset_urgency_hint(self.window, nb_unread)

View file

@ -19,6 +19,21 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
self.control = None self.control = None
# remove events associated with this session from the queue
def remove_events(self, types):
any_removed = False
for event in gajim.events.get_events(self.conn, self.jid, types=types):
if event.parameters[8] != self:
continue
r = gajim.events.remove_events(self.conn, self.jid, event)
if not_any_removed:
any_removed = r
return any_removed
# extracts chatstate from a <message/> stanza # extracts chatstate from a <message/> stanza
def get_chatstate(self, msg, msgtxt): def get_chatstate(self, msg, msgtxt):
composing_xep = None composing_xep = None
@ -93,16 +108,18 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
jid = jid.replace('@', '') jid = jid.replace('@', '')
groupchat_control = gajim.interface.msg_win_mgr.get_control(jid, self.conn.name) groupchat_control = gajim.interface.msg_win_mgr.get_control(jid, self.conn.name)
if not groupchat_control and \
jid in gajim.interface.minimized_controls[self.conn.name]: # XXX fixme
groupchat_control = self.minimized_controls[self.conn.name][jid] # if not groupchat_control and \
# jid in gajim.interface.minimized_controls[self.conn.name]:
# groupchat_control = self.minimized_controls[self.conn.name][jid]
pm = False pm = False
if groupchat_control and groupchat_control.type_id == \ # if groupchat_control and groupchat_control.type_id == \
message_control.TYPE_GC: # message_control.TYPE_GC:
# It's a Private message # It's a Private message
pm = True # pm = True
msg_type = 'pm' # msg_type = 'pm'
jid_of_control = full_jid_with_resource jid_of_control = full_jid_with_resource
@ -184,6 +201,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
def roster_message(self, jid, msg, tim, encrypted=False, msg_type='', def roster_message(self, jid, msg, tim, encrypted=False, msg_type='',
subject=None, resource='', msg_id=None, user_nick='', subject=None, resource='', msg_id=None, user_nick='',
advanced_notif_num=None, xhtml=None, form_node=None): advanced_notif_num=None, xhtml=None, form_node=None):
contact = None contact = None
# if chat window will be for specific resource # if chat window will be for specific resource
resource_for_chat = resource resource_for_chat = resource