merged in session_centric branch
This commit is contained in:
parent
d69d5266b5
commit
2162c3730a
|
@ -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,18 +693,20 @@ 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.account, self.contact, self.session)
|
||||
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)
|
||||
|
@ -722,11 +724,10 @@ 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, ctrl) # Enabled Urgent hint
|
||||
self.parent_win.show_title(True, self) # Enabled Urgent hint
|
||||
else:
|
||||
self.parent_win.show_title(False, ctrl) # Disabled Urgent hint
|
||||
self.parent_win.show_title(False, self) # Disabled Urgent hint
|
||||
|
||||
def toggle_emoticons(self):
|
||||
'''hide show emoticons_button and make sure emoticons_menu is always there
|
||||
|
@ -856,7 +857,7 @@ class ChatControlBase(MessageControl):
|
|||
|
||||
# we don't want to always resize in height the message_textview
|
||||
# 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
|
||||
|
@ -867,13 +868,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)
|
||||
|
@ -883,10 +884,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
|
||||
|
@ -916,27 +917,26 @@ 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 gajim.events.remove_events(self.account, self.get_full_jid(),
|
||||
types = types_list):
|
||||
if not self.session.remove_events(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_control(
|
||||
groupchat_control = gajim.interface.msg_win_mgr.get_gc_control(
|
||||
room_jid, self.account)
|
||||
if room_jid in gajim.interface.minimized_controls[self.account]:
|
||||
groupchat_control = \
|
||||
|
@ -947,15 +947,14 @@ class ChatControlBase(MessageControl):
|
|||
if contact:
|
||||
gajim.interface.roster.draw_contact(room_jid, self.account)
|
||||
groupchat_control.draw_contact(nick)
|
||||
mw = gajim.interface.msg_win_mgr.get_window(room_jid, self.account)
|
||||
if mw:
|
||||
mw.redraw_tab(groupchat_control)
|
||||
if groupchat_control.parent_win:
|
||||
groupchat_control.parent_win.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
|
||||
|
@ -1053,7 +1052,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()
|
||||
|
@ -1080,7 +1079,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
|
||||
|
@ -1126,12 +1125,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:
|
||||
|
@ -1154,17 +1153,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
|
||||
|
@ -1176,14 +1175,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)
|
||||
|
@ -1268,15 +1267,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
|
||||
|
@ -1363,7 +1362,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
|
||||
|
||||
|
@ -1388,12 +1387,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.' %\
|
||||
|
@ -1549,7 +1548,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
|
||||
|
@ -1601,7 +1600,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):
|
||||
|
@ -1625,13 +1624,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:
|
||||
|
@ -1672,7 +1671,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:
|
||||
|
@ -1700,7 +1699,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:
|
||||
|
@ -1719,7 +1718,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')
|
||||
|
@ -1760,7 +1759,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,
|
||||
|
@ -1774,7 +1773,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\
|
||||
|
@ -1814,22 +1813,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',
|
||||
|
@ -1885,7 +1884,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
|
||||
|
@ -1914,7 +1913,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
|
||||
|
@ -1926,7 +1925,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
|
||||
|
@ -1946,7 +1945,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
|
||||
|
@ -1963,10 +1962,15 @@ 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)
|
||||
|
@ -1986,6 +1990,8 @@ 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
|
||||
|
@ -2173,12 +2179,6 @@ 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,8 +2203,13 @@ 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
|
||||
if is_pm:
|
||||
# Is it a pm ?
|
||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(jid)
|
||||
control = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
|
||||
self.account)
|
||||
if control and control.type_id == message_control.TYPE_GC:
|
||||
control.update_ui()
|
||||
control.parent_win.show_title()
|
||||
typ = 'pm'
|
||||
|
@ -2265,18 +2270,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()
|
||||
|
@ -2307,7 +2312,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):
|
||||
|
@ -2317,7 +2322,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'''
|
||||
|
|
|
@ -49,7 +49,8 @@ if dbus_support.supported:
|
|||
import dbus
|
||||
from music_track_listener import MusicTrackListener
|
||||
|
||||
from common.stanza_session import EncryptedStanzaSession
|
||||
from session import ChatControlSession
|
||||
import tictactoe
|
||||
|
||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||
'invisible', 'error']
|
||||
|
@ -994,7 +995,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()
|
||||
|
||||
|
@ -1046,6 +1047,7 @@ 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:
|
||||
|
@ -1261,7 +1263,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):
|
||||
|
@ -1291,7 +1293,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)
|
||||
|
@ -1499,6 +1501,8 @@ 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()
|
||||
|
@ -1506,8 +1510,23 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
if not mtype:
|
||||
mtype = 'normal'
|
||||
|
||||
if not mtype == 'groupchat':
|
||||
session = self.get_session(frm, thread_id, mtype)
|
||||
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 thread_id and not session.received_thread_id:
|
||||
session.received_thread_id = True
|
||||
|
@ -1516,66 +1535,57 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
if msg.getTag('event') is not None:
|
||||
self._pubsubEventCB(con, msg)
|
||||
return
|
||||
# check if the message is a xep70-confirmation-request
|
||||
if msg.getTag('confirm') and msg.getTag('confirm').namespace == \
|
||||
common.xmpp.NS_HTTP_AUTH:
|
||||
|
||||
# check if the message is a XEP-0070 confirmation request
|
||||
if msg.getTag('confirm', namespace=common.xmpp.NS_HTTP_AUTH):
|
||||
self._HttpAuthCB(con, msg)
|
||||
return
|
||||
if msg.getTag('feature') and msg.getTag('feature').namespace == \
|
||||
common.xmpp.NS_FEATURE:
|
||||
|
||||
# check if the message is a XEP-0020 feature negotiation request
|
||||
if msg.getTag('feature', namespace=common.xmpp.NS_FEATURE):
|
||||
if gajim.HAVE_PYCRYPTO:
|
||||
self._FeatureNegCB(con, msg, session)
|
||||
return
|
||||
if msg.getTag('init') and msg.getTag('init').namespace == \
|
||||
common.xmpp.NS_ESESSION_INIT:
|
||||
if 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))
|
||||
|
||||
e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
|
||||
if e2e_tag:
|
||||
if msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO):
|
||||
encrypted = True
|
||||
|
||||
try:
|
||||
msg = session.decrypt_stanza(msg)
|
||||
except:
|
||||
self.dispatch('FAILED_DECRYPT', (frm, tim))
|
||||
self.dispatch('FAILED_DECRYPT', (frm, tim, session))
|
||||
|
||||
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
|
||||
|
@ -1589,94 +1599,54 @@ 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)
|
||||
decmsg = decmsg.replace('\x00', '')
|
||||
if decmsg:
|
||||
msgtxt = decmsg
|
||||
encrypted = True
|
||||
msgtxt = decmsg.replace('\x00', '')
|
||||
encrypted = True
|
||||
if mtype == 'error':
|
||||
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
|
||||
self.dispatch_error_message(msg, msgtxt, session, frm, tim, subject)
|
||||
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))
|
||||
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:
|
||||
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)))
|
||||
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))
|
||||
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(
|
||||
|
@ -1690,117 +1660,114 @@ 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)
|
||||
|
||||
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
|
||||
statusCode = msg.getStatusCode()
|
||||
|
||||
def get_session(self, jid, thread_id, type):
|
||||
if not msg.getTag('body'): # no <body>
|
||||
# It could be a config change. See
|
||||
# http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
|
||||
if msg.getTag('x'):
|
||||
if statusCode != []:
|
||||
self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode))
|
||||
return
|
||||
|
||||
# Ignore message from room in which we are not
|
||||
if not self.last_history_time.has_key(jid):
|
||||
return
|
||||
|
||||
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msg.getXHTML(),
|
||||
statusCode))
|
||||
|
||||
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
|
||||
|
||||
if not no_log_for:
|
||||
no_log_for = ''
|
||||
|
||||
no_log_for = no_log_for.split()
|
||||
|
||||
tim_int = int(float(mktime(tim)))
|
||||
|
||||
if self.name not in no_log_for and jid not in no_log_for and not \
|
||||
tim_int <= self.last_history_time[jid] and msgtxt and frm.find('/') >= 0:
|
||||
# if frm.find('/') < 0, it means message comes from room itself
|
||||
# usually it hold description and can be send at each connection
|
||||
# so don't store it in logs
|
||||
try:
|
||||
gajim.logger.write('gc_msg', frm, msgtxt, tim=tim)
|
||||
# save the time we log to avoid duplicate logs
|
||||
self.last_history_time[jid] = tim_int
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
|
||||
|
||||
def dispatch_invite_message(self, invite, frm):
|
||||
item = invite.getTag('invite')
|
||||
jid_from = item.getAttr('from')
|
||||
reason = item.getTagData('reason')
|
||||
item = invite.getTag('password')
|
||||
password = invite.getTagData('password')
|
||||
|
||||
is_continued = False
|
||||
if invite.getTag('invite').getTag('continue'):
|
||||
is_continued = True
|
||||
self.dispatch('GC_INVITATION',(frm, jid_from, reason, password,
|
||||
is_continued))
|
||||
|
||||
def get_or_create_session(self, jid, thread_id):
|
||||
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
|
||||
session = self.find_session(jid, thread_id, type)
|
||||
|
||||
pm = True
|
||||
if not gajim.interface.is_pm_contact(jid, self.name):
|
||||
pm = False
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
|
||||
session = self.find_session(jid, thread_id)
|
||||
|
||||
if session:
|
||||
return session
|
||||
|
||||
if pm:
|
||||
return self.make_new_session(jid, thread_id, type = 'pm')
|
||||
else:
|
||||
# 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
|
||||
return self.make_new_session(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):
|
||||
def find_session(self, jid, thread_id):
|
||||
try:
|
||||
if type == 'chat' and not thread_id:
|
||||
if 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 move_session(self, original_jid, thread_id, to_resource):
|
||||
'''moves a session to another resource.'''
|
||||
session = self.sessions[original_jid][thread_id]
|
||||
|
||||
del self.sessions[original_jid][thread_id]
|
||||
|
||||
new_jid = gajim.get_jid_without_resource(original_jid) + '/' + to_resource
|
||||
session.jid = common.xmpp.JID(new_jid)
|
||||
|
||||
if not new_jid in self.sessions:
|
||||
self.sessions[new_jid] = {}
|
||||
|
||||
self.sessions[new_jid][thread_id] = session
|
||||
|
||||
def find_null_session(self, jid):
|
||||
'''finds all of the sessions between us and jid that jid hasn't sent a thread_id in yet.
|
||||
'''finds all of the sessions between us and a remote jid in which we
|
||||
haven't received a thread_id yet and returns the session that we last
|
||||
sent a message to.'''
|
||||
|
||||
returns the session that we last sent a message to.'''
|
||||
|
||||
sessions_with_jid = self.sessions[jid].values()
|
||||
no_threadid_sessions = filter(lambda s: not s.received_thread_id, sessions_with_jid)
|
||||
sessions = self.sessions[jid].values()
|
||||
no_threadid_sessions = filter(lambda s: not s.received_thread_id, sessions)
|
||||
|
||||
if no_threadid_sessions:
|
||||
no_threadid_sessions.sort(key=lambda s: s.last_send)
|
||||
|
@ -1808,8 +1775,16 @@ returns the session that we last sent a message to.'''
|
|||
else:
|
||||
return None
|
||||
|
||||
def make_new_session(self, jid, thread_id = None, type = 'chat'):
|
||||
sess = EncryptedStanzaSession(self, common.xmpp.JID(jid), thread_id, type)
|
||||
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)
|
||||
|
||||
if not jid in self.sessions:
|
||||
self.sessions[jid] = {}
|
||||
|
@ -2303,7 +2278,7 @@ returns the session that we last 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)
|
||||
|
|
|
@ -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,
|
||||
optionnaly only from given type
|
||||
optionally 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: []
|
||||
optionnaly only from given type'''
|
||||
optionally only from given type'''
|
||||
if not self._events.has_key(account):
|
||||
return []
|
||||
if not jid:
|
||||
|
|
|
@ -178,7 +178,7 @@ gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI,
|
|||
xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER,
|
||||
xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED,
|
||||
'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING,
|
||||
xmpp.NS_TIME_REVISED]
|
||||
xmpp.NS_TIME_REVISED, xmpp.NS_GAMING]
|
||||
# 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_control(room_jid, account):
|
||||
if interface.msg_win_mgr.get_gc_control(room_jid, account):
|
||||
# It's a pm, so if we have real jid it's in contact.jid
|
||||
gc_contact = contacts.get_gc_contact(account, room_jid, nick)
|
||||
if not gc_contact:
|
||||
|
|
|
@ -32,10 +32,23 @@ 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)])
|
||||
|
||||
|
@ -70,7 +83,7 @@ class StanzaSession(object):
|
|||
def cancelled_negotiation(self):
|
||||
'''A negotiation has been cancelled, so reset this session to its default state.'''
|
||||
|
||||
if hasattr(self, 'control'):
|
||||
if self.control:
|
||||
self.control.on_cancel_session_negotiation()
|
||||
|
||||
self.status = None
|
||||
|
@ -132,8 +145,6 @@ 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 = {}
|
||||
|
||||
|
@ -767,7 +778,7 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.status = 'active'
|
||||
self.enable_encryption = True
|
||||
|
||||
if hasattr(self, 'control'):
|
||||
if self.control:
|
||||
self.control.print_esession_details()
|
||||
|
||||
def final_steps_alice(self, form):
|
||||
|
@ -787,15 +798,14 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
|
||||
self.do_retained_secret(k, srs)
|
||||
|
||||
# don't need to calculate ks_s here
|
||||
|
||||
# ks_s doesn't need to be calculated here
|
||||
self.kc_s, self.km_s, self.ks_s = self.generate_initiator_keys(k)
|
||||
self.kc_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
|
||||
|
@ -803,7 +813,7 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
self.status = 'active'
|
||||
self.enable_encryption = True
|
||||
|
||||
if hasattr(self, 'control'):
|
||||
if self.control:
|
||||
self.control.print_esession_details()
|
||||
|
||||
# calculate and store the new retained secret
|
||||
|
@ -891,17 +901,6 @@ otherwise, list the fields we haven't implemented'''
|
|||
# preventing falsified messages from going through.
|
||||
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
|
||||
|
|
|
@ -658,7 +658,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
|
|||
frm = unicode(frm)
|
||||
jid = frm
|
||||
|
||||
session = self.get_session(frm, thread_id, mtype)
|
||||
session = self.get_or_create_session(frm, thread_id, mtype)
|
||||
|
||||
if thread_id and not session.received_thread_id:
|
||||
session.received_thread_id = True
|
||||
|
@ -794,7 +794,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
|
|||
|
||||
raise common.xmpp.NodeProcessed
|
||||
|
||||
def get_session(self, jid, thread_id, type):
|
||||
def get_or_create_session(self, jid, thread_id, type):
|
||||
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
|
||||
session = self.find_session(jid, thread_id, type)
|
||||
|
||||
|
|
336
src/gajim.py
336
src/gajim.py
|
@ -241,6 +241,7 @@ from chat_control import ChatControl
|
|||
from groupchat_control import GroupchatControl
|
||||
from groupchat_control import PrivateChatControl
|
||||
from atom_window import AtomWindow
|
||||
from session import ChatControlSession
|
||||
|
||||
import common.sleepy
|
||||
|
||||
|
@ -257,7 +258,7 @@ from common.xmpp import Message as XmppMessage
|
|||
|
||||
try:
|
||||
import otr, otr_windows
|
||||
|
||||
|
||||
gajim.otr_module = otr
|
||||
gajim.otr_windows = otr_windows
|
||||
except ImportError:
|
||||
|
@ -636,7 +637,7 @@ class Interface:
|
|||
title = data[1]
|
||||
prompt = data[2]
|
||||
proposed_nick = data[3]
|
||||
gc_control = self.msg_win_mgr.get_control(room_jid, account)
|
||||
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||
if not gc_control and \
|
||||
room_jid in self.minimized_controls[account]:
|
||||
gc_control = self.minimized_controls[account][room_jid]
|
||||
|
@ -690,9 +691,10 @@ class Interface:
|
|||
(jid_from, file_props))
|
||||
conn.disconnect_transfer(file_props)
|
||||
return
|
||||
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]))
|
||||
|
||||
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]))
|
||||
|
||||
def handle_event_con_type(self, account, con_type):
|
||||
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'
|
||||
|
@ -922,146 +924,12 @@ class Interface:
|
|||
self.handle_event_gc_notify(account, (jid, array[1], status_message,
|
||||
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_control(jid, account)
|
||||
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||
if not gc_control and \
|
||||
jid in self.minimized_controls[account]:
|
||||
gc_control = self.minimized_controls[account][jid]
|
||||
|
@ -1291,21 +1159,22 @@ class Interface:
|
|||
win.set_values(vcard)
|
||||
|
||||
# show avatar in chat
|
||||
win = None
|
||||
ctrl = None
|
||||
ctrls = []
|
||||
if resource and self.msg_win_mgr.has_window(
|
||||
jid + '/' + resource, account):
|
||||
win = self.msg_win_mgr.get_window(jid + '/' + resource,
|
||||
account)
|
||||
ctrl = win.get_control(jid + '/' + resource, account)
|
||||
ctrls = win.get_controls(jid + '/' + resource, account)
|
||||
elif self.msg_win_mgr.has_window(jid, account):
|
||||
win = self.msg_win_mgr.get_window(jid, account)
|
||||
ctrl = win.get_control(jid, account)
|
||||
if win and ctrl.type_id != message_control.TYPE_GC:
|
||||
ctrl.show_avatar()
|
||||
ctrls = win.get_controls(jid, account)
|
||||
|
||||
for ctrl in ctrls:
|
||||
if ctrl.type_id != message_control.TYPE_GC:
|
||||
ctrl.show_avatar()
|
||||
|
||||
# Show avatar in roster or gc_roster
|
||||
gc_ctrl = self.msg_win_mgr.get_control(jid, account)
|
||||
gc_ctrl = self.msg_win_mgr.get_gc_control(jid, account)
|
||||
if not gc_ctrl and \
|
||||
jid in self.minimized_controls[account]:
|
||||
gc_ctrl = self.minimized_controls[account][jid]
|
||||
|
@ -1362,21 +1231,25 @@ class Interface:
|
|||
|
||||
# Get the window and control for the updated status, this may be a
|
||||
# PrivateChatControl
|
||||
control = self.msg_win_mgr.get_control(room_jid, account)
|
||||
control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||
|
||||
if not control and \
|
||||
room_jid in self.minimized_controls[account]:
|
||||
control = self.minimized_controls[account][room_jid]
|
||||
|
||||
if control and control.type_id != message_control.TYPE_GC:
|
||||
if not control or (control and control.type_id != message_control.TYPE_GC):
|
||||
return
|
||||
if control:
|
||||
control.chg_contact_status(nick, show, status, array[4], array[5],
|
||||
array[6], array[7], array[8], array[9], array[10], array[11])
|
||||
|
||||
ctrl = self.msg_win_mgr.get_control(fjid, account)
|
||||
control.chg_contact_status(nick, show, status, array[4], array[5],
|
||||
array[6], array[7], array[8], array[9], array[10], array[11])
|
||||
|
||||
# print status in chat window and update status/GPG image
|
||||
if ctrl:
|
||||
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):
|
||||
statusCode = array[9]
|
||||
if '303' in statusCode:
|
||||
new_nick = array[10]
|
||||
|
@ -1412,7 +1285,7 @@ class Interface:
|
|||
jids = array[0].split('/', 1)
|
||||
room_jid = jids[0]
|
||||
|
||||
gc_control = self.msg_win_mgr.get_control(room_jid, account)
|
||||
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||
if not gc_control and \
|
||||
room_jid in self.minimized_controls[account]:
|
||||
gc_control = self.minimized_controls[account][room_jid]
|
||||
|
@ -1440,7 +1313,7 @@ class Interface:
|
|||
jids = array[0].split('/', 1)
|
||||
jid = jids[0]
|
||||
|
||||
gc_control = self.msg_win_mgr.get_control(jid, account)
|
||||
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||
|
||||
if not gc_control and \
|
||||
jid in self.minimized_controls[account]:
|
||||
|
@ -1505,7 +1378,7 @@ class Interface:
|
|||
jid = array[0]
|
||||
statusCode = array[1]
|
||||
|
||||
gc_control = self.msg_win_mgr.get_control(jid, account)
|
||||
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
|
||||
if not gc_control and \
|
||||
jid in self.minimized_controls[account]:
|
||||
gc_control = self.minimized_controls[account][jid]
|
||||
|
@ -1564,7 +1437,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_control(room_jid, account)
|
||||
ctrl = win.get_gc_control(room_jid, account)
|
||||
win.remove_tab(ctrl, 3)
|
||||
|
||||
dlg = dialogs.InputDialog(_('Password Required'),
|
||||
|
@ -1992,9 +1865,9 @@ class Interface:
|
|||
AtomWindow.newAtomEntry(atom_entry)
|
||||
|
||||
def handle_event_failed_decrypt(self, account, data):
|
||||
jid, tim = data
|
||||
jid, tim, session = data
|
||||
|
||||
ctrl = self.msg_win_mgr.get_control(jid, account)
|
||||
ctrl = self.msg_win_mgr.get_control(jid, account, session.thread_id)
|
||||
if ctrl:
|
||||
ctrl.print_conversation_line('Unable to decrypt message from %s\nIt may have been tampered with.' % (jid), 'status', '', tim)
|
||||
else:
|
||||
|
@ -2133,6 +2006,8 @@ 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()
|
||||
|
@ -2392,7 +2267,6 @@ 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,
|
||||
|
@ -2842,9 +2716,10 @@ class Interface:
|
|||
'''joins the room immediately'''
|
||||
if self.msg_win_mgr.has_window(room_jid, account) and \
|
||||
gajim.gc_connected[account][room_jid]:
|
||||
win = self.msg_win_mgr.get_window(room_jid, account)
|
||||
gc_ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
|
||||
win = gc_ctrl.parent_win
|
||||
win.window.present()
|
||||
win.set_active_tab(room_jid, account)
|
||||
win.set_active_tab(gc_ctrl)
|
||||
dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
|
||||
return
|
||||
minimized_control_exists = False
|
||||
|
@ -2869,9 +2744,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_win = self.msg_win_mgr.get_window(room_jid, account)
|
||||
gc_win.set_active_tab(room_jid, account)
|
||||
gc_win.window.present()
|
||||
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()
|
||||
gajim.connections[account].join_gc(nick, room_jid, password)
|
||||
if password:
|
||||
gajim.gc_passwords[room_jid] = password
|
||||
|
@ -2895,18 +2770,42 @@ 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_)
|
||||
|
||||
chat_control = \
|
||||
PrivateChatControl(mw, gc_contact, contact, account, session)
|
||||
mw.new_tab(chat_control)
|
||||
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)
|
||||
|
||||
if len(gajim.events.get_events(account, fjid)):
|
||||
# We call this here to avoid race conditions with widget validation
|
||||
chat_control.read_queue()
|
||||
session.control.read_queue()
|
||||
|
||||
def new_chat(self, contact, account, resource = None, session = None):
|
||||
def new_chat(self, session, contact, account, resource = None):
|
||||
# Get target window, create a control, and associate it with the window
|
||||
type_ = message_control.TYPE_CHAT
|
||||
|
||||
|
@ -2922,9 +2821,7 @@ class Interface:
|
|||
|
||||
mw.new_tab(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()
|
||||
return chat_control
|
||||
|
||||
def new_chat_from_jid(self, account, fjid):
|
||||
jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
|
||||
|
@ -2933,35 +2830,68 @@ class Interface:
|
|||
if not contact:
|
||||
added_to_roster = True
|
||||
contact = self.roster.add_to_not_in_the_roster(account, jid,
|
||||
resource = resource)
|
||||
resource=resource)
|
||||
|
||||
session = gajim.connections[account].get_or_create_session(fjid, None)
|
||||
|
||||
if not self.msg_win_mgr.has_window(fjid, account):
|
||||
self.new_chat(contact, account, resource = resource)
|
||||
mw = self.msg_win_mgr.get_window(fjid, account)
|
||||
mw.set_active_tab(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)
|
||||
mw.window.present()
|
||||
# For JEP-0172
|
||||
if added_to_roster:
|
||||
mc = mw.get_control(fjid, account)
|
||||
mc.user_nick = gajim.nicks[account]
|
||||
session.control.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
|
||||
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)
|
||||
|
||||
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()
|
||||
|
||||
# last message is long time ago
|
||||
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()
|
||||
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()
|
||||
|
||||
win.window.present()
|
||||
|
||||
################################################################################
|
||||
|
@ -3152,7 +3082,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:
|
||||
|
@ -3176,6 +3106,18 @@ 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
|
||||
|
|
|
@ -43,6 +43,7 @@ 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
|
||||
|
@ -57,7 +58,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:
|
||||
|
@ -120,7 +121,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_control(room_jid, account)
|
||||
room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
|
||||
if gajim.interface.minimized_controls[account].has_key(room_jid):
|
||||
room_ctrl = gajim.interface.minimized_controls[account][room_jid]
|
||||
self.room_name = room_ctrl.name
|
||||
|
@ -222,6 +223,9 @@ 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'),
|
||||
|
@ -456,9 +460,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()
|
||||
|
@ -619,9 +623,8 @@ class GroupchatControl(ChatControlBase):
|
|||
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
|
||||
|
||||
# We print if window is opened
|
||||
pm_control = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
|
||||
if pm_control:
|
||||
pm_control.print_conversation(msg, tim = tim, xhtml = xhtml)
|
||||
if session.control:
|
||||
session.control.print_conversation(msg, tim = tim, xhtml = xhtml)
|
||||
return
|
||||
|
||||
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
|
||||
|
@ -645,7 +648,7 @@ class GroupchatControl(ChatControlBase):
|
|||
self.parent_win.show_title()
|
||||
self.parent_win.redraw_tab(self)
|
||||
else:
|
||||
self._start_private_message(nick, session)
|
||||
self._start_private_message(nick)
|
||||
# Scroll to line
|
||||
self.list_treeview.expand_row(path[0:1], False)
|
||||
self.list_treeview.scroll_to_cell(path)
|
||||
|
@ -870,10 +873,9 @@ 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)
|
||||
if ctrl:
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(fjid, self.account):
|
||||
gc_contact.show = 'offline'
|
||||
gc_contact.status = ''
|
||||
ctrl.update_ui()
|
||||
|
@ -898,18 +900,17 @@ 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 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 if 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
|
||||
|
||||
self._start_private_message(nick)
|
||||
ctrl = self._start_private_message(nick)
|
||||
if msg:
|
||||
gajim.interface.msg_win_mgr.get_control(fjid, self.account).\
|
||||
send_message(msg)
|
||||
ctrl.send_message(msg)
|
||||
|
||||
def on_send_file(self, widget, gc_contact):
|
||||
'''sends a file to a contact in the room'''
|
||||
|
@ -1615,7 +1616,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_control(fjid, self.account)
|
||||
ctrl = gajim.interface.msg_win_mgr.get_gc_control(fjid, self.account)
|
||||
if ctrl:
|
||||
contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||
contact.show = 'offline'
|
||||
|
@ -2028,7 +2029,7 @@ class GroupchatControl(ChatControlBase):
|
|||
menu.show_all()
|
||||
menu.popup(None, None, None, event.button, event.time)
|
||||
|
||||
def _start_private_message(self, nick, session = None):
|
||||
def _start_private_message(self, nick):
|
||||
gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
|
||||
nick_jid = gc_c.get_full_jid()
|
||||
|
||||
|
@ -2036,9 +2037,14 @@ 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)
|
||||
win.set_active_tab(nick_jid, self.account)
|
||||
|
||||
ctrl = win.get_controls(nick_jid, self.account)[0]
|
||||
|
||||
win.set_active_tab(ctrl)
|
||||
win.window.present()
|
||||
|
||||
return ctrl
|
||||
|
||||
def on_row_activated(self, widget, path):
|
||||
'''When an iter is activated (dubblick or single click if gnome is set
|
||||
this way'''
|
||||
|
@ -2097,7 +2103,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()
|
||||
|
|
|
@ -117,25 +117,24 @@ class MessageControl:
|
|||
return len(gajim.events.get_events(self.account, self.contact.jid))
|
||||
|
||||
def set_session(self, session):
|
||||
if hasattr(self, 'session') and session == self.session:
|
||||
oldsession = None
|
||||
if hasattr(self, 'session'):
|
||||
oldsession = self.session
|
||||
|
||||
if oldsession and session == oldsession:
|
||||
return
|
||||
|
||||
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 was_encrypted:
|
||||
self.print_esession_details()
|
||||
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()
|
||||
|
||||
def send_message(self, message, keyID = '', type = 'chat',
|
||||
chatstate = None, msg_id = None, composing_xep = None, resource = None,
|
||||
|
@ -145,12 +144,6 @@ 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,
|
||||
|
@ -158,7 +151,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),
|
||||
|
|
|
@ -154,8 +154,9 @@ class MessageWindow(object):
|
|||
|
||||
def get_num_controls(self):
|
||||
n = 0
|
||||
for dict in self._controls.values():
|
||||
n += len(dict)
|
||||
for jid_dict in self._controls.values():
|
||||
for dict in jid_dict.values():
|
||||
n += len(dict)
|
||||
return n
|
||||
|
||||
def resize(self, width, height):
|
||||
|
@ -208,7 +209,11 @@ class MessageWindow(object):
|
|||
if not self._controls.has_key(control.account):
|
||||
self._controls[control.account] = {}
|
||||
fjid = control.get_full_jid()
|
||||
self._controls[control.account][fjid] = control
|
||||
|
||||
if not self._controls[control.account].has_key(fjid):
|
||||
self._controls[control.account][fjid] = {}
|
||||
|
||||
self._controls[control.account][fjid][control.session.thread_id] = control
|
||||
|
||||
if self.get_num_controls() == 2:
|
||||
# is first conversation_textview scrolled down ?
|
||||
|
@ -412,8 +417,7 @@ class MessageWindow(object):
|
|||
else:
|
||||
gtkgui_helpers.set_unset_urgency_hint(self.window, False)
|
||||
|
||||
def set_active_tab(self, jid, acct):
|
||||
ctrl = self._controls[acct][jid]
|
||||
def set_active_tab(self, ctrl):
|
||||
ctrl_page = self.notebook.page_num(ctrl.widget)
|
||||
self.notebook.set_current_page(ctrl_page)
|
||||
|
||||
|
@ -445,7 +449,13 @@ class MessageWindow(object):
|
|||
self.notebook.remove_page(self.notebook.page_num(ctrl.widget))
|
||||
|
||||
fjid = ctrl.get_full_jid()
|
||||
del self._controls[ctrl.account][fjid]
|
||||
thread_id = ctrl.session.thread_id
|
||||
|
||||
del self._controls[ctrl.account][fjid][thread_id]
|
||||
|
||||
if len(self._controls[ctrl.account][fjid]) == 0:
|
||||
del self._controls[ctrl.account][fjid]
|
||||
|
||||
if len(self._controls[ctrl.account]) == 0:
|
||||
del self._controls[ctrl.account]
|
||||
|
||||
|
@ -549,16 +559,16 @@ class MessageWindow(object):
|
|||
for ctrl in self.controls():
|
||||
ctrl.update_tags()
|
||||
|
||||
def get_control(self, key, acct):
|
||||
def get_control(self, key, acct, thread_id):
|
||||
'''Return the MessageControl for jid or n, where n is a notebook page index.
|
||||
When key is an int index acct may be None'''
|
||||
When key is an int index acct and thread_id may be None'''
|
||||
if isinstance(key, str):
|
||||
key = unicode(key, 'utf-8')
|
||||
|
||||
if isinstance(key, unicode):
|
||||
jid = key
|
||||
try:
|
||||
return self._controls[acct][jid]
|
||||
return self._controls[acct][jid][thread_id]
|
||||
except:
|
||||
return None
|
||||
else:
|
||||
|
@ -569,24 +579,45 @@ class MessageWindow(object):
|
|||
nth_child = notebook.get_nth_page(page_num)
|
||||
return self._widget_to_control(nth_child)
|
||||
|
||||
def change_key(self, old_jid, new_jid, acct):
|
||||
'''Change the key of a control'''
|
||||
def get_gc_control(self, jid, acct):
|
||||
return self.get_control(jid, acct, 'gc')
|
||||
|
||||
def get_controls(self, jid, acct):
|
||||
try:
|
||||
# Check if control exists
|
||||
ctrl = self._controls[acct][old_jid]
|
||||
except:
|
||||
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'''
|
||||
try:
|
||||
# Check if controls exists
|
||||
ctrls = self._controls[acct][old_jid]
|
||||
except KeyError:
|
||||
return
|
||||
self._controls[acct][new_jid] = self._controls[acct][old_jid]
|
||||
self._controls[acct][new_jid] = ctrls
|
||||
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 ctrl_dict in self._controls.values():
|
||||
for ctrl in ctrl_dict.values():
|
||||
yield ctrl
|
||||
for jid_dict in self._controls.values():
|
||||
for ctrl_dict in jid_dict.values():
|
||||
for ctrl in ctrl_dict.values():
|
||||
yield ctrl
|
||||
|
||||
def move_to_next_unread_tab(self, forward):
|
||||
ind = self.notebook.get_current_page()
|
||||
|
@ -604,7 +635,7 @@ class MessageWindow(object):
|
|||
ind = ind - 1
|
||||
if ind < 0:
|
||||
ind = self.notebook.get_n_pages() - 1
|
||||
ctrl = self.get_control(ind, None)
|
||||
ctrl = self.get_control(ind, None, None)
|
||||
if ctrl.get_nb_unread() > 0:
|
||||
found = True
|
||||
break # found
|
||||
|
@ -796,8 +827,19 @@ class MessageWindowMgr(gobject.GObject):
|
|||
|
||||
def get_window(self, jid, acct):
|
||||
for win in self.windows():
|
||||
if win.get_control(jid, acct):
|
||||
return win
|
||||
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)
|
||||
|
||||
return None
|
||||
|
||||
def has_window(self, jid, acct):
|
||||
|
@ -930,11 +972,11 @@ class MessageWindowMgr(gobject.GObject):
|
|||
del self._windows[k]
|
||||
return
|
||||
|
||||
def get_control(self, jid, acct):
|
||||
def get_control(self, jid, acct, session):
|
||||
'''Amongst all windows, return the MessageControl for jid'''
|
||||
win = self.get_window(jid, acct)
|
||||
if win:
|
||||
return win.get_control(jid, acct)
|
||||
return win.get_control(jid, acct, session)
|
||||
return None
|
||||
|
||||
def get_controls(self, type = None, acct = None):
|
||||
|
@ -946,6 +988,14 @@ 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
|
||||
|
|
|
@ -53,8 +53,7 @@ try:
|
|||
except:
|
||||
USER_HAS_GROWL = False
|
||||
|
||||
|
||||
def get_show_in_roster(event, account, contact):
|
||||
def get_show_in_roster(event, account, contact, session = None):
|
||||
'''Return True if this event must be shown in roster, else False'''
|
||||
if event == 'gc_message_received':
|
||||
return True
|
||||
|
@ -65,8 +64,10 @@ def get_show_in_roster(event, account, contact):
|
|||
if gajim.config.get_per('notifications', str(num), 'roster') == 'no':
|
||||
return False
|
||||
if event == 'message_received':
|
||||
chat_control = helpers.get_chat_control(account, contact)
|
||||
if chat_control:
|
||||
if session:
|
||||
if session.control:
|
||||
return False
|
||||
elif helpers.get_chat_control(account, contact):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ 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
|
||||
|
@ -1133,6 +1135,51 @@ 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
|
||||
|
@ -1434,9 +1481,9 @@ class RosterWindow:
|
|||
session = gajim.connections[account].make_new_session(jid)
|
||||
|
||||
tim = time.localtime(float(result[2]))
|
||||
self.on_message(jid, result[1], tim, account, msg_type = 'chat',
|
||||
msg_id = result[0], session = session)
|
||||
|
||||
session.roster_message(jid, result[1], tim, msg_type='chat',
|
||||
msg_id=result[0])
|
||||
|
||||
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
|
||||
|
@ -1496,10 +1543,10 @@ class RosterWindow:
|
|||
gajim.transport_avatar[account][host] = [contact1.jid]
|
||||
else:
|
||||
gajim.transport_avatar[account][host].append(contact1.jid)
|
||||
# If we already have a chat window opened, update it with new contact
|
||||
|
||||
# If we already have chat windows opened, update them with new contact
|
||||
# instance
|
||||
chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
|
||||
if chat_control:
|
||||
for chat_control in gajim.interface.msg_win_mgr.get_chat_controls(ji, account):
|
||||
chat_control.contact = contact1
|
||||
|
||||
def enable_syncing_status_msg_from_current_music_track(self, enabled):
|
||||
|
@ -1830,9 +1877,10 @@ class RosterWindow:
|
|||
account):
|
||||
win = gajim.interface.msg_win_mgr.get_window(jid_with_resource,
|
||||
account)
|
||||
ctrl = win.get_control(jid_with_resource, account)
|
||||
ctrl.update_ui()
|
||||
win.redraw_tab(ctrl)
|
||||
for ctrl in win.get_controls(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
|
||||
|
@ -1841,19 +1889,20 @@ 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)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
# unset custom status
|
||||
if gajim.interface.status_sent_to_users.has_key(account) and \
|
||||
|
@ -1973,119 +2022,6 @@ class RosterWindow:
|
|||
gajim.config.get('roster_width'),
|
||||
gajim.config.get('roster_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():
|
||||
|
@ -2151,8 +2087,8 @@ class RosterWindow:
|
|||
|
||||
def on_quit_request(self, widget = None):
|
||||
''' user want to quit. Check if he should be warned about messages
|
||||
pending. Send offline to all connected account. We do NOT really quit
|
||||
gajim here '''
|
||||
pending. Terminate all sessions and send offline to all connected
|
||||
account. We do NOT really quit gajim here '''
|
||||
accounts = gajim.connections.keys()
|
||||
get_msg = False
|
||||
for acct in accounts:
|
||||
|
@ -2169,7 +2105,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
|
||||
|
@ -2192,13 +2128,15 @@ 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...
|
||||
|
@ -2630,9 +2568,8 @@ class RosterWindow:
|
|||
u.name = new_text
|
||||
gajim.connections[account].update_contact(jid, new_text, u.groups)
|
||||
self.draw_contact(jid, account)
|
||||
# Update opened chat
|
||||
ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
|
||||
if ctrl:
|
||||
# Update opened chats
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_controls(jid, account):
|
||||
ctrl.update_ui()
|
||||
win = gajim.interface.msg_win_mgr.get_window(jid, account)
|
||||
win.redraw_tab(ctrl)
|
||||
|
@ -2719,8 +2656,7 @@ class RosterWindow:
|
|||
keyID = keyID[0]
|
||||
keys[contact.jid] = keyID
|
||||
|
||||
if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
|
||||
ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account)
|
||||
for ctrl in gajim.interface.msg_win_mgr.get_chat_controls(contact.jid, account):
|
||||
ctrl.update_ui()
|
||||
keys_str = ''
|
||||
for jid in keys:
|
||||
|
@ -2866,7 +2802,7 @@ class RosterWindow:
|
|||
ctrl.account, ctrl.type_id)
|
||||
ctrl.parent_win = mw
|
||||
mw.new_tab(ctrl)
|
||||
mw.set_active_tab(jid, account)
|
||||
mw.set_active_tab(ctrl)
|
||||
mw.window.present()
|
||||
del gajim.interface.minimized_controls[account][jid]
|
||||
|
||||
|
@ -3029,7 +2965,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 dubble click if we press icon of a metacontact
|
||||
# Don't handle double click if we press icon of a metacontact
|
||||
titer = model.get_iter(path)
|
||||
if x > x_min and x < x_min + 27 and type_ == 'contact' and \
|
||||
model.iter_has_child(titer):
|
||||
|
@ -3296,7 +3232,19 @@ 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
|
||||
|
@ -3347,8 +3295,8 @@ class RosterWindow:
|
|||
self.show_treeview_menu(event)
|
||||
|
||||
def on_row_activated(self, widget, path):
|
||||
'''When an iter is activated (dubblick or single click if gnome is set
|
||||
this way'''
|
||||
'''When an iter is activated (double-click 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]
|
||||
|
@ -4012,6 +3960,7 @@ 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)
|
||||
|
@ -4864,7 +4813,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
|
||||
|
@ -5009,6 +4958,10 @@ 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:
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
from common import helpers
|
||||
|
||||
from common import exceptions
|
||||
from common import gajim
|
||||
from common import stanza_session
|
||||
from common import contacts
|
||||
|
||||
import common.xmpp
|
||||
|
||||
import dialogs
|
||||
|
||||
import message_control
|
||||
|
||||
import notify
|
||||
|
||||
class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
||||
def __init__(self, conn, jid, thread_id, type = 'chat'):
|
||||
stanza_session.EncryptedStanzaSession.__init__(self, conn, jid, thread_id, type = 'chat')
|
||||
|
||||
self.control = None
|
||||
|
||||
def acknowledge_termination(self):
|
||||
# the other party terminated the session. we'll keep the control around, though.
|
||||
stanza_session.EncryptedStanzaSession.acknowledge_termination(self)
|
||||
|
||||
if self.control:
|
||||
self.control.session = None
|
||||
|
||||
# remove events associated with this session from the queue
|
||||
def remove_events(self, types):
|
||||
any_removed = False
|
||||
|
||||
for event in gajim.events.get_events(self.conn, self.jid, types=types):
|
||||
if event.parameters[8] != self:
|
||||
continue
|
||||
|
||||
r = gajim.events.remove_events(self.conn, self.jid, event)
|
||||
|
||||
if not_any_removed:
|
||||
any_removed = r
|
||||
|
||||
return any_removed
|
||||
|
||||
# extracts chatstate from a <message/> stanza
|
||||
def get_chatstate(self, msg, msgtxt):
|
||||
composing_xep = None
|
||||
chatstate = None
|
||||
|
||||
# chatstates - look for chatstate tags in a message if not delayed
|
||||
delayed = msg.getTag('x', namespace=common.xmpp.NS_DELAY) != None
|
||||
if not delayed:
|
||||
composing_xep = False
|
||||
children = msg.getChildren()
|
||||
for child in children:
|
||||
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
|
||||
chatstate = child.getName()
|
||||
composing_xep = 'XEP-0085'
|
||||
break
|
||||
# No XEP-0085 support, fallback to XEP-0022
|
||||
if not chatstate:
|
||||
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
|
||||
if chatstate_child:
|
||||
chatstate = 'active'
|
||||
composing_xep = 'XEP-0022'
|
||||
if not msgtxt and chatstate_child.getTag('composing'):
|
||||
chatstate = 'composing'
|
||||
|
||||
return (composing_xep, chatstate)
|
||||
|
||||
# dispatch a received <message> stanza
|
||||
def received(self, full_jid_with_resource, msgtxt, tim, encrypted, subject, msg):
|
||||
msg_type = msg.getType()
|
||||
msg_id = None
|
||||
|
||||
# XEP-0172 User Nickname
|
||||
user_nick = msg.getTagData('nick')
|
||||
if not user_nick:
|
||||
user_nick =''
|
||||
|
||||
form_node = None
|
||||
for xtag in msg.getTags('x'):
|
||||
if xtag.getNamespace() == common.xmpp.NS_DATA:
|
||||
form_node = xtag
|
||||
break
|
||||
|
||||
composing_xep, chatstate = self.get_chatstate(msg, msgtxt)
|
||||
|
||||
xhtml = msg.getXHTML()
|
||||
|
||||
if msg_type == 'chat':
|
||||
if not msg.getTag('body') and chatstate is None:
|
||||
return
|
||||
|
||||
log_type = 'chat_msg_recv'
|
||||
else:
|
||||
log_type = 'single_msg_recv'
|
||||
|
||||
if self.is_loggable() and msgtxt:
|
||||
try:
|
||||
msg_id = gajim.logger.write(log_type, full_jid_with_resource, msgtxt,
|
||||
tim=tim, subject=subject)
|
||||
except exceptions.PysqliteOperationalError, e:
|
||||
gajim.dispatch('ERROR', (_('Disk WriteError'), str(e)))
|
||||
|
||||
treat_as = gajim.config.get('treat_incoming_messages')
|
||||
if treat_as:
|
||||
msg_type = treat_as
|
||||
|
||||
jid = gajim.get_jid_without_resource(full_jid_with_resource)
|
||||
resource = gajim.get_resource_from_jid(full_jid_with_resource)
|
||||
|
||||
if gajim.config.get('ignore_incoming_xhtml'):
|
||||
xhtml = None
|
||||
if gajim.jid_is_transport(jid):
|
||||
jid = jid.replace('@', '')
|
||||
|
||||
groupchat_control = gajim.interface.msg_win_mgr.get_gc_control(jid, self.conn.name)
|
||||
|
||||
if not groupchat_control and \
|
||||
jid in gajim.interface.minimized_controls[self.conn.name]:
|
||||
groupchat_control = gajim.interface.minimized_controls[self.conn.name][jid]
|
||||
|
||||
pm = False
|
||||
if groupchat_control and groupchat_control.type_id == \
|
||||
message_control.TYPE_GC:
|
||||
# It's a Private message
|
||||
pm = True
|
||||
msg_type = 'pm'
|
||||
|
||||
jid_of_control = full_jid_with_resource
|
||||
|
||||
# Handle chat states
|
||||
contact = gajim.contacts.get_contact(self.conn.name, jid, resource)
|
||||
if contact:
|
||||
if contact.composing_xep != 'XEP-0085': # We cache xep85 support
|
||||
contact.composing_xep = composing_xep
|
||||
if self.control and self.control.type_id == message_control.TYPE_CHAT:
|
||||
if chatstate is not None:
|
||||
# other peer sent us reply, so he supports jep85 or jep22
|
||||
contact.chatstate = chatstate
|
||||
if contact.our_chatstate == 'ask': # we were jep85 disco?
|
||||
contact.our_chatstate = 'active' # no more
|
||||
self.control.handle_incoming_chatstate()
|
||||
elif contact.chatstate != 'active':
|
||||
# got no valid jep85 answer, peer does not support it
|
||||
contact.chatstate = False
|
||||
elif chatstate == 'active':
|
||||
# Brand new message, incoming.
|
||||
contact.our_chatstate = chatstate
|
||||
contact.chatstate = chatstate
|
||||
if msg_id: # Do not overwrite an existing msg_id with None
|
||||
contact.msg_id = msg_id
|
||||
|
||||
# THIS MUST BE AFTER chatstates handling
|
||||
# AND BEFORE playsound (else we ear sounding on chatstates!)
|
||||
if not msgtxt: # empty message text
|
||||
return
|
||||
|
||||
if gajim.config.get('ignore_unknown_contacts') and \
|
||||
not gajim.contacts.get_contacts(self.conn.name, jid) and not pm:
|
||||
return
|
||||
|
||||
if not contact:
|
||||
# contact is not in the roster, create a fake one to display
|
||||
# notification
|
||||
contact = contacts.Contact(jid = jid, resource = resource)
|
||||
advanced_notif_num = notify.get_advanced_notification('message_received',
|
||||
self.conn.name, contact)
|
||||
|
||||
# Is it a first or next message received ?
|
||||
first = False
|
||||
if not self.control:
|
||||
first = True
|
||||
|
||||
if pm:
|
||||
nickname = resource
|
||||
groupchat_control.on_private_message(nickname, msgtxt, tim,
|
||||
xhtml, self, msg_id)
|
||||
else:
|
||||
self.roster_message(jid, msgtxt, tim, encrypted, msg_type,
|
||||
subject, resource, msg_id, user_nick, advanced_notif_num,
|
||||
xhtml=xhtml, form_node=form_node)
|
||||
|
||||
nickname = gajim.get_name_from_jid(self.conn.name, jid)
|
||||
# Check and do wanted notifications
|
||||
msg = msgtxt
|
||||
if subject:
|
||||
msg = _('Subject: %s') % subject + '\n' + msg
|
||||
focused = False
|
||||
|
||||
if self.control:
|
||||
parent_win = self.control.parent_win
|
||||
if self.control == parent_win.get_active_control() and \
|
||||
parent_win.window.has_focus:
|
||||
focused = True
|
||||
|
||||
notify.notify('new_message', jid_of_control, self.conn.name, [msg_type,
|
||||
first, nickname, msg, focused], advanced_notif_num)
|
||||
|
||||
if gajim.interface.remote_ctrl:
|
||||
gajim.interface.remote_ctrl.raise_signal('NewMessage',
|
||||
(self.conn.name, [full_jid_with_resource, msgtxt, tim,
|
||||
encrypted, msg_type, subject, chatstate, msg_id,
|
||||
composing_xep, user_nick, xhtml, form_node]))
|
||||
|
||||
# display the message or show notification in the roster
|
||||
def roster_message(self, jid, msg, tim, encrypted=False, msg_type='',
|
||||
subject=None, resource='', msg_id=None, user_nick='',
|
||||
advanced_notif_num=None, xhtml=None, form_node=None):
|
||||
|
||||
contact = None
|
||||
# if chat window will be for specific resource
|
||||
resource_for_chat = resource
|
||||
|
||||
fjid = jid
|
||||
|
||||
# Try to catch the contact with correct resource
|
||||
if resource:
|
||||
fjid = jid + '/' + resource
|
||||
contact = gajim.contacts.get_contact(self.conn.name, jid, resource)
|
||||
|
||||
highest_contact = gajim.contacts.get_contact_with_highest_priority(
|
||||
self.conn.name, jid)
|
||||
if not contact:
|
||||
# If there is another resource, it may be a message from an invisible
|
||||
# resource
|
||||
lcontact = gajim.contacts.get_contacts(self.conn.name, jid)
|
||||
if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \
|
||||
lcontact[0].show != 'offline')) and jid.find('@') > 0:
|
||||
contact = gajim.contacts.copy_contact(highest_contact)
|
||||
contact.resource = resource
|
||||
if resource:
|
||||
fjid = jid + '/' + resource
|
||||
contact.priority = 0
|
||||
contact.show = 'offline'
|
||||
contact.status = ''
|
||||
gajim.contacts.add_contact(self.conn.name, contact)
|
||||
|
||||
else:
|
||||
# Default to highest prio
|
||||
fjid = jid
|
||||
resource_for_chat = None
|
||||
contact = highest_contact
|
||||
|
||||
if not contact:
|
||||
# contact is not in roster
|
||||
contact = gajim.interface.roster.add_to_not_in_the_roster(
|
||||
self.conn.name, jid, user_nick)
|
||||
|
||||
# If visible, try to get first line of contact in roster
|
||||
path = None
|
||||
iters = gajim.interface.roster._get_contact_iter(jid, self.conn.name,
|
||||
contact=contact)
|
||||
if iters:
|
||||
path = gajim.interface.roster.modelfilter.get_path(iters[0])
|
||||
|
||||
# Do we have a queue?
|
||||
no_queue = len(gajim.events.get_events(self.conn.name, fjid)) == 0
|
||||
|
||||
popup = helpers.allow_popup_window(self.conn.name, advanced_notif_num)
|
||||
|
||||
if msg_type == 'normal' and popup: # it's single message to be autopopuped
|
||||
dialogs.SingleMessageWindow(self.conn.name, contact.jid,
|
||||
action='receive', from_whom=jid, subject=subject, message=msg,
|
||||
resource=resource, session=self, form_node=form_node)
|
||||
return
|
||||
|
||||
# We print if window is opened and it's not a single message
|
||||
if self.control and msg_type != 'normal':
|
||||
typ = ''
|
||||
|
||||
if msg_type == 'error':
|
||||
typ = 'status'
|
||||
|
||||
self.control.print_conversation(msg, typ, tim=tim, encrypted=encrypted,
|
||||
subject=subject, xhtml=xhtml)
|
||||
|
||||
if msg_id:
|
||||
gajim.logger.set_read_messages([msg_id])
|
||||
|
||||
return
|
||||
|
||||
# We save it in a queue
|
||||
type_ = 'chat'
|
||||
event_type = 'message_received'
|
||||
|
||||
if msg_type == 'normal':
|
||||
type_ = 'normal'
|
||||
event_type = 'single_message_received'
|
||||
|
||||
show_in_roster = notify.get_show_in_roster(event_type, self.conn.name, contact, self)
|
||||
show_in_systray = notify.get_show_in_systray(event_type, self.conn.name, contact)
|
||||
|
||||
event = gajim.events.create_event(type_, (msg, subject, msg_type, tim,
|
||||
encrypted, resource, msg_id, xhtml, self, form_node),
|
||||
show_in_roster=show_in_roster, show_in_systray=show_in_systray)
|
||||
|
||||
gajim.events.add_event(self.conn.name, fjid, event)
|
||||
|
||||
if popup:
|
||||
if not self.control:
|
||||
self.control = gajim.interface.roster.new_chat(self, contact, self.conn.name, resource=resource_for_chat)
|
||||
|
||||
if len(gajim.events.get_events(self.conn.name, fjid)):
|
||||
self.control.read_queue()
|
||||
|
||||
if path and not gajim.interface.dragging and gajim.config.get(
|
||||
'scroll_roster_to_last_message'):
|
||||
# we curently see contact in our roster
|
||||
# show and select his line in roster
|
||||
# do not change selection while DND'ing
|
||||
self.tree.expand_row(path[0:1], False)
|
||||
self.tree.expand_row(path[0:2], False)
|
||||
self.tree.scroll_to_cell(path)
|
||||
self.tree.set_cursor(path)
|
||||
else:
|
||||
if no_queue: # We didn't have a queue: we change icons
|
||||
gajim.interface.roster.draw_contact(jid, self.conn.name)
|
||||
|
||||
gajim.interface.roster.show_title() # we show the * or [n]
|
||||
# Show contact in roster (if he is invisible for example) and select
|
||||
# line
|
||||
gajim.interface.roster.show_and_select_contact_if_having_events(jid,
|
||||
self.conn.name)
|
|
@ -0,0 +1,427 @@
|
|||
from common import stanza_session
|
||||
from common import xmpp
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
from gtk import gdk
|
||||
import cairo
|
||||
|
||||
# implements <http://pidgin-games.sourceforge.net/xep/tictactoe.html#invite>
|
||||
|
||||
games_ns = 'http://jabber.org/protocol/games'
|
||||
|
||||
class InvalidMove(Exception):
|
||||
pass
|
||||
|
||||
class TicTacToeSession(stanza_session.StanzaSession):
|
||||
# initiate a session
|
||||
def begin(self, rows = 3, cols = 3, role_s = 'x'):
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
|
||||
self.role_s = role_s
|
||||
|
||||
self.strike = 3
|
||||
|
||||
if self.role_s == 'x':
|
||||
self.role_o = 'o'
|
||||
else:
|
||||
self.role_o = 'x'
|
||||
|
||||
self.send_invitation()
|
||||
|
||||
self.next_move_id = 1
|
||||
self.received = self.wait_for_invite_response
|
||||
|
||||
def send_invitation(self):
|
||||
msg = xmpp.Message()
|
||||
|
||||
invite = msg.NT.invite
|
||||
invite.setNamespace(games_ns)
|
||||
|
||||
game = invite.NT.game
|
||||
game.setAttr('var', games_ns + '/tictactoe')
|
||||
|
||||
x = xmpp.DataForm(typ='submit')
|
||||
|
||||
game.addChild(node=x)
|
||||
|
||||
self.send(msg)
|
||||
|
||||
def read_invitation(self, msg):
|
||||
invite = msg.getTag('invite', namespace=games_ns)
|
||||
game = invite.getTag('game')
|
||||
x = game.getTag('x', namespace='jabber:x:data')
|
||||
|
||||
form = xmpp.DataForm(node=x)
|
||||
|
||||
if form.getField('role'):
|
||||
self.role_o = form.getField('role').getValues()[0]
|
||||
else:
|
||||
self.role_o = 'x'
|
||||
|
||||
if form.getField('rows'):
|
||||
self.rows = int(form.getField('rows').getValues()[0])
|
||||
else:
|
||||
self.rows = 3
|
||||
|
||||
if form.getField('cols'):
|
||||
self.cols = int(form.getField('cols').getValues()[0])
|
||||
else:
|
||||
self.cols = 3
|
||||
|
||||
# number in a row needed to win
|
||||
if form.getField('strike'):
|
||||
self.strike = int(form.getField('strike').getValues()[0])
|
||||
else:
|
||||
self.strike = 3
|
||||
|
||||
# received an invitation
|
||||
def invited(self, msg):
|
||||
self.read_invitation(msg)
|
||||
|
||||
# XXX prompt user
|
||||
# "accept, reject, ignore"
|
||||
|
||||
# the number of the move about to be made
|
||||
self.next_move_id = 1
|
||||
|
||||
# display the board
|
||||
self.board = TicTacToeBoard(self, self.rows, self.cols)
|
||||
|
||||
# accept the invitation, join the game
|
||||
response = xmpp.Message()
|
||||
|
||||
join = response.NT.join
|
||||
join.setNamespace(games_ns)
|
||||
|
||||
self.send(response)
|
||||
|
||||
if self.role_o == 'x':
|
||||
self.role_s = 'o'
|
||||
|
||||
self.their_turn()
|
||||
else:
|
||||
self.role_s = 'x'
|
||||
self.role_o = 'o'
|
||||
|
||||
self.our_turn()
|
||||
|
||||
# just sent an invitation, expecting a reply
|
||||
def wait_for_invite_response(self, msg):
|
||||
if msg.getTag('join', namespace=games_ns):
|
||||
self.board = TicTacToeBoard(self, self.rows, self.cols)
|
||||
|
||||
if self.role_s == 'x':
|
||||
self.our_turn()
|
||||
else:
|
||||
self.their_turn()
|
||||
|
||||
elif msg.getTag('decline', namespace=games_ns):
|
||||
# XXX notify the user
|
||||
|
||||
# XXX end session
|
||||
pass
|
||||
|
||||
# silently ignores any received messages
|
||||
def ignore(self, msg):
|
||||
pass
|
||||
|
||||
def game_over(self, msg):
|
||||
invite = msg.getTag('invite', namespace=games_ns)
|
||||
|
||||
# ignore messages unless they're renewing the game
|
||||
if invite and invite.getAttr('type') == 'renew':
|
||||
self.invited(msg)
|
||||
|
||||
def wait_for_move(self, msg):
|
||||
turn = msg.getTag('turn', namespace=games_ns)
|
||||
move = turn.getTag('move', namespace='http://jabber.org/protocol/games/tictactoe')
|
||||
|
||||
row = int(move.getAttr('row'))
|
||||
col = int(move.getAttr('col'))
|
||||
id = int(move.getAttr('id'))
|
||||
|
||||
if id != self.next_move_id:
|
||||
print 'unexpected move id, lost a move somewhere?'
|
||||
return
|
||||
|
||||
try:
|
||||
self.board.mark(row, col, self.role_o)
|
||||
except InvalidMove, e:
|
||||
# received an invalid move, end the game.
|
||||
|
||||
# XXX notify the user
|
||||
self.terminate('cheating')
|
||||
return
|
||||
|
||||
# check win conditions
|
||||
if self.board.check_for_strike(self.role_o, row, col, self.strike):
|
||||
self.lost()
|
||||
elif self.board.full():
|
||||
self.drawn()
|
||||
else:
|
||||
self.next_move_id += 1
|
||||
|
||||
self.our_turn()
|
||||
|
||||
def is_my_turn(self):
|
||||
# XXX not great semantics
|
||||
return self.received == self.ignore
|
||||
|
||||
def our_turn(self):
|
||||
# ignore messages until we've made our move
|
||||
self.received = self.ignore
|
||||
self.board.set_title('your turn')
|
||||
|
||||
def their_turn(self):
|
||||
self.received = self.wait_for_move
|
||||
self.board.set_title('their turn')
|
||||
|
||||
# called when the board receives input
|
||||
def move(self, row, col):
|
||||
try:
|
||||
self.board.mark(row, col, self.role_s)
|
||||
except InvalidMove, e:
|
||||
print 'you made an invalid move'
|
||||
return
|
||||
|
||||
self.send_move(row, col)
|
||||
|
||||
# check win conditions
|
||||
if self.board.check_for_strike(self.role_s, row, col, self.strike):
|
||||
self.won()
|
||||
elif self.board.full():
|
||||
self.drawn()
|
||||
else:
|
||||
self.next_move_id += 1
|
||||
|
||||
self.their_turn()
|
||||
|
||||
# sends a move message
|
||||
def send_move(self, row, column):
|
||||
msg = xmpp.Message()
|
||||
msg.setType('chat')
|
||||
|
||||
turn = msg.NT.turn
|
||||
turn.setNamespace(games_ns)
|
||||
|
||||
move = turn.NT.move
|
||||
move.setNamespace(games_ns+'/tictactoe')
|
||||
|
||||
move.setAttr('row', str(row))
|
||||
move.setAttr('col', str(column))
|
||||
move.setAttr('id', str(self.next_move_id))
|
||||
|
||||
self.send(msg)
|
||||
|
||||
# sends a termination message and ends the game
|
||||
def terminate(self, reason):
|
||||
msg = xmpp.Message()
|
||||
|
||||
terminate = msg.NT.terminate
|
||||
terminate.setNamespace(games_ns)
|
||||
terminate.setAttr('reason', reason)
|
||||
|
||||
self.send(msg)
|
||||
|
||||
self.received = self.game_over
|
||||
|
||||
def won(self):
|
||||
self.terminate('won')
|
||||
self.board.won()
|
||||
|
||||
def lost(self):
|
||||
self.terminate('lost')
|
||||
self.board.lost()
|
||||
|
||||
def drawn(self):
|
||||
self.terminate('draw')
|
||||
self.board.drawn()
|
||||
|
||||
class TicTacToeBoard:
|
||||
def __init__(self, session, rows, cols):
|
||||
self.session = session
|
||||
|
||||
self.state = 'None'
|
||||
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
|
||||
self.board = [ [None] * self.cols for r in xrange(self.rows) ]
|
||||
|
||||
self.setup_window()
|
||||
|
||||
# check if the last move (at row r and column c) won the game
|
||||
def check_for_strike(self, p, r, c, strike):
|
||||
# number in a row: up and down, left and right
|
||||
tallyI = 0
|
||||
tally_ = 0
|
||||
|
||||
# number in a row: diagonal
|
||||
# (imagine L or F as two sides of a right triangle: L\ or F/)
|
||||
tallyL = 0
|
||||
tallyF = 0
|
||||
|
||||
# convert real columns to internal columns
|
||||
r -= 1
|
||||
c -= 1
|
||||
|
||||
for d in xrange(-strike, strike):
|
||||
r_in_range = 0 <= r+d < self.rows
|
||||
c_in_range = 0 <= c+d < self.cols
|
||||
|
||||
# vertical check
|
||||
if r_in_range:
|
||||
tallyI = tallyI + 1
|
||||
if self.board[r+d][c] != p:
|
||||
tallyI = 0
|
||||
|
||||
# horizontal check
|
||||
if c_in_range:
|
||||
tally_ = tally_ + 1
|
||||
if self.board[r][c+d] != p:
|
||||
tally_ = 0
|
||||
|
||||
# diagonal checks
|
||||
if r_in_range and c_in_range:
|
||||
tallyL = tallyL + 1
|
||||
if self.board[r+d][c+d] != p:
|
||||
tallyL = 0
|
||||
|
||||
if r_in_range and 0 <= c-d < self.cols:
|
||||
tallyF = tallyF + 1
|
||||
if self.board[r+d][c-d] != p:
|
||||
tallyF = 0
|
||||
|
||||
if any([t == strike for t in (tallyL, tallyF, tallyI, tally_)]):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# is the board full?
|
||||
def full(self):
|
||||
for r in xrange(self.rows):
|
||||
for c in xrange(self.cols):
|
||||
if self.board[r][c] == None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def setup_window(self):
|
||||
self.win = gtk.Window()
|
||||
|
||||
self.title_prefix = 'tic-tac-toe with %s' % self.session.jid
|
||||
self.set_title()
|
||||
|
||||
self.win.set_app_paintable(True)
|
||||
|
||||
self.win.add_events(gdk.BUTTON_PRESS_MASK)
|
||||
self.win.connect('button-press-event', self.clicked)
|
||||
self.win.connect('expose-event', self.expose)
|
||||
|
||||
self.win.show_all()
|
||||
|
||||
def clicked(self, widget, event):
|
||||
if not self.session.is_my_turn():
|
||||
return
|
||||
|
||||
(height, width) = widget.get_size()
|
||||
|
||||
# convert click co-ordinates to row and column
|
||||
|
||||
row_height = height // self.rows
|
||||
col_width = width // self.cols
|
||||
|
||||
row = int(event.y // row_height) + 1
|
||||
column = int(event.x // col_width) + 1
|
||||
|
||||
self.session.move(row, column)
|
||||
|
||||
# this actually draws the board
|
||||
def expose(self, widget, event):
|
||||
win = widget.window
|
||||
|
||||
cr = win.cairo_create()
|
||||
|
||||
cr.set_source_rgb(1.0, 1.0, 1.0)
|
||||
|
||||
cr.set_operator(cairo.OPERATOR_SOURCE)
|
||||
cr.paint()
|
||||
|
||||
(width, height) = widget.get_size()
|
||||
|
||||
row_height = height // self.rows
|
||||
col_width = width // self.cols
|
||||
|
||||
for i in xrange(self.rows):
|
||||
for j in xrange(self.cols):
|
||||
if self.board[i][j] == 'x':
|
||||
self.draw_x(cr, i, j, row_height, col_width)
|
||||
elif self.board[i][j] == 'o':
|
||||
self.draw_o(cr, i, j, row_height, col_width)
|
||||
|
||||
# XXX draw 'won', 'lost', 'draw'
|
||||
|
||||
def draw_x(self, cr, row, col, row_height, col_width):
|
||||
cr.set_source_rgb(0, 0, 0)
|
||||
|
||||
top = row_height * (row + 0.2)
|
||||
bottom = row_height * (row + 0.8)
|
||||
|
||||
left = col_width * (col + 0.2)
|
||||
right = col_width * (col + 0.8)
|
||||
|
||||
cr.set_line_width(row_height / 5)
|
||||
|
||||
cr.move_to(left, top)
|
||||
cr.line_to(right, bottom)
|
||||
|
||||
cr.move_to(right, top)
|
||||
cr.line_to(left, bottom)
|
||||
|
||||
cr.stroke()
|
||||
|
||||
def draw_o(self, cr, row, col, row_height, col_width):
|
||||
cr.set_source_rgb(0, 0, 0)
|
||||
|
||||
x = col_width * (col + 0.5)
|
||||
y = row_height * (row + 0.5)
|
||||
|
||||
cr.arc(x, y, row_height/4, 0, 2.0*3.2) # slightly further than 2*pi
|
||||
|
||||
cr.set_line_width(row_height / 5)
|
||||
cr.stroke()
|
||||
|
||||
# mark a move on the board
|
||||
def mark(self, row, column, player):
|
||||
if self.board[row-1][column-1]:
|
||||
raise InvalidMove
|
||||
else:
|
||||
self.board[row-1][column-1] = player
|
||||
|
||||
self.win.queue_draw()
|
||||
|
||||
def set_title(self, suffix = None):
|
||||
str = self.title_prefix
|
||||
|
||||
if suffix:
|
||||
str += ': ' + suffix
|
||||
|
||||
self.win.set_title(str)
|
||||
|
||||
def won(self):
|
||||
self.state = 'won'
|
||||
self.set_title('you won!')
|
||||
self.win.queue_draw()
|
||||
|
||||
def lost(self):
|
||||
self.state = 'lost'
|
||||
self.set_title('you lost.')
|
||||
self.win.queue_draw()
|
||||
|
||||
def drawn(self):
|
||||
self.state = 'drawn'
|
||||
self.win.set_title(self.title_prefix + ': a draw.')
|
||||
self.win.queue_draw()
|
|
@ -122,11 +122,9 @@ class VcardWindow:
|
|||
jid = self.contact.jid
|
||||
# Update roster
|
||||
gajim.interface.roster.draw_avatar(jid, self.account)
|
||||
# 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:
|
||||
# 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:
|
||||
ctrl.show_avatar()
|
||||
|
||||
def on_vcard_information_window_destroy(self, widget):
|
||||
|
|
Loading…
Reference in New Issue