Reverted r9614 and r9615.

Sorry, bct, but I think it is to early to merge that as it completely
breaks. It seems nobody in gajim@conference.gajim.org considers it
usable yet.
I don't know if you got asterix' ok for it and I'm sorry if I reverted
it now although you had his ok, but having broken trunk is very
contra-productive. I think it was just too early to merge.
This commit is contained in:
js 2008-05-11 13:17:28 +00:00
parent 24203f367f
commit 573568f9ac
16 changed files with 721 additions and 1392 deletions

View File

@ -6,7 +6,7 @@
## Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
## Julien Pivotto <roidelapluie@gmail.com>
## Stephan Erb <steve-e@h3c.de>
## Stephan Erb <steve-e@h3c.de>
##
## This file is part of Gajim.
##
@ -104,8 +104,8 @@ class ChatControlBase(MessageControl):
type_]))
def draw_banner(self):
'''Draw the fat line at the top of the window that
houses the icon, jid, ...
'''Draw the fat line at the top of the window that
houses the icon, jid, ...
'''
self.draw_banner_text()
self._update_banner_state_image()
@ -133,7 +133,7 @@ class ChatControlBase(MessageControl):
def status_url_clicked(self, widget, 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):
MessageControl.__init__(self, type_id, parent_win, widget_name,
contact, acct, resource = resource);
@ -238,7 +238,7 @@ class ChatControlBase(MessageControl):
self.msg_textview.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
gtk.DEST_DEFAULT_HIGHLIGHT,
self.dnd_list, gtk.gdk.ACTION_COPY)
self.update_font()
# Hook up send button
@ -266,7 +266,7 @@ class ChatControlBase(MessageControl):
# loop removing non-existant dictionaries
# iterating on a copy
for lang in dict(langs):
try:
try:
spell.set_language(langs[lang])
except:
del langs[lang]
@ -330,7 +330,7 @@ class ChatControlBase(MessageControl):
menu.show_all()
# moved from ChatControl
# moved from ChatControl
def _on_banner_eventbox_button_press_event(self, widget, event):
'''If right-clicked, show popup'''
if event.button == 3: # right click
@ -362,7 +362,7 @@ class ChatControlBase(MessageControl):
self.disconnect_style_event(banner_name_label)
self.disconnect_style_event(self.banner_status_label)
if bgcolor:
banner_eventbox.modify_bg(gtk.STATE_NORMAL,
banner_eventbox.modify_bg(gtk.STATE_NORMAL,
gtk.gdk.color_parse(bgcolor))
default_bg = False
else:
@ -391,14 +391,14 @@ class ChatControlBase(MessageControl):
if found:
widget.disconnect(id)
del self.handlers[id]
def connect_style_event(self, widget, set_fg = False, set_bg = False):
self.disconnect_style_event(widget)
id = widget.connect('style-set', self._on_style_set_event, set_fg, set_bg)
self.handlers[id] = widget
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[1] == True -> set bg color'''
banner_eventbox = self.xml.get_widget('banner_eventbox')
@ -564,7 +564,7 @@ class ChatControlBase(MessageControl):
send_message = False
else: # ENTER
send_message = True
if gajim.connections[self.account].connected < 2: # we are not connected
dialogs.ErrorDialog(_('A connection is not available'),
_('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
max_size = gajim.config.get('key_up_lines')
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[max_size - 1] = message
# self.sent_history_pos has changed if we browsed sent_history,
@ -693,20 +693,18 @@ class ChatControlBase(MessageControl):
kind in ('incoming', 'incoming_queue'):
# we want to have save this message in events list
# other_tags_for_text == ['marked'] --> highlighted gc message
type_ = 'printed_' + self.type_id
event = 'message_received'
if gc_message:
if 'marked' in other_tags_for_text:
type_ = 'printed_marked_gc_msg'
else:
type_ = 'printed_gc_msg'
event = 'gc_message_received'
else:
type_ = 'printed_' + self.type_id
event = 'message_received'
show_in_roster = notify.get_show_in_roster(event,
self.account, self.contact, self.session)
self.account, self.contact)
show_in_systray = notify.get_show_in_systray(event,
self.account, self.contact, type_)
event = gajim.events.create_event(type_, None,
show_in_roster = show_in_roster,
show_in_systray = show_in_systray)
@ -724,10 +722,11 @@ class ChatControlBase(MessageControl):
not self.parent_win.is_active() or not end) and \
kind in ('incoming', 'incoming_queue'):
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():
self.parent_win.show_title(True, self) # Enabled Urgent hint
self.parent_win.show_title(True, ctrl) # Enabled Urgent hint
else:
self.parent_win.show_title(False, self) # Disabled Urgent hint
self.parent_win.show_title(False, ctrl) # Disabled Urgent hint
def toggle_emoticons(self):
'''hide show emoticons_button and make sure emoticons_menu is always there
@ -857,7 +856,7 @@ class ChatControlBase(MessageControl):
# we don't want to always resize in height the message_textview
# 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
# we set to automatic the scrollbar policy
diff_y = message_height - requisition.height
@ -868,13 +867,13 @@ class ChatControlBase(MessageControl):
'vscrollbar-policy')
# scroll only when scrollbar appear
if policy != gtk.POLICY_AUTOMATIC:
self.msg_scrolledwindow.set_property('vscrollbar-policy',
self.msg_scrolledwindow.set_property('vscrollbar-policy',
gtk.POLICY_AUTOMATIC)
self.msg_scrolledwindow.set_property('height-request',
self.msg_scrolledwindow.set_property('height-request',
message_height + conversation_height - min_height)
self.bring_scroll_to_end(msg_textview)
else:
self.msg_scrolledwindow.set_property('vscrollbar-policy',
self.msg_scrolledwindow.set_property('vscrollbar-policy',
gtk.POLICY_NEVER)
self.msg_scrolledwindow.set_property('height-request', -1)
self.conv_textview.bring_scroll_to_end(diff_y - 18, False)
@ -884,10 +883,10 @@ class ChatControlBase(MessageControl):
# enable scrollbar automatic policy for horizontal scrollbar
# if message we have in message_textview is too big
if requisition.width > message_width:
self.msg_scrolledwindow.set_property('hscrollbar-policy',
self.msg_scrolledwindow.set_property('hscrollbar-policy',
gtk.POLICY_AUTOMATIC)
else:
self.msg_scrolledwindow.set_property('hscrollbar-policy',
self.msg_scrolledwindow.set_property('hscrollbar-policy',
gtk.POLICY_NEVER)
return True
@ -917,26 +916,27 @@ class ChatControlBase(MessageControl):
types_list = ['printed_' + type_, type_]
if not len(gajim.events.get_events(self.account, jid, types_list)):
return
return
if not self.parent_win:
return
if self.conv_textview.at_the_end() and \
self.parent_win.get_active_control() == self and \
self.parent_win.window.is_active():
# we are at the end
if not self.session.remove_events(types_list):
if not gajim.events.remove_events(self.account, self.get_full_jid(),
types = types_list):
# There were events to remove
self.redraw_after_event_removed(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 '''
self.parent_win.redraw_tab(self)
self.parent_win.show_title()
# TODO : get the contact and check notify.get_show_in_roster()
if self.type_id == message_control.TYPE_PM:
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
groupchat_control = gajim.interface.msg_win_mgr.get_gc_control(
groupchat_control = gajim.interface.msg_win_mgr.get_control(
room_jid, self.account)
if room_jid in gajim.interface.minimized_controls[self.account]:
groupchat_control = \
@ -947,14 +947,15 @@ class ChatControlBase(MessageControl):
if contact:
gajim.interface.roster.draw_contact(room_jid, self.account)
groupchat_control.draw_contact(nick)
if groupchat_control.parent_win:
groupchat_control.parent_win.redraw_tab(groupchat_control)
mw = gajim.interface.msg_win_mgr.get_window(room_jid, self.account)
if mw:
mw.redraw_tab(groupchat_control)
else:
gajim.interface.roster.draw_contact(jid, self.account)
gajim.interface.roster.show_title()
def sent_messages_scroll(self, direction, conv_buf):
size = len(self.sent_history)
size = len(self.sent_history)
if self.orig_msg is None:
# user was typing something and then went into history, so save
# whatever is already typed
@ -1052,7 +1053,7 @@ class ChatControl(ChatControlBase):
self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
gajim.config.get('hide_chat_banner'))
# Add lock image to show chat encryption
self.lock_image = self.xml.get_widget('lock_image')
self.lock_tooltip = gtk.Tooltips()
@ -1079,7 +1080,7 @@ class ChatControl(ChatControlBase):
if gajim.get_transport_name_from_jid(self.contact.jid) or \
gajim.connections[self.account].is_zeroconf:
convert_to_gc_button.set_sensitive(False)
# 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
self.show_bigger_avatar_timeout_id = None
@ -1125,12 +1126,12 @@ class ChatControl(ChatControlBase):
gajim.encrypted_chats[self.account].append(contact.jid)
msg = _('GPG encryption enabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session:
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.session.is_loggable())
self.status_tooltip = gtk.Tooltips()
if gajim.otr_module:
@ -1153,17 +1154,17 @@ class ChatControl(ChatControlBase):
return
avatar_w = avatar_pixbuf.get_width()
avatar_h = avatar_pixbuf.get_height()
scaled_buf = self.xml.get_widget('avatar_image').get_pixbuf()
scaled_buf_w = scaled_buf.get_width()
scaled_buf_h = scaled_buf.get_height()
# do we have something bigger to show?
if avatar_w > scaled_buf_w or avatar_h > scaled_buf_h:
# wait for 0.5 sec in case we leave earlier
self.show_bigger_avatar_timeout_id = gobject.timeout_add(500,
self.show_bigger_avatar, widget)
def on_avatar_eventbox_leave_notify_event(self, widget, event):
'''we left the eventbox area that holds the avatar img'''
# did we add a timeout? if yes remove it
@ -1175,14 +1176,14 @@ class ChatControl(ChatControlBase):
if event.button == 3: # right click
menu = gtk.Menu()
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
id = menuitem.connect('activate',
id = menuitem.connect('activate',
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
self.contact.jid, self.account, self.contact.get_shown_name() + \
'.jpeg')
self.handlers[id] = menuitem
menu.append(menuitem)
menu.show_all()
menu.connect('selection-done', lambda w:w.destroy())
menu.connect('selection-done', lambda w:w.destroy())
# show the menu
menu.show_all()
menu.popup(None, None, None, event.button, event.time)
@ -1267,15 +1268,15 @@ class ChatControl(ChatControlBase):
banner_status_img.set_from_pixbuf(scaled_pix)
def draw_banner_text(self):
'''Draw the text in the fat line at the top of the window that
houses the name, jid.
'''Draw the text in the fat line at the top of the window that
houses the name, jid.
'''
contact = self.contact
jid = contact.jid
banner_name_label = self.xml.get_widget('banner_name_label')
banner_eventbox = self.xml.get_widget('banner_eventbox')
name = contact.get_shown_name()
if self.resource:
name += '/' + self.resource
@ -1362,7 +1363,7 @@ class ChatControl(ChatControlBase):
ec.remove(self.contact.jid)
self.gpg_is_active = False
msg = _('GPG encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
if self.session:
self.session.loggable = True
@ -1387,12 +1388,12 @@ class ChatControl(ChatControlBase):
gajim.config.add_per('contacts', self.contact.jid)
gajim.config.set_per('contacts', self.contact.jid, 'gpg_enabled',
self.gpg_is_active)
self._show_lock_image(self.gpg_is_active, 'GPG', self.gpg_is_active, self.session and \
self.session.is_loggable())
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'
logged_string = chat_logged and 'will' or 'will NOT'
tooltip = '%s Encryption %s active. \nYour chat session %s be logged.' %\
@ -1548,7 +1549,7 @@ class ChatControl(ChatControlBase):
# 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!
self.reset_kbd_mouse_timeout_vars()
return True # loop forever
return True # loop forever
def check_for_possible_inactive_chatstate(self, arg):
''' did we move mouse over that window or wrote something in message
@ -1600,7 +1601,7 @@ class ChatControl(ChatControlBase):
msg = _('E2E encryption disabled')
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
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,
subject=None, xhtml=None, simple=False):
@ -1624,13 +1625,13 @@ class ChatControl(ChatControlBase):
if self.session and self.session.enable_encryption:
if not encrypted:
msg = _('The following message was NOT encrypted')
ChatControlBase.print_conversation_line(self, msg,
ChatControlBase.print_conversation_line(self, msg,
'status', '', tim)
else:
# GPG encryption
if encrypted and not self.gpg_is_active:
msg = _('The following message was encrypted')
ChatControlBase.print_conversation_line(self, msg,
ChatControlBase.print_conversation_line(self, msg,
'status', '', tim)
self._toggle_gpg()
elif not encrypted and self.gpg_is_active:
@ -1671,7 +1672,7 @@ class ChatControl(ChatControlBase):
elif num_unread > 1:
unread = '[' + unicode(num_unread) + ']'
# Draw tab label using chatstate
# Draw tab label using chatstate
theme = gajim.config.get('roster_theme')
color = None
if not chatstate:
@ -1699,7 +1700,7 @@ class ChatControl(ChatControlBase):
color = self.lighten_color(color)
else: # active or not chatstate, get color from gtk
color = self.parent_win.notebook.style.fg[gtk.STATE_ACTIVE]
name = self.contact.get_shown_name()
if self.resource:
@ -1718,7 +1719,7 @@ class ChatControl(ChatControlBase):
['printed_' + self.type_id, self.type_id]))
# Set tab image (always 16x16); unread messages show the 'event' image
tab_img = None
if num_unread and gajim.config.get('show_unread_tab_icon'):
img_16 = gajim.interface.roster.get_appropriate_state_images(
self.contact.jid, icon_name = 'event')
@ -1759,7 +1760,7 @@ class ChatControl(ChatControlBase):
convert_to_gc_menuitem = xml.get_widget('convert_to_groupchat')
muc_icon = gtkgui_helpers.load_icon('muc_active')
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]
send_file_menuitem.add_accelerator('activate', ag, gtk.keysyms.f, gtk.gdk.CONTROL_MASK,
@ -1773,7 +1774,7 @@ class ChatControl(ChatControlBase):
contact = self.parent_win.get_active_contact()
jid = contact.jid
# check if we support and use gpg
if not gajim.config.get_per('accounts', self.account, 'keyid') or\
not gajim.connections[self.account].USE_GPG or\
@ -1813,22 +1814,22 @@ class ChatControl(ChatControlBase):
convert_to_gc_menuitem.set_sensitive(False)
# connect signals
id = history_menuitem.connect('activate',
id = history_menuitem.connect('activate',
self._on_history_menuitem_activate)
self.handlers[id] = history_menuitem
id = send_file_menuitem.connect('activate',
id = send_file_menuitem.connect('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',
self._on_add_to_roster_menuitem_activate)
self.handlers[id] = add_to_roster_menuitem
id = toggle_gpg_menuitem.connect('activate',
self._on_toggle_gpg_menuitem_activate)
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.handlers[id] = toggle_e2e_menuitem
id = information_menuitem.connect('activate',
self.handlers[id] = toggle_e2e_menuitem
id = information_menuitem.connect('activate',
self._on_contact_information_menuitem_activate)
self.handlers[id] = information_menuitem
id = convert_to_gc_menuitem.connect('activate',
@ -1884,7 +1885,7 @@ class ChatControl(ChatControlBase):
# JEP 85 does not allow resending the same chatstate
# this function checks for that and just returns so it's safe to call it
# with same state.
# This functions also checks for violation in state transitions
# and raises RuntimeException with appropriate message
# more on that http://www.jabber.org/jeps/jep-0085.html#statechart
@ -1913,7 +1914,7 @@ class ChatControl(ChatControlBase):
if contact.composing_xep is False: # jid cannot do xep85 nor xep22
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
if contact.our_chatstate == state:
return
@ -1925,7 +1926,7 @@ class ChatControl(ChatControlBase):
# in self.send_message() because we need REAL message (with <body>)
# for that procedure so return to make sure we send only once
# 'active' until we know peer supports jep85
return
return
if contact.our_chatstate == 'ask':
return
@ -1945,7 +1946,7 @@ class ChatControl(ChatControlBase):
MessageControl.send_message(self, None, chatstate = 'active')
contact.our_chatstate = 'active'
self.reset_kbd_mouse_timeout_vars()
# if we're inactive prevent composing (JEP violation)
elif contact.our_chatstate == 'inactive' and state == 'composing':
# go active before
@ -1962,15 +1963,10 @@ class ChatControl(ChatControlBase):
def shutdown(self):
# destroy banner tooltip - bug #pygtk for that!
self.status_tooltip.destroy()
# Send 'gone' chatstate
self.send_chatstate('gone', self.contact)
self.contact.chatstate = None
self.contact.our_chatstate = None
# terminate session
self.session.control = None
# Disconnect timer callbacks
gobject.source_remove(self.possible_paused_timeout_id)
gobject.source_remove(self.possible_inactive_timeout_id)
@ -1990,8 +1986,6 @@ class ChatControl(ChatControlBase):
self.msg_textview.destroy()
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]\
[self.get_full_jid()] < 2:
# 2 seconds
@ -2179,6 +2173,12 @@ class ChatControl(ChatControlBase):
if hasattr(self, 'session') and self.session and self.session.enable_encryption:
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
message_ids = []
for event in events:
@ -2203,13 +2203,8 @@ class ChatControl(ChatControlBase):
types = [self.type_id])
typ = 'chat' # Is it a normal chat or a pm ?
# reset to status image in gc if it is a 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:
if is_pm:
control.update_ui()
control.parent_win.show_title()
typ = 'pm'
@ -2270,18 +2265,18 @@ class ChatControl(ChatControlBase):
window.set_app_paintable(True)
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.realize()
window.window.set_back_pixmap(pixmap, False) # make it transparent
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 += small_avatar.allocation.x
y0 += small_avatar.allocation.y
center_x= x0 + (small_avatar.allocation.width / 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)
# make the cursor invisible so we can see the image
invisible_cursor = gtkgui_helpers.get_invisible_cursor()
@ -2312,7 +2307,7 @@ class ChatControl(ChatControlBase):
c = self.gc_contact
else:
c = self.contact
gajim.interface.instances['file_transfers'].show_file_send_request(
gajim.interface.instances['file_transfers'].show_file_send_request(
self.account, c)
def _on_add_to_roster_menuitem_activate(self, widget):
@ -2322,7 +2317,7 @@ class ChatControl(ChatControlBase):
gajim.interface.roster.on_info(widget, self.contact, self.account)
def _on_toggle_gpg_menuitem_activate(self, widget):
self._toggle_gpg()
self._toggle_gpg()
def _on_convert_to_gc_menuitem_activate(self, widget):
'''user want to invite some friends to chat'''

View File

@ -49,8 +49,7 @@ if dbus_support.supported:
import dbus
from music_track_listener import MusicTrackListener
from session import ChatControlSession
import tictactoe
from common.stanza_session import EncryptedStanzaSession
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'error']
@ -995,7 +994,7 @@ class ConnectionVcard:
gajim.interface.remove_avatar_files(our_jid)
self.awaiting_answers[id] = (VCARD_PUBLISHED, iq2)
def _IqCB(self, con, iq_obj):
id = iq_obj.getID()
@ -1047,7 +1046,6 @@ class ConnectionVcard:
our_jid = gajim.get_jid_from_account(self.name)
if iq_obj.getType() == 'error' and jid == our_jid:
# our server doesn't support vcard
gajim.log.debug('xxx error xxx')
self.vcard_supported = False
if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error':
if frm and frm != our_jid:
@ -1263,7 +1261,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
reply.addChild(node=common.xmpp.ErrorNode('service-unavailable', typ='cancel'))
con.send(reply)
raise common.xmpp.NodeProcessed
def _InitE2ECB(self, con, stanza, session):
@ -1293,7 +1291,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
errmsg = iq_obj.getErrorMsg()
errcode = iq_obj.getErrorCode()
self.dispatch('ERROR_ANSWER', (id, jid_from, errmsg, errcode))
def _PrivateCB(self, con, iq_obj):
'''
Private Data (XEP 048 and 049)
@ -1501,8 +1499,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
def _messageCB(self, con, msg):
'''Called when we receive a message'''
gajim.log.debug('MessageCB')
frm = helpers.get_full_jid_from_iq(msg)
mtype = msg.getType()
thread_id = msg.getThread()
@ -1510,23 +1506,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not mtype:
mtype = 'normal'
game_invite = msg.getTag('invite', namespace='http://jabber.org/protocol/games')
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 not mtype == 'groupchat':
session = self.get_session(frm, thread_id, mtype)
if thread_id and not session.received_thread_id:
session.received_thread_id = True
@ -1535,57 +1516,66 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if msg.getTag('event') is not None:
self._pubsubEventCB(con, msg)
return
# check if the message is a XEP-0070 confirmation request
if msg.getTag('confirm', namespace=common.xmpp.NS_HTTP_AUTH):
# check if the message is a xep70-confirmation-request
if msg.getTag('confirm') and msg.getTag('confirm').namespace == \
common.xmpp.NS_HTTP_AUTH:
self._HttpAuthCB(con, msg)
return
# check if the message is a XEP-0020 feature negotiation request
if msg.getTag('feature', namespace=common.xmpp.NS_FEATURE):
if msg.getTag('feature') and msg.getTag('feature').namespace == \
common.xmpp.NS_FEATURE:
if gajim.HAVE_PYCRYPTO:
self._FeatureNegCB(con, msg, session)
return
if msg.getTag('init', namespace=common.xmpp.NS_ESESSION_INIT):
if msg.getTag('init') and msg.getTag('init').namespace == \
common.xmpp.NS_ESESSION_INIT:
self._InitE2ECB(con, msg, session)
encrypted = False
tim = msg.getTimestamp()
tim = helpers.datetime_tuple(tim)
tim = localtime(timegm(tim))
if msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO):
e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
if e2e_tag:
encrypted = True
try:
msg = session.decrypt_stanza(msg)
except:
self.dispatch('FAILED_DECRYPT', (frm, tim, session))
self.dispatch('FAILED_DECRYPT', (frm, tim))
msgtxt = msg.getBody()
msghtml = msg.getXHTML()
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)
jid = helpers.get_jid_from_iq(msg)
addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS)
# Be sure it comes from one of our resource, else ignore address element
if addressTag and jid == gajim.get_jid_from_account(self.name):
address = addressTag.getTag('address', attrs={'type': 'ofrom'})
if address:
frm = address.getAttr('jid')
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
invite = None
encTag = msg.getTag('x', namespace=common.xmpp.NS_ENCRYPTED)
if not encTag:
invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER)
if invite and not invite.getTag('invite'):
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
# invitation
# stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED
@ -1599,54 +1589,94 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('GC_INVITATION', (room_jid, frm, '', None,
is_continued))
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:
#decrypt
encmsg = encTag.getData()
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID:
decmsg = self.gpg.decrypt(encmsg, keyID)
# \x00 chars are not allowed in C (so in GTK)
msgtxt = decmsg.replace('\x00', '')
encrypted = True
decmsg = decmsg.replace('\x00', '')
if decmsg:
msgtxt = decmsg
encrypted = True
if mtype == 'error':
self.dispatch_error_message(msg, msgtxt, session, frm, tim, subject)
elif mtype == 'groupchat':
self.dispatch_gc_message(msg, subject, frm, msgtxt, jid, tim)
elif invite is not None:
self.dispatch_invite_message(invite, frm)
else:
# XXX horrible hack
if isinstance(session, ChatControlSession):
session.received(frm, msgtxt, tim, encrypted, subject, msg)
else:
session.received(msg)
# END messageCB
# process and dispatch an error message
def dispatch_error_message(self, msg, msgtxt, session, frm, tim, subject):
error_msg = msg.getErrorMsg()
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))
# process and dispatch a groupchat message
def dispatch_gc_message(self, msg, subject, frm, msgtxt, jid, tim):
has_timestamp = bool(msg.timestamp)
if subject is not None:
self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp))
error_msg = msg.getErrorMsg()
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':
has_timestamp = False
if msg.timestamp:
has_timestamp = True
if subject is not None:
self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp))
else:
statusCode = msg.getStatusCode()
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, msghtml,
statusCode))
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)))
return
elif mtype == 'chat': # it's type 'chat'
if gajim.otr_module and isinstance(msgtxt, unicode):
otr_msg_tuple = gajim.otr_module.otrl_message_receiving(
@ -1660,114 +1690,117 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# text in <body> and <html>
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(),
gajim.get_jid_from_account(self.name).encode(), gajim.OTR_PROTO, 1,
(gajim.otr_add_appdata, self.name))[0]
tlvs = otr_msg_tuple[2]
ctx.app_data.handle_tlv(tlvs)
statusCode = msg.getStatusCode()
if not msg.getTag('body') and chatstate is None: #no <body>
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
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):
def get_session(self, jid, thread_id, type):
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
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)
session = self.find_session(jid, thread_id, type)
if session:
return session
if pm:
return self.make_new_session(jid, thread_id, type = 'pm')
else:
return self.make_new_session(jid, thread_id)
# it's possible we initiated a session with a bare JID and this is the
# 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
def find_session(self, jid, thread_id):
self.move_session(bare_jid, thread_id, jid.split("/")[1])
return session
return self.make_new_session(jid, thread_id, type)
def find_session(self, jid, thread_id, type):
try:
if not thread_id:
if type == 'chat' and not thread_id:
return self.find_null_session(jid)
else:
return self.sessions[jid][thread_id]
except KeyError:
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):
try:
del self.sessions[jid][thread_id]
if not self.sessions[jid]:
del self.sessions[jid]
except KeyError:
pass
def find_null_session(self, jid):
'''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.'''
def move_session(self, original_jid, thread_id, to_resource):
'''moves a session to another resource.'''
session = self.sessions[original_jid][thread_id]
sessions = self.sessions[jid].values()
no_threadid_sessions = filter(lambda s: not s.received_thread_id, sessions)
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):
'''finds all of the sessions between us and jid that jid hasn't sent a thread_id in yet.
returns the session that we last sent a message to.'''
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:
no_threadid_sessions.sort(key=lambda s: s.last_send)
@ -1775,16 +1808,8 @@ sent a message to.'''
else:
return None
def make_new_session(self, jid, thread_id=None, type='chat', klass=None):
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)
def make_new_session(self, jid, thread_id = None, type = 'chat'):
sess = EncryptedStanzaSession(self, common.xmpp.JID(jid), thread_id, type)
if not jid in self.sessions:
self.sessions[jid] = {}
@ -2278,7 +2303,7 @@ sent a message to.'''
self.dispatch('RESOURCE_CONFLICT', ())
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
con.RegisterHandler('message', self._messageCB)
con.RegisterHandler('presence', self._presenceCB)

View File

@ -116,7 +116,7 @@ class Events:
def remove_events(self, account, jid, event = None, types = []):
'''if event is not specified, remove all events from this jid,
optionally only from given type
optionnaly only from given type
return True if no such event found'''
if not self._events.has_key(account):
return True
@ -168,7 +168,7 @@ class Events:
'''returns all events from the given account of the form
{jid1: [], jid2: []}
if jid is given, returns all events from the given jid in a list: []
optionally only from given type'''
optionnaly only from given type'''
if not self._events.has_key(account):
return []
if not jid:

View File

@ -178,7 +178,7 @@ gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI,
xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER,
xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED,
'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING,
xmpp.NS_TIME_REVISED, xmpp.NS_GAMING]
xmpp.NS_TIME_REVISED]
# Optional features gajim supports
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
return fjid # we return the real jid
real_jid = fjid
if interface.msg_win_mgr.get_gc_control(room_jid, account):
if interface.msg_win_mgr.get_control(room_jid, account):
# 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)
if not gc_contact:

