merged in session_centric branch
This commit is contained in:
parent
d69d5266b5
commit
2162c3730a
|
@ -6,7 +6,7 @@
|
||||||
## Dimitur Kirov <dkirov@gmail.com>
|
## Dimitur Kirov <dkirov@gmail.com>
|
||||||
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
|
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
|
||||||
## Julien Pivotto <roidelapluie@gmail.com>
|
## Julien Pivotto <roidelapluie@gmail.com>
|
||||||
## Stephan Erb <steve-e@h3c.de>
|
## Stephan Erb <steve-e@h3c.de>
|
||||||
##
|
##
|
||||||
## This file is part of Gajim.
|
## This file is part of Gajim.
|
||||||
##
|
##
|
||||||
|
@ -104,8 +104,8 @@ class ChatControlBase(MessageControl):
|
||||||
type_]))
|
type_]))
|
||||||
|
|
||||||
def draw_banner(self):
|
def draw_banner(self):
|
||||||
'''Draw the fat line at the top of the window that
|
'''Draw the fat line at the top of the window that
|
||||||
houses the icon, jid, ...
|
houses the icon, jid, ...
|
||||||
'''
|
'''
|
||||||
self.draw_banner_text()
|
self.draw_banner_text()
|
||||||
self._update_banner_state_image()
|
self._update_banner_state_image()
|
||||||
|
@ -133,7 +133,7 @@ class ChatControlBase(MessageControl):
|
||||||
def status_url_clicked(self, widget, url):
|
def status_url_clicked(self, widget, url):
|
||||||
helpers.launch_browser_mailer('url', url)
|
helpers.launch_browser_mailer('url', url)
|
||||||
|
|
||||||
def __init__(self, type_id, parent_win, widget_name, contact, acct,
|
def __init__(self, type_id, parent_win, widget_name, contact, acct,
|
||||||
resource = None):
|
resource = None):
|
||||||
MessageControl.__init__(self, type_id, parent_win, widget_name,
|
MessageControl.__init__(self, type_id, parent_win, widget_name,
|
||||||
contact, acct, resource = resource);
|
contact, acct, resource = resource);
|
||||||
|
@ -238,7 +238,7 @@ class ChatControlBase(MessageControl):
|
||||||
self.msg_textview.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
|
self.msg_textview.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
|
||||||
gtk.DEST_DEFAULT_HIGHLIGHT,
|
gtk.DEST_DEFAULT_HIGHLIGHT,
|
||||||
self.dnd_list, gtk.gdk.ACTION_COPY)
|
self.dnd_list, gtk.gdk.ACTION_COPY)
|
||||||
|
|
||||||
self.update_font()
|
self.update_font()
|
||||||
|
|
||||||
# Hook up send button
|
# Hook up send button
|
||||||
|
@ -266,7 +266,7 @@ class ChatControlBase(MessageControl):
|
||||||
# loop removing non-existant dictionaries
|
# loop removing non-existant dictionaries
|
||||||
# iterating on a copy
|
# iterating on a copy
|
||||||
for lang in dict(langs):
|
for lang in dict(langs):
|
||||||
try:
|
try:
|
||||||
spell.set_language(langs[lang])
|
spell.set_language(langs[lang])
|
||||||
except:
|
except:
|
||||||
del langs[lang]
|
del langs[lang]
|
||||||
|
@ -330,7 +330,7 @@ class ChatControlBase(MessageControl):
|
||||||
|
|
||||||
menu.show_all()
|
menu.show_all()
|
||||||
|
|
||||||
# moved from ChatControl
|
# moved from ChatControl
|
||||||
def _on_banner_eventbox_button_press_event(self, widget, event):
|
def _on_banner_eventbox_button_press_event(self, widget, event):
|
||||||
'''If right-clicked, show popup'''
|
'''If right-clicked, show popup'''
|
||||||
if event.button == 3: # right click
|
if event.button == 3: # right click
|
||||||
|
@ -362,7 +362,7 @@ class ChatControlBase(MessageControl):
|
||||||
self.disconnect_style_event(banner_name_label)
|
self.disconnect_style_event(banner_name_label)
|
||||||
self.disconnect_style_event(self.banner_status_label)
|
self.disconnect_style_event(self.banner_status_label)
|
||||||
if bgcolor:
|
if bgcolor:
|
||||||
banner_eventbox.modify_bg(gtk.STATE_NORMAL,
|
banner_eventbox.modify_bg(gtk.STATE_NORMAL,
|
||||||
gtk.gdk.color_parse(bgcolor))
|
gtk.gdk.color_parse(bgcolor))
|
||||||
default_bg = False
|
default_bg = False
|
||||||
else:
|
else:
|
||||||
|
@ -391,14 +391,14 @@ 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)
|
||||||
self.handlers[id] = widget
|
self.handlers[id] = widget
|
||||||
|
|
||||||
def _on_style_set_event(self, widget, style, *opts):
|
def _on_style_set_event(self, widget, style, *opts):
|
||||||
'''set style of widget from style class *.Frame.Eventbox
|
'''set style of widget from style class *.Frame.Eventbox
|
||||||
opts[0] == True -> set fg color
|
opts[0] == True -> set fg color
|
||||||
opts[1] == True -> set bg color'''
|
opts[1] == True -> set bg color'''
|
||||||
banner_eventbox = self.xml.get_widget('banner_eventbox')
|
banner_eventbox = self.xml.get_widget('banner_eventbox')
|
||||||
|
@ -564,7 +564,7 @@ class ChatControlBase(MessageControl):
|
||||||
send_message = False
|
send_message = False
|
||||||
else: # ENTER
|
else: # ENTER
|
||||||
send_message = True
|
send_message = True
|
||||||
|
|
||||||
if gajim.connections[self.account].connected < 2: # we are not connected
|
if gajim.connections[self.account].connected < 2: # we are not connected
|
||||||
dialogs.ErrorDialog(_('A connection is not available'),
|
dialogs.ErrorDialog(_('A connection is not available'),
|
||||||
_('Your message can not be sent until you are connected.'))
|
_('Your message can not be sent until you are connected.'))
|
||||||
|
@ -644,7 +644,7 @@ class ChatControlBase(MessageControl):
|
||||||
# we don't want size of the buffer to grow indefinately
|
# we don't want size of the buffer to grow indefinately
|
||||||
max_size = gajim.config.get('key_up_lines')
|
max_size = gajim.config.get('key_up_lines')
|
||||||
if size >= max_size:
|
if size >= max_size:
|
||||||
for i in xrange(0, size - 1):
|
for i in xrange(0, size - 1):
|
||||||
self.sent_history[i] = self.sent_history[i + 1]
|
self.sent_history[i] = self.sent_history[i + 1]
|
||||||
self.sent_history[max_size - 1] = message
|
self.sent_history[max_size - 1] = message
|
||||||
# self.sent_history_pos has changed if we browsed sent_history,
|
# self.sent_history_pos has changed if we browsed sent_history,
|
||||||
|
@ -693,18 +693,20 @@ class ChatControlBase(MessageControl):
|
||||||
kind in ('incoming', 'incoming_queue'):
|
kind in ('incoming', 'incoming_queue'):
|
||||||
# we want to have save this message in events list
|
# we want to have save this message in events list
|
||||||
# other_tags_for_text == ['marked'] --> highlighted gc message
|
# other_tags_for_text == ['marked'] --> highlighted gc message
|
||||||
type_ = 'printed_' + self.type_id
|
|
||||||
event = 'message_received'
|
|
||||||
if gc_message:
|
if gc_message:
|
||||||
if 'marked' in other_tags_for_text:
|
if 'marked' in other_tags_for_text:
|
||||||
type_ = 'printed_marked_gc_msg'
|
type_ = 'printed_marked_gc_msg'
|
||||||
else:
|
else:
|
||||||
type_ = 'printed_gc_msg'
|
type_ = 'printed_gc_msg'
|
||||||
event = 'gc_message_received'
|
event = 'gc_message_received'
|
||||||
|
else:
|
||||||
|
type_ = 'printed_' + self.type_id
|
||||||
|
event = 'message_received'
|
||||||
show_in_roster = notify.get_show_in_roster(event,
|
show_in_roster = notify.get_show_in_roster(event,
|
||||||
self.account, self.contact)
|
self.account, self.contact, self.session)
|
||||||
show_in_systray = notify.get_show_in_systray(event,
|
show_in_systray = notify.get_show_in_systray(event,
|
||||||
self.account, self.contact, type_)
|
self.account, self.contact, type_)
|
||||||
|
|
||||||
event = gajim.events.create_event(type_, None,
|
event = gajim.events.create_event(type_, None,
|
||||||
show_in_roster = show_in_roster,
|
show_in_roster = show_in_roster,
|
||||||
show_in_systray = show_in_systray)
|
show_in_systray = show_in_systray)
|
||||||
|
@ -722,11 +724,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
|
||||||
|
@ -856,7 +857,7 @@ class ChatControlBase(MessageControl):
|
||||||
|
|
||||||
# we don't want to always resize in height the message_textview
|
# we don't want to always resize in height the message_textview
|
||||||
# so we have minimum on conversation_textview's scrolled window
|
# so we have minimum on conversation_textview's scrolled window
|
||||||
# but we also want to avoid window resizing so if we reach that
|
# but we also want to avoid window resizing so if we reach that
|
||||||
# minimum for conversation_textview and maximum for message_textview
|
# minimum for conversation_textview and maximum for message_textview
|
||||||
# we set to automatic the scrollbar policy
|
# we set to automatic the scrollbar policy
|
||||||
diff_y = message_height - requisition.height
|
diff_y = message_height - requisition.height
|
||||||
|
@ -867,13 +868,13 @@ class ChatControlBase(MessageControl):
|
||||||
'vscrollbar-policy')
|
'vscrollbar-policy')
|
||||||
# scroll only when scrollbar appear
|
# scroll only when scrollbar appear
|
||||||
if policy != gtk.POLICY_AUTOMATIC:
|
if policy != gtk.POLICY_AUTOMATIC:
|
||||||
self.msg_scrolledwindow.set_property('vscrollbar-policy',
|
self.msg_scrolledwindow.set_property('vscrollbar-policy',
|
||||||
gtk.POLICY_AUTOMATIC)
|
gtk.POLICY_AUTOMATIC)
|
||||||
self.msg_scrolledwindow.set_property('height-request',
|
self.msg_scrolledwindow.set_property('height-request',
|
||||||
message_height + conversation_height - min_height)
|
message_height + conversation_height - min_height)
|
||||||
self.bring_scroll_to_end(msg_textview)
|
self.bring_scroll_to_end(msg_textview)
|
||||||
else:
|
else:
|
||||||
self.msg_scrolledwindow.set_property('vscrollbar-policy',
|
self.msg_scrolledwindow.set_property('vscrollbar-policy',
|
||||||
gtk.POLICY_NEVER)
|
gtk.POLICY_NEVER)
|
||||||
self.msg_scrolledwindow.set_property('height-request', -1)
|
self.msg_scrolledwindow.set_property('height-request', -1)
|
||||||
self.conv_textview.bring_scroll_to_end(diff_y - 18, False)
|
self.conv_textview.bring_scroll_to_end(diff_y - 18, False)
|
||||||
|
@ -883,10 +884,10 @@ class ChatControlBase(MessageControl):
|
||||||
# enable scrollbar automatic policy for horizontal scrollbar
|
# enable scrollbar automatic policy for horizontal scrollbar
|
||||||
# if message we have in message_textview is too big
|
# if message we have in message_textview is too big
|
||||||
if requisition.width > message_width:
|
if requisition.width > message_width:
|
||||||
self.msg_scrolledwindow.set_property('hscrollbar-policy',
|
self.msg_scrolledwindow.set_property('hscrollbar-policy',
|
||||||
gtk.POLICY_AUTOMATIC)
|
gtk.POLICY_AUTOMATIC)
|
||||||
else:
|
else:
|
||||||
self.msg_scrolledwindow.set_property('hscrollbar-policy',
|
self.msg_scrolledwindow.set_property('hscrollbar-policy',
|
||||||
gtk.POLICY_NEVER)
|
gtk.POLICY_NEVER)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -916,27 +917,26 @@ class ChatControlBase(MessageControl):
|
||||||
types_list = ['printed_' + type_, type_]
|
types_list = ['printed_' + type_, type_]
|
||||||
|
|
||||||
if not len(gajim.events.get_events(self.account, jid, types_list)):
|
if not len(gajim.events.get_events(self.account, jid, types_list)):
|
||||||
return
|
return
|
||||||
if not self.parent_win:
|
if not self.parent_win:
|
||||||
return
|
return
|
||||||
if self.conv_textview.at_the_end() and \
|
if self.conv_textview.at_the_end() and \
|
||||||
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(types_list):
|
||||||
types = types_list):
|
|
||||||
# There were events to remove
|
# There were events to remove
|
||||||
self.redraw_after_event_removed(jid)
|
self.redraw_after_event_removed(jid)
|
||||||
|
|
||||||
def redraw_after_event_removed(self, jid):
|
def redraw_after_event_removed(self, jid):
|
||||||
''' We just removed a 'printed_*' event, redraw contact in roster or
|
''' We just removed a 'printed_*' event, redraw contact in roster or
|
||||||
gc_roster and titles in roster and msg_win '''
|
gc_roster and titles in roster and msg_win '''
|
||||||
self.parent_win.redraw_tab(self)
|
self.parent_win.redraw_tab(self)
|
||||||
self.parent_win.show_title()
|
self.parent_win.show_title()
|
||||||
# TODO : get the contact and check notify.get_show_in_roster()
|
# TODO : get the contact and check notify.get_show_in_roster()
|
||||||
if self.type_id == message_control.TYPE_PM:
|
if self.type_id == message_control.TYPE_PM:
|
||||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||||
groupchat_control = gajim.interface.msg_win_mgr.get_control(
|
groupchat_control = gajim.interface.msg_win_mgr.get_gc_control(
|
||||||
room_jid, self.account)
|
room_jid, self.account)
|
||||||
if room_jid in gajim.interface.minimized_controls[self.account]:
|
if room_jid in gajim.interface.minimized_controls[self.account]:
|
||||||
groupchat_control = \
|
groupchat_control = \
|
||||||
|
@ -947,15 +947,14 @@ class ChatControlBase(MessageControl):
|
||||||
if contact:
|
if contact:
|
||||||
gajim.interface.roster.draw_contact(room_jid, self.account)
|
gajim.interface.roster.draw_contact(room_jid, self.account)
|
||||||
groupchat_control.draw_contact(nick)
|
groupchat_control.draw_contact(nick)
|
||||||
mw = gajim.interface.msg_win_mgr.get_window(room_jid, self.account)
|
if groupchat_control.parent_win:
|
||||||
if mw:
|
groupchat_control.parent_win.redraw_tab(groupchat_control)
|
||||||
mw.redraw_tab(groupchat_control)
|
|
||||||
else:
|
else:
|
||||||
gajim.interface.roster.draw_contact(jid, self.account)
|
gajim.interface.roster.draw_contact(jid, self.account)
|
||||||
gajim.interface.roster.show_title()
|
gajim.interface.roster.show_title()
|
||||||
|
|
||||||
def sent_messages_scroll(self, direction, conv_buf):
|
def sent_messages_scroll(self, direction, conv_buf):
|
||||||
size = len(self.sent_history)
|
size = len(self.sent_history)
|
||||||
if self.orig_msg is None:
|
if self.orig_msg is None:
|
||||||
# user was typing something and then went into history, so save
|
# user was typing something and then went into history, so save
|
||||||
# whatever is already typed
|
# whatever is already typed
|
||||||
|
@ -1053,7 +1052,7 @@ 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()
|
||||||
|
@ -1080,7 +1079,7 @@ class ChatControl(ChatControlBase):
|
||||||
if gajim.get_transport_name_from_jid(self.contact.jid) or \
|
if gajim.get_transport_name_from_jid(self.contact.jid) or \
|
||||||
gajim.connections[self.account].is_zeroconf:
|
gajim.connections[self.account].is_zeroconf:
|
||||||
convert_to_gc_button.set_sensitive(False)
|
convert_to_gc_button.set_sensitive(False)
|
||||||
|
|
||||||
# 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
|
||||||
|
@ -1126,12 +1125,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()
|
||||||
|
|
||||||
if gajim.otr_module:
|
if gajim.otr_module:
|
||||||
|
@ -1154,17 +1153,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
|
||||||
|
@ -1176,14 +1175,14 @@ class ChatControl(ChatControlBase):
|
||||||
if event.button == 3: # right click
|
if event.button == 3: # right click
|
||||||
menu = gtk.Menu()
|
menu = gtk.Menu()
|
||||||
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
||||||
id = menuitem.connect('activate',
|
id = menuitem.connect('activate',
|
||||||
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
|
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
|
||||||
self.contact.jid, self.account, self.contact.get_shown_name() + \
|
self.contact.jid, self.account, self.contact.get_shown_name() + \
|
||||||
'.jpeg')
|
'.jpeg')
|
||||||
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)
|
||||||
|
@ -1268,15 +1267,15 @@ class ChatControl(ChatControlBase):
|
||||||
banner_status_img.set_from_pixbuf(scaled_pix)
|
banner_status_img.set_from_pixbuf(scaled_pix)
|
||||||
|
|
||||||
def draw_banner_text(self):
|
def draw_banner_text(self):
|
||||||
'''Draw the text in the fat line at the top of the window that
|
'''Draw the text in the fat line at the top of the window that
|
||||||
houses the name, jid.
|
houses the name, jid.
|
||||||
'''
|
'''
|
||||||
contact = self.contact
|
contact = self.contact
|
||||||
jid = contact.jid
|
jid = contact.jid
|
||||||
|
|
||||||
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
|
||||||
|
@ -1363,7 +1362,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
|
||||||
|
|
||||||
|
@ -1388,12 +1387,12 @@ 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'
|
||||||
logged_string = chat_logged and 'will' or 'will NOT'
|
logged_string = chat_logged and 'will' or 'will NOT'
|
||||||
tooltip = '%s Encryption %s active. \nYour chat session %s be logged.' %\
|
tooltip = '%s Encryption %s active. \nYour chat session %s be logged.' %\
|
||||||
|
@ -1549,7 +1548,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
|
||||||
|
@ -1601,7 +1600,7 @@ class ChatControl(ChatControlBase):
|
||||||
msg = _('E2E encryption disabled')
|
msg = _('E2E encryption disabled')
|
||||||
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
|
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
|
||||||
self._show_lock_image(e2e_is_active, 'E2E', e2e_is_active, self.session and \
|
self._show_lock_image(e2e_is_active, 'E2E', e2e_is_active, self.session and \
|
||||||
self.session.is_loggable())
|
self.session.is_loggable())
|
||||||
|
|
||||||
def print_conversation(self, text, frm='', tim=None, encrypted=False,
|
def print_conversation(self, text, frm='', tim=None, encrypted=False,
|
||||||
subject=None, xhtml=None, simple=False):
|
subject=None, xhtml=None, simple=False):
|
||||||
|
@ -1625,13 +1624,13 @@ class ChatControl(ChatControlBase):
|
||||||
if self.session and self.session.enable_encryption:
|
if self.session and self.session.enable_encryption:
|
||||||
if not encrypted:
|
if not encrypted:
|
||||||
msg = _('The following message was NOT encrypted')
|
msg = _('The following message was NOT encrypted')
|
||||||
ChatControlBase.print_conversation_line(self, msg,
|
ChatControlBase.print_conversation_line(self, msg,
|
||||||
'status', '', tim)
|
'status', '', tim)
|
||||||
else:
|
else:
|
||||||
# GPG encryption
|
# GPG encryption
|
||||||
if encrypted and not self.gpg_is_active:
|
if encrypted and not self.gpg_is_active:
|
||||||
msg = _('The following message was encrypted')
|
msg = _('The following message was encrypted')
|
||||||
ChatControlBase.print_conversation_line(self, msg,
|
ChatControlBase.print_conversation_line(self, msg,
|
||||||
'status', '', tim)
|
'status', '', tim)
|
||||||
self._toggle_gpg()
|
self._toggle_gpg()
|
||||||
elif not encrypted and self.gpg_is_active:
|
elif not encrypted and self.gpg_is_active:
|
||||||
|
@ -1672,7 +1671,7 @@ class ChatControl(ChatControlBase):
|
||||||
elif num_unread > 1:
|
elif num_unread > 1:
|
||||||
unread = '[' + unicode(num_unread) + ']'
|
unread = '[' + unicode(num_unread) + ']'
|
||||||
|
|
||||||
# Draw tab label using chatstate
|
# Draw tab label using chatstate
|
||||||
theme = gajim.config.get('roster_theme')
|
theme = gajim.config.get('roster_theme')
|
||||||
color = None
|
color = None
|
||||||
if not chatstate:
|
if not chatstate:
|
||||||
|
@ -1700,7 +1699,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:
|
||||||
|
@ -1719,7 +1718,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')
|
||||||
|
@ -1760,7 +1759,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 = gtkgui_helpers.load_icon('muc_active')
|
muc_icon = gtkgui_helpers.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]
|
||||||
send_file_menuitem.add_accelerator('activate', ag, gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
|
send_file_menuitem.add_accelerator('activate', ag, gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
|
||||||
|
@ -1774,7 +1773,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\
|
||||||
|
@ -1814,22 +1813,22 @@ class ChatControl(ChatControlBase):
|
||||||
convert_to_gc_menuitem.set_sensitive(False)
|
convert_to_gc_menuitem.set_sensitive(False)
|
||||||
|
|
||||||
# connect signals
|
# connect signals
|
||||||
id = history_menuitem.connect('activate',
|
id = history_menuitem.connect('activate',
|
||||||
self._on_history_menuitem_activate)
|
self._on_history_menuitem_activate)
|
||||||
self.handlers[id] = history_menuitem
|
self.handlers[id] = history_menuitem
|
||||||
id = send_file_menuitem.connect('activate',
|
id = send_file_menuitem.connect('activate',
|
||||||
self._on_send_file_menuitem_activate)
|
self._on_send_file_menuitem_activate)
|
||||||
self.handlers[id] = send_file_menuitem
|
self.handlers[id] = send_file_menuitem
|
||||||
id = add_to_roster_menuitem.connect('activate',
|
id = add_to_roster_menuitem.connect('activate',
|
||||||
self._on_add_to_roster_menuitem_activate)
|
self._on_add_to_roster_menuitem_activate)
|
||||||
self.handlers[id] = add_to_roster_menuitem
|
self.handlers[id] = add_to_roster_menuitem
|
||||||
id = toggle_gpg_menuitem.connect('activate',
|
id = toggle_gpg_menuitem.connect('activate',
|
||||||
self._on_toggle_gpg_menuitem_activate)
|
self._on_toggle_gpg_menuitem_activate)
|
||||||
self.handlers[id] = toggle_gpg_menuitem
|
self.handlers[id] = toggle_gpg_menuitem
|
||||||
id = toggle_e2e_menuitem.connect('activate',
|
id = toggle_e2e_menuitem.connect('activate',
|
||||||
self._on_toggle_e2e_menuitem_activate)
|
self._on_toggle_e2e_menuitem_activate)
|
||||||
self.handlers[id] = toggle_e2e_menuitem
|
self.handlers[id] = toggle_e2e_menuitem
|
||||||
id = information_menuitem.connect('activate',
|
id = information_menuitem.connect('activate',
|
||||||
self._on_contact_information_menuitem_activate)
|
self._on_contact_information_menuitem_activate)
|
||||||
self.handlers[id] = information_menuitem
|
self.handlers[id] = information_menuitem
|
||||||
id = convert_to_gc_menuitem.connect('activate',
|
id = convert_to_gc_menuitem.connect('activate',
|
||||||
|
@ -1885,7 +1884,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
|
||||||
|
@ -1914,7 +1913,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
|
||||||
|
@ -1926,7 +1925,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
|
||||||
|
@ -1946,7 +1945,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
|
||||||
|
@ -1963,10 +1962,15 @@ class ChatControl(ChatControlBase):
|
||||||
def shutdown(self):
|
def shutdown(self):
|
||||||
# destroy banner tooltip - bug #pygtk for that!
|
# destroy banner tooltip - bug #pygtk for that!
|
||||||
self.status_tooltip.destroy()
|
self.status_tooltip.destroy()
|
||||||
|
|
||||||
# Send 'gone' chatstate
|
# Send 'gone' chatstate
|
||||||
self.send_chatstate('gone', self.contact)
|
self.send_chatstate('gone', self.contact)
|
||||||
self.contact.chatstate = None
|
self.contact.chatstate = None
|
||||||
self.contact.our_chatstate = None
|
self.contact.our_chatstate = None
|
||||||
|
|
||||||
|
# terminate session
|
||||||
|
self.session.control = None
|
||||||
|
|
||||||
# Disconnect timer callbacks
|
# Disconnect timer callbacks
|
||||||
gobject.source_remove(self.possible_paused_timeout_id)
|
gobject.source_remove(self.possible_paused_timeout_id)
|
||||||
gobject.source_remove(self.possible_inactive_timeout_id)
|
gobject.source_remove(self.possible_inactive_timeout_id)
|
||||||
|
@ -1986,6 +1990,8 @@ class ChatControl(ChatControlBase):
|
||||||
self.msg_textview.destroy()
|
self.msg_textview.destroy()
|
||||||
|
|
||||||
def allow_shutdown(self, method):
|
def allow_shutdown(self, method):
|
||||||
|
print repr(self.get_full_jid())
|
||||||
|
print repr(gajim.last_message_time[self.account])
|
||||||
if time.time() - gajim.last_message_time[self.account]\
|
if time.time() - gajim.last_message_time[self.account]\
|
||||||
[self.get_full_jid()] < 2:
|
[self.get_full_jid()] < 2:
|
||||||
# 2 seconds
|
# 2 seconds
|
||||||
|
@ -2173,12 +2179,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:
|
||||||
|
@ -2203,8 +2203,13 @@ 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 ?
|
||||||
|
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||||
|
control = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
|
||||||
|
self.account)
|
||||||
|
if control and control.type_id == message_control.TYPE_GC:
|
||||||
control.update_ui()
|
control.update_ui()
|
||||||
control.parent_win.show_title()
|
control.parent_win.show_title()
|
||||||
typ = 'pm'
|
typ = 'pm'
|
||||||
|
@ -2265,18 +2270,18 @@ class ChatControl(ChatControlBase):
|
||||||
window.set_app_paintable(True)
|
window.set_app_paintable(True)
|
||||||
if gtk.gtk_version >= (2, 10, 0) and gtk.pygtk_version >= (2, 10, 0):
|
if gtk.gtk_version >= (2, 10, 0) and gtk.pygtk_version >= (2, 10, 0):
|
||||||
window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLTIP)
|
window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLTIP)
|
||||||
|
|
||||||
window.realize()
|
window.realize()
|
||||||
window.window.set_back_pixmap(pixmap, False) # make it transparent
|
window.window.set_back_pixmap(pixmap, False) # make it transparent
|
||||||
window.window.shape_combine_mask(mask, 0, 0)
|
window.window.shape_combine_mask(mask, 0, 0)
|
||||||
|
|
||||||
# make the bigger avatar window show up centered
|
# make the bigger avatar window show up centered
|
||||||
x0, y0 = small_avatar.window.get_origin()
|
x0, y0 = small_avatar.window.get_origin()
|
||||||
x0 += small_avatar.allocation.x
|
x0 += small_avatar.allocation.x
|
||||||
y0 += small_avatar.allocation.y
|
y0 += small_avatar.allocation.y
|
||||||
center_x= x0 + (small_avatar.allocation.width / 2)
|
center_x= x0 + (small_avatar.allocation.width / 2)
|
||||||
center_y = y0 + (small_avatar.allocation.height / 2)
|
center_y = y0 + (small_avatar.allocation.height / 2)
|
||||||
pos_x, pos_y = center_x - (avatar_w / 2), center_y - (avatar_h / 2)
|
pos_x, pos_y = center_x - (avatar_w / 2), center_y - (avatar_h / 2)
|
||||||
window.move(pos_x, pos_y)
|
window.move(pos_x, pos_y)
|
||||||
# make the cursor invisible so we can see the image
|
# make the cursor invisible so we can see the image
|
||||||
invisible_cursor = gtkgui_helpers.get_invisible_cursor()
|
invisible_cursor = gtkgui_helpers.get_invisible_cursor()
|
||||||
|
@ -2307,7 +2312,7 @@ class ChatControl(ChatControlBase):
|
||||||
c = self.gc_contact
|
c = self.gc_contact
|
||||||
else:
|
else:
|
||||||
c = self.contact
|
c = self.contact
|
||||||
gajim.interface.instances['file_transfers'].show_file_send_request(
|
gajim.interface.instances['file_transfers'].show_file_send_request(
|
||||||
self.account, c)
|
self.account, c)
|
||||||
|
|
||||||
def _on_add_to_roster_menuitem_activate(self, widget):
|
def _on_add_to_roster_menuitem_activate(self, widget):
|
||||||
|
@ -2317,7 +2322,7 @@ class ChatControl(ChatControlBase):
|
||||||
gajim.interface.roster.on_info(widget, self.contact, self.account)
|
gajim.interface.roster.on_info(widget, self.contact, self.account)
|
||||||
|
|
||||||
def _on_toggle_gpg_menuitem_activate(self, widget):
|
def _on_toggle_gpg_menuitem_activate(self, widget):
|
||||||
self._toggle_gpg()
|
self._toggle_gpg()
|
||||||
|
|
||||||
def _on_convert_to_gc_menuitem_activate(self, widget):
|
def _on_convert_to_gc_menuitem_activate(self, widget):
|
||||||
'''user want to invite some friends to chat'''
|
'''user want to invite some friends to chat'''
|
||||||
|
|
|
@ -49,7 +49,8 @@ if dbus_support.supported:
|
||||||
import dbus
|
import dbus
|
||||||
from music_track_listener import MusicTrackListener
|
from music_track_listener import MusicTrackListener
|
||||||
|
|
||||||
from common.stanza_session import EncryptedStanzaSession
|
from session import ChatControlSession
|
||||||
|
import tictactoe
|
||||||
|
|
||||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||||
'invisible', 'error']
|
'invisible', 'error']
|
||||||
|
@ -994,7 +995,7 @@ class ConnectionVcard:
|
||||||
gajim.interface.remove_avatar_files(our_jid)
|
gajim.interface.remove_avatar_files(our_jid)
|
||||||
|
|
||||||
self.awaiting_answers[id] = (VCARD_PUBLISHED, iq2)
|
self.awaiting_answers[id] = (VCARD_PUBLISHED, iq2)
|
||||||
|
|
||||||
def _IqCB(self, con, iq_obj):
|
def _IqCB(self, con, iq_obj):
|
||||||
id = iq_obj.getID()
|
id = iq_obj.getID()
|
||||||
|
|
||||||
|
@ -1046,6 +1047,7 @@ class ConnectionVcard:
|
||||||
our_jid = gajim.get_jid_from_account(self.name)
|
our_jid = gajim.get_jid_from_account(self.name)
|
||||||
if iq_obj.getType() == 'error' and jid == our_jid:
|
if iq_obj.getType() == 'error' and jid == our_jid:
|
||||||
# our server doesn't support vcard
|
# our server doesn't support vcard
|
||||||
|
gajim.log.debug('xxx error xxx')
|
||||||
self.vcard_supported = False
|
self.vcard_supported = False
|
||||||
if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error':
|
if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error':
|
||||||
if frm and frm != our_jid:
|
if frm and frm != our_jid:
|
||||||
|
@ -1261,7 +1263,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
reply.addChild(node=common.xmpp.ErrorNode('service-unavailable', typ='cancel'))
|
reply.addChild(node=common.xmpp.ErrorNode('service-unavailable', typ='cancel'))
|
||||||
|
|
||||||
con.send(reply)
|
con.send(reply)
|
||||||
|
|
||||||
raise common.xmpp.NodeProcessed
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
def _InitE2ECB(self, con, stanza, session):
|
def _InitE2ECB(self, con, stanza, session):
|
||||||
|
@ -1291,7 +1293,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
errmsg = iq_obj.getErrorMsg()
|
errmsg = iq_obj.getErrorMsg()
|
||||||
errcode = iq_obj.getErrorCode()
|
errcode = iq_obj.getErrorCode()
|
||||||
self.dispatch('ERROR_ANSWER', (id, jid_from, errmsg, errcode))
|
self.dispatch('ERROR_ANSWER', (id, jid_from, errmsg, errcode))
|
||||||
|
|
||||||
def _PrivateCB(self, con, iq_obj):
|
def _PrivateCB(self, con, iq_obj):
|
||||||
'''
|
'''
|
||||||
Private Data (XEP 048 and 049)
|
Private Data (XEP 048 and 049)
|
||||||
|
@ -1499,6 +1501,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
|
|
||||||
def _messageCB(self, con, msg):
|
def _messageCB(self, con, msg):
|
||||||
'''Called when we receive a message'''
|
'''Called when we receive a message'''
|
||||||
|
gajim.log.debug('MessageCB')
|
||||||
|
|
||||||
frm = helpers.get_full_jid_from_iq(msg)
|
frm = helpers.get_full_jid_from_iq(msg)
|
||||||
mtype = msg.getType()
|
mtype = msg.getType()
|
||||||
thread_id = msg.getThread()
|
thread_id = msg.getThread()
|
||||||
|
@ -1506,8 +1510,23 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
if not mtype:
|
if not mtype:
|
||||||
mtype = 'normal'
|
mtype = 'normal'
|
||||||
|
|
||||||
if not mtype == 'groupchat':
|
game_invite = msg.getTag('invite', namespace='http://jabber.org/protocol/games')
|
||||||
session = self.get_session(frm, thread_id, mtype)
|
if game_invite:
|
||||||
|
game = game_invite.getTag('game')
|
||||||
|
|
||||||
|
if game.getAttr('var') == \
|
||||||
|
'http://jabber.org/protocol/games/tictactoe':
|
||||||
|
klass = tictactoe.TicTacToeSession
|
||||||
|
|
||||||
|
# this assumes that the invitation came with a thread_id we haven't
|
||||||
|
# seen
|
||||||
|
session = self.make_new_session(frm, thread_id, klass=klass)
|
||||||
|
|
||||||
|
session.invited(msg)
|
||||||
|
|
||||||
|
return
|
||||||
|
elif mtype != 'groupchat':
|
||||||
|
session = self.get_or_create_session(frm, thread_id)
|
||||||
|
|
||||||
if thread_id and not session.received_thread_id:
|
if thread_id and not session.received_thread_id:
|
||||||
session.received_thread_id = True
|
session.received_thread_id = True
|
||||||
|
@ -1516,66 +1535,57 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
if msg.getTag('event') is not None:
|
if msg.getTag('event') is not None:
|
||||||
self._pubsubEventCB(con, msg)
|
self._pubsubEventCB(con, msg)
|
||||||
return
|
return
|
||||||
# check if the message is a xep70-confirmation-request
|
|
||||||
if msg.getTag('confirm') and msg.getTag('confirm').namespace == \
|
# check if the message is a XEP-0070 confirmation request
|
||||||
common.xmpp.NS_HTTP_AUTH:
|
if msg.getTag('confirm', namespace=common.xmpp.NS_HTTP_AUTH):
|
||||||
self._HttpAuthCB(con, msg)
|
self._HttpAuthCB(con, msg)
|
||||||
return
|
return
|
||||||
if msg.getTag('feature') and msg.getTag('feature').namespace == \
|
|
||||||
common.xmpp.NS_FEATURE:
|
# check if the message is a XEP-0020 feature negotiation request
|
||||||
|
if msg.getTag('feature', namespace=common.xmpp.NS_FEATURE):
|
||||||
if gajim.HAVE_PYCRYPTO:
|
if gajim.HAVE_PYCRYPTO:
|
||||||
self._FeatureNegCB(con, msg, session)
|
self._FeatureNegCB(con, msg, session)
|
||||||
return
|
return
|
||||||
if msg.getTag('init') and msg.getTag('init').namespace == \
|
if msg.getTag('init', namespace=common.xmpp.NS_ESESSION_INIT):
|
||||||
common.xmpp.NS_ESESSION_INIT:
|
|
||||||
self._InitE2ECB(con, msg, session)
|
self._InitE2ECB(con, msg, session)
|
||||||
|
|
||||||
encrypted = False
|
encrypted = False
|
||||||
tim = msg.getTimestamp()
|
tim = msg.getTimestamp()
|
||||||
tim = helpers.datetime_tuple(tim)
|
tim = helpers.datetime_tuple(tim)
|
||||||
tim = localtime(timegm(tim))
|
tim = localtime(timegm(tim))
|
||||||
|
|
||||||
e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
|
if msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO):
|
||||||
if e2e_tag:
|
|
||||||
encrypted = True
|
encrypted = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
msg = session.decrypt_stanza(msg)
|
msg = session.decrypt_stanza(msg)
|
||||||
except:
|
except:
|
||||||
self.dispatch('FAILED_DECRYPT', (frm, tim))
|
self.dispatch('FAILED_DECRYPT', (frm, tim, session))
|
||||||
|
|
||||||
msgtxt = msg.getBody()
|
msgtxt = msg.getBody()
|
||||||
msghtml = msg.getXHTML()
|
|
||||||
subject = msg.getSubject() # if not there, it's None
|
subject = msg.getSubject() # if not there, it's None
|
||||||
tim = msg.getTimestamp()
|
|
||||||
tim = helpers.datetime_tuple(tim)
|
|
||||||
tim = localtime(timegm(tim))
|
|
||||||
frm = helpers.get_full_jid_from_iq(msg)
|
frm = helpers.get_full_jid_from_iq(msg)
|
||||||
jid = helpers.get_jid_from_iq(msg)
|
jid = helpers.get_jid_from_iq(msg)
|
||||||
|
|
||||||
addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS)
|
addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS)
|
||||||
|
|
||||||
# Be sure it comes from one of our resource, else ignore address element
|
# Be sure it comes from one of our resource, else ignore address element
|
||||||
if addressTag and jid == gajim.get_jid_from_account(self.name):
|
if addressTag and jid == gajim.get_jid_from_account(self.name):
|
||||||
address = addressTag.getTag('address', attrs={'type': 'ofrom'})
|
address = addressTag.getTag('address', attrs={'type': 'ofrom'})
|
||||||
if address:
|
if address:
|
||||||
frm = address.getAttr('jid')
|
frm = address.getAttr('jid')
|
||||||
jid = gajim.get_jid_without_resource(frm)
|
jid = gajim.get_jid_without_resource(frm)
|
||||||
no_log_for = gajim.config.get_per('accounts', self.name,
|
|
||||||
'no_log_for')
|
|
||||||
if not no_log_for:
|
|
||||||
no_log_for = ''
|
|
||||||
no_log_for = no_log_for.split()
|
|
||||||
chatstate = None
|
|
||||||
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
|
|
||||||
decmsg = ''
|
|
||||||
# invitations
|
# invitations
|
||||||
invite = None
|
invite = None
|
||||||
|
encTag = msg.getTag('x', namespace=common.xmpp.NS_ENCRYPTED)
|
||||||
|
|
||||||
if not encTag:
|
if not encTag:
|
||||||
invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER)
|
invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER)
|
||||||
if invite and not invite.getTag('invite'):
|
if invite and not invite.getTag('invite'):
|
||||||
invite = None
|
invite = None
|
||||||
delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) is not None
|
|
||||||
msg_id = None
|
|
||||||
composing_xep = None
|
|
||||||
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED
|
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED
|
||||||
# invitation
|
# invitation
|
||||||
# stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED
|
# stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED
|
||||||
|
@ -1589,94 +1599,54 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
self.dispatch('GC_INVITATION', (room_jid, frm, '', None,
|
self.dispatch('GC_INVITATION', (room_jid, frm, '', None,
|
||||||
is_continued))
|
is_continued))
|
||||||
return
|
return
|
||||||
form_node = None
|
|
||||||
for xtag in xtags:
|
|
||||||
if xtag.getNamespace() == common.xmpp.NS_DATA:
|
|
||||||
form_node = xtag
|
|
||||||
break
|
|
||||||
# chatstates - look for chatstate tags in a message if not delayed
|
|
||||||
if not delayed:
|
|
||||||
composing_xep = False
|
|
||||||
children = msg.getChildren()
|
|
||||||
for child in children:
|
|
||||||
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
|
|
||||||
chatstate = child.getName()
|
|
||||||
composing_xep = 'XEP-0085'
|
|
||||||
break
|
|
||||||
# No XEP-0085 support, fallback to XEP-0022
|
|
||||||
if not chatstate:
|
|
||||||
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
|
|
||||||
if chatstate_child:
|
|
||||||
chatstate = 'active'
|
|
||||||
composing_xep = 'XEP-0022'
|
|
||||||
if not msgtxt and chatstate_child.getTag('composing'):
|
|
||||||
chatstate = 'composing'
|
|
||||||
# XEP-0172 User Nickname
|
|
||||||
user_nick = msg.getTagData('nick')
|
|
||||||
if not user_nick:
|
|
||||||
user_nick = ''
|
|
||||||
|
|
||||||
if encTag and self.USE_GPG:
|
if encTag and self.USE_GPG:
|
||||||
#decrypt
|
|
||||||
encmsg = encTag.getData()
|
encmsg = encTag.getData()
|
||||||
|
|
||||||
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
|
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
|
||||||
if keyID:
|
if keyID:
|
||||||
decmsg = self.gpg.decrypt(encmsg, keyID)
|
decmsg = self.gpg.decrypt(encmsg, keyID)
|
||||||
# \x00 chars are not allowed in C (so in GTK)
|
# \x00 chars are not allowed in C (so in GTK)
|
||||||
decmsg = decmsg.replace('\x00', '')
|
msgtxt = decmsg.replace('\x00', '')
|
||||||
if decmsg:
|
encrypted = True
|
||||||
msgtxt = decmsg
|
|
||||||
encrypted = True
|
|
||||||
if mtype == 'error':
|
if mtype == 'error':
|
||||||
error_msg = msg.getErrorMsg()
|
self.dispatch_error_message(msg, msgtxt, session, frm, tim, subject)
|
||||||
if not error_msg:
|
|
||||||
error_msg = msgtxt
|
|
||||||
msgtxt = None
|
|
||||||
if session.is_loggable():
|
|
||||||
try:
|
|
||||||
gajim.logger.write('error', frm, error_msg, tim = tim,
|
|
||||||
subject = subject)
|
|
||||||
except exceptions.PysqliteOperationalError, e:
|
|
||||||
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
|
||||||
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt,
|
|
||||||
tim))
|
|
||||||
return
|
|
||||||
elif mtype == 'groupchat':
|
elif mtype == 'groupchat':
|
||||||
has_timestamp = False
|
self.dispatch_gc_message(msg, subject, frm, msgtxt, jid, tim)
|
||||||
if msg.timestamp:
|
elif invite is not None:
|
||||||
has_timestamp = True
|
self.dispatch_invite_message(invite, frm)
|
||||||
if subject is not None:
|
else:
|
||||||
self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp))
|
# XXX horrible hack
|
||||||
|
if isinstance(session, ChatControlSession):
|
||||||
|
session.received(frm, msgtxt, tim, encrypted, subject, msg)
|
||||||
else:
|
else:
|
||||||
statusCode = msg.getStatusCode()
|
session.received(msg)
|
||||||
if not msg.getTag('body'): #no <body>
|
# END messageCB
|
||||||
# It could be a config change. See
|
|
||||||
# http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
|
# process and dispatch an error message
|
||||||
if msg.getTag('x'):
|
def dispatch_error_message(self, msg, msgtxt, session, frm, tim, subject):
|
||||||
if statusCode != []:
|
error_msg = msg.getErrorMsg()
|
||||||
self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode))
|
|
||||||
return
|
if not error_msg:
|
||||||
# Ignore message from room in which we are not
|
error_msg = msgtxt
|
||||||
if not self.last_history_time.has_key(jid):
|
msgtxt = None
|
||||||
return
|
|
||||||
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msghtml,
|
if session.is_loggable():
|
||||||
statusCode))
|
try:
|
||||||
tim_int = int(float(mktime(tim)))
|
gajim.logger.write('error', frm, error_msg, tim=tim,
|
||||||
if self.name not in no_log_for and jid not in no_log_for and not \
|
subject=subject)
|
||||||
tim_int <= self.last_history_time[jid] and msgtxt \
|
except exceptions.PysqliteOperationalError, e:
|
||||||
and frm.find('/') >= 0:
|
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
||||||
# if frm.find('/') < 0, it means message comes from room itself
|
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt,
|
||||||
# usually it hold description and can be send at each connection
|
tim))
|
||||||
# so don't store it in logs
|
|
||||||
try:
|
# process and dispatch a groupchat message
|
||||||
gajim.logger.write('gc_msg', frm, msgtxt, tim = tim)
|
def dispatch_gc_message(self, msg, subject, frm, msgtxt, jid, tim):
|
||||||
# save the time we log to avoid duplicate logs
|
has_timestamp = bool(msg.timestamp)
|
||||||
self.last_history_time[jid] = tim_int
|
|
||||||
except exceptions.PysqliteOperationalError, e:
|
if subject is not None:
|
||||||
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp))
|
||||||
return
|
return
|
||||||
elif mtype == 'chat': # it's type 'chat'
|
|
||||||
|
|
||||||
if gajim.otr_module and isinstance(msgtxt, unicode):
|
if gajim.otr_module and isinstance(msgtxt, unicode):
|
||||||
otr_msg_tuple = gajim.otr_module.otrl_message_receiving(
|
otr_msg_tuple = gajim.otr_module.otrl_message_receiving(
|
||||||
|
@ -1690,117 +1660,114 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
||||||
# text in <body> and <html>
|
# text in <body> and <html>
|
||||||
msghtml = msgtxt
|
msghtml = msgtxt
|
||||||
|
|
||||||
if gajim.otr_module.otrl_tlv_find(otr_msg_tuple[2],
|
|
||||||
gajim.otr_module.OTRL_TLV_DISCONNECTED) != None:
|
|
||||||
gajim.otr_ui_ops.gajim_log("%s has ended his/her private conversation"
|
|
||||||
" with you; you should do the same."%frm, self.name,
|
|
||||||
frm)
|
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(frm, self.name)
|
|
||||||
if ctrl:
|
|
||||||
ctrl.update_ui()
|
|
||||||
|
|
||||||
ctx = gajim.otr_module.otrl_context_find(gajim.connections[self.name].otr_userstates, frm.encode(),
|
ctx = gajim.otr_module.otrl_context_find(gajim.connections[self.name].otr_userstates, frm.encode(),
|
||||||
gajim.get_jid_from_account(self.name).encode(), gajim.OTR_PROTO, 1,
|
gajim.get_jid_from_account(self.name).encode(), gajim.OTR_PROTO, 1,
|
||||||
(gajim.otr_add_appdata, self.name))[0]
|
(gajim.otr_add_appdata, self.name))[0]
|
||||||
tlvs = otr_msg_tuple[2]
|
tlvs = otr_msg_tuple[2]
|
||||||
ctx.app_data.handle_tlv(tlvs)
|
ctx.app_data.handle_tlv(tlvs)
|
||||||
|
|
||||||
if not msg.getTag('body') and chatstate is None: #no <body>
|
statusCode = msg.getStatusCode()
|
||||||
return
|
|
||||||
if msg.getTag('body') and session.is_loggable() and msgtxt:
|
|
||||||
try:
|
|
||||||
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt,
|
|
||||||
tim = tim, subject = subject)
|
|
||||||
except exceptions.PysqliteOperationalError, e:
|
|
||||||
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
|
||||||
else: # it's single message
|
|
||||||
if invite is not None:
|
|
||||||
item = invite.getTag('invite')
|
|
||||||
jid_from = item.getAttr('from')
|
|
||||||
reason = item.getTagData('reason')
|
|
||||||
item = invite.getTag('password')
|
|
||||||
password = invite.getTagData('password')
|
|
||||||
is_continued = False
|
|
||||||
if invite.getTag('invite').getTag('continue'):
|
|
||||||
is_continued = True
|
|
||||||
self.dispatch('GC_INVITATION',(frm, jid_from, reason, password,
|
|
||||||
is_continued))
|
|
||||||
return
|
|
||||||
if session.is_loggable()and msgtxt:
|
|
||||||
try:
|
|
||||||
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
|
|
||||||
subject = subject)
|
|
||||||
except exceptions.PysqliteOperationalError, e:
|
|
||||||
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
|
||||||
mtype = 'normal'
|
|
||||||
treat_as = gajim.config.get('treat_incoming_messages')
|
|
||||||
if treat_as:
|
|
||||||
mtype = treat_as
|
|
||||||
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype,
|
|
||||||
subject, chatstate, msg_id, composing_xep, user_nick, msghtml,
|
|
||||||
session, form_node))
|
|
||||||
# END messageCB
|
|
||||||
|
|
||||||
def get_session(self, jid, thread_id, type):
|
if not msg.getTag('body'): # no <body>
|
||||||
|
# It could be a config change. See
|
||||||
|
# http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
|
||||||
|
if msg.getTag('x'):
|
||||||
|
if statusCode != []:
|
||||||
|
self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ignore message from room in which we are not
|
||||||
|
if not self.last_history_time.has_key(jid):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msg.getXHTML(),
|
||||||
|
statusCode))
|
||||||
|
|
||||||
|
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
|
||||||
|
|
||||||
|
if not no_log_for:
|
||||||
|
no_log_for = ''
|
||||||
|
|
||||||
|
no_log_for = no_log_for.split()
|
||||||
|
|
||||||
|
tim_int = int(float(mktime(tim)))
|
||||||
|
|
||||||
|
if self.name not in no_log_for and jid not in no_log_for and not \
|
||||||
|
tim_int <= self.last_history_time[jid] and msgtxt and frm.find('/') >= 0:
|
||||||
|
# if frm.find('/') < 0, it means message comes from room itself
|
||||||
|
# usually it hold description and can be send at each connection
|
||||||
|
# so don't store it in logs
|
||||||
|
try:
|
||||||
|
gajim.logger.write('gc_msg', frm, msgtxt, tim=tim)
|
||||||
|
# save the time we log to avoid duplicate logs
|
||||||
|
self.last_history_time[jid] = tim_int
|
||||||
|
except exceptions.PysqliteOperationalError, e:
|
||||||
|
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
||||||
|
|
||||||
|
def dispatch_invite_message(self, invite, frm):
|
||||||
|
item = invite.getTag('invite')
|
||||||
|
jid_from = item.getAttr('from')
|
||||||
|
reason = item.getTagData('reason')
|
||||||
|
item = invite.getTag('password')
|
||||||
|
password = invite.getTagData('password')
|
||||||
|
|
||||||
|
is_continued = False
|
||||||
|
if invite.getTag('invite').getTag('continue'):
|
||||||
|
is_continued = True
|
||||||
|
self.dispatch('GC_INVITATION',(frm, jid_from, reason, password,
|
||||||
|
is_continued))
|
||||||
|
|
||||||
|
def get_or_create_session(self, jid, thread_id):
|
||||||
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
|
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
|
||||||
session = self.find_session(jid, thread_id, type)
|
|
||||||
|
pm = True
|
||||||
|
if not gajim.interface.is_pm_contact(jid, self.name):
|
||||||
|
pm = False
|
||||||
|
jid = gajim.get_jid_without_resource(jid)
|
||||||
|
|
||||||
|
session = self.find_session(jid, thread_id)
|
||||||
|
|
||||||
if session:
|
if session:
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
if pm:
|
||||||
|
return self.make_new_session(jid, thread_id, type = 'pm')
|
||||||
else:
|
else:
|
||||||
# it's possible we initiated a session with a bare JID and this is the
|
return self.make_new_session(jid, thread_id)
|
||||||
# first time we've seen a resource
|
|
||||||
bare_jid = gajim.get_jid_without_resource(jid)
|
|
||||||
if bare_jid != jid:
|
|
||||||
session = self.find_session(bare_jid, thread_id, type)
|
|
||||||
if session:
|
|
||||||
if not session.received_thread_id:
|
|
||||||
thread_id = session.thread_id
|
|
||||||
|
|
||||||
self.move_session(bare_jid, thread_id, jid.split("/")[1])
|
def find_session(self, jid, thread_id):
|
||||||
return session
|
|
||||||
|
|
||||||
return self.make_new_session(jid, thread_id, type)
|
|
||||||
|
|
||||||
def find_session(self, jid, thread_id, type):
|
|
||||||
try:
|
try:
|
||||||
if type == 'chat' and not thread_id:
|
if not thread_id:
|
||||||
return self.find_null_session(jid)
|
return self.find_null_session(jid)
|
||||||
else:
|
else:
|
||||||
return self.sessions[jid][thread_id]
|
return self.sessions[jid][thread_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def terminate_sessions(self):
|
||||||
|
'''send termination messages and delete all active sessions'''
|
||||||
|
for jid in self.sessions:
|
||||||
|
for thread_id in self.sessions[jid]:
|
||||||
|
self.sessions[jid][thread_id].terminate()
|
||||||
|
|
||||||
|
self.sessions = {}
|
||||||
|
|
||||||
def delete_session(self, jid, thread_id):
|
def delete_session(self, jid, thread_id):
|
||||||
try:
|
try:
|
||||||
del self.sessions[jid][thread_id]
|
del self.sessions[jid][thread_id]
|
||||||
|
|
||||||
if not self.sessions[jid]:
|
if not self.sessions[jid]:
|
||||||
del self.sessions[jid]
|
del self.sessions[jid]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def move_session(self, original_jid, thread_id, to_resource):
|
|
||||||
'''moves a session to another resource.'''
|
|
||||||
session = self.sessions[original_jid][thread_id]
|
|
||||||
|
|
||||||
del self.sessions[original_jid][thread_id]
|
|
||||||
|
|
||||||
new_jid = gajim.get_jid_without_resource(original_jid) + '/' + to_resource
|
|
||||||
session.jid = common.xmpp.JID(new_jid)
|
|
||||||
|
|
||||||
if not new_jid in self.sessions:
|
|
||||||
self.sessions[new_jid] = {}
|
|
||||||
|
|
||||||
self.sessions[new_jid][thread_id] = session
|
|
||||||
|
|
||||||
def find_null_session(self, jid):
|
def find_null_session(self, jid):
|
||||||
'''finds all of the sessions between us and jid that jid hasn't sent a thread_id in yet.
|
'''finds all of the sessions between us and a remote jid in which we
|
||||||
|
haven't received a thread_id yet and returns the session that we last
|
||||||
|
sent a message to.'''
|
||||||
|
|
||||||
returns the session that we last sent a message to.'''
|
sessions = self.sessions[jid].values()
|
||||||
|
no_threadid_sessions = filter(lambda s: not s.received_thread_id, sessions)
|
||||||
sessions_with_jid = self.sessions[jid].values()
|
|
||||||
no_threadid_sessions = filter(lambda s: not s.received_thread_id, sessions_with_jid)
|
|
||||||
|
|
||||||
if no_threadid_sessions:
|
if no_threadid_sessions:
|
||||||
no_threadid_sessions.sort(key=lambda s: s.last_send)
|
no_threadid_sessions.sort(key=lambda s: s.last_send)
|
||||||
|
@ -1808,8 +1775,16 @@ returns the session that we last sent a message to.'''
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def make_new_session(self, jid, thread_id = None, type = 'chat'):
|
def make_new_session(self, jid, thread_id=None, type='chat', klass=None):
|
||||||
sess = EncryptedStanzaSession(self, common.xmpp.JID(jid), thread_id, type)
|
if not klass:
|
||||||
|
klass = ChatControlSession
|
||||||
|
|
||||||
|
# determine if this session is a pm session
|
||||||
|
# if not, discard the resource
|
||||||
|
if not type == 'pm':
|
||||||
|
jid = gajim.get_jid_without_resource(jid)
|
||||||
|
|
||||||
|
sess = klass(self, common.xmpp.JID(jid), thread_id, type)
|
||||||
|
|
||||||
if not jid in self.sessions:
|
if not jid in self.sessions:
|
||||||
self.sessions[jid] = {}
|
self.sessions[jid] = {}
|
||||||
|
@ -2303,7 +2278,7 @@ returns the session that we last sent a message to.'''
|
||||||
self.dispatch('RESOURCE_CONFLICT', ())
|
self.dispatch('RESOURCE_CONFLICT', ())
|
||||||
|
|
||||||
def _register_handlers(self, con, con_type):
|
def _register_handlers(self, con, con_type):
|
||||||
# try to find another way to register handlers in each class
|
# try to find another way to register handlers in each class
|
||||||
# that defines handlers
|
# that defines handlers
|
||||||
con.RegisterHandler('message', self._messageCB)
|
con.RegisterHandler('message', self._messageCB)
|
||||||
con.RegisterHandler('presence', self._presenceCB)
|
con.RegisterHandler('presence', self._presenceCB)
|
||||||
|
|
|
@ -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
|
||||||
|
@ -168,7 +168,7 @@ class Events:
|
||||||
'''returns all events from the given account of the form
|
'''returns all events from the given account of the form
|
||||||
{jid1: [], jid2: []}
|
{jid1: [], jid2: []}
|
||||||
if jid is given, returns all events from the given jid in a list: []
|
if jid is given, returns all events from the given jid in a list: []
|
||||||
optionnaly only from given type'''
|
optionally only from given type'''
|
||||||
if not self._events.has_key(account):
|
if not self._events.has_key(account):
|
||||||
return []
|
return []
|
||||||
if not jid:
|
if not jid:
|
||||||
|
|
|
@ -178,7 +178,7 @@ gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI,
|
||||||
xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER,
|
xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER,
|
||||||
xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED,
|
xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED,
|
||||||
'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING,
|
'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING,
|
||||||
xmpp.NS_TIME_REVISED]
|
xmpp.NS_TIME_REVISED, xmpp.NS_GAMING]
|
||||||
# Optional features gajim supports
|
# Optional features gajim supports
|
||||||
gajim_optional_features = []
|
gajim_optional_features = []
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ def get_real_jid_from_fjid(account, fjid):
|
||||||
if not nick: # It's not a fake_jid, it is a real jid
|
if not nick: # It's not a fake_jid, it is a real jid
|
||||||
return fjid # we return the real jid
|
return fjid # we return the real jid
|
||||||
real_jid = fjid
|
real_jid = fjid
|
||||||
if interface.msg_win_mgr.get_control(room_jid, account):
|
if interface.msg_win_mgr.get_gc_control(room_jid, account):
|
||||||
# It's a pm, so if we have real jid it's in contact.jid
|
# It's a pm, so if we have real jid it's in contact.jid
|
||||||
gc_contact = contacts.get_gc_contact(account, room_jid, nick)
|
gc_contact = contacts.get_gc_contact(account, room_jid, nick)
|
||||||
if not gc_contact:
|
if not gc_contact:
|
||||||
|
|
|
@ -32,10 +32,23 @@ class StanzaSession(object):
|
||||||
else:
|
else:
|
||||||
self.thread_id = self.generate_thread_id()
|
self.thread_id = self.generate_thread_id()
|
||||||
|
|
||||||
|
self.loggable = True
|
||||||
|
|
||||||
self.last_send = 0
|
self.last_send = 0
|
||||||
self.status = None
|
self.status = None
|
||||||
self.negotiated = {}
|
self.negotiated = {}
|
||||||
|
|
||||||
|
def is_loggable(self):
|
||||||
|
account = self.conn.name
|
||||||
|
no_log_for = gajim.config.get_per('accounts', account, 'no_log_for')
|
||||||
|
|
||||||
|
if not no_log_for:
|
||||||
|
no_log_for = ''
|
||||||
|
|
||||||
|
no_log_for = no_log_for.split()
|
||||||
|
|
||||||
|
return self.loggable and account not in no_log_for and self.jid not in no_log_for
|
||||||
|
|
||||||
def generate_thread_id(self):
|
def generate_thread_id(self):
|
||||||
return "".join([random.choice(string.ascii_letters) for x in xrange(0,32)])
|
return "".join([random.choice(string.ascii_letters) for x in xrange(0,32)])
|
||||||
|
|
||||||
|
@ -70,7 +83,7 @@ class StanzaSession(object):
|
||||||
def cancelled_negotiation(self):
|
def cancelled_negotiation(self):
|
||||||
'''A negotiation has been cancelled, so reset this session to its default state.'''
|
'''A negotiation has been cancelled, so reset this session to its default state.'''
|
||||||
|
|
||||||
if hasattr(self, 'control'):
|
if self.control:
|
||||||
self.control.on_cancel_session_negotiation()
|
self.control.on_cancel_session_negotiation()
|
||||||
|
|
||||||
self.status = None
|
self.status = None
|
||||||
|
@ -132,8 +145,6 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
def __init__(self, conn, jid, thread_id, type = 'chat'):
|
def __init__(self, conn, jid, thread_id, type = 'chat'):
|
||||||
StanzaSession.__init__(self, conn, jid, thread_id, type = 'chat')
|
StanzaSession.__init__(self, conn, jid, thread_id, type = 'chat')
|
||||||
|
|
||||||
self.loggable = True
|
|
||||||
|
|
||||||
self.xes = {}
|
self.xes = {}
|
||||||
self.es = {}
|
self.es = {}
|
||||||
|
|
||||||
|
@ -767,7 +778,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
self.status = 'active'
|
self.status = 'active'
|
||||||
self.enable_encryption = True
|
self.enable_encryption = True
|
||||||
|
|
||||||
if hasattr(self, 'control'):
|
if self.control:
|
||||||
self.control.print_esession_details()
|
self.control.print_esession_details()
|
||||||
|
|
||||||
def final_steps_alice(self, form):
|
def final_steps_alice(self, form):
|
||||||
|
@ -787,15 +798,14 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
|
|
||||||
self.do_retained_secret(k, srs)
|
self.do_retained_secret(k, srs)
|
||||||
|
|
||||||
# don't need to calculate ks_s here
|
# ks_s doesn't need to be calculated here
|
||||||
|
|
||||||
self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(k)
|
self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(k)
|
||||||
self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(k)
|
self.kc_o, self.km_o, self.ks_o = self.generate_responder_keys(k)
|
||||||
|
|
||||||
# 4.6.2 Verifying Bob's Identity
|
# 4.6.2 Verifying Bob's Identity
|
||||||
|
|
||||||
self.verify_identity(form, self.d, False, 'b')
|
self.verify_identity(form, self.d, False, 'b')
|
||||||
# Note: If Alice discovers an error then she SHOULD ignore any encrypted content she received in the stanza.
|
# Note: If Alice discovers an error then she SHOULD ignore any encrypted content she received in the stanza.
|
||||||
|
|
||||||
if self.negotiated['logging'] == 'mustnot':
|
if self.negotiated['logging'] == 'mustnot':
|
||||||
self.loggable = False
|
self.loggable = False
|
||||||
|
@ -803,7 +813,7 @@ class EncryptedStanzaSession(StanzaSession):
|
||||||
self.status = 'active'
|
self.status = 'active'
|
||||||
self.enable_encryption = True
|
self.enable_encryption = True
|
||||||
|
|
||||||
if hasattr(self, 'control'):
|
if self.control:
|
||||||
self.control.print_esession_details()
|
self.control.print_esession_details()
|
||||||
|
|
||||||
# calculate and store the new retained secret
|
# calculate and store the new retained secret
|
||||||
|
@ -891,17 +901,6 @@ otherwise, list the fields we haven't implemented'''
|
||||||
# preventing falsified messages from going through.
|
# preventing falsified messages from going through.
|
||||||
self.km_o = ''
|
self.km_o = ''
|
||||||
|
|
||||||
def is_loggable(self):
|
|
||||||
account = self.conn.name
|
|
||||||
no_log_for = gajim.config.get_per('accounts', account, 'no_log_for')
|
|
||||||
|
|
||||||
if not no_log_for:
|
|
||||||
no_log_for = ''
|
|
||||||
|
|
||||||
no_log_for = no_log_for.split()
|
|
||||||
|
|
||||||
return self.loggable and account not in no_log_for and self.jid not in no_log_for
|
|
||||||
|
|
||||||
def cancelled_negotiation(self):
|
def cancelled_negotiation(self):
|
||||||
StanzaSession.cancelled_negotiation(self)
|
StanzaSession.cancelled_negotiation(self)
|
||||||
self.enable_encryption = False
|
self.enable_encryption = False
|
||||||
|
|
|
@ -658,7 +658,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
|
||||||
frm = unicode(frm)
|
frm = unicode(frm)
|
||||||
jid = frm
|
jid = frm
|
||||||
|
|
||||||
session = self.get_session(frm, thread_id, mtype)
|
session = self.get_or_create_session(frm, thread_id, mtype)
|
||||||
|
|
||||||
if thread_id and not session.received_thread_id:
|
if thread_id and not session.received_thread_id:
|
||||||
session.received_thread_id = True
|
session.received_thread_id = True
|
||||||
|
@ -794,7 +794,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
|
||||||
|
|
||||||
raise common.xmpp.NodeProcessed
|
raise common.xmpp.NodeProcessed
|
||||||
|
|
||||||
def get_session(self, jid, thread_id, type):
|
def get_or_create_session(self, jid, thread_id, type):
|
||||||
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
|
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
|
||||||
session = self.find_session(jid, thread_id, type)
|
session = self.find_session(jid, thread_id, type)
|
||||||
|
|
||||||
|
|
336
src/gajim.py
336
src/gajim.py
|
@ -241,6 +241,7 @@ from chat_control import ChatControl
|
||||||
from groupchat_control import GroupchatControl
|
from groupchat_control import GroupchatControl
|
||||||
from groupchat_control import PrivateChatControl
|
from groupchat_control import PrivateChatControl
|
||||||
from atom_window import AtomWindow
|
from atom_window import AtomWindow
|
||||||
|
from session import ChatControlSession
|
||||||
|
|
||||||
import common.sleepy
|
import common.sleepy
|
||||||
|
|
||||||
|
@ -257,7 +258,7 @@ from common.xmpp import Message as XmppMessage
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import otr, otr_windows
|
import otr, otr_windows
|
||||||
|
|
||||||
gajim.otr_module = otr
|
gajim.otr_module = otr
|
||||||
gajim.otr_windows = otr_windows
|
gajim.otr_windows = otr_windows
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -636,7 +637,7 @@ class Interface:
|
||||||
title = data[1]
|
title = data[1]
|
||||||
prompt = data[2]
|
prompt = data[2]
|
||||||
proposed_nick = data[3]
|
proposed_nick = data[3]
|
||||||
gc_control = self.msg_win_mgr.get_control(room_jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
if not gc_control and \
|
if not gc_control and \
|
||||||
room_jid in self.minimized_controls[account]:
|
room_jid in self.minimized_controls[account]:
|
||||||
gc_control = self.minimized_controls[account][room_jid]
|
gc_control = self.minimized_controls[account][room_jid]
|
||||||
|
@ -690,9 +691,10 @@ class Interface:
|
||||||
(jid_from, file_props))
|
(jid_from, file_props))
|
||||||
conn.disconnect_transfer(file_props)
|
conn.disconnect_transfer(file_props)
|
||||||
return
|
return
|
||||||
ctrl = self.msg_win_mgr.get_control(jid_from, account)
|
|
||||||
if ctrl and ctrl.type_id == message_control.TYPE_GC:
|
for ctrl in self.msg_win_mgr.get_chat_controls(jid_from, account):
|
||||||
ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
|
if ctrl.type_id == message_control.TYPE_GC:
|
||||||
|
ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
|
||||||
|
|
||||||
def handle_event_con_type(self, account, con_type):
|
def handle_event_con_type(self, account, con_type):
|
||||||
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'
|
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'
|
||||||
|
@ -922,146 +924,12 @@ class Interface:
|
||||||
self.handle_event_gc_notify(account, (jid, array[1], status_message,
|
self.handle_event_gc_notify(account, (jid, array[1], status_message,
|
||||||
array[3], None, None, None, None, None, [], None, None))
|
array[3], None, None, None, None, None, [], None, None))
|
||||||
|
|
||||||
def handle_event_msg(self, account, array):
|
|
||||||
# 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject,
|
|
||||||
# chatstate, msg_id, composing_xep, user_nick, xhtml, session, form_node))
|
|
||||||
# user_nick is JEP-0172
|
|
||||||
|
|
||||||
full_jid_with_resource = array[0]
|
|
||||||
jid = gajim.get_jid_without_resource(full_jid_with_resource)
|
|
||||||
resource = gajim.get_resource_from_jid(full_jid_with_resource)
|
|
||||||
|
|
||||||
message = array[1]
|
|
||||||
encrypted = array[3]
|
|
||||||
msg_type = array[4]
|
|
||||||
subject = array[5]
|
|
||||||
chatstate = array[6]
|
|
||||||
msg_id = array[7]
|
|
||||||
composing_xep = array[8]
|
|
||||||
xhtml = array[10]
|
|
||||||
session = array[11]
|
|
||||||
if gajim.config.get('ignore_incoming_xhtml'):
|
|
||||||
xhtml = None
|
|
||||||
if gajim.jid_is_transport(jid):
|
|
||||||
jid = jid.replace('@', '')
|
|
||||||
|
|
||||||
groupchat_control = self.msg_win_mgr.get_control(jid, account)
|
|
||||||
if not groupchat_control and \
|
|
||||||
jid in self.minimized_controls[account]:
|
|
||||||
groupchat_control = self.minimized_controls[account][jid]
|
|
||||||
pm = False
|
|
||||||
if groupchat_control and groupchat_control.type_id == \
|
|
||||||
message_control.TYPE_GC:
|
|
||||||
# It's a Private message
|
|
||||||
pm = True
|
|
||||||
msg_type = 'pm'
|
|
||||||
|
|
||||||
chat_control = None
|
|
||||||
jid_of_control = full_jid_with_resource
|
|
||||||
highest_contact = gajim.contacts.get_contact_with_highest_priority(
|
|
||||||
account, jid)
|
|
||||||
# Look for a chat control that has the given resource, or default to one
|
|
||||||
# without resource
|
|
||||||
ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account)
|
|
||||||
if ctrl:
|
|
||||||
chat_control = ctrl
|
|
||||||
elif not pm and (not highest_contact or not highest_contact.resource):
|
|
||||||
# unknow contact or offline message
|
|
||||||
jid_of_control = jid
|
|
||||||
chat_control = self.msg_win_mgr.get_control(jid, account)
|
|
||||||
elif highest_contact and resource != highest_contact.resource and \
|
|
||||||
highest_contact.show != 'offline':
|
|
||||||
jid_of_control = full_jid_with_resource
|
|
||||||
chat_control = None
|
|
||||||
elif not pm:
|
|
||||||
jid_of_control = jid
|
|
||||||
chat_control = self.msg_win_mgr.get_control(jid, account)
|
|
||||||
|
|
||||||
# Handle chat states
|
|
||||||
contact = gajim.contacts.get_contact(account, jid, resource)
|
|
||||||
if contact:
|
|
||||||
if contact.composing_xep != 'XEP-0085': # We cache xep85 support
|
|
||||||
contact.composing_xep = composing_xep
|
|
||||||
if chat_control and chat_control.type_id == message_control.TYPE_CHAT:
|
|
||||||
if chatstate is not None:
|
|
||||||
# other peer sent us reply, so he supports jep85 or jep22
|
|
||||||
contact.chatstate = chatstate
|
|
||||||
if contact.our_chatstate == 'ask': # we were jep85 disco?
|
|
||||||
contact.our_chatstate = 'active' # no more
|
|
||||||
chat_control.handle_incoming_chatstate()
|
|
||||||
elif contact.chatstate != 'active':
|
|
||||||
# got no valid jep85 answer, peer does not support it
|
|
||||||
contact.chatstate = False
|
|
||||||
elif chatstate == 'active':
|
|
||||||
# Brand new message, incoming.
|
|
||||||
contact.our_chatstate = chatstate
|
|
||||||
contact.chatstate = chatstate
|
|
||||||
if msg_id: # Do not overwrite an existing msg_id with None
|
|
||||||
contact.msg_id = msg_id
|
|
||||||
|
|
||||||
# THIS MUST BE AFTER chatstates handling
|
|
||||||
# AND BEFORE playsound (else we ear sounding on chatstates!)
|
|
||||||
if not message: # empty message text
|
|
||||||
return
|
|
||||||
|
|
||||||
if gajim.config.get('ignore_unknown_contacts') and \
|
|
||||||
not gajim.contacts.get_contacts(account, jid) and not pm:
|
|
||||||
return
|
|
||||||
if not contact:
|
|
||||||
# contact is not in the roster, create a fake one to display
|
|
||||||
# notification
|
|
||||||
contact = common.contacts.Contact(jid = jid, resource = resource)
|
|
||||||
advanced_notif_num = notify.get_advanced_notification('message_received',
|
|
||||||
account, contact)
|
|
||||||
|
|
||||||
# Is it a first or next message received ?
|
|
||||||
first = False
|
|
||||||
if msg_type == 'normal':
|
|
||||||
if not gajim.events.get_events(account, jid, ['normal']):
|
|
||||||
first = True
|
|
||||||
elif not chat_control and not gajim.events.get_events(account,
|
|
||||||
jid_of_control, [msg_type]): # msg_type can be chat or pm
|
|
||||||
first = True
|
|
||||||
|
|
||||||
if pm:
|
|
||||||
nickname = resource
|
|
||||||
groupchat_control.on_private_message(nickname, message, array[2],
|
|
||||||
xhtml, session, msg_id)
|
|
||||||
else:
|
|
||||||
# array: (jid, msg, time, encrypted, msg_type, subject)
|
|
||||||
if encrypted:
|
|
||||||
self.roster.on_message(jid, message, array[2], account, array[3],
|
|
||||||
msg_type, subject, resource, msg_id, array[9],
|
|
||||||
advanced_notif_num, session=session, form_node=array[12])
|
|
||||||
else:
|
|
||||||
# xhtml in last element
|
|
||||||
self.roster.on_message(jid, message, array[2], account, array[3],
|
|
||||||
msg_type, subject, resource, msg_id, array[9],
|
|
||||||
advanced_notif_num, xhtml=xhtml, session=session,
|
|
||||||
form_node=array[12])
|
|
||||||
nickname = gajim.get_name_from_jid(account, jid)
|
|
||||||
# Check and do wanted notifications
|
|
||||||
msg = message
|
|
||||||
if subject:
|
|
||||||
msg = _('Subject: %s') % subject + '\n' + msg
|
|
||||||
focused = False
|
|
||||||
if chat_control:
|
|
||||||
parent_win = chat_control.parent_win
|
|
||||||
if chat_control == parent_win.get_active_control() and \
|
|
||||||
parent_win.window.has_focus:
|
|
||||||
focused = True
|
|
||||||
notify.notify('new_message', jid_of_control, account, [msg_type,
|
|
||||||
first, nickname, msg, focused], advanced_notif_num)
|
|
||||||
|
|
||||||
if self.remote_ctrl:
|
|
||||||
self.remote_ctrl.raise_signal('NewMessage', (account, array))
|
|
||||||
|
|
||||||
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))
|
||||||
full_jid_with_resource = array[0]
|
full_jid_with_resource = array[0]
|
||||||
jids = full_jid_with_resource.split('/', 1)
|
jids = full_jid_with_resource.split('/', 1)
|
||||||
jid = jids[0]
|
jid = jids[0]
|
||||||
gc_control = self.msg_win_mgr.get_control(jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||||
if not gc_control and \
|
if not gc_control and \
|
||||||
jid in self.minimized_controls[account]:
|
jid in self.minimized_controls[account]:
|
||||||
gc_control = self.minimized_controls[account][jid]
|
gc_control = self.minimized_controls[account][jid]
|
||||||
|
@ -1291,21 +1159,22 @@ class Interface:
|
||||||
win.set_values(vcard)
|
win.set_values(vcard)
|
||||||
|
|
||||||
# show avatar in chat
|
# show avatar in chat
|
||||||
win = None
|
ctrls = []
|
||||||
ctrl = None
|
|
||||||
if resource and self.msg_win_mgr.has_window(
|
if resource and self.msg_win_mgr.has_window(
|
||||||
jid + '/' + resource, account):
|
jid + '/' + resource, account):
|
||||||
win = self.msg_win_mgr.get_window(jid + '/' + resource,
|
win = self.msg_win_mgr.get_window(jid + '/' + resource,
|
||||||
account)
|
account)
|
||||||
ctrl = win.get_control(jid + '/' + resource, account)
|
ctrls = win.get_controls(jid + '/' + resource, account)
|
||||||
elif self.msg_win_mgr.has_window(jid, account):
|
elif self.msg_win_mgr.has_window(jid, account):
|
||||||
win = self.msg_win_mgr.get_window(jid, account)
|
win = self.msg_win_mgr.get_window(jid, account)
|
||||||
ctrl = win.get_control(jid, account)
|
ctrls = win.get_controls(jid, account)
|
||||||
if win and ctrl.type_id != message_control.TYPE_GC:
|
|
||||||
ctrl.show_avatar()
|
for ctrl in ctrls:
|
||||||
|
if ctrl.type_id != message_control.TYPE_GC:
|
||||||
|
ctrl.show_avatar()
|
||||||
|
|
||||||
# Show avatar in roster or gc_roster
|
# Show avatar in roster or gc_roster
|
||||||
gc_ctrl = self.msg_win_mgr.get_control(jid, account)
|
gc_ctrl = self.msg_win_mgr.get_gc_control(jid, account)
|
||||||
if not gc_ctrl and \
|
if not gc_ctrl and \
|
||||||
jid in self.minimized_controls[account]:
|
jid in self.minimized_controls[account]:
|
||||||
gc_ctrl = self.minimized_controls[account][jid]
|
gc_ctrl = self.minimized_controls[account][jid]
|
||||||
|
@ -1362,21 +1231,25 @@ class Interface:
|
||||||
|
|
||||||
# Get the window and control for the updated status, this may be a
|
# Get the window and control for the updated status, this may be a
|
||||||
# PrivateChatControl
|
# PrivateChatControl
|
||||||
control = self.msg_win_mgr.get_control(room_jid, account)
|
control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
|
|
||||||
if not control and \
|
if not control and \
|
||||||
room_jid in self.minimized_controls[account]:
|
room_jid in self.minimized_controls[account]:
|
||||||
control = self.minimized_controls[account][room_jid]
|
control = self.minimized_controls[account][room_jid]
|
||||||
|
|
||||||
if control and control.type_id != message_control.TYPE_GC:
|
if not control or (control and control.type_id != message_control.TYPE_GC):
|
||||||
return
|
return
|
||||||
if control:
|
|
||||||
control.chg_contact_status(nick, show, status, array[4], array[5],
|
|
||||||
array[6], array[7], array[8], array[9], array[10], array[11])
|
|
||||||
|
|
||||||
ctrl = self.msg_win_mgr.get_control(fjid, account)
|
control.chg_contact_status(nick, show, status, array[4], array[5],
|
||||||
|
array[6], array[7], array[8], array[9], array[10], array[11])
|
||||||
|
|
||||||
# print status in chat window and update status/GPG image
|
contact = gajim.contacts.\
|
||||||
if ctrl:
|
get_contact_with_highest_priority(account, room_jid)
|
||||||
|
if contact:
|
||||||
|
self.roster.draw_contact(room_jid, account)
|
||||||
|
|
||||||
|
# print status in chat windows and update status/GPG image
|
||||||
|
for ctrl in self.msg_win_mgr.get_chat_controls(fjid, account):
|
||||||
statusCode = array[9]
|
statusCode = array[9]
|
||||||
if '303' in statusCode:
|
if '303' in statusCode:
|
||||||
new_nick = array[10]
|
new_nick = array[10]
|
||||||
|
@ -1412,7 +1285,7 @@ class Interface:
|
||||||
jids = array[0].split('/', 1)
|
jids = array[0].split('/', 1)
|
||||||
room_jid = jids[0]
|
room_jid = jids[0]
|
||||||
|
|
||||||
gc_control = self.msg_win_mgr.get_control(room_jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
if not gc_control and \
|
if not gc_control and \
|
||||||
room_jid in self.minimized_controls[account]:
|
room_jid in self.minimized_controls[account]:
|
||||||
gc_control = self.minimized_controls[account][room_jid]
|
gc_control = self.minimized_controls[account][room_jid]
|
||||||
|
@ -1440,7 +1313,7 @@ class Interface:
|
||||||
jids = array[0].split('/', 1)
|
jids = array[0].split('/', 1)
|
||||||
jid = jids[0]
|
jid = jids[0]
|
||||||
|
|
||||||
gc_control = self.msg_win_mgr.get_control(jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||||
|
|
||||||
if not gc_control and \
|
if not gc_control and \
|
||||||
jid in self.minimized_controls[account]:
|
jid in self.minimized_controls[account]:
|
||||||
|
@ -1505,7 +1378,7 @@ class Interface:
|
||||||
jid = array[0]
|
jid = array[0]
|
||||||
statusCode = array[1]
|
statusCode = array[1]
|
||||||
|
|
||||||
gc_control = self.msg_win_mgr.get_control(jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||||
if not gc_control and \
|
if not gc_control and \
|
||||||
jid in self.minimized_controls[account]:
|
jid in self.minimized_controls[account]:
|
||||||
gc_control = self.minimized_controls[account][jid]
|
gc_control = self.minimized_controls[account][jid]
|
||||||
|
@ -1564,7 +1437,7 @@ class Interface:
|
||||||
self.roster.on_disconnect(None, room_jid, account)
|
self.roster.on_disconnect(None, room_jid, account)
|
||||||
else:
|
else:
|
||||||
win = self.msg_win_mgr.get_window(room_jid, account)
|
win = self.msg_win_mgr.get_window(room_jid, account)
|
||||||
ctrl = win.get_control(room_jid, account)
|
ctrl = win.get_gc_control(room_jid, account)
|
||||||
win.remove_tab(ctrl, 3)
|
win.remove_tab(ctrl, 3)
|
||||||
|
|
||||||
dlg = dialogs.InputDialog(_('Password Required'),
|
dlg = dialogs.InputDialog(_('Password Required'),
|
||||||
|
@ -1992,9 +1865,9 @@ class Interface:
|
||||||
AtomWindow.newAtomEntry(atom_entry)
|
AtomWindow.newAtomEntry(atom_entry)
|
||||||
|
|
||||||
def handle_event_failed_decrypt(self, account, data):
|
def handle_event_failed_decrypt(self, account, data):
|
||||||
jid, tim = data
|
jid, tim, session = data
|
||||||
|
|
||||||
ctrl = self.msg_win_mgr.get_control(jid, account)
|
ctrl = self.msg_win_mgr.get_control(jid, account, session.thread_id)
|
||||||
if ctrl:
|
if ctrl:
|
||||||
ctrl.print_conversation_line('Unable to decrypt message from %s\nIt may have been tampered with.' % (jid), 'status', '', tim)
|
ctrl.print_conversation_line('Unable to decrypt message from %s\nIt may have been tampered with.' % (jid), 'status', '', tim)
|
||||||
else:
|
else:
|
||||||
|
@ -2133,6 +2006,8 @@ class Interface:
|
||||||
if ctrl:
|
if ctrl:
|
||||||
new_sess = gajim.connections[account].make_new_session(str(jid))
|
new_sess = gajim.connections[account].make_new_session(str(jid))
|
||||||
ctrl.set_session(new_sess)
|
ctrl.set_session(new_sess)
|
||||||
|
gajim.connections[account].delete_session(str(jid),
|
||||||
|
session.thread_id)
|
||||||
|
|
||||||
if was_encrypted:
|
if was_encrypted:
|
||||||
ctrl.print_esession_details()
|
ctrl.print_esession_details()
|
||||||
|
@ -2392,7 +2267,6 @@ class Interface:
|
||||||
'ERROR_ANSWER': self.handle_event_error_answer,
|
'ERROR_ANSWER': self.handle_event_error_answer,
|
||||||
'STATUS': self.handle_event_status,
|
'STATUS': self.handle_event_status,
|
||||||
'NOTIFY': self.handle_event_notify,
|
'NOTIFY': self.handle_event_notify,
|
||||||
'MSG': self.handle_event_msg,
|
|
||||||
'MSGERROR': self.handle_event_msgerror,
|
'MSGERROR': self.handle_event_msgerror,
|
||||||
'MSGSENT': self.handle_event_msgsent,
|
'MSGSENT': self.handle_event_msgsent,
|
||||||
'MSGNOTSENT': self.handle_event_msgnotsent,
|
'MSGNOTSENT': self.handle_event_msgnotsent,
|
||||||
|
@ -2842,9 +2716,10 @@ class Interface:
|
||||||
'''joins the room immediately'''
|
'''joins the room immediately'''
|
||||||
if self.msg_win_mgr.has_window(room_jid, account) and \
|
if self.msg_win_mgr.has_window(room_jid, account) and \
|
||||||
gajim.gc_connected[account][room_jid]:
|
gajim.gc_connected[account][room_jid]:
|
||||||
win = self.msg_win_mgr.get_window(room_jid, account)
|
gc_ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
|
win = gc_ctrl.parent_win
|
||||||
win.window.present()
|
win.window.present()
|
||||||
win.set_active_tab(room_jid, account)
|
win.set_active_tab(gc_ctrl)
|
||||||
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
|
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
|
||||||
return
|
return
|
||||||
minimized_control_exists = False
|
minimized_control_exists = False
|
||||||
|
@ -2869,9 +2744,9 @@ class Interface:
|
||||||
not self.msg_win_mgr.has_window(room_jid, account):
|
not self.msg_win_mgr.has_window(room_jid, account):
|
||||||
self.new_room(room_jid, nick, account, is_continued=is_continued)
|
self.new_room(room_jid, nick, account, is_continued=is_continued)
|
||||||
if not minimized_control_exists:
|
if not minimized_control_exists:
|
||||||
gc_win = self.msg_win_mgr.get_window(room_jid, account)
|
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
gc_win.set_active_tab(room_jid, account)
|
gc_control.parent_win.set_active_tab(gc_control)
|
||||||
gc_win.window.present()
|
gc_control.parent_win.window.present()
|
||||||
gajim.connections[account].join_gc(nick, room_jid, password)
|
gajim.connections[account].join_gc(nick, room_jid, password)
|
||||||
if password:
|
if password:
|
||||||
gajim.gc_passwords[room_jid] = password
|
gajim.gc_passwords[room_jid] = password
|
||||||
|
@ -2895,18 +2770,42 @@ class Interface:
|
||||||
contact = gajim.contacts.contact_from_gc_contact(gc_contact)
|
contact = gajim.contacts.contact_from_gc_contact(gc_contact)
|
||||||
type_ = message_control.TYPE_PM
|
type_ = message_control.TYPE_PM
|
||||||
fjid = gc_contact.room_jid + '/' + gc_contact.name
|
fjid = gc_contact.room_jid + '/' + gc_contact.name
|
||||||
mw = self.msg_win_mgr.get_window(fjid, account)
|
|
||||||
if not mw:
|
|
||||||
mw = self.msg_win_mgr.create_window(contact, account, type_)
|
|
||||||
|
|
||||||
chat_control = \
|
conn = gajim.connections[account]
|
||||||
PrivateChatControl(mw, gc_contact, contact, account, session)
|
|
||||||
mw.new_tab(chat_control)
|
if not session and fjid in conn.sessions:
|
||||||
|
sessions = filter(lambda s: isinstance(s, ChatControlSession),
|
||||||
|
conn.sessions[fjid].values())
|
||||||
|
|
||||||
|
# look for an existing session with a chat control
|
||||||
|
for s in sessions:
|
||||||
|
if s.control:
|
||||||
|
session = s
|
||||||
|
break
|
||||||
|
|
||||||
|
if not session and not len(sessions) == 0:
|
||||||
|
# there are no sessions with chat controls, just take the first one
|
||||||
|
session = sessions[0]
|
||||||
|
|
||||||
|
if not session:
|
||||||
|
# couldn't find an existing ChatControlSession, just make a new one
|
||||||
|
|
||||||
|
session = conn.make_new_session(fjid, None, 'pm')
|
||||||
|
|
||||||
|
if not session.control:
|
||||||
|
mw = self.msg_win_mgr.get_window(fjid, account)
|
||||||
|
if not mw:
|
||||||
|
mw = self.msg_win_mgr.create_window(contact, account, type_)
|
||||||
|
|
||||||
|
session.control = PrivateChatControl(mw, gc_contact, contact, account,
|
||||||
|
session)
|
||||||
|
mw.new_tab(session.control)
|
||||||
|
|
||||||
if len(gajim.events.get_events(account, fjid)):
|
if len(gajim.events.get_events(account, fjid)):
|
||||||
# We call this here to avoid race conditions with widget validation
|
# We call this here to avoid race conditions with widget validation
|
||||||
chat_control.read_queue()
|
session.control.read_queue()
|
||||||
|
|
||||||
def new_chat(self, contact, account, resource = None, session = None):
|
def new_chat(self, session, contact, account, resource = None):
|
||||||
# Get target window, create a control, and associate it with the window
|
# Get target window, create a control, and associate it with the window
|
||||||
type_ = message_control.TYPE_CHAT
|
type_ = message_control.TYPE_CHAT
|
||||||
|
|
||||||
|
@ -2922,9 +2821,7 @@ class Interface:
|
||||||
|
|
||||||
mw.new_tab(chat_control)
|
mw.new_tab(chat_control)
|
||||||
|
|
||||||
if len(gajim.events.get_events(account, fjid)):
|
return chat_control
|
||||||
# We call this here to avoid race conditions with widget validation
|
|
||||||
chat_control.read_queue()
|
|
||||||
|
|
||||||
def new_chat_from_jid(self, account, fjid):
|
def new_chat_from_jid(self, account, fjid):
|
||||||
jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
|
jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
|
||||||
|
@ -2933,35 +2830,68 @@ class Interface:
|
||||||
if not contact:
|
if not contact:
|
||||||
added_to_roster = True
|
added_to_roster = True
|
||||||
contact = self.roster.add_to_not_in_the_roster(account, jid,
|
contact = self.roster.add_to_not_in_the_roster(account, jid,
|
||||||
resource = resource)
|
resource=resource)
|
||||||
|
|
||||||
|
session = gajim.connections[account].get_or_create_session(fjid, None)
|
||||||
|
|
||||||
if not self.msg_win_mgr.has_window(fjid, account):
|
if not self.msg_win_mgr.has_window(fjid, account):
|
||||||
self.new_chat(contact, account, resource = resource)
|
session.control = self.new_chat(session, contact, account,
|
||||||
mw = self.msg_win_mgr.get_window(fjid, account)
|
resource=resource)
|
||||||
mw.set_active_tab(fjid, account)
|
if len(gajim.events.get_events(account, fjid)):
|
||||||
|
session.control.read_queue()
|
||||||
|
|
||||||
|
mw = session.control.parent_win
|
||||||
|
mw.set_active_tab(session.control)
|
||||||
mw.window.present()
|
mw.window.present()
|
||||||
# For JEP-0172
|
# For JEP-0172
|
||||||
if added_to_roster:
|
if added_to_roster:
|
||||||
mc = mw.get_control(fjid, account)
|
session.control.user_nick = gajim.nicks[account]
|
||||||
mc.user_nick = gajim.nicks[account]
|
|
||||||
|
|
||||||
def on_open_chat_window(self, widget, contact, account, resource = None,
|
def on_open_chat_window(self, widget, contact, account, resource=None,
|
||||||
session = None):
|
session=None):
|
||||||
# Get the window containing the chat
|
# Get the window containing the chat
|
||||||
fjid = contact.jid
|
fjid = contact.jid
|
||||||
if resource:
|
if resource:
|
||||||
fjid += '/' + resource
|
fjid += '/' + resource
|
||||||
win = self.msg_win_mgr.get_window(fjid, account)
|
|
||||||
if not win:
|
conn = gajim.connections[account]
|
||||||
self.new_chat(contact, account, resource = resource, session = session)
|
|
||||||
win = self.msg_win_mgr.get_window(fjid, account)
|
if not session and contact.jid in conn.sessions:
|
||||||
ctrl = win.get_control(fjid, account)
|
sessions = filter(lambda s: isinstance(s, ChatControlSession),
|
||||||
|
conn.sessions[contact.jid].values())
|
||||||
|
|
||||||
|
# look for an existing session with a chat control
|
||||||
|
for s in sessions:
|
||||||
|
if s.control:
|
||||||
|
session = s
|
||||||
|
break
|
||||||
|
|
||||||
|
if not session and not len(sessions) == 0:
|
||||||
|
# there are no sessions with chat controls, just take the first one
|
||||||
|
session = sessions[0]
|
||||||
|
|
||||||
|
if not session:
|
||||||
|
# couldn't find an existing ChatControlSession, just make a new one
|
||||||
|
session = conn.make_new_session(fjid, None, 'chat')
|
||||||
|
|
||||||
|
if not session.control:
|
||||||
|
# open a new chat control
|
||||||
|
session.control = self.new_chat(session, contact, account,
|
||||||
|
resource=resource)
|
||||||
|
|
||||||
|
if len(gajim.events.get_events(account, fjid)):
|
||||||
|
session.control.read_queue()
|
||||||
|
|
||||||
# last message is long time ago
|
# last message is long time ago
|
||||||
gajim.last_message_time[account][ctrl.get_full_jid()] = 0
|
gajim.last_message_time[account][session.control.get_full_jid()] = 0
|
||||||
win.set_active_tab(fjid, account)
|
|
||||||
if gajim.connections[account].is_zeroconf and \
|
win = session.control.parent_win
|
||||||
gajim.connections[account].status in ('offline', 'invisible'):
|
win.set_active_tab(session.control)
|
||||||
win.get_control(fjid, account).got_disconnected()
|
|
||||||
|
if conn.is_zeroconf and conn.status in ('offline', 'invisible'):
|
||||||
|
for ctrl in win.get_controls(fjid, account):
|
||||||
|
ctrl.got_disconnected()
|
||||||
|
|
||||||
win.window.present()
|
win.window.present()
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
@ -3152,7 +3082,7 @@ class Interface:
|
||||||
'password': password,
|
'password': password,
|
||||||
'nick': nick
|
'nick': nick
|
||||||
}
|
}
|
||||||
place_found = False
|
place_found = False
|
||||||
index = 0
|
index = 0
|
||||||
# check for duplicate entry and respect alpha order
|
# check for duplicate entry and respect alpha order
|
||||||
for bookmark in gajim.connections[account].bookmarks:
|
for bookmark in gajim.connections[account].bookmarks:
|
||||||
|
@ -3176,6 +3106,18 @@ class Interface:
|
||||||
_('You can manage your bookmarks via Actions menu in your roster.'))
|
_('You can manage your bookmarks via Actions menu in your roster.'))
|
||||||
|
|
||||||
|
|
||||||
|
# does JID exist only within a groupchat?
|
||||||
|
def is_pm_contact(self, fjid, account):
|
||||||
|
bare_jid = gajim.get_jid_without_resource(fjid)
|
||||||
|
|
||||||
|
gc_ctrl = self.msg_win_mgr.get_gc_control(bare_jid, account)
|
||||||
|
|
||||||
|
if not gc_ctrl and \
|
||||||
|
bare_jid in self.minimized_controls[account]:
|
||||||
|
gc_ctrl = self.minimized_controls[self.name][bare_jid]
|
||||||
|
|
||||||
|
return gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC
|
||||||
|
|
||||||
def create_ipython_window(self):
|
def create_ipython_window(self):
|
||||||
try:
|
try:
|
||||||
from ipython_view import IPythonView
|
from ipython_view import IPythonView
|
||||||
|
|
|
@ -43,6 +43,7 @@ import cell_renderer_image
|
||||||
|
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import helpers
|
from common import helpers
|
||||||
|
from common.stanza_session import StanzaSession
|
||||||
|
|
||||||
from chat_control import ChatControl
|
from chat_control import ChatControl
|
||||||
from chat_control import ChatControlBase
|
from chat_control import ChatControlBase
|
||||||
|
@ -57,7 +58,7 @@ C_TYPE, # type of the row ('contact' or 'role')
|
||||||
C_TEXT, # text shown in the cellrenderer
|
C_TEXT, # text shown in the cellrenderer
|
||||||
C_AVATAR, # avatar of the contact
|
C_AVATAR, # avatar of the contact
|
||||||
) = range(5)
|
) = range(5)
|
||||||
|
|
||||||
def set_renderer_color(treeview, renderer, set_background = True):
|
def set_renderer_color(treeview, renderer, set_background = True):
|
||||||
'''set style for group row, using PRELIGHT system color'''
|
'''set style for group row, using PRELIGHT system color'''
|
||||||
if set_background:
|
if set_background:
|
||||||
|
@ -120,7 +121,7 @@ class PrivateChatControl(ChatControl):
|
||||||
|
|
||||||
def __init__(self, parent_win, gc_contact, contact, account, session):
|
def __init__(self, parent_win, gc_contact, contact, account, session):
|
||||||
room_jid = contact.jid.split('/')[0]
|
room_jid = contact.jid.split('/')[0]
|
||||||
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, account)
|
room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
if gajim.interface.minimized_controls[account].has_key(room_jid):
|
if gajim.interface.minimized_controls[account].has_key(room_jid):
|
||||||
room_ctrl = gajim.interface.minimized_controls[account][room_jid]
|
room_ctrl = gajim.interface.minimized_controls[account][room_jid]
|
||||||
self.room_name = room_ctrl.name
|
self.room_name = room_ctrl.name
|
||||||
|
@ -222,6 +223,9 @@ class GroupchatControl(ChatControlBase):
|
||||||
self.new_nick = ''
|
self.new_nick = ''
|
||||||
self.name = self.room_jid.split('@')[0]
|
self.name = self.room_jid.split('@')[0]
|
||||||
|
|
||||||
|
self.session = StanzaSession(gajim.connections[self.account],
|
||||||
|
self.room_jid, 'gc', 'gc')
|
||||||
|
|
||||||
compact_view = gajim.config.get('compact_view')
|
compact_view = gajim.config.get('compact_view')
|
||||||
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'),
|
||||||
|
@ -456,9 +460,9 @@ class GroupchatControl(ChatControlBase):
|
||||||
'state_muc_msg_color')
|
'state_muc_msg_color')
|
||||||
if color_name:
|
if color_name:
|
||||||
color = gtk.gdk.colormap_get_system().alloc_color(color_name)
|
color = gtk.gdk.colormap_get_system().alloc_color(color_name)
|
||||||
|
|
||||||
label_str = self.name
|
label_str = self.name
|
||||||
|
|
||||||
# count waiting highlighted messages
|
# count waiting highlighted messages
|
||||||
unread = ''
|
unread = ''
|
||||||
num_unread = self.get_nb_unread()
|
num_unread = self.get_nb_unread()
|
||||||
|
@ -619,9 +623,8 @@ class GroupchatControl(ChatControlBase):
|
||||||
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
|
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
|
||||||
|
|
||||||
# We print if window is opened
|
# We print if window is opened
|
||||||
pm_control = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
|
if session.control:
|
||||||
if pm_control:
|
session.control.print_conversation(msg, tim = tim, xhtml = xhtml)
|
||||||
pm_control.print_conversation(msg, tim = tim, xhtml = xhtml)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
|
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
|
||||||
|
@ -645,7 +648,7 @@ class GroupchatControl(ChatControlBase):
|
||||||
self.parent_win.show_title()
|
self.parent_win.show_title()
|
||||||
self.parent_win.redraw_tab(self)
|
self.parent_win.redraw_tab(self)
|
||||||
else:
|
else:
|
||||||
self._start_private_message(nick, session)
|
self._start_private_message(nick)
|
||||||
# Scroll to line
|
# Scroll to line
|
||||||
self.list_treeview.expand_row(path[0:1], False)
|
self.list_treeview.expand_row(path[0:1], False)
|
||||||
self.list_treeview.scroll_to_cell(path)
|
self.list_treeview.scroll_to_cell(path)
|
||||||
|
@ -870,10 +873,9 @@ class GroupchatControl(ChatControlBase):
|
||||||
for nick in nick_list:
|
for nick in nick_list:
|
||||||
# Update pm chat window
|
# Update pm chat window
|
||||||
fjid = self.room_jid + '/' + nick
|
fjid = self.room_jid + '/' + nick
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
|
|
||||||
gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
|
gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
|
||||||
nick)
|
nick)
|
||||||
if ctrl:
|
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(fjid, self.account):
|
||||||
gc_contact.show = 'offline'
|
gc_contact.show = 'offline'
|
||||||
gc_contact.status = ''
|
gc_contact.status = ''
|
||||||
ctrl.update_ui()
|
ctrl.update_ui()
|
||||||
|
@ -898,18 +900,17 @@ class GroupchatControl(ChatControlBase):
|
||||||
# Recalculate column width for ellipsizin
|
# Recalculate column width for ellipsizin
|
||||||
self.list_treeview.columns_autosize()
|
self.list_treeview.columns_autosize()
|
||||||
|
|
||||||
def on_send_pm(self, widget = None, model = None, iter = None, nick = None,
|
def on_send_pm(self, widget=None, model=None, iter=None, nick=None,
|
||||||
msg = None):
|
msg=None):
|
||||||
'''opens a chat window and msg is not None sends private message to a
|
'''opens a chat window and if msg is not None sends private message to a
|
||||||
contact in a room'''
|
contact in a room'''
|
||||||
if nick is None:
|
if nick is None:
|
||||||
nick = model[iter][C_NICK].decode('utf-8')
|
nick = model[iter][C_NICK].decode('utf-8')
|
||||||
fjid = gajim.construct_fjid(self.room_jid, nick) # 'fake' jid
|
fjid = gajim.construct_fjid(self.room_jid, nick) # 'fake' jid
|
||||||
|
|
||||||
self._start_private_message(nick)
|
ctrl = self._start_private_message(nick)
|
||||||
if msg:
|
if msg:
|
||||||
gajim.interface.msg_win_mgr.get_control(fjid, self.account).\
|
ctrl.send_message(msg)
|
||||||
send_message(msg)
|
|
||||||
|
|
||||||
def on_send_file(self, widget, gc_contact):
|
def on_send_file(self, widget, gc_contact):
|
||||||
'''sends a file to a contact in the room'''
|
'''sends a file to a contact in the room'''
|
||||||
|
@ -1615,7 +1616,7 @@ class GroupchatControl(ChatControlBase):
|
||||||
for nick in nick_list:
|
for nick in nick_list:
|
||||||
# Update pm chat window
|
# Update pm chat window
|
||||||
fjid = self.room_jid + '/' + nick
|
fjid = self.room_jid + '/' + nick
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
|
ctrl = gajim.interface.msg_win_mgr.get_gc_control(fjid, self.account)
|
||||||
if ctrl:
|
if ctrl:
|
||||||
contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||||
contact.show = 'offline'
|
contact.show = 'offline'
|
||||||
|
@ -2028,7 +2029,7 @@ class GroupchatControl(ChatControlBase):
|
||||||
menu.show_all()
|
menu.show_all()
|
||||||
menu.popup(None, None, None, event.button, event.time)
|
menu.popup(None, None, None, event.button, event.time)
|
||||||
|
|
||||||
def _start_private_message(self, nick, session = None):
|
def _start_private_message(self, nick):
|
||||||
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||||
nick_jid = gc_c.get_full_jid()
|
nick_jid = gc_c.get_full_jid()
|
||||||
|
|
||||||
|
@ -2036,9 +2037,14 @@ class GroupchatControl(ChatControlBase):
|
||||||
if not win:
|
if not win:
|
||||||
gajim.interface.new_private_chat(gc_c, self.account)
|
gajim.interface.new_private_chat(gc_c, self.account)
|
||||||
win = gajim.interface.msg_win_mgr.get_window(nick_jid, self.account)
|
win = gajim.interface.msg_win_mgr.get_window(nick_jid, self.account)
|
||||||
win.set_active_tab(nick_jid, self.account)
|
|
||||||
|
ctrl = win.get_controls(nick_jid, self.account)[0]
|
||||||
|
|
||||||
|
win.set_active_tab(ctrl)
|
||||||
win.window.present()
|
win.window.present()
|
||||||
|
|
||||||
|
return ctrl
|
||||||
|
|
||||||
def on_row_activated(self, widget, path):
|
def on_row_activated(self, widget, path):
|
||||||
'''When an iter is activated (dubblick or single click if gnome is set
|
'''When an iter is activated (dubblick or single click if gnome is set
|
||||||
this way'''
|
this way'''
|
||||||
|
@ -2097,7 +2103,7 @@ class GroupchatControl(ChatControlBase):
|
||||||
return
|
return
|
||||||
|
|
||||||
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK:
|
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK:
|
||||||
self.on_row_activated(widget, path)
|
self.on_row_activated(widget, path)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
model = widget.get_model()
|
model = widget.get_model()
|
||||||
|
|
|
@ -117,25 +117,24 @@ class MessageControl:
|
||||||
return len(gajim.events.get_events(self.account, self.contact.jid))
|
return len(gajim.events.get_events(self.account, self.contact.jid))
|
||||||
|
|
||||||
def set_session(self, session):
|
def set_session(self, session):
|
||||||
if hasattr(self, 'session') and session == self.session:
|
oldsession = None
|
||||||
|
if hasattr(self, 'session'):
|
||||||
|
oldsession = self.session
|
||||||
|
|
||||||
|
if oldsession and session == oldsession:
|
||||||
return
|
return
|
||||||
|
|
||||||
was_encrypted = False
|
|
||||||
|
|
||||||
if hasattr(self, 'session') and self.session:
|
|
||||||
if self.session.enable_encryption:
|
|
||||||
was_encrypted = True
|
|
||||||
|
|
||||||
gajim.connections[self.account].delete_session(self.session.jid,
|
|
||||||
self.session.thread_id)
|
|
||||||
|
|
||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
if session:
|
if session:
|
||||||
session.control = self
|
session.control = self
|
||||||
|
|
||||||
if was_encrypted:
|
if oldsession:
|
||||||
self.print_esession_details()
|
self.parent_win.change_thread_key(self.contact.jid, self.account,
|
||||||
|
oldsession.thread_id, session.thread_id)
|
||||||
|
|
||||||
|
if oldsession.enable_encryption:
|
||||||
|
self.print_esession_details()
|
||||||
|
|
||||||
def send_message(self, message, keyID = '', type = 'chat',
|
def send_message(self, message, keyID = '', type = 'chat',
|
||||||
chatstate = None, msg_id = None, composing_xep = None, resource = None,
|
chatstate = None, msg_id = None, composing_xep = None, resource = None,
|
||||||
|
@ -145,12 +144,6 @@ class MessageControl:
|
||||||
jid = self.contact.jid
|
jid = self.contact.jid
|
||||||
original_message = message
|
original_message = message
|
||||||
|
|
||||||
if not self.session:
|
|
||||||
fjid = self.contact.get_full_jid()
|
|
||||||
new_session = gajim.connections[self.account].make_new_session(fjid)
|
|
||||||
|
|
||||||
self.set_session(new_session)
|
|
||||||
|
|
||||||
if gajim.otr_module:
|
if gajim.otr_module:
|
||||||
if type == 'chat' and isinstance(message, unicode):
|
if type == 'chat' and isinstance(message, unicode):
|
||||||
d = {'kwargs':{'keyID':keyID, 'type':type,
|
d = {'kwargs':{'keyID':keyID, 'type':type,
|
||||||
|
@ -158,7 +151,7 @@ class MessageControl:
|
||||||
'composing_xep':composing_xep, 'resource':self.resource,
|
'composing_xep':composing_xep, 'resource':self.resource,
|
||||||
'user_nick':user_nick, 'session':self.session,
|
'user_nick':user_nick, 'session':self.session,
|
||||||
'original_message':original_message}, 'account':self.account}
|
'original_message':original_message}, 'account':self.account}
|
||||||
|
|
||||||
new_msg = gajim.otr_module.otrl_message_sending(
|
new_msg = gajim.otr_module.otrl_message_sending(
|
||||||
gajim.connections[self.account].otr_userstates,
|
gajim.connections[self.account].otr_userstates,
|
||||||
(gajim.otr_ui_ops, d),
|
(gajim.otr_ui_ops, d),
|
||||||
|
|
|
@ -154,8 +154,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):
|
||||||
|
@ -208,7 +209,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 ?
|
||||||
|
@ -412,8 +417,7 @@ 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, ctrl):
|
||||||
ctrl = self._controls[acct][jid]
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -445,7 +449,13 @@ 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]
|
||||||
|
|
||||||
|
@ -549,16 +559,16 @@ class MessageWindow(object):
|
||||||
for ctrl in self.controls():
|
for ctrl in self.controls():
|
||||||
ctrl.update_tags()
|
ctrl.update_tags()
|
||||||
|
|
||||||
def get_control(self, key, acct):
|
def get_control(self, key, acct, thread_id):
|
||||||
'''Return the MessageControl for jid or n, where n is a notebook page index.
|
'''Return the MessageControl for jid or n, where n is a notebook page index.
|
||||||
When key is an int index acct may be None'''
|
When key is an int index acct and thread_id may be None'''
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
key = unicode(key, 'utf-8')
|
key = unicode(key, 'utf-8')
|
||||||
|
|
||||||
if isinstance(key, unicode):
|
if isinstance(key, unicode):
|
||||||
jid = key
|
jid = key
|
||||||
try:
|
try:
|
||||||
return self._controls[acct][jid]
|
return self._controls[acct][jid][thread_id]
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
@ -569,24 +579,45 @@ class MessageWindow(object):
|
||||||
nth_child = notebook.get_nth_page(page_num)
|
nth_child = notebook.get_nth_page(page_num)
|
||||||
return self._widget_to_control(nth_child)
|
return self._widget_to_control(nth_child)
|
||||||
|
|
||||||
def change_key(self, old_jid, new_jid, acct):
|
def get_gc_control(self, jid, acct):
|
||||||
'''Change the key of a control'''
|
return self.get_control(jid, acct, 'gc')
|
||||||
|
|
||||||
|
def get_controls(self, jid, acct):
|
||||||
try:
|
try:
|
||||||
# Check if control exists
|
return self._controls[acct][jid].values()
|
||||||
ctrl = self._controls[acct][old_jid]
|
except KeyError:
|
||||||
except:
|
return []
|
||||||
|
|
||||||
|
def change_key(self, old_jid, new_jid, acct):
|
||||||
|
'''Change the JID key of a control'''
|
||||||
|
try:
|
||||||
|
# Check if controls exists
|
||||||
|
ctrls = self._controls[acct][old_jid]
|
||||||
|
except KeyError:
|
||||||
return
|
return
|
||||||
self._controls[acct][new_jid] = self._controls[acct][old_jid]
|
self._controls[acct][new_jid] = ctrls
|
||||||
del self._controls[acct][old_jid]
|
del self._controls[acct][old_jid]
|
||||||
if old_jid in gajim.last_message_time[acct]:
|
if old_jid in gajim.last_message_time[acct]:
|
||||||
gajim.last_message_time[acct][new_jid] = \
|
gajim.last_message_time[acct][new_jid] = \
|
||||||
gajim.last_message_time[acct][old_jid]
|
gajim.last_message_time[acct][old_jid]
|
||||||
del gajim.last_message_time[acct][old_jid]
|
del gajim.last_message_time[acct][old_jid]
|
||||||
|
|
||||||
|
def change_thread_key(self, jid, acct, old_thread_id, new_thread_id):
|
||||||
|
'''Change the thread_id key of a control'''
|
||||||
|
try:
|
||||||
|
# Check if control exists
|
||||||
|
ctrl = self._controls[acct][jid][old_thread_id]
|
||||||
|
except KeyError:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._controls[acct][jid][new_thread_id] = ctrl
|
||||||
|
del self._controls[acct][jid][old_thread_id]
|
||||||
|
|
||||||
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()
|
||||||
|
@ -604,7 +635,7 @@ class MessageWindow(object):
|
||||||
ind = ind - 1
|
ind = ind - 1
|
||||||
if ind < 0:
|
if ind < 0:
|
||||||
ind = self.notebook.get_n_pages() - 1
|
ind = self.notebook.get_n_pages() - 1
|
||||||
ctrl = self.get_control(ind, None)
|
ctrl = self.get_control(ind, None, None)
|
||||||
if ctrl.get_nb_unread() > 0:
|
if ctrl.get_nb_unread() > 0:
|
||||||
found = True
|
found = True
|
||||||
break # found
|
break # found
|
||||||
|
@ -796,8 +827,19 @@ class MessageWindowMgr(gobject.GObject):
|
||||||
|
|
||||||
def get_window(self, jid, acct):
|
def get_window(self, jid, acct):
|
||||||
for win in self.windows():
|
for win in self.windows():
|
||||||
if win.get_control(jid, acct):
|
try:
|
||||||
return win
|
if win._controls[acct][jid]:
|
||||||
|
return win
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_gc_control(self, jid, acct):
|
||||||
|
win = self.get_window(jid, acct)
|
||||||
|
|
||||||
|
if win:
|
||||||
|
return win.get_gc_control(jid, acct)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def has_window(self, jid, acct):
|
def has_window(self, jid, acct):
|
||||||
|
@ -930,11 +972,11 @@ class MessageWindowMgr(gobject.GObject):
|
||||||
del self._windows[k]
|
del self._windows[k]
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_control(self, jid, acct):
|
def get_control(self, jid, acct, session):
|
||||||
'''Amongst all windows, return the MessageControl for jid'''
|
'''Amongst all windows, return the MessageControl for jid'''
|
||||||
win = self.get_window(jid, acct)
|
win = self.get_window(jid, acct)
|
||||||
if win:
|
if win:
|
||||||
return win.get_control(jid, acct)
|
return win.get_control(jid, acct, session)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_controls(self, type = None, acct = None):
|
def get_controls(self, type = None, acct = None):
|
||||||
|
@ -946,6 +988,14 @@ class MessageWindowMgr(gobject.GObject):
|
||||||
ctrls.append(c)
|
ctrls.append(c)
|
||||||
return ctrls
|
return ctrls
|
||||||
|
|
||||||
|
def get_chat_controls(self, jid, acct):
|
||||||
|
win = self.get_window(jid, acct)
|
||||||
|
|
||||||
|
if win:
|
||||||
|
return win.get_controls(jid, acct)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
def windows(self):
|
def windows(self):
|
||||||
for w in self._windows.values():
|
for w in self._windows.values():
|
||||||
yield w
|
yield w
|
||||||
|
|
|
@ -53,8 +53,7 @@ try:
|
||||||
except:
|
except:
|
||||||
USER_HAS_GROWL = False
|
USER_HAS_GROWL = False
|
||||||
|
|
||||||
|
def get_show_in_roster(event, account, contact, session = None):
|
||||||
def get_show_in_roster(event, account, contact):
|
|
||||||
'''Return True if this event must be shown in roster, else False'''
|
'''Return True if this event must be shown in roster, else False'''
|
||||||
if event == 'gc_message_received':
|
if event == 'gc_message_received':
|
||||||
return True
|
return True
|
||||||
|
@ -65,8 +64,10 @@ def get_show_in_roster(event, account, contact):
|
||||||
if gajim.config.get_per('notifications', str(num), 'roster') == 'no':
|
if gajim.config.get_per('notifications', str(num), 'roster') == 'no':
|
||||||
return False
|
return False
|
||||||
if event == 'message_received':
|
if event == 'message_received':
|
||||||
chat_control = helpers.get_chat_control(account, contact)
|
if session:
|
||||||
if chat_control:
|
if session.control:
|
||||||
|
return False
|
||||||
|
elif helpers.get_chat_control(account, contact):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,8 @@ from common import i18n
|
||||||
|
|
||||||
from message_window import MessageWindowMgr
|
from message_window import MessageWindowMgr
|
||||||
|
|
||||||
|
from session import ChatControlSession
|
||||||
|
|
||||||
from common import dbus_support
|
from common import dbus_support
|
||||||
if dbus_support.supported:
|
if dbus_support.supported:
|
||||||
from music_track_listener import MusicTrackListener
|
from music_track_listener import MusicTrackListener
|
||||||
|
@ -1133,6 +1135,51 @@ class RosterWindow:
|
||||||
self.model[child_iter][C_AVATAR_PIXBUF] = scaled_pixbuf
|
self.model[child_iter][C_AVATAR_PIXBUF] = scaled_pixbuf
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def join_gc_room(self, account, room_jid, nick, password, minimize=False,
|
||||||
|
is_continued=False):
|
||||||
|
'''joins the room immediately'''
|
||||||
|
if gajim.interface.msg_win_mgr.has_window(room_jid, account) and \
|
||||||
|
gajim.gc_connected[account][room_jid]:
|
||||||
|
win = gajim.interface.msg_win_mgr.get_window(room_jid, account)
|
||||||
|
ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
|
||||||
|
win.window.present()
|
||||||
|
win.set_active_tab(ctrl)
|
||||||
|
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
|
||||||
|
return
|
||||||
|
minimized_control_exists = False
|
||||||
|
if room_jid in gajim.interface.minimized_controls[account]:
|
||||||
|
minimized_control_exists = True
|
||||||
|
invisible_show = gajim.SHOW_LIST.index('invisible')
|
||||||
|
if gajim.connections[account].connected == invisible_show:
|
||||||
|
dialogs.ErrorDialog(
|
||||||
|
_('You cannot join a group chat while you are invisible'))
|
||||||
|
return
|
||||||
|
if minimize and not minimized_control_exists and \
|
||||||
|
not gajim.interface.msg_win_mgr.has_window(room_jid, account):
|
||||||
|
contact = gajim.contacts.create_contact(jid = room_jid, name = nick)
|
||||||
|
gc_control = GroupchatControl(None, contact, account)
|
||||||
|
gajim.interface.minimized_controls[account][room_jid] = gc_control
|
||||||
|
gajim.connections[account].join_gc(nick, room_jid, password)
|
||||||
|
if password:
|
||||||
|
gajim.gc_passwords[room_jid] = password
|
||||||
|
self.add_groupchat_to_roster(account, room_jid)
|
||||||
|
return
|
||||||
|
if not minimized_control_exists and \
|
||||||
|
not gajim.interface.msg_win_mgr.has_window(room_jid, account):
|
||||||
|
self.new_room(room_jid, nick, account, is_continued=is_continued)
|
||||||
|
if not minimized_control_exists:
|
||||||
|
gc_win = gajim.interface.msg_win_mgr.get_window(room_jid, account)
|
||||||
|
gc_control = gc_win.get_gc_control(room_jid, account)
|
||||||
|
gc_win.set_active_tab(gc_control)
|
||||||
|
gc_win.window.present()
|
||||||
|
gajim.connections[account].join_gc(nick, room_jid, password)
|
||||||
|
if password:
|
||||||
|
gajim.gc_passwords[room_jid] = password
|
||||||
|
contact = gajim.contacts.get_contact_with_highest_priority(account, \
|
||||||
|
room_jid)
|
||||||
|
if contact or minimized_control_exists:
|
||||||
|
self.add_groupchat_to_roster(account, room_jid)
|
||||||
|
|
||||||
def draw_completely_and_show_if_needed(self, jid, account):
|
def draw_completely_and_show_if_needed(self, jid, account):
|
||||||
'''Draw contact, account and groups of given jid
|
'''Draw contact, account and groups of given jid
|
||||||
Show contact if it has pending events
|
Show contact if it has pending events
|
||||||
|
@ -1434,9 +1481,9 @@ class RosterWindow:
|
||||||
session = gajim.connections[account].make_new_session(jid)
|
session = gajim.connections[account].make_new_session(jid)
|
||||||
|
|
||||||
tim = time.localtime(float(result[2]))
|
tim = time.localtime(float(result[2]))
|
||||||
self.on_message(jid, result[1], tim, account, msg_type = 'chat',
|
session.roster_message(jid, result[1], tim, msg_type='chat',
|
||||||
msg_id = result[0], session = session)
|
msg_id=result[0])
|
||||||
|
|
||||||
elif (time.time() - result[2]) > 2592000:
|
elif (time.time() - result[2]) > 2592000:
|
||||||
# ok, here we see that we have a message in unread messages table
|
# ok, here we see that we have a message in unread messages table
|
||||||
# that is older than a month. It is probably from someone not in our
|
# that is older than a month. It is probably from someone not in our
|
||||||
|
@ -1496,10 +1543,10 @@ class RosterWindow:
|
||||||
gajim.transport_avatar[account][host] = [contact1.jid]
|
gajim.transport_avatar[account][host] = [contact1.jid]
|
||||||
else:
|
else:
|
||||||
gajim.transport_avatar[account][host].append(contact1.jid)
|
gajim.transport_avatar[account][host].append(contact1.jid)
|
||||||
# If we already have a chat window opened, update it with new contact
|
|
||||||
|
# If we already have chat windows opened, update them with new contact
|
||||||
# instance
|
# instance
|
||||||
chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
|
for chat_control in gajim.interface.msg_win_mgr.get_chat_controls(ji, account):
|
||||||
if chat_control:
|
|
||||||
chat_control.contact = contact1
|
chat_control.contact = contact1
|
||||||
|
|
||||||
def enable_syncing_status_msg_from_current_music_track(self, enabled):
|
def enable_syncing_status_msg_from_current_music_track(self, enabled):
|
||||||
|
@ -1830,9 +1877,10 @@ class RosterWindow:
|
||||||
account):
|
account):
|
||||||
win = gajim.interface.msg_win_mgr.get_window(jid_with_resource,
|
win = gajim.interface.msg_win_mgr.get_window(jid_with_resource,
|
||||||
account)
|
account)
|
||||||
ctrl = win.get_control(jid_with_resource, account)
|
for ctrl in win.get_controls(jid_with_resource, account):
|
||||||
ctrl.update_ui()
|
ctrl.update_ui()
|
||||||
win.redraw_tab(ctrl)
|
win.redraw_tab(ctrl)
|
||||||
|
|
||||||
gajim.contacts.remove_contact(account, contact)
|
gajim.contacts.remove_contact(account, contact)
|
||||||
elif contact.jid == gajim.get_jid_from_account(account) and show == 'offline':
|
elif contact.jid == gajim.get_jid_from_account(account) and show == 'offline':
|
||||||
# Our SelfContact went offline. Remove him
|
# Our SelfContact went offline. Remove him
|
||||||
|
@ -1841,19 +1889,20 @@ class RosterWindow:
|
||||||
# print status in chat window and update status/GPG image
|
# print status in chat window and update status/GPG image
|
||||||
if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
|
if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
|
||||||
win = gajim.interface.msg_win_mgr.get_window(contact.jid, account)
|
win = gajim.interface.msg_win_mgr.get_window(contact.jid, account)
|
||||||
ctrl = win.get_control(contact.jid, account)
|
|
||||||
ctrl.contact = gajim.contacts.get_contact_with_highest_priority(
|
|
||||||
account, contact.jid)
|
|
||||||
ctrl.update_ui()
|
|
||||||
win.redraw_tab(ctrl)
|
|
||||||
|
|
||||||
uf_show = helpers.get_uf_show(show)
|
uf_show = helpers.get_uf_show(show)
|
||||||
ctrl.print_conversation(_('%s is now %s') % (name, uf_show),
|
|
||||||
'status')
|
for ctrl in win.get_controls(contact.jid, account):
|
||||||
if status:
|
ctrl.contact = gajim.contacts.get_contact_with_highest_priority(
|
||||||
ctrl.print_conversation(' (', 'status', simple=True)
|
account, contact.jid)
|
||||||
ctrl.print_conversation('%s' % (status), 'status', simple=True)
|
ctrl.update_ui()
|
||||||
ctrl.print_conversation(')', 'status', simple=True)
|
win.redraw_tab(ctrl)
|
||||||
|
|
||||||
|
ctrl.print_conversation(_('%s is now %s') % (name, uf_show),
|
||||||
|
'status')
|
||||||
|
if status:
|
||||||
|
ctrl.print_conversation(' (', 'status', simple=True)
|
||||||
|
ctrl.print_conversation('%s' % (status), 'status', simple=True)
|
||||||
|
ctrl.print_conversation(')', 'status', simple=True)
|
||||||
|
|
||||||
# unset custom status
|
# unset custom status
|
||||||
if gajim.interface.status_sent_to_users.has_key(account) and \
|
if gajim.interface.status_sent_to_users.has_key(account) and \
|
||||||
|
@ -1973,119 +2022,6 @@ class RosterWindow:
|
||||||
gajim.config.get('roster_width'),
|
gajim.config.get('roster_width'),
|
||||||
gajim.config.get('roster_height'))
|
gajim.config.get('roster_height'))
|
||||||
|
|
||||||
def on_message(self, jid, msg, tim, account, encrypted=False, msg_type='',
|
|
||||||
subject=None, resource='', msg_id=None, user_nick='',
|
|
||||||
advanced_notif_num=None, xhtml=None, session=None, form_node=None):
|
|
||||||
'''when we receive a message'''
|
|
||||||
contact = None
|
|
||||||
# if chat window will be for specific resource
|
|
||||||
resource_for_chat = resource
|
|
||||||
fjid = jid
|
|
||||||
# Try to catch the contact with correct resource
|
|
||||||
if resource:
|
|
||||||
fjid = jid + '/' + resource
|
|
||||||
contact = gajim.contacts.get_contact(account, jid, resource)
|
|
||||||
highest_contact = gajim.contacts.get_contact_with_highest_priority(
|
|
||||||
account, jid)
|
|
||||||
if not contact:
|
|
||||||
# If there is another resource, it may be a message from an invisible
|
|
||||||
# resource
|
|
||||||
lcontact = gajim.contacts.get_contacts(account, jid)
|
|
||||||
if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \
|
|
||||||
lcontact[0].show != 'offline')) and jid.find('@') > 0:
|
|
||||||
contact = gajim.contacts.copy_contact(highest_contact)
|
|
||||||
contact.resource = resource
|
|
||||||
if resource:
|
|
||||||
fjid = jid + '/' + resource
|
|
||||||
contact.priority = 0
|
|
||||||
contact.show = 'offline'
|
|
||||||
contact.status = ''
|
|
||||||
gajim.contacts.add_contact(account, contact)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Default to highest prio
|
|
||||||
fjid = jid
|
|
||||||
resource_for_chat = None
|
|
||||||
contact = highest_contact
|
|
||||||
if not contact:
|
|
||||||
# contact is not in roster
|
|
||||||
contact = self.add_to_not_in_the_roster(account, jid, user_nick)
|
|
||||||
|
|
||||||
|
|
||||||
# If visible, try to get first line of contact in roster
|
|
||||||
path = None
|
|
||||||
iters = self._get_contact_iter(jid, account, contact = contact)
|
|
||||||
if iters:
|
|
||||||
path = self.modelfilter.get_path(iters[0])
|
|
||||||
|
|
||||||
# Look for a chat control that has the given resource
|
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, account)
|
|
||||||
if not ctrl:
|
|
||||||
# if not, if message comes from highest prio, get control or open one
|
|
||||||
# without resource
|
|
||||||
if highest_contact and contact.resource == highest_contact.resource \
|
|
||||||
and not jid == gajim.get_jid_from_account(account):
|
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
|
|
||||||
fjid = jid
|
|
||||||
resource_for_chat = None
|
|
||||||
|
|
||||||
# Do we have a queue?
|
|
||||||
no_queue = len(gajim.events.get_events(account, fjid)) == 0
|
|
||||||
|
|
||||||
popup = helpers.allow_popup_window(account, advanced_notif_num)
|
|
||||||
|
|
||||||
if msg_type == 'normal' and popup: # it's single message to be autopopuped
|
|
||||||
dialogs.SingleMessageWindow(account, contact.jid, action='receive',
|
|
||||||
from_whom=jid, subject=subject, message=msg, resource=resource,
|
|
||||||
session=session, form_node=form_node)
|
|
||||||
return
|
|
||||||
|
|
||||||
# We print if window is opened and it's not a single message
|
|
||||||
if ctrl and msg_type != 'normal':
|
|
||||||
typ = ''
|
|
||||||
if msg_type == 'error':
|
|
||||||
typ = 'status'
|
|
||||||
if session:
|
|
||||||
ctrl.set_session(session)
|
|
||||||
ctrl.print_conversation(msg, typ, tim = tim, encrypted = encrypted,
|
|
||||||
subject = subject, xhtml = xhtml)
|
|
||||||
if msg_id:
|
|
||||||
gajim.logger.set_read_messages([msg_id])
|
|
||||||
return
|
|
||||||
|
|
||||||
# We save it in a queue
|
|
||||||
type_ = 'chat'
|
|
||||||
event_type = 'message_received'
|
|
||||||
if msg_type == 'normal':
|
|
||||||
type_ = 'normal'
|
|
||||||
event_type = 'single_message_received'
|
|
||||||
show_in_roster = notify.get_show_in_roster(event_type, account, contact)
|
|
||||||
show_in_systray = notify.get_show_in_systray(event_type, account, contact)
|
|
||||||
event = gajim.events.create_event(type_, (msg, subject, msg_type, tim,
|
|
||||||
encrypted, resource, msg_id, xhtml, session, form_node),
|
|
||||||
show_in_roster=show_in_roster, show_in_systray=show_in_systray)
|
|
||||||
gajim.events.add_event(account, fjid, event)
|
|
||||||
if popup:
|
|
||||||
if not ctrl:
|
|
||||||
gajim.interface.new_chat(contact, account, \
|
|
||||||
resource=resource_for_chat)
|
|
||||||
if path and not self.dragging and gajim.config.get(
|
|
||||||
'scroll_roster_to_last_message'):
|
|
||||||
# we curently see contact in our roster
|
|
||||||
# show and select his line in roster
|
|
||||||
# do not change selection while DND'ing
|
|
||||||
self.tree.expand_row(path[0:1], False)
|
|
||||||
self.tree.expand_row(path[0:2], False)
|
|
||||||
self.tree.scroll_to_cell(path)
|
|
||||||
self.tree.set_cursor(path)
|
|
||||||
else:
|
|
||||||
if no_queue: # We didn't have a queue: we change icons
|
|
||||||
self.draw_contact(jid, account)
|
|
||||||
self.show_title() # we show the * or [n]
|
|
||||||
# Show contact in roster (if he is invisible for example) and select
|
|
||||||
# line
|
|
||||||
self.show_and_select_contact_if_having_events(jid, account)
|
|
||||||
|
|
||||||
def close_all_from_dict(self, dic):
|
def close_all_from_dict(self, dic):
|
||||||
'''close all the windows in the given dictionary'''
|
'''close all the windows in the given dictionary'''
|
||||||
for w in dic.values():
|
for w in dic.values():
|
||||||
|
@ -2151,8 +2087,8 @@ class RosterWindow:
|
||||||
|
|
||||||
def on_quit_request(self, widget = None):
|
def on_quit_request(self, widget = None):
|
||||||
''' user want to quit. Check if he should be warned about messages
|
''' user want to quit. Check if he should be warned about messages
|
||||||
pending. Send offline to all connected account. We do NOT really quit
|
pending. Terminate all sessions and send offline to all connected
|
||||||
gajim here '''
|
account. We do NOT really quit gajim here '''
|
||||||
accounts = gajim.connections.keys()
|
accounts = gajim.connections.keys()
|
||||||
get_msg = False
|
get_msg = False
|
||||||
for acct in accounts:
|
for acct in accounts:
|
||||||
|
@ -2169,7 +2105,7 @@ class RosterWindow:
|
||||||
unread = gajim.events.get_nb_events()
|
unread = gajim.events.get_nb_events()
|
||||||
if not gajim.config.get('notify_on_all_muc_messages'):
|
if not gajim.config.get('notify_on_all_muc_messages'):
|
||||||
unread_not_to_notify = gajim.events.get_nb_events(['printed_gc_msg'])
|
unread_not_to_notify = gajim.events.get_nb_events(['printed_gc_msg'])
|
||||||
unread -= unread_not_to_notify
|
unread -= unread_not_to_notify
|
||||||
|
|
||||||
# check if we have recent messages
|
# check if we have recent messages
|
||||||
recent = False
|
recent = False
|
||||||
|
@ -2192,13 +2128,15 @@ class RosterWindow:
|
||||||
|
|
||||||
self.quit_on_next_offline = 0
|
self.quit_on_next_offline = 0
|
||||||
for acct in accounts:
|
for acct in accounts:
|
||||||
|
gajim.connections[acct].terminate_sessions()
|
||||||
|
|
||||||
if gajim.connections[acct].connected:
|
if gajim.connections[acct].connected:
|
||||||
self.quit_on_next_offline += 1
|
self.quit_on_next_offline += 1
|
||||||
self.send_status(acct, 'offline', message)
|
self.send_status(acct, 'offline', message)
|
||||||
|
|
||||||
if not self.quit_on_next_offline:
|
if not self.quit_on_next_offline:
|
||||||
self.quit_gtkgui_interface()
|
self.quit_gtkgui_interface()
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
### Menu and GUI callbacks
|
### Menu and GUI callbacks
|
||||||
### FIXME: order callbacks in itself...
|
### FIXME: order callbacks in itself...
|
||||||
|
@ -2630,9 +2568,8 @@ class RosterWindow:
|
||||||
u.name = new_text
|
u.name = new_text
|
||||||
gajim.connections[account].update_contact(jid, new_text, u.groups)
|
gajim.connections[account].update_contact(jid, new_text, u.groups)
|
||||||
self.draw_contact(jid, account)
|
self.draw_contact(jid, account)
|
||||||
# Update opened chat
|
# Update opened chats
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
|
for ctrl in gajim.interface.msg_win_mgr.get_controls(jid, account):
|
||||||
if ctrl:
|
|
||||||
ctrl.update_ui()
|
ctrl.update_ui()
|
||||||
win = gajim.interface.msg_win_mgr.get_window(jid, account)
|
win = gajim.interface.msg_win_mgr.get_window(jid, account)
|
||||||
win.redraw_tab(ctrl)
|
win.redraw_tab(ctrl)
|
||||||
|
@ -2719,8 +2656,7 @@ class RosterWindow:
|
||||||
keyID = keyID[0]
|
keyID = keyID[0]
|
||||||
keys[contact.jid] = keyID
|
keys[contact.jid] = keyID
|
||||||
|
|
||||||
if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
|
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(contact.jid, account):
|
||||||
ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account)
|
|
||||||
ctrl.update_ui()
|
ctrl.update_ui()
|
||||||
keys_str = ''
|
keys_str = ''
|
||||||
for jid in keys:
|
for jid in keys:
|
||||||
|
@ -2866,7 +2802,7 @@ class RosterWindow:
|
||||||
ctrl.account, ctrl.type_id)
|
ctrl.account, ctrl.type_id)
|
||||||
ctrl.parent_win = mw
|
ctrl.parent_win = mw
|
||||||
mw.new_tab(ctrl)
|
mw.new_tab(ctrl)
|
||||||
mw.set_active_tab(jid, account)
|
mw.set_active_tab(ctrl)
|
||||||
mw.window.present()
|
mw.window.present()
|
||||||
del gajim.interface.minimized_controls[account][jid]
|
del gajim.interface.minimized_controls[account][jid]
|
||||||
|
|
||||||
|
@ -3029,7 +2965,7 @@ class RosterWindow:
|
||||||
x_min = 0
|
x_min = 0
|
||||||
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
|
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
|
||||||
not event.state & gtk.gdk.CONTROL_MASK:
|
not event.state & gtk.gdk.CONTROL_MASK:
|
||||||
# Don't handle dubble click if we press icon of a metacontact
|
# Don't handle double click if we press icon of a metacontact
|
||||||
titer = model.get_iter(path)
|
titer = model.get_iter(path)
|
||||||
if x > x_min and x < x_min + 27 and type_ == 'contact' and \
|
if x > x_min and x < x_min + 27 and type_ == 'contact' and \
|
||||||
model.iter_has_child(titer):
|
model.iter_has_child(titer):
|
||||||
|
@ -3296,7 +3232,19 @@ class RosterWindow:
|
||||||
|
|
||||||
def on_profile_avatar_menuitem_activate(self, widget, account):
|
def on_profile_avatar_menuitem_activate(self, widget, account):
|
||||||
gajim.interface.edit_own_details(account)
|
gajim.interface.edit_own_details(account)
|
||||||
|
|
||||||
|
def play_tictactoe(self, widget, contact, account, resource=None):
|
||||||
|
jid = contact.jid
|
||||||
|
|
||||||
|
if resource is not None:
|
||||||
|
jid = jid + u'/' + resource
|
||||||
|
|
||||||
|
import tictactoe
|
||||||
|
|
||||||
|
sess = gajim.connections[account].make_new_session(jid,
|
||||||
|
klass=tictactoe.TicTacToeSession)
|
||||||
|
sess.begin()
|
||||||
|
|
||||||
def on_execute_command(self, widget, contact, account, resource=None):
|
def on_execute_command(self, widget, contact, account, resource=None):
|
||||||
'''Execute command. Full JID needed; if it is other contact,
|
'''Execute command. Full JID needed; if it is other contact,
|
||||||
resource is necessary. Widget is unnecessary, only to be
|
resource is necessary. Widget is unnecessary, only to be
|
||||||
|
@ -3347,8 +3295,8 @@ class RosterWindow:
|
||||||
self.show_treeview_menu(event)
|
self.show_treeview_menu(event)
|
||||||
|
|
||||||
def on_row_activated(self, widget, path):
|
def on_row_activated(self, widget, path):
|
||||||
'''When an iter is activated (dubblick or single click if gnome is set
|
'''When an iter is activated (double-click or single click if gnome is
|
||||||
this way'''
|
set this way'''
|
||||||
model = self.modelfilter
|
model = self.modelfilter
|
||||||
account = model[path][C_ACCOUNT].decode('utf-8')
|
account = model[path][C_ACCOUNT].decode('utf-8')
|
||||||
type_ = model[path][C_TYPE]
|
type_ = model[path][C_TYPE]
|
||||||
|
@ -4012,6 +3960,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)
|
||||||
|
@ -4864,7 +4813,7 @@ class RosterWindow:
|
||||||
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
|
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
|
||||||
menu.show_all()
|
menu.show_all()
|
||||||
menu.popup(None, None, None, event_button, event.time)
|
menu.popup(None, None, None, event_button, event.time)
|
||||||
|
|
||||||
def make_contact_menu(self, event, titer):
|
def make_contact_menu(self, event, titer):
|
||||||
'''Make contact\'s popup menu'''
|
'''Make contact\'s popup menu'''
|
||||||
model = self.modelfilter
|
model = self.modelfilter
|
||||||
|
@ -5009,6 +4958,10 @@ class RosterWindow:
|
||||||
execute_command_menuitem = xml.get_widget(
|
execute_command_menuitem = xml.get_widget(
|
||||||
'execute_command_menuitem')
|
'execute_command_menuitem')
|
||||||
|
|
||||||
|
tictactoe_menuitem = xml.get_widget('tictactoe_menuitem')
|
||||||
|
tictactoe_menuitem.connect('activate', self.play_tictactoe, contact,
|
||||||
|
account, contact.resource)
|
||||||
|
|
||||||
# send custom status icon
|
# send custom status icon
|
||||||
blocked = False
|
blocked = False
|
||||||
if jid in gajim.connections[account].blocked_contacts:
|
if jid in gajim.connections[account].blocked_contacts:
|
||||||
|
|
|
@ -0,0 +1,324 @@
|
||||||
|
from common import helpers
|
||||||
|
|
||||||
|
from common import exceptions
|
||||||
|
from common import gajim
|
||||||
|
from common import stanza_session
|
||||||
|
from common import contacts
|
||||||
|
|
||||||
|
import common.xmpp
|
||||||
|
|
||||||
|
import dialogs
|
||||||
|
|
||||||
|
import message_control
|
||||||
|
|
||||||
|
import notify
|
||||||
|
|
||||||
|
class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
||||||
|
def __init__(self, conn, jid, thread_id, type = 'chat'):
|
||||||
|
stanza_session.EncryptedStanzaSession.__init__(self, conn, jid, thread_id, type = 'chat')
|
||||||
|
|
||||||
|
self.control = None
|
||||||
|
|
||||||
|
def acknowledge_termination(self):
|
||||||
|
# the other party terminated the session. we'll keep the control around, though.
|
||||||
|
stanza_session.EncryptedStanzaSession.acknowledge_termination(self)
|
||||||
|
|
||||||
|
if self.control:
|
||||||
|
self.control.session = 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
|
||||||
|
def get_chatstate(self, msg, msgtxt):
|
||||||
|
composing_xep = None
|
||||||
|
chatstate = None
|
||||||
|
|
||||||
|
# chatstates - look for chatstate tags in a message if not delayed
|
||||||
|
delayed = msg.getTag('x', namespace=common.xmpp.NS_DELAY) != None
|
||||||
|
if not delayed:
|
||||||
|
composing_xep = False
|
||||||
|
children = msg.getChildren()
|
||||||
|
for child in children:
|
||||||
|
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
|
||||||
|
chatstate = child.getName()
|
||||||
|
composing_xep = 'XEP-0085'
|
||||||
|
break
|
||||||
|
# No XEP-0085 support, fallback to XEP-0022
|
||||||
|
if not chatstate:
|
||||||
|
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
|
||||||
|
if chatstate_child:
|
||||||
|
chatstate = 'active'
|
||||||
|
composing_xep = 'XEP-0022'
|
||||||
|
if not msgtxt and chatstate_child.getTag('composing'):
|
||||||
|
chatstate = 'composing'
|
||||||
|
|
||||||
|
return (composing_xep, chatstate)
|
||||||
|
|
||||||
|
# dispatch a received <message> stanza
|
||||||
|
def received(self, full_jid_with_resource, msgtxt, tim, encrypted, subject, msg):
|
||||||
|
msg_type = msg.getType()
|
||||||
|
msg_id = None
|
||||||
|
|
||||||
|
# XEP-0172 User Nickname
|
||||||
|
user_nick = msg.getTagData('nick')
|
||||||
|
if not user_nick:
|
||||||
|
user_nick =''
|
||||||
|
|
||||||
|
form_node = None
|
||||||
|
for xtag in msg.getTags('x'):
|
||||||
|
if xtag.getNamespace() == common.xmpp.NS_DATA:
|
||||||
|
form_node = xtag
|
||||||
|
break
|
||||||
|
|
||||||
|
composing_xep, chatstate = self.get_chatstate(msg, msgtxt)
|
||||||
|
|
||||||
|
xhtml = msg.getXHTML()
|
||||||
|
|
||||||
|
if msg_type == 'chat':
|
||||||
|
if not msg.getTag('body') and chatstate is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
log_type = 'chat_msg_recv'
|
||||||
|
else:
|
||||||
|
log_type = 'single_msg_recv'
|
||||||
|
|
||||||
|
if self.is_loggable() and msgtxt:
|
||||||
|
try:
|
||||||
|
msg_id = gajim.logger.write(log_type, full_jid_with_resource, msgtxt,
|
||||||
|
tim=tim, subject=subject)
|
||||||
|
except exceptions.PysqliteOperationalError, e:
|
||||||
|
gajim.dispatch('ERROR', (_('Disk WriteError'), str(e)))
|
||||||
|
|
||||||
|
treat_as = gajim.config.get('treat_incoming_messages')
|
||||||
|
if treat_as:
|
||||||
|
msg_type = treat_as
|
||||||
|
|
||||||
|
jid = gajim.get_jid_without_resource(full_jid_with_resource)
|
||||||
|
resource = gajim.get_resource_from_jid(full_jid_with_resource)
|
||||||
|
|
||||||
|
if gajim.config.get('ignore_incoming_xhtml'):
|
||||||
|
xhtml = None
|
||||||
|
if gajim.jid_is_transport(jid):
|
||||||
|
jid = jid.replace('@', '')
|
||||||
|
|
||||||
|
groupchat_control = gajim.interface.msg_win_mgr.get_gc_control(jid, self.conn.name)
|
||||||
|
|
||||||
|
if not groupchat_control and \
|
||||||
|
jid in gajim.interface.minimized_controls[self.conn.name]:
|
||||||
|
groupchat_control = gajim.interface.minimized_controls[self.conn.name][jid]
|
||||||
|
|
||||||
|
pm = False
|
||||||
|
if groupchat_control and groupchat_control.type_id == \
|
||||||
|
message_control.TYPE_GC:
|
||||||
|
# It's a Private message
|
||||||
|
pm = True
|
||||||
|
msg_type = 'pm'
|
||||||
|
|
||||||
|
jid_of_control = full_jid_with_resource
|
||||||
|
|
||||||
|
# Handle chat states
|
||||||
|
contact = gajim.contacts.get_contact(self.conn.name, jid, resource)
|
||||||
|
if contact:
|
||||||
|
if contact.composing_xep != 'XEP-0085': # We cache xep85 support
|
||||||
|
contact.composing_xep = composing_xep
|
||||||
|
if self.control and self.control.type_id == message_control.TYPE_CHAT:
|
||||||
|
if chatstate is not None:
|
||||||
|
# other peer sent us reply, so he supports jep85 or jep22
|
||||||
|
contact.chatstate = chatstate
|
||||||
|
if contact.our_chatstate == 'ask': # we were jep85 disco?
|
||||||
|
contact.our_chatstate = 'active' # no more
|
||||||
|
self.control.handle_incoming_chatstate()
|
||||||
|
elif contact.chatstate != 'active':
|
||||||
|
# got no valid jep85 answer, peer does not support it
|
||||||
|
contact.chatstate = False
|
||||||
|
elif chatstate == 'active':
|
||||||
|
# Brand new message, incoming.
|
||||||
|
contact.our_chatstate = chatstate
|
||||||
|
contact.chatstate = chatstate
|
||||||
|
if msg_id: # Do not overwrite an existing msg_id with None
|
||||||
|
contact.msg_id = msg_id
|
||||||
|
|
||||||
|
# THIS MUST BE AFTER chatstates handling
|
||||||
|
# AND BEFORE playsound (else we ear sounding on chatstates!)
|
||||||
|
if not msgtxt: # empty message text
|
||||||
|
return
|
||||||
|
|
||||||
|
if gajim.config.get('ignore_unknown_contacts') and \
|
||||||
|
not gajim.contacts.get_contacts(self.conn.name, jid) and not pm:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not contact:
|
||||||
|
# contact is not in the roster, create a fake one to display
|
||||||
|
# notification
|
||||||
|
contact = contacts.Contact(jid = jid, resource = resource)
|
||||||
|
advanced_notif_num = notify.get_advanced_notification('message_received',
|
||||||
|
self.conn.name, contact)
|
||||||
|
|
||||||
|
# Is it a first or next message received ?
|
||||||
|
first = False
|
||||||
|
if not self.control:
|
||||||
|
first = True
|
||||||
|
|
||||||
|
if pm:
|
||||||
|
nickname = resource
|
||||||
|
groupchat_control.on_private_message(nickname, msgtxt, tim,
|
||||||
|
xhtml, self, msg_id)
|
||||||
|
else:
|
||||||
|
self.roster_message(jid, msgtxt, tim, encrypted, msg_type,
|
||||||
|
subject, resource, msg_id, user_nick, advanced_notif_num,
|
||||||
|
xhtml=xhtml, form_node=form_node)
|
||||||
|
|
||||||
|
nickname = gajim.get_name_from_jid(self.conn.name, jid)
|
||||||
|
# Check and do wanted notifications
|
||||||
|
msg = msgtxt
|
||||||
|
if subject:
|
||||||
|
msg = _('Subject: %s') % subject + '\n' + msg
|
||||||
|
focused = False
|
||||||
|
|
||||||
|
if self.control:
|
||||||
|
parent_win = self.control.parent_win
|
||||||
|
if self.control == parent_win.get_active_control() and \
|
||||||
|
parent_win.window.has_focus:
|
||||||
|
focused = True
|
||||||
|
|
||||||
|
notify.notify('new_message', jid_of_control, self.conn.name, [msg_type,
|
||||||
|
first, nickname, msg, focused], advanced_notif_num)
|
||||||
|
|
||||||
|
if gajim.interface.remote_ctrl:
|
||||||
|
gajim.interface.remote_ctrl.raise_signal('NewMessage',
|
||||||
|
(self.conn.name, [full_jid_with_resource, msgtxt, tim,
|
||||||
|
encrypted, msg_type, subject, chatstate, msg_id,
|
||||||
|
composing_xep, user_nick, xhtml, form_node]))
|
||||||
|
|
||||||
|
# display the message or show notification in the roster
|
||||||
|
def roster_message(self, jid, msg, tim, encrypted=False, msg_type='',
|
||||||
|
subject=None, resource='', msg_id=None, user_nick='',
|
||||||
|
advanced_notif_num=None, xhtml=None, form_node=None):
|
||||||
|
|
||||||
|
contact = None
|
||||||
|
# if chat window will be for specific resource
|
||||||
|
resource_for_chat = resource
|
||||||
|
|
||||||
|
fjid = jid
|
||||||
|
|
||||||
|
# Try to catch the contact with correct resource
|
||||||
|
if resource:
|
||||||
|
fjid = jid + '/' + resource
|
||||||
|
contact = gajim.contacts.get_contact(self.conn.name, jid, resource)
|
||||||
|
|
||||||
|
highest_contact = gajim.contacts.get_contact_with_highest_priority(
|
||||||
|
self.conn.name, jid)
|
||||||
|
if not contact:
|
||||||
|
# If there is another resource, it may be a message from an invisible
|
||||||
|
# resource
|
||||||
|
lcontact = gajim.contacts.get_contacts(self.conn.name, jid)
|
||||||
|
if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \
|
||||||
|
lcontact[0].show != 'offline')) and jid.find('@') > 0:
|
||||||
|
contact = gajim.contacts.copy_contact(highest_contact)
|
||||||
|
contact.resource = resource
|
||||||
|
if resource:
|
||||||
|
fjid = jid + '/' + resource
|
||||||
|
contact.priority = 0
|
||||||
|
contact.show = 'offline'
|
||||||
|
contact.status = ''
|
||||||
|
gajim.contacts.add_contact(self.conn.name, contact)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Default to highest prio
|
||||||
|
fjid = jid
|
||||||
|
resource_for_chat = None
|
||||||
|
contact = highest_contact
|
||||||
|
|
||||||
|
if not contact:
|
||||||
|
# contact is not in roster
|
||||||
|
contact = gajim.interface.roster.add_to_not_in_the_roster(
|
||||||
|
self.conn.name, jid, user_nick)
|
||||||
|
|
||||||
|
# If visible, try to get first line of contact in roster
|
||||||
|
path = None
|
||||||
|
iters = gajim.interface.roster._get_contact_iter(jid, self.conn.name,
|
||||||
|
contact=contact)
|
||||||
|
if iters:
|
||||||
|
path = gajim.interface.roster.modelfilter.get_path(iters[0])
|
||||||
|
|
||||||
|
# Do we have a queue?
|
||||||
|
no_queue = len(gajim.events.get_events(self.conn.name, fjid)) == 0
|
||||||
|
|
||||||
|
popup = helpers.allow_popup_window(self.conn.name, advanced_notif_num)
|
||||||
|
|
||||||
|
if msg_type == 'normal' and popup: # it's single message to be autopopuped
|
||||||
|
dialogs.SingleMessageWindow(self.conn.name, contact.jid,
|
||||||
|
action='receive', from_whom=jid, subject=subject, message=msg,
|
||||||
|
resource=resource, session=self, form_node=form_node)
|
||||||
|
return
|
||||||
|
|
||||||
|
# We print if window is opened and it's not a single message
|
||||||
|
if self.control and msg_type != 'normal':
|
||||||
|
typ = ''
|
||||||
|
|
||||||
|
if msg_type == 'error':
|
||||||
|
typ = 'status'
|
||||||
|
|
||||||
|
self.control.print_conversation(msg, typ, tim=tim, encrypted=encrypted,
|
||||||
|
subject=subject, xhtml=xhtml)
|
||||||
|
|
||||||
|
if msg_id:
|
||||||
|
gajim.logger.set_read_messages([msg_id])
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# We save it in a queue
|
||||||
|
type_ = 'chat'
|
||||||
|
event_type = 'message_received'
|
||||||
|
|
||||||
|
if msg_type == 'normal':
|
||||||
|
type_ = 'normal'
|
||||||
|
event_type = 'single_message_received'
|
||||||
|
|
||||||
|
show_in_roster = notify.get_show_in_roster(event_type, self.conn.name, contact, self)
|
||||||
|
show_in_systray = notify.get_show_in_systray(event_type, self.conn.name, contact)
|
||||||
|
|
||||||
|
event = gajim.events.create_event(type_, (msg, subject, msg_type, tim,
|
||||||
|
encrypted, resource, msg_id, xhtml, self, form_node),
|
||||||
|
show_in_roster=show_in_roster, show_in_systray=show_in_systray)
|
||||||
|
|
||||||
|
gajim.events.add_event(self.conn.name, fjid, event)
|
||||||
|
|
||||||
|
if popup:
|
||||||
|
if not self.control:
|
||||||
|
self.control = gajim.interface.roster.new_chat(self, contact, self.conn.name, resource=resource_for_chat)
|
||||||
|
|
||||||
|
if len(gajim.events.get_events(self.conn.name, fjid)):
|
||||||
|
self.control.read_queue()
|
||||||
|
|
||||||
|
if path and not gajim.interface.dragging and gajim.config.get(
|
||||||
|
'scroll_roster_to_last_message'):
|
||||||
|
# we curently see contact in our roster
|
||||||
|
# show and select his line in roster
|
||||||
|
# do not change selection while DND'ing
|
||||||
|
self.tree.expand_row(path[0:1], False)
|
||||||
|
self.tree.expand_row(path[0:2], False)
|
||||||
|
self.tree.scroll_to_cell(path)
|
||||||
|
self.tree.set_cursor(path)
|
||||||
|
else:
|
||||||
|
if no_queue: # We didn't have a queue: we change icons
|
||||||
|
gajim.interface.roster.draw_contact(jid, self.conn.name)
|
||||||
|
|
||||||
|
gajim.interface.roster.show_title() # we show the * or [n]
|
||||||
|
# Show contact in roster (if he is invisible for example) and select
|
||||||
|
# line
|
||||||
|
gajim.interface.roster.show_and_select_contact_if_having_events(jid,
|
||||||
|
self.conn.name)
|
|
@ -0,0 +1,427 @@
|
||||||
|
from common import stanza_session
|
||||||
|
from common import xmpp
|
||||||
|
|
||||||
|
import pygtk
|
||||||
|
pygtk.require('2.0')
|
||||||
|
import gtk
|
||||||
|
from gtk import gdk
|
||||||
|
import cairo
|
||||||
|
|
||||||
|
# implements <http://pidgin-games.sourceforge.net/xep/tictactoe.html#invite>
|
||||||
|
|
||||||
|
games_ns = 'http://jabber.org/protocol/games'
|
||||||
|
|
||||||
|
class InvalidMove(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TicTacToeSession(stanza_session.StanzaSession):
|
||||||
|
# initiate a session
|
||||||
|
def begin(self, rows = 3, cols = 3, role_s = 'x'):
|
||||||
|
self.rows = rows
|
||||||
|
self.cols = cols
|
||||||
|
|
||||||
|
self.role_s = role_s
|
||||||
|
|
||||||
|
self.strike = 3
|
||||||
|
|
||||||
|
if self.role_s == 'x':
|
||||||
|
self.role_o = 'o'
|
||||||
|
else:
|
||||||
|
self.role_o = 'x'
|
||||||
|
|
||||||
|
self.send_invitation()
|
||||||
|
|
||||||
|
self.next_move_id = 1
|
||||||
|
self.received = self.wait_for_invite_response
|
||||||
|
|
||||||
|
def send_invitation(self):
|
||||||
|
msg = xmpp.Message()
|
||||||
|
|
||||||
|
invite = msg.NT.invite
|
||||||
|
invite.setNamespace(games_ns)
|
||||||
|
|
||||||
|
game = invite.NT.game
|
||||||
|
game.setAttr('var', games_ns + '/tictactoe')
|
||||||
|
|
||||||
|
x = xmpp.DataForm(typ='submit')
|
||||||
|
|
||||||
|
game.addChild(node=x)
|
||||||
|
|
||||||
|
self.send(msg)
|
||||||
|
|
||||||
|
def read_invitation(self, msg):
|
||||||
|
invite = msg.getTag('invite', namespace=games_ns)
|
||||||
|
game = invite.getTag('game')
|
||||||
|
x = game.getTag('x', namespace='jabber:x:data')
|
||||||
|
|
||||||
|
form = xmpp.DataForm(node=x)
|
||||||
|
|
||||||
|
if form.getField('role'):
|
||||||
|
self.role_o = form.getField('role').getValues()[0]
|
||||||
|
else:
|
||||||
|
self.role_o = 'x'
|
||||||
|
|
||||||
|
if form.getField('rows'):
|
||||||
|
self.rows = int(form.getField('rows').getValues()[0])
|
||||||
|
else:
|
||||||
|
self.rows = 3
|
||||||
|
|
||||||
|
if form.getField('cols'):
|
||||||
|
self.cols = int(form.getField('cols').getValues()[0])
|
||||||
|
else:
|
||||||
|
self.cols = 3
|
||||||
|
|
||||||
|
# number in a row needed to win
|
||||||
|
if form.getField('strike'):
|
||||||
|
self.strike = int(form.getField('strike').getValues()[0])
|
||||||
|
else:
|
||||||
|
self.strike = 3
|
||||||
|
|
||||||
|
# received an invitation
|
||||||
|
def invited(self, msg):
|
||||||
|
self.read_invitation(msg)
|
||||||
|
|
||||||
|
# XXX prompt user
|
||||||
|
# "accept, reject, ignore"
|
||||||
|
|
||||||
|
# the number of the move about to be made
|
||||||
|
self.next_move_id = 1
|
||||||
|
|
||||||
|
# display the board
|
||||||
|
self.board = TicTacToeBoard(self, self.rows, self.cols)
|
||||||
|
|
||||||
|
# accept the invitation, join the game
|
||||||
|
response = xmpp.Message()
|
||||||
|
|
||||||
|
join = response.NT.join
|
||||||
|
join.setNamespace(games_ns)
|
||||||
|
|
||||||
|
self.send(response)
|
||||||
|
|
||||||
|
if self.role_o == 'x':
|
||||||
|
self.role_s = 'o'
|
||||||
|
|
||||||
|
self.their_turn()
|
||||||
|
else:
|
||||||
|
self.role_s = 'x'
|
||||||
|
self.role_o = 'o'
|
||||||
|
|
||||||
|
self.our_turn()
|
||||||
|
|
||||||
|
# just sent an invitation, expecting a reply
|
||||||
|
def wait_for_invite_response(self, msg):
|
||||||
|
if msg.getTag('join', namespace=games_ns):
|
||||||
|
self.board = TicTacToeBoard(self, self.rows, self.cols)
|
||||||
|
|
||||||
|
if self.role_s == 'x':
|
||||||
|
self.our_turn()
|
||||||
|
else:
|
||||||
|
self.their_turn()
|
||||||
|
|
||||||
|
elif msg.getTag('decline', namespace=games_ns):
|
||||||
|
# XXX notify the user
|
||||||
|
|
||||||
|
# XXX end session
|
||||||
|
pass
|
||||||
|
|
||||||
|
# silently ignores any received messages
|
||||||
|
def ignore(self, msg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def game_over(self, msg):
|
||||||
|
invite = msg.getTag('invite', namespace=games_ns)
|
||||||
|
|
||||||
|
# ignore messages unless they're renewing the game
|
||||||
|
if invite and invite.getAttr('type') == 'renew':
|
||||||
|
self.invited(msg)
|
||||||
|
|
||||||
|
def wait_for_move(self, msg):
|
||||||
|
turn = msg.getTag('turn', namespace=games_ns)
|
||||||
|
move = turn.getTag('move', namespace='http://jabber.org/protocol/games/tictactoe')
|
||||||
|
|
||||||
|
row = int(move.getAttr('row'))
|
||||||
|
col = int(move.getAttr('col'))
|
||||||
|
id = int(move.getAttr('id'))
|
||||||
|
|
||||||
|
if id != self.next_move_id:
|
||||||
|
print 'unexpected move id, lost a move somewhere?'
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.board.mark(row, col, self.role_o)
|
||||||
|
except InvalidMove, e:
|
||||||
|
# received an invalid move, end the game.
|
||||||
|
|
||||||
|
# XXX notify the user
|
||||||
|
self.terminate('cheating')
|
||||||
|
return
|
||||||
|
|
||||||
|
# check win conditions
|
||||||
|
if self.board.check_for_strike(self.role_o, row, col, self.strike):
|
||||||
|
self.lost()
|
||||||
|
elif self.board.full():
|
||||||
|
self.drawn()
|
||||||
|
else:
|
||||||
|
self.next_move_id += 1
|
||||||
|
|
||||||
|
self.our_turn()
|
||||||
|
|
||||||
|
def is_my_turn(self):
|
||||||
|
# XXX not great semantics
|
||||||
|
return self.received == self.ignore
|
||||||
|
|
||||||
|
def our_turn(self):
|
||||||
|
# ignore messages until we've made our move
|
||||||
|
self.received = self.ignore
|
||||||
|
self.board.set_title('your turn')
|
||||||
|
|
||||||
|
def their_turn(self):
|
||||||
|
self.received = self.wait_for_move
|
||||||
|
self.board.set_title('their turn')
|
||||||
|
|
||||||
|
# called when the board receives input
|
||||||
|
def move(self, row, col):
|
||||||
|
try:
|
||||||
|
self.board.mark(row, col, self.role_s)
|
||||||
|
except InvalidMove, e:
|
||||||
|
print 'you made an invalid move'
|
||||||
|
return
|
||||||
|
|
||||||
|
self.send_move(row, col)
|
||||||
|
|
||||||
|
# check win conditions
|
||||||
|
if self.board.check_for_strike(self.role_s, row, col, self.strike):
|
||||||
|
self.won()
|
||||||
|
elif self.board.full():
|
||||||
|
self.drawn()
|
||||||
|
else:
|
||||||
|
self.next_move_id += 1
|
||||||
|
|
||||||
|
self.their_turn()
|
||||||
|
|
||||||
|
# sends a move message
|
||||||
|
def send_move(self, row, column):
|
||||||
|
msg = xmpp.Message()
|
||||||
|
msg.setType('chat')
|
||||||
|
|
||||||
|
turn = msg.NT.turn
|
||||||
|
turn.setNamespace(games_ns)
|
||||||
|
|
||||||
|
move = turn.NT.move
|
||||||
|
move.setNamespace(games_ns+'/tictactoe')
|
||||||
|
|
||||||
|
move.setAttr('row', str(row))
|
||||||
|
move.setAttr('col', str(column))
|
||||||
|
move.setAttr('id', str(self.next_move_id))
|
||||||
|
|
||||||
|
self.send(msg)
|
||||||
|
|
||||||
|
# sends a termination message and ends the game
|
||||||
|
def terminate(self, reason):
|
||||||
|
msg = xmpp.Message()
|
||||||
|
|
||||||
|
terminate = msg.NT.terminate
|
||||||
|
terminate.setNamespace(games_ns)
|
||||||
|
terminate.setAttr('reason', reason)
|
||||||
|
|
||||||
|
self.send(msg)
|
||||||
|
|
||||||
|
self.received = self.game_over
|
||||||
|
|
||||||
|
def won(self):
|
||||||
|
self.terminate('won')
|
||||||
|
self.board.won()
|
||||||
|
|
||||||
|
def lost(self):
|
||||||
|
self.terminate('lost')
|
||||||
|
self.board.lost()
|
||||||
|
|
||||||
|
def drawn(self):
|
||||||
|
self.terminate('draw')
|
||||||
|
self.board.drawn()
|
||||||
|
|
||||||
|
class TicTacToeBoard:
|
||||||
|
def __init__(self, session, rows, cols):
|
||||||
|
self.session = session
|
||||||
|
|
||||||
|
self.state = 'None'
|
||||||
|
|
||||||
|
self.rows = rows
|
||||||
|
self.cols = cols
|
||||||
|
|
||||||
|
self.board = [ [None] * self.cols for r in xrange(self.rows) ]
|
||||||
|
|
||||||
|
self.setup_window()
|
||||||
|
|
||||||
|
# check if the last move (at row r and column c) won the game
|
||||||
|
def check_for_strike(self, p, r, c, strike):
|
||||||
|
# number in a row: up and down, left and right
|
||||||
|
tallyI = 0
|
||||||
|
tally_ = 0
|
||||||
|
|
||||||
|
# number in a row: diagonal
|
||||||
|
# (imagine L or F as two sides of a right triangle: L\ or F/)
|
||||||
|
tallyL = 0
|
||||||
|
tallyF = 0
|
||||||
|
|
||||||
|
# convert real columns to internal columns
|
||||||
|
r -= 1
|
||||||
|
c -= 1
|
||||||
|
|
||||||
|
for d in xrange(-strike, strike):
|
||||||
|
r_in_range = 0 <= r+d < self.rows
|
||||||
|
c_in_range = 0 <= c+d < self.cols
|
||||||
|
|
||||||
|
# vertical check
|
||||||
|
if r_in_range:
|
||||||
|
tallyI = tallyI + 1
|
||||||
|
if self.board[r+d][c] != p:
|
||||||
|
tallyI = 0
|
||||||
|
|
||||||
|
# horizontal check
|
||||||
|
if c_in_range:
|
||||||
|
tally_ = tally_ + 1
|
||||||
|
if self.board[r][c+d] != p:
|
||||||
|
tally_ = 0
|
||||||
|
|
||||||
|
# diagonal checks
|
||||||
|
if r_in_range and c_in_range:
|
||||||
|
tallyL = tallyL + 1
|
||||||
|
if self.board[r+d][c+d] != p:
|
||||||
|
tallyL = 0
|
||||||
|
|
||||||
|
if r_in_range and 0 <= c-d < self.cols:
|
||||||
|
tallyF = tallyF + 1
|
||||||
|
if self.board[r+d][c-d] != p:
|
||||||
|
tallyF = 0
|
||||||
|
|
||||||
|
if any([t == strike for t in (tallyL, tallyF, tallyI, tally_)]):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# is the board full?
|
||||||
|
def full(self):
|
||||||
|
for r in xrange(self.rows):
|
||||||
|
for c in xrange(self.cols):
|
||||||
|
if self.board[r][c] == None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setup_window(self):
|
||||||
|
self.win = gtk.Window()
|
||||||
|
|
||||||
|
self.title_prefix = 'tic-tac-toe with %s' % self.session.jid
|
||||||
|
self.set_title()
|
||||||
|
|
||||||
|
self.win.set_app_paintable(True)
|
||||||
|
|
||||||
|
self.win.add_events(gdk.BUTTON_PRESS_MASK)
|
||||||
|
self.win.connect('button-press-event', self.clicked)
|
||||||
|
self.win.connect('expose-event', self.expose)
|
||||||
|
|
||||||
|
self.win.show_all()
|
||||||
|
|
||||||
|
def clicked(self, widget, event):
|
||||||
|
if not self.session.is_my_turn():
|
||||||
|
return
|
||||||
|
|
||||||
|
(height, width) = widget.get_size()
|
||||||
|
|
||||||
|
# convert click co-ordinates to row and column
|
||||||
|
|
||||||
|
row_height = height // self.rows
|
||||||
|
col_width = width // self.cols
|
||||||
|
|
||||||
|
row = int(event.y // row_height) + 1
|
||||||
|
column = int(event.x // col_width) + 1
|
||||||
|
|
||||||
|
self.session.move(row, column)
|
||||||
|
|
||||||
|
# this actually draws the board
|
||||||
|
def expose(self, widget, event):
|
||||||
|
win = widget.window
|
||||||
|
|
||||||
|
cr = win.cairo_create()
|
||||||
|
|
||||||
|
cr.set_source_rgb(1.0, 1.0, 1.0)
|
||||||
|
|
||||||
|
cr.set_operator(cairo.OPERATOR_SOURCE)
|
||||||
|
cr.paint()
|
||||||
|
|
||||||
|
(width, height) = widget.get_size()
|
||||||
|
|
||||||
|
row_height = height // self.rows
|
||||||
|
col_width = width // self.cols
|
||||||
|
|
||||||
|
for i in xrange(self.rows):
|
||||||
|
for j in xrange(self.cols):
|
||||||
|
if self.board[i][j] == 'x':
|
||||||
|
self.draw_x(cr, i, j, row_height, col_width)
|
||||||
|
elif self.board[i][j] == 'o':
|
||||||
|
self.draw_o(cr, i, j, row_height, col_width)
|
||||||
|
|
||||||
|
# XXX draw 'won', 'lost', 'draw'
|
||||||
|
|
||||||
|
def draw_x(self, cr, row, col, row_height, col_width):
|
||||||
|
cr.set_source_rgb(0, 0, 0)
|
||||||
|
|
||||||
|
top = row_height * (row + 0.2)
|
||||||
|
bottom = row_height * (row + 0.8)
|
||||||
|
|
||||||
|
left = col_width * (col + 0.2)
|
||||||
|
right = col_width * (col + 0.8)
|
||||||
|
|
||||||
|
cr.set_line_width(row_height / 5)
|
||||||
|
|
||||||
|
cr.move_to(left, top)
|
||||||
|
cr.line_to(right, bottom)
|
||||||
|
|
||||||
|
cr.move_to(right, top)
|
||||||
|
cr.line_to(left, bottom)
|
||||||
|
|
||||||
|
cr.stroke()
|
||||||
|
|
||||||
|
def draw_o(self, cr, row, col, row_height, col_width):
|
||||||
|
cr.set_source_rgb(0, 0, 0)
|
||||||
|
|
||||||
|
x = col_width * (col + 0.5)
|
||||||
|
y = row_height * (row + 0.5)
|
||||||
|
|
||||||
|
cr.arc(x, y, row_height/4, 0, 2.0*3.2) # slightly further than 2*pi
|
||||||
|
|
||||||
|
cr.set_line_width(row_height / 5)
|
||||||
|
cr.stroke()
|
||||||
|
|
||||||
|
# mark a move on the board
|
||||||
|
def mark(self, row, column, player):
|
||||||
|
if self.board[row-1][column-1]:
|
||||||
|
raise InvalidMove
|
||||||
|
else:
|
||||||
|
self.board[row-1][column-1] = player
|
||||||
|
|
||||||
|
self.win.queue_draw()
|
||||||
|
|
||||||
|
def set_title(self, suffix = None):
|
||||||
|
str = self.title_prefix
|
||||||
|
|
||||||
|
if suffix:
|
||||||
|
str += ': ' + suffix
|
||||||
|
|
||||||
|
self.win.set_title(str)
|
||||||
|
|
||||||
|
def won(self):
|
||||||
|
self.state = 'won'
|
||||||
|
self.set_title('you won!')
|
||||||
|
self.win.queue_draw()
|
||||||
|
|
||||||
|
def lost(self):
|
||||||
|
self.state = 'lost'
|
||||||
|
self.set_title('you lost.')
|
||||||
|
self.win.queue_draw()
|
||||||
|
|
||||||
|
def drawn(self):
|
||||||
|
self.state = 'drawn'
|
||||||
|
self.win.set_title(self.title_prefix + ': a draw.')
|
||||||
|
self.win.queue_draw()
|
|
@ -122,11 +122,9 @@ class VcardWindow:
|
||||||
jid = self.contact.jid
|
jid = self.contact.jid
|
||||||
# Update roster
|
# Update roster
|
||||||
gajim.interface.roster.draw_avatar(jid, self.account)
|
gajim.interface.roster.draw_avatar(jid, self.account)
|
||||||
# Update chat window
|
# Update chat windows
|
||||||
if gajim.interface.msg_win_mgr.has_window(jid, self.account):
|
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(jid, self.account):
|
||||||
win = gajim.interface.msg_win_mgr.get_window(jid, self.account)
|
if ctrl.type_id != message_control.TYPE_GC:
|
||||||
ctrl = win.get_control(jid, self.account)
|
|
||||||
if win and ctrl.type_id != message_control.TYPE_GC:
|
|
||||||
ctrl.show_avatar()
|
ctrl.show_avatar()
|
||||||
|
|
||||||
def on_vcard_information_window_destroy(self, widget):
|
def on_vcard_information_window_destroy(self, widget):
|
||||||
|
|
Loading…
Reference in New Issue