View File

@ -253,10 +253,12 @@ def user_nickname(items, name, jid):
if nick is not None:
contact.contact_name = nick
gajim.interface.roster.draw_contact(user, name)
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(user, name):
ctrl = gajim.interface.msg_win_mgr.get_control(user, name)
if ctrl:
ctrl.update_ui()
ctrl.parent_win.redraw_tab(ctrl)
ctrl.parent_win.show_title()
win = gajim.interface.msg_win_mgr.get_window(user, name)
win.redraw_tab(ctrl)
win.show_title()
elif retract:
contact.contact_name = ''

View File

@ -32,23 +32,10 @@ class StanzaSession(object):
else:
self.thread_id = self.generate_thread_id()
self.loggable = True
self.last_send = 0
self.status = None
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):
return "".join([random.choice(string.ascii_letters) for x in xrange(0,32)])
@ -83,7 +70,7 @@ class StanzaSession(object):
def cancelled_negotiation(self):
'''A negotiation has been cancelled, so reset this session to its default state.'''
if self.control:
if hasattr(self, 'control'):
self.control.on_cancel_session_negotiation()
self.status = None
@ -145,6 +132,8 @@ class EncryptedStanzaSession(StanzaSession):
def __init__(self, conn, jid, thread_id, type = 'chat'):
StanzaSession.__init__(self, conn, jid, thread_id, type = 'chat')
self.loggable = True
self.xes = {}
self.es = {}
@ -778,7 +767,7 @@ class EncryptedStanzaSession(StanzaSession):
self.status = 'active'
self.enable_encryption = True
if self.control:
if hasattr(self, 'control'):
self.control.print_esession_details()
def final_steps_alice(self, form):
@ -798,14 +787,15 @@ class EncryptedStanzaSession(StanzaSession):
self.do_retained_secret(k, srs)
# ks_s doesn't need to be calculated here
# don't need to calculate ks_s here
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)
# 4.6.2 Verifying Bob's Identity
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':
self.loggable = False
@ -813,7 +803,7 @@ class EncryptedStanzaSession(StanzaSession):
self.status = 'active'
self.enable_encryption = True
if self.control:
if hasattr(self, 'control'):
self.control.print_esession_details()
# calculate and store the new retained secret
@ -901,6 +891,17 @@ otherwise, list the fields we haven't implemented'''
# preventing falsified messages from going through.
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):
StanzaSession.cancelled_negotiation(self)
self.enable_encryption = False

View File

@ -658,7 +658,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
frm = unicode(frm)
jid = frm
session = self.get_or_create_session(frm, thread_id, mtype)
session = self.get_session(frm, thread_id, mtype)
if thread_id and not session.received_thread_id:
session.received_thread_id = True
@ -794,7 +794,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
raise common.xmpp.NodeProcessed
def get_or_create_session(self, jid, thread_id, type):
def get_session(self, jid, thread_id, type):
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
session = self.find_session(jid, thread_id, type)

View File

@ -241,7 +241,6 @@ from chat_control import ChatControl
from groupchat_control import GroupchatControl
from groupchat_control import PrivateChatControl
from atom_window import AtomWindow
from session import ChatControlSession
import common.sleepy
@ -258,7 +257,7 @@ from common.xmpp import Message as XmppMessage
try:
import otr, otr_windows
gajim.otr_module = otr
gajim.otr_windows = otr_windows
except ImportError:
@ -637,7 +636,7 @@ class Interface:
title = data[1]
prompt = data[2]
proposed_nick = data[3]
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
gc_control = self.msg_win_mgr.get_control(room_jid, account)
if not gc_control and \
room_jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][room_jid]
@ -691,10 +690,9 @@ class Interface:
(jid_from, file_props))
conn.disconnect_transfer(file_props)
return
for ctrl in self.msg_win_mgr.get_chat_controls(jid_from, account):
if ctrl.type_id == message_control.TYPE_GC:
ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
ctrl = self.msg_win_mgr.get_control(jid_from, account)
if ctrl and 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):
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'
@ -924,12 +922,146 @@ class Interface:
self.handle_event_gc_notify(account, (jid, array[1], status_message,
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):
#'MSGERROR' (account, (jid, error_code, error_msg, msg, time))
full_jid_with_resource = array[0]
jids = full_jid_with_resource.split('/', 1)
jid = jids[0]
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
@ -1159,22 +1291,21 @@ class Interface:
win.set_values(vcard)
# show avatar in chat
ctrls = []
win = None
ctrl = None
if resource and self.msg_win_mgr.has_window(
jid + '/' + resource, account):
win = self.msg_win_mgr.get_window(jid + '/' + resource,
account)
ctrls = win.get_controls(jid + '/' + resource, account)
ctrl = win.get_control(jid + '/' + resource, account)
elif self.msg_win_mgr.has_window(jid, account):
win = self.msg_win_mgr.get_window(jid, account)
ctrls = win.get_controls(jid, account)
for ctrl in ctrls:
if ctrl.type_id != message_control.TYPE_GC:
ctrl.show_avatar()
ctrl = win.get_control(jid, account)
if win and ctrl.type_id != message_control.TYPE_GC:
ctrl.show_avatar()
# Show avatar in roster or gc_roster
gc_ctrl = self.msg_win_mgr.get_gc_control(jid, account)
gc_ctrl = self.msg_win_mgr.get_control(jid, account)
if not gc_ctrl and \
jid in self.minimized_controls[account]:
gc_ctrl = self.minimized_controls[account][jid]
@ -1231,25 +1362,21 @@ class Interface:
# Get the window and control for the updated status, this may be a
# PrivateChatControl
control = self.msg_win_mgr.get_gc_control(room_jid, account)
control = self.msg_win_mgr.get_control(room_jid, account)
if not control and \
room_jid in self.minimized_controls[account]:
control = self.minimized_controls[account][room_jid]
if not control or (control and control.type_id != message_control.TYPE_GC):
if control and control.type_id != message_control.TYPE_GC:
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])
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)
contact = gajim.contacts.\
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):
# print status in chat window and update status/GPG image
if ctrl:
statusCode = array[9]
if '303' in statusCode:
new_nick = array[10]
@ -1285,7 +1412,7 @@ class Interface:
jids = array[0].split('/', 1)
room_jid = jids[0]
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
gc_control = self.msg_win_mgr.get_control(room_jid, account)
if not gc_control and \
room_jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][room_jid]
@ -1313,7 +1440,7 @@ class Interface:
jids = array[0].split('/', 1)
jid = jids[0]
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
jid in self.minimized_controls[account]:
@ -1378,7 +1505,7 @@ class Interface:
jid = array[0]
statusCode = array[1]
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
gc_control = self.msg_win_mgr.get_control(jid, account)
if not gc_control and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
@ -1437,7 +1564,7 @@ class Interface:
self.roster.on_disconnect(None, room_jid, account)
else:
win = self.msg_win_mgr.get_window(room_jid, account)
ctrl = win.get_gc_control(room_jid, account)
ctrl = win.get_control(room_jid, account)
win.remove_tab(ctrl, 3)
dlg = dialogs.InputDialog(_('Password Required'),
@ -1865,9 +1992,9 @@ class Interface:
AtomWindow.newAtomEntry(atom_entry)
def handle_event_failed_decrypt(self, account, data):
jid, tim, session = data
jid, tim = data
ctrl = self.msg_win_mgr.get_control(jid, account, session.thread_id)
ctrl = self.msg_win_mgr.get_control(jid, account)
if ctrl:
ctrl.print_conversation_line('Unable to decrypt message from %s\nIt may have been tampered with.' % (jid), 'status', '', tim)
else:
@ -2006,8 +2133,6 @@ class Interface:
if ctrl:
new_sess = gajim.connections[account].make_new_session(str(jid))
ctrl.set_session(new_sess)
gajim.connections[account].delete_session(str(jid),
session.thread_id)
if was_encrypted:
ctrl.print_esession_details()
@ -2267,6 +2392,7 @@ class Interface:
'ERROR_ANSWER': self.handle_event_error_answer,
'STATUS': self.handle_event_status,
'NOTIFY': self.handle_event_notify,
'MSG': self.handle_event_msg,
'MSGERROR': self.handle_event_msgerror,
'MSGSENT': self.handle_event_msgsent,
'MSGNOTSENT': self.handle_event_msgnotsent,
@ -2716,10 +2842,9 @@ class Interface:
'''joins the room immediately'''
if self.msg_win_mgr.has_window(room_jid, account) and \
gajim.gc_connected[account][room_jid]:
gc_ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
win = gc_ctrl.parent_win
win = self.msg_win_mgr.get_window(room_jid, account)
win.window.present()
win.set_active_tab(gc_ctrl)
win.set_active_tab(room_jid, account)
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
return
minimized_control_exists = False
@ -2744,9 +2869,9 @@ class Interface:
not self.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_control = self.msg_win_mgr.get_gc_control(room_jid, account)
gc_control.parent_win.set_active_tab(gc_control)
gc_control.parent_win.window.present()
gc_win = self.msg_win_mgr.get_window(room_jid, account)
gc_win.set_active_tab(room_jid, account)
gc_win.window.present()
gajim.connections[account].join_gc(nick, room_jid, password)
if password:
gajim.gc_passwords[room_jid] = password
@ -2770,42 +2895,18 @@ class Interface:
contact = gajim.contacts.contact_from_gc_contact(gc_contact)
type_ = message_control.TYPE_PM
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_)
conn = gajim.connections[account]
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)
chat_control = \
PrivateChatControl(mw, gc_contact, contact, account, session)
mw.new_tab(chat_control)
if len(gajim.events.get_events(account, fjid)):
# We call this here to avoid race conditions with widget validation
session.control.read_queue()
chat_control.read_queue()
def new_chat(self, session, contact, account, resource = None):
def new_chat(self, contact, account, resource = None, session = None):
# Get target window, create a control, and associate it with the window
type_ = message_control.TYPE_CHAT
@ -2821,7 +2922,9 @@ class Interface:
mw.new_tab(chat_control)
return chat_control
if len(gajim.events.get_events(account, fjid)):
# We call this here to avoid race conditions with widget validation
chat_control.read_queue()
def new_chat_from_jid(self, account, fjid):
jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
@ -2830,68 +2933,35 @@ class Interface:
if not contact:
added_to_roster = True
contact = self.roster.add_to_not_in_the_roster(account, jid,
resource=resource)
session = gajim.connections[account].get_or_create_session(fjid, None)
resource = resource)
if not self.msg_win_mgr.has_window(fjid, account):
session.control = self.new_chat(session, contact, account,
resource=resource)
if len(gajim.events.get_events(account, fjid)):
session.control.read_queue()
mw = session.control.parent_win
mw.set_active_tab(session.control)
self.new_chat(contact, account, resource = resource)
mw = self.msg_win_mgr.get_window(fjid, account)
mw.set_active_tab(fjid, account)
mw.window.present()
# For JEP-0172
if added_to_roster:
session.control.user_nick = gajim.nicks[account]
mc = mw.get_control(fjid, account)
mc.user_nick = gajim.nicks[account]
def on_open_chat_window(self, widget, contact, account, resource=None,
session=None):
def on_open_chat_window(self, widget, contact, account, resource = None,
session = None):
# Get the window containing the chat
fjid = contact.jid
if resource:
fjid += '/' + resource
conn = gajim.connections[account]
if not session and contact.jid in conn.sessions:
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()
win = self.msg_win_mgr.get_window(fjid, account)
if not win:
self.new_chat(contact, account, resource = resource, session = session)
win = self.msg_win_mgr.get_window(fjid, account)
ctrl = win.get_control(fjid, account)
# last message is long time ago
gajim.last_message_time[account][session.control.get_full_jid()] = 0
win = session.control.parent_win
win.set_active_tab(session.control)
if conn.is_zeroconf and conn.status in ('offline', 'invisible'):
for ctrl in win.get_controls(fjid, account):
ctrl.got_disconnected()
gajim.last_message_time[account][ctrl.get_full_jid()] = 0
win.set_active_tab(fjid, account)
if gajim.connections[account].is_zeroconf and \
gajim.connections[account].status in ('offline', 'invisible'):
win.get_control(fjid, account).got_disconnected()
win.window.present()
################################################################################
@ -3082,7 +3152,7 @@ class Interface:
'password': password,
'nick': nick
}
place_found = False
place_found = False
index = 0
# check for duplicate entry and respect alpha order
for bookmark in gajim.connections[account].bookmarks:
@ -3106,18 +3176,6 @@ class Interface:
_('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):
try:
from ipython_view import IPythonView

View File

@ -43,7 +43,6 @@ import cell_renderer_image
from common import gajim
from common import helpers
from common.stanza_session import StanzaSession
from chat_control import ChatControl
from chat_control import ChatControlBase
@ -58,7 +57,7 @@ C_TYPE, # type of the row ('contact' or 'role')
C_TEXT, # text shown in the cellrenderer
C_AVATAR, # avatar of the contact
) = range(5)
def set_renderer_color(treeview, renderer, set_background = True):
'''set style for group row, using PRELIGHT system color'''
if set_background:
@ -121,7 +120,7 @@ class PrivateChatControl(ChatControl):
def __init__(self, parent_win, gc_contact, contact, account, session):
room_jid = contact.jid.split('/')[0]
room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
room_ctrl = gajim.interface.msg_win_mgr.get_control(room_jid, account)
if gajim.interface.minimized_controls[account].has_key(room_jid):
room_ctrl = gajim.interface.minimized_controls[account][room_jid]
self.room_name = room_ctrl.name
@ -223,9 +222,6 @@ class GroupchatControl(ChatControlBase):
self.new_nick = ''
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')
self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_widget('banner_eventbox'),
@ -460,9 +456,9 @@ class GroupchatControl(ChatControlBase):
'state_muc_msg_color')
if color_name:
color = gtk.gdk.colormap_get_system().alloc_color(color_name)
label_str = self.name
# count waiting highlighted messages
unread = ''
num_unread = self.get_nb_unread()
@ -623,8 +619,9 @@ class GroupchatControl(ChatControlBase):
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
# We print if window is opened
if session.control:
session.control.print_conversation(msg, tim = tim, xhtml = xhtml)
pm_control = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
if pm_control:
pm_control.print_conversation(msg, tim = tim, xhtml = xhtml)
return
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
@ -648,7 +645,7 @@ class GroupchatControl(ChatControlBase):
self.parent_win.show_title()
self.parent_win.redraw_tab(self)
else:
self._start_private_message(nick)
self._start_private_message(nick, session)
# Scroll to line
self.list_treeview.expand_row(path[0:1], False)
self.list_treeview.scroll_to_cell(path)
@ -873,9 +870,10 @@ class GroupchatControl(ChatControlBase):
for nick in nick_list:
# Update pm chat window
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,
nick)
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(fjid, self.account):
if ctrl:
gc_contact.show = 'offline'
gc_contact.status = ''
ctrl.update_ui()
@ -900,17 +898,18 @@ class GroupchatControl(ChatControlBase):
# Recalculate column width for ellipsizin
self.list_treeview.columns_autosize()
def on_send_pm(self, widget=None, model=None, iter=None, nick=None,
msg=None):
'''opens a chat window and if msg is not None sends private message to a
def on_send_pm(self, widget = None, model = None, iter = None, nick = None,
msg = None):
'''opens a chat window and msg is not None sends private message to a
contact in a room'''
if nick is None:
nick = model[iter][C_NICK].decode('utf-8')
fjid = gajim.construct_fjid(self.room_jid, nick) # 'fake' jid
ctrl = self._start_private_message(nick)
self._start_private_message(nick)
if msg:
ctrl.send_message(msg)
gajim.interface.msg_win_mgr.get_control(fjid, self.account).\
send_message(msg)
def on_send_file(self, widget, gc_contact):
'''sends a file to a contact in the room'''
@ -1616,7 +1615,7 @@ class GroupchatControl(ChatControlBase):
for nick in nick_list:
# Update pm chat window
fjid = self.room_jid + '/' + nick
ctrl = gajim.interface.msg_win_mgr.get_gc_control(fjid, self.account)
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
if ctrl:
contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
contact.show = 'offline'
@ -2029,7 +2028,7 @@ class GroupchatControl(ChatControlBase):
menu.show_all()
menu.popup(None, None, None, event.button, event.time)
def _start_private_message(self, nick):
def _start_private_message(self, nick, session = None):
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
nick_jid = gc_c.get_full_jid()
@ -2037,14 +2036,9 @@ class GroupchatControl(ChatControlBase):
if not win:
gajim.interface.new_private_chat(gc_c, self.account)
win = gajim.interface.msg_win_mgr.get_window(nick_jid, self.account)
ctrl = win.get_controls(nick_jid, self.account)[0]
win.set_active_tab(ctrl)
win.set_active_tab(nick_jid, self.account)
win.window.present()
return ctrl
def on_row_activated(self, widget, path):
'''When an iter is activated (dubblick or single click if gnome is set
this way'''
@ -2103,7 +2097,7 @@ class GroupchatControl(ChatControlBase):
return
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
else:
model = widget.get_model()

View File

@ -117,24 +117,25 @@ class MessageControl:
return len(gajim.events.get_events(self.account, self.contact.jid))
def set_session(self, session):
oldsession = None
if hasattr(self, 'session'):
oldsession = self.session
if oldsession and session == oldsession:
if hasattr(self, 'session') and session == self.session:
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
if session:
session.control = self
if oldsession:
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()
if was_encrypted:
self.print_esession_details()
def send_message(self, message, keyID = '', type = 'chat',
chatstate = None, msg_id = None, composing_xep = None, resource = None,
@ -144,6 +145,12 @@ class MessageControl:
jid = self.contact.jid
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 type == 'chat' and isinstance(message, unicode):
d = {'kwargs':{'keyID':keyID, 'type':type,
@ -151,7 +158,7 @@ class MessageControl:
'composing_xep':composing_xep, 'resource':self.resource,
'user_nick':user_nick, 'session':self.session,
'original_message':original_message}, 'account':self.account}
new_msg = gajim.otr_module.otrl_message_sending(
gajim.connections[self.account].otr_userstates,
(gajim.otr_ui_ops, d),

View File

@ -154,9 +154,8 @@ class MessageWindow(object):
def get_num_controls(self):
n = 0
for jid_dict in self._controls.values():
for dict in jid_dict.values():
n += len(dict)
for dict in self._controls.values():
n += len(dict)
return n
def resize(self, width, height):
@ -209,11 +208,7 @@ class MessageWindow(object):
if not self._controls.has_key(control.account):
self._controls[control.account] = {}
fjid = control.get_full_jid()
if not self._controls[control.account].has_key(fjid):
self._controls[control.account][fjid] = {}
self._controls[control.account][fjid][control.session.thread_id] = control
self._controls[control.account][fjid] = control
if self.get_num_controls() == 2:
# is first conversation_textview scrolled down ?
@ -417,7 +412,8 @@ class MessageWindow(object):
else:
gtkgui_helpers.set_unset_urgency_hint(self.window, False)
def set_active_tab(self, ctrl):
def set_active_tab(self, jid, acct):
ctrl = self._controls[acct][jid]
ctrl_page = self.notebook.page_num(ctrl.widget)
self.notebook.set_current_page(ctrl_page)
@ -449,13 +445,7 @@ class MessageWindow(object):
self.notebook.remove_page(self.notebook.page_num(ctrl.widget))
fjid = ctrl.get_full_jid()
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]
del self._controls[ctrl.account][fjid]
if len(self._controls[ctrl.account]) == 0:
del self._controls[ctrl.account]
@ -559,16 +549,16 @@ class MessageWindow(object):
for ctrl in self.controls():
ctrl.update_tags()
def get_control(self, key, acct, thread_id):
def get_control(self, key, acct):
'''Return the MessageControl for jid or n, where n is a notebook page index.
When key is an int index acct and thread_id may be None'''
When key is an int index acct may be None'''
if isinstance(key, str):
key = unicode(key, 'utf-8')
if isinstance(key, unicode):
jid = key
try:
return self._controls[acct][jid][thread_id]
return self._controls[acct][jid]
except:
return None
else:
@ -579,45 +569,24 @@ class MessageWindow(object):
nth_child = notebook.get_nth_page(page_num)
return self._widget_to_control(nth_child)
def get_gc_control(self, jid, acct):
return self.get_control(jid, acct, 'gc')
def get_controls(self, jid, acct):
try:
return self._controls[acct][jid].values()
except KeyError:
return []
def change_key(self, old_jid, new_jid, acct):
'''Change the JID key of a control'''
'''Change the key of a control'''
try:
# Check if controls exists
ctrls = self._controls[acct][old_jid]
except KeyError:
# Check if control exists
ctrl = self._controls[acct][old_jid]
except:
return
self._controls[acct][new_jid] = ctrls
self._controls[acct][new_jid] = self._controls[acct][old_jid]
del self._controls[acct][old_jid]
if old_jid in gajim.last_message_time[acct]:
gajim.last_message_time[acct][new_jid] = \
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):
for jid_dict in self._controls.values():
for ctrl_dict in jid_dict.values():
for ctrl in ctrl_dict.values():
yield ctrl
for ctrl_dict in self._controls.values():
for ctrl in ctrl_dict.values():
yield ctrl
def move_to_next_unread_tab(self, forward):
ind = self.notebook.get_current_page()
@ -635,7 +604,7 @@ class MessageWindow(object):
ind = ind - 1
if ind < 0:
ind = self.notebook.get_n_pages() - 1
ctrl = self.get_control(ind, None, None)
ctrl = self.get_control(ind, None)
if ctrl.get_nb_unread() > 0:
found = True
break # found
@ -827,19 +796,8 @@ class MessageWindowMgr(gobject.GObject):
def get_window(self, jid, acct):
for win in self.windows():
try:
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)
if win.get_control(jid, acct):
return win
return None
def has_window(self, jid, acct):
@ -972,11 +930,11 @@ class MessageWindowMgr(gobject.GObject):
del self._windows[k]
return
def get_control(self, jid, acct, session):
def get_control(self, jid, acct):
'''Amongst all windows, return the MessageControl for jid'''
win = self.get_window(jid, acct)
if win:
return win.get_control(jid, acct, session)
return win.get_control(jid, acct)
return None
def get_controls(self, type = None, acct = None):
@ -988,14 +946,6 @@ class MessageWindowMgr(gobject.GObject):
ctrls.append(c)
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):
for w in self._windows.values():
yield w

View File

@ -53,7 +53,8 @@ try:
except:
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'''
if event == 'gc_message_received':
return True
@ -64,10 +65,8 @@ def get_show_in_roster(event, account, contact, session = None):
if gajim.config.get_per('notifications', str(num), 'roster') == 'no':
return False
if event == 'message_received':
if session:
if session.control:
return False
elif helpers.get_chat_control(account, contact):
chat_control = helpers.get_chat_control(account, contact)
if chat_control:
return False
return True

View File

@ -53,8 +53,6 @@ from common import i18n
from message_window import MessageWindowMgr
from session import ChatControlSession
from common import dbus_support
if dbus_support.supported:
from music_track_listener import MusicTrackListener
@ -1135,51 +1133,6 @@ class RosterWindow:
self.model[child_iter][C_AVATAR_PIXBUF] = scaled_pixbuf
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):
'''Draw contact, account and groups of given jid
Show contact if it has pending events
@ -1481,9 +1434,9 @@ class RosterWindow:
session = gajim.connections[account].make_new_session(jid)
tim = time.localtime(float(result[2]))
session.roster_message(jid, result[1], tim, msg_type='chat',
msg_id=result[0])
self.on_message(jid, result[1], tim, account, msg_type = 'chat',
msg_id = result[0], session = session)
elif (time.time() - result[2]) > 2592000:
# 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
@ -1543,10 +1496,10 @@ class RosterWindow:
gajim.transport_avatar[account][host] = [contact1.jid]
else:
gajim.transport_avatar[account][host].append(contact1.jid)
# If we already have chat windows opened, update them with new contact
# If we already have a chat window opened, update it with new contact
# instance
for chat_control in gajim.interface.msg_win_mgr.get_chat_controls(ji, account):
chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
if chat_control:
chat_control.contact = contact1
def enable_syncing_status_msg_from_current_music_track(self, enabled):
@ -1877,10 +1830,9 @@ class RosterWindow:
account):
win = gajim.interface.msg_win_mgr.get_window(jid_with_resource,
account)
for ctrl in win.get_controls(jid_with_resource, account):
ctrl.update_ui()
win.redraw_tab(ctrl)
ctrl = win.get_control(jid_with_resource, account)
ctrl.update_ui()
win.redraw_tab(ctrl)
gajim.contacts.remove_contact(account, contact)
elif contact.jid == gajim.get_jid_from_account(account) and show == 'offline':
# Our SelfContact went offline. Remove him
@ -1889,20 +1841,19 @@ class RosterWindow:
# print status in chat window and update status/GPG image
if gajim.interface.msg_win_mgr.has_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)
for ctrl in win.get_controls(contact.jid, account):
ctrl.contact = gajim.contacts.get_contact_with_highest_priority(
account, contact.jid)
ctrl.update_ui()
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)
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
if gajim.interface.status_sent_to_users.has_key(account) and \
@ -2022,6 +1973,119 @@ class RosterWindow:
gajim.config.get('roster_width'),
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):
'''close all the windows in the given dictionary'''
for w in dic.values():
@ -2087,8 +2151,8 @@ class RosterWindow:
def on_quit_request(self, widget = None):
''' user want to quit. Check if he should be warned about messages
pending. Terminate all sessions and send offline to all connected
account. We do NOT really quit gajim here '''
pending. Send offline to all connected account. We do NOT really quit
gajim here '''
accounts = gajim.connections.keys()
get_msg = False
for acct in accounts:
@ -2105,7 +2169,7 @@ class RosterWindow:
unread = gajim.events.get_nb_events()
if not gajim.config.get('notify_on_all_muc_messages'):
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
recent = False
@ -2128,15 +2192,13 @@ class RosterWindow:
self.quit_on_next_offline = 0
for acct in accounts:
gajim.connections[acct].terminate_sessions()
if gajim.connections[acct].connected:
self.quit_on_next_offline += 1
self.send_status(acct, 'offline', message)
if not self.quit_on_next_offline:
self.quit_gtkgui_interface()
################################################################################
### Menu and GUI callbacks
### FIXME: order callbacks in itself...
@ -2568,8 +2630,9 @@ class RosterWindow:
u.name = new_text
gajim.connections[account].update_contact(jid, new_text, u.groups)
self.draw_contact(jid, account)
# Update opened chats
for ctrl in gajim.interface.msg_win_mgr.get_controls(jid, account):
# Update opened chat
ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
if ctrl:
ctrl.update_ui()
win = gajim.interface.msg_win_mgr.get_window(jid, account)
win.redraw_tab(ctrl)
@ -2656,7 +2719,8 @@ class RosterWindow:
keyID = keyID[0]
keys[contact.jid] = keyID
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(contact.jid, account):
if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account)
ctrl.update_ui()
keys_str = ''
for jid in keys:
@ -2802,7 +2866,7 @@ class RosterWindow:
ctrl.account, ctrl.type_id)
ctrl.parent_win = mw
mw.new_tab(ctrl)
mw.set_active_tab(ctrl)
mw.set_active_tab(jid, account)
mw.window.present()
del gajim.interface.minimized_controls[account][jid]
@ -2965,7 +3029,7 @@ class RosterWindow:
x_min = 0
if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
not event.state & gtk.gdk.CONTROL_MASK:
# Don't handle double click if we press icon of a metacontact
# Don't handle dubble click if we press icon of a metacontact
titer = model.get_iter(path)
if x > x_min and x < x_min + 27 and type_ == 'contact' and \
model.iter_has_child(titer):
@ -3232,19 +3296,7 @@ class RosterWindow:
def on_profile_avatar_menuitem_activate(self, widget, 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):
'''Execute command. Full JID needed; if it is other contact,
resource is necessary. Widget is unnecessary, only to be
@ -3295,8 +3347,8 @@ class RosterWindow:
self.show_treeview_menu(event)
def on_row_activated(self, widget, path):
'''When an iter is activated (double-click or single click if gnome is
set this way'''
'''When an iter is activated (dubblick or single click if gnome is set
this way'''
model = self.modelfilter
account = model[path][C_ACCOUNT].decode('utf-8')
type_ = model[path][C_TYPE]
@ -3960,7 +4012,6 @@ class RosterWindow:
start = '[' + str(nb_unread) + '] '
elif nb_unread == 1:
start = '* '
self.window.set_title(start + 'Gajim')
gtkgui_helpers.set_unset_urgency_hint(self.window, nb_unread)
@ -4813,7 +4864,7 @@ class RosterWindow:
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
def make_contact_menu(self, event, titer):
'''Make contact\'s popup menu'''
model = self.modelfilter
@ -4958,10 +5009,6 @@ class RosterWindow:
execute_command_menuitem = xml.get_widget(
'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
blocked = False
if jid in gajim.connections[account].blocked_contacts:

View File

@ -1,324 +0,0 @@
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)

View File

@ -1,427 +0,0 @@
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()

View File

@ -122,9 +122,11 @@ class VcardWindow:
jid = self.contact.jid
# Update roster
gajim.interface.roster.draw_avatar(jid, self.account)
# Update chat windows
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(jid, self.account):
if ctrl.type_id != message_control.TYPE_GC:
# Update chat window
if gajim.interface.msg_win_mgr.has_window(jid, self.account):
win = gajim.interface.msg_win_mgr.get_window(jid, self.account)
ctrl = win.get_control(jid, self.account)
if win and ctrl.type_id != message_control.TYPE_GC:
ctrl.show_avatar()
def on_vcard_information_window_destroy(self, widget):