massive everything-breaking overhaul for per-session windows

This commit is contained in:
Brendan Taylor 2007-06-05 21:26:45 +00:00
parent d696f79c55
commit 17c5bf5e52
13 changed files with 319 additions and 188 deletions

View File

@ -577,7 +577,7 @@ class ChatControlBase(MessageControl):
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)
if gc_message:
@ -606,7 +606,7 @@ 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)
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid, self.account, self.session.thread_id)
if not self.parent_win.is_active():
self.parent_win.show_title(True, ctrl) # Enabled Urgent hint
else:
@ -905,10 +905,10 @@ class ChatControl(ChatControlBase):
old_msg_kind = None # last kind of the printed message
CHAT_CMDS = ['clear', 'compact', 'help', 'ping']
def __init__(self, parent_win, contact, acct, resource = None):
def __init__(self, parent_win, contact, acct, session, resource = None):
ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
'chat_child_vbox', contact, acct, resource)
# for muc use:
# widget = self.xml.get_widget('muc_window_actions_button')
widget = self.xml.get_widget('message_window_actions_button')
@ -935,7 +935,7 @@ class ChatControl(ChatControlBase):
# it is on enter-notify and leave-notify so no need to be per jid
self.show_bigger_avatar_timeout_id = None
self.bigger_avatar_window = None
self.show_avatar(self.contact.resource)
self.show_avatar(self.contact.resource)
# chatstate timers and state
self.reset_kbd_mouse_timeout_vars()
@ -949,7 +949,7 @@ class ChatControl(ChatControlBase):
id = message_tv_buffer.connect('changed',
self._on_message_tv_buffer_changed)
self.handlers[id] = message_tv_buffer
widget = self.xml.get_widget('avatar_eventbox')
id = widget.connect('enter-notify-event',
self.on_avatar_eventbox_enter_notify_event)
@ -969,7 +969,9 @@ class ChatControl(ChatControlBase):
if self.contact.jid in gajim.encrypted_chats[self.account]:
self.xml.get_widget('gpg_togglebutton').set_active(True)
self.session = session
self.status_tooltip = gtk.Tooltips()
self.update_ui()
# restore previous conversation
@ -1797,7 +1799,7 @@ class ChatControl(ChatControlBase):
# 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)
control = gajim.interface.msg_win_mgr.get_control(room_jid, self.account, self.session.thread_id)
if control and control.type_id == message_control.TYPE_GC:
is_pm = True
# list of message ids which should be marked as read
@ -1815,9 +1817,6 @@ class ChatControl(ChatControlBase):
encrypted = data[4], subject = data[1], xhtml = data[7])
if len(data) > 6 and isinstance(data[6], int):
message_ids.append(data[6])
if len(data) > 8:
self.set_thread_id(data[8])
if message_ids:
gajim.logger.set_read_messages(message_ids)
gajim.events.remove_events(self.account, jid_with_resource,

View File

@ -48,6 +48,8 @@ log = logging.getLogger('gajim.c.connection')
import gtkgui_helpers
import time
class Connection(ConnectionHandlers):
'''Connection class'''
def __init__(self, name):
@ -839,7 +841,7 @@ class Connection(ConnectionHandlers):
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
chatstate = None, msg_id = None, composing_jep = None, resource = None,
user_nick = None, xhtml = None, thread = None):
user_nick = None, xhtml = None, session = None):
if not self.connection:
return 1
if msg and not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
@ -884,8 +886,10 @@ class Connection(ConnectionHandlers):
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
# XEP-0201
if thread:
msg_iq.setTag("thread").setData(thread)
if session:
session.last_send = time.time()
if session.thread_id:
msg_iq.setThread(session.thread_id)
# JEP-0172: user_nickname
if user_nick:

View File

@ -37,6 +37,8 @@ from common import atom
from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
from common.stanza_session import StanzaSession
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'error']
# kind of events we can wait for an answer
@ -1171,6 +1173,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# keep the latest subscribed event for each jid to prevent loop when we
# acknoledge presences
self.subscribed_events = {}
# keep track of sessions this connection has with other JIDs
self.sessions = {}
try:
idle.init()
except:
@ -1197,13 +1203,13 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('HTTP_AUTH', (method, url, id, iq_obj, msg));
raise common.xmpp.NodeProcessed
def _FeatureNegCB(self, con, stanza):
def _FeatureNegCB(self, con, stanza, session):
gajim.log.debug('FeatureNegCB')
feature = stanza.getTag('feature')
form = common.xmpp.DataForm(node=feature.getTag('x'))
if form['FORM_TYPE'] == 'urn:xmpp:ssn':
self.dispatch('SESSION_NEG', (stanza.getFrom(), stanza.getThread(), form))
self.dispatch('SESSION_NEG', (stanza.getFrom(), session, form))
else:
reply = stanza.buildReply()
reply.setType('error')
@ -1410,6 +1416,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
def _messageCB(self, con, msg):
'''Called when we receive a message'''
frm = helpers.get_full_jid_from_iq(msg)
mtype = msg.getType()
thread_id = msg.getThread()
if not mtype:
mtype = 'normal'
session = self.get_session(frm, thread_id, mtype)
if thread_id and not session.received_thread_id:
session.received_thread_id = True
# check if the message is pubsub#event
if msg.getTag('event') is not None:
self._pubsubEventCB(con, msg)
@ -1421,18 +1438,15 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
return
if msg.getTag('feature') and msg.getTag('feature').namespace == \
common.xmpp.NS_FEATURE:
self._FeatureNegCB(con, msg)
self._FeatureNegCB(con, msg, session)
return
msgtxt = msg.getBody()
msghtml = msg.getXHTML()
mtype = msg.getType()
subject = msg.getSubject() # if not there, it's None
thread = msg.getThread()
tim = msg.getTimestamp()
tim = time.strptime(tim, '%Y%m%dT%H:%M:%S')
tim = time.localtime(timegm(tim))
frm = helpers.get_full_jid_from_iq(msg)
jid = helpers.get_jid_from_iq(msg)
no_log_for = gajim.config.get_per('accounts', self.name,
'no_log_for')
@ -1541,13 +1555,42 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
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_jep, user_nick, msghtml, thread))
subject, chatstate, msg_id, composing_jep, user_nick, msghtml, session))
# END messageCB
def get_session(self, jid, thread_id, type):
'''returns an existing session between this connection and 'jid' or starts a new one.'''
try:
if type == 'chat' and not thread_id:
return self.find_null_session(jid)
else:
return self.sessions[jid][thread_id]
except KeyError:
return self.make_new_session(jid, thread_id, type)
def find_null_session(self, jid):
'''returns the session between this connecting and 'jid' that we last sent a message in.'''
all = self.sessions[jid].values()
null_sessions = filter(lambda s: not s.received_thread_id, all)
null_sessions.sort(key=lambda s: s.last_send)
return null_sessions[-1]
def make_new_session(self, jid, thread_id = None, type = 'chat'):
sess = StanzaSession(self, jid, thread_id, type)
if not jid in self.sessions:
self.sessions[jid] = {}
self.sessions[jid][sess.thread_id] = sess
return sess
def _pubsubEventCB(self, con, msg):
''' Called when we receive <message/> with pubsub event. '''
# TODO: Logging? (actually services where logging would be useful, should

View File

@ -16,7 +16,7 @@
import common.gajim
import random, string
#import random, string
class Contact:
'''Information concerning each contact'''
@ -53,18 +53,18 @@ class Contact:
self.last_status_time = last_status_time
# XEP-0201
self.sessions = {}
# self.sessions = {}
def new_session(self):
thread_id = "".join([random.choice(string.letters) for x in xrange(0,32)])
self.sessions[self.get_full_jid()] = thread_id
return thread_id
# def new_session(self):
# thread_id = "".join([random.choice(string.letters) for x in xrange(0,32)])
# self.sessions[self.get_full_jid()] = thread_id
# return thread_id
def get_session(self):
try:
return self.sessions[self.get_full_jid()]
except KeyError:
return None
# def get_session(self):
# try:
# return self.sessions[self.get_full_jid()]
# except KeyError:
# return None
def get_full_jid(self):
if self.resource:
@ -169,7 +169,7 @@ class Contacts:
return Contact(jid, name, groups, show, status, sub, ask, resource,
priority, keyID, our_chatstate, chatstate, last_status_time,
composing_jep)
def copy_contact(self, contact):
return self.create_contact(jid = contact.jid, name = contact.name,
groups = contact.groups, show = contact.show, status = contact.status,

View File

@ -820,7 +820,7 @@ def allow_sound_notification(sound_event, advanced_notif_num = None):
return True
return False
def get_chat_control(account, contact):
def get_chat_control(account, contact, session):
full_jid_with_resource = contact.jid
if contact.resource:
full_jid_with_resource += '/' + contact.resource
@ -829,16 +829,16 @@ def get_chat_control(account, contact):
# Look for a chat control that has the given resource, or default to
# one without resource
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid_with_resource,
account)
account, session.thread_id)
if ctrl:
return ctrl
elif not highest_contact or not highest_contact.resource:
# unknow contact or offline message
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
return gajim.interface.msg_win_mgr.get_control(contact.jid, account, session.thread_id)
elif highest_contact and contact.resource != \
highest_contact.resource:
return None
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
return gajim.interface.msg_win_mgr.get_control(contact.jid, account, session.thread_id)
def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
'''Cut the chars after 'max_chars' on each line

View File

@ -0,0 +1,51 @@
import gajim
from common import xmpp
from common import helpers
import random
import string
class StanzaSession:
def __init__(self, conn, jid, thread_id, type):
self.conn = conn
if isinstance(jid, str) or isinstance(jid, unicode):
self.jid = xmpp.JID(jid)
else:
self.jid = jid
self.type = type
if thread_id:
self.received_thread_id = True
self.thread_id = thread_id
else:
self.received_thread_id = False
if type == 'normal':
self.thread_id = None
else:
self.thread_id = self.generate_thread_id()
self.last_send = 0
def generate_thread_id(self):
return "".join([random.choice(string.letters) for x in xrange(0,32)])
def get_control(self, advanced_notif_num = None):
account = self.conn.name
highest_contact = gajim.contacts.get_contact_with_highest_priority(account, str(self.jid))
contact = gajim.contacts.get_contact(account, self.jid.getStripped(), self.jid.getResource())
if isinstance(contact, list):
# there was no resource (maybe we're reading unread messages after shutdown). just take the first one for now :/
contact = contact[0]
ctrl = gajim.interface.msg_win_mgr.get_control(str(self.jid), account, self.thread_id)
# if not ctrl:
# if highest_contact and contact.resource == highest_contact.resource and not str(self.jid) == gajim.get_jid_from_account(account):
# ctrl = gajim.interface.msg_win_mgr.get_control(self.jid.getStripped(), account, self.thread_id)
if not ctrl and helpers.allow_popup_window(account, advanced_notif_num):
gajim.new_chat(contact, account, resource = resource_for_chat, session = self)
return ctrl

View File

@ -1404,7 +1404,7 @@ class SynchroniseSelectAccountDialog:
if not iter:
return
remote_account = model.get_value(iter, 0).decode('utf-8')
if gajim.connections[remote_account].connected < 2:
ErrorDialog(_('This account is not connected to the server'),
_('You cannot synchronize with an account unless it is connected.'))
@ -1686,7 +1686,7 @@ class SingleMessageWindow:
or 'receive'.
'''
def __init__(self, account, to = '', action = '', from_whom = '',
subject = '', message = '', resource = '', thread = None):
subject = '', message = '', resource = '', session = None):
self.account = account
self.action = action
@ -1695,7 +1695,7 @@ class SingleMessageWindow:
self.to = to
self.from_whom = from_whom
self.resource = resource
self.thread = thread
self.session = session
self.xml = gtkgui_helpers.get_glade('single_message_window.glade')
self.window = self.xml.get_widget('single_message_window')
@ -1897,7 +1897,7 @@ class SingleMessageWindow:
# FIXME: allow GPG message some day
gajim.connections[self.account].send_message(to_whom_jid, message,
keyID = None, type = 'normal', subject=subject, thread = self.thread)
keyID = None, type = 'normal', subject=subject, session = self.session)
self.subject_entry.set_text('') # we sent ok, clear the subject
self.message_tv_buffer.set_text('') # we sent ok, clear the textview
@ -1914,7 +1914,7 @@ class SingleMessageWindow:
self.window.destroy()
SingleMessageWindow(self.account, to = self.from_whom,
action = 'send', from_whom = self.from_whom, subject = self.subject,
message = self.message, thread = self.thread)
message = self.message, session = self.session)
def on_send_and_close_button_clicked(self, widget):
self.send_single_message()
@ -2099,7 +2099,7 @@ class PrivacyListWindow:
jid_entry_completion.set_text_column(0)
jid_entry_completion.set_model(jids_list_store)
jid_entry_completion.set_popup_completion(True)
self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
if action == 'EDIT':
self.refresh_rules()

View File

@ -443,9 +443,9 @@ 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_controls(jid=jid_from, acct=account):
if ctrl and ctrl.type_id == message_control.TYPE_GC:
ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
def handle_event_con_type(self, account, con_type):
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'
@ -667,7 +667,7 @@ class Interface:
def handle_event_msg(self, account, array):
# 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject,
# chatstate, msg_id, composing_jep, user_nick, xhtml, thread))
# chatstate, msg_id, composing_jep, user_nick, xhtml, session))
# user_nick is JEP-0172
full_jid_with_resource = array[0]
@ -682,13 +682,13 @@ class Interface:
msg_id = array[7]
composing_jep = array[8]
xhtml = array[10]
thread = array[11]
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)
groupchat_control = self.msg_win_mgr.get_control(jid, account, session.thread_id)
if not groupchat_control and \
gajim.interface.minimized_controls.has_key(account) and \
jid in gajim.interface.minimized_controls[account]:
@ -700,28 +700,29 @@ class Interface:
pm = True
msg_type = 'pm'
chat_control = None
jid_of_control = full_jid_with_resource
# 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):
chat_control = session.get_control()
# ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account, session.thread_id)
# 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)
# jid_of_control = jid
# chat_control = self.msg_win_mgr.get_control(jid, account, session.thread_id)
# 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, session.thread_id)
# Handle chat states
# Handle chat states
contact = gajim.contacts.get_contact(account, jid, resource)
if contact and isinstance(contact, list):
contact = contact[0]
@ -739,7 +740,7 @@ class Interface:
# got no valid jep85 answer, peer does not support it
contact.chatstate = False
elif chatstate == 'active':
# Brand new message, incoming.
# Brand new message, incoming.
contact.our_chatstate = chatstate
contact.chatstate = chatstate
if msg_id: # Do not overwrite an existing msg_id with None
@ -753,10 +754,12 @@ class Interface:
if gajim.config.get('ignore_unknown_contacts') and \
not gajim.contacts.get_contact(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)
contact = common.contacts.Contact(jid = jid, resource = resource)
advanced_notif_num = notify.get_advanced_notification('message_received',
account, contact)
@ -765,8 +768,8 @@ class Interface:
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
elif not chat_control and not gajim.events.get_events(account,
full_jid_with_resource, [msg_type]): # msg_type can be chat or pm
first = True
if pm:
@ -778,18 +781,19 @@ class Interface:
if encrypted:
self.roster.on_message(jid, message, array[2], account, array[3],
msg_type, subject, resource, msg_id, array[9],
advanced_notif_num, thread = thread)
advanced_notif_num, session = session)
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, thread = thread)
advanced_notif_num, xhtml = xhtml, session = session)
nickname = gajim.get_name_from_jid(account, jid)
# Check and do wanted notifications
# Check and do wanted notifications
msg = message
if subject:
msg = _('Subject: %s') % subject + '\n' + msg
notify.notify('new_message', jid_of_control, account, [msg_type,
notify.notify('new_message', full_jid_with_resource, account, [msg_type,
first, nickname, msg], advanced_notif_num)
if self.remote_ctrl:
@ -986,7 +990,7 @@ class Interface:
resource = ''
if vcard.has_key('resource'):
resource = vcard['resource']
# vcard window
win = None
if self.instances[account]['infos'].has_key(jid):
@ -1008,11 +1012,14 @@ class Interface:
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()
for ctrl in self.msg_win_mgr.get_controls(jid=jid, acct=account):
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)
# XXX get_gc_control?
if gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC:
gc_ctrl.draw_avatar(resource)
else:
@ -1656,25 +1663,24 @@ class Interface:
AtomWindow.newAtomEntry(atom_entry)
def handle_session_negotiation(self, account, data):
jid, thread_id, form = data
jid, session, form = data
# XXX check negotiation state, etc.
# XXX check if we can autoaccept
if form.getType() == 'form':
ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account)
if not ctrl:
resource = jid.getResource()
contact = gajim.contacts.get_contact(account, str(jid), resource)
if not contact:
connection = gajim.connections[account]
contact = gajim.contacts.create_contact(jid = jid.getStripped(), resource = resource, show = connection.get_status())
self.roster.new_chat(contact, account, resource = resource)
ctrl = session.get_control()
# ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account)
# if not ctrl:
# resource = jid.getResource()
# contact = gajim.contacts.get_contact(account, str(jid), resource)
# if not contact:
# connection = gajim.connections[account]
# contact = gajim.contacts.create_contact(jid = jid.getStripped(), resource = resource, show = connection.get_status())
# self.roster.new_chat(contact, account, resource = resource)
ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account)
# ctrl = gajim.interface.msg_win_mgr.get_control(str(jid), account)
ctrl.set_thread_id(thread_id)
negotiation.FeatureNegotiationWindow(account, jid, thread_id, form)
negotiation.FeatureNegotiationWindow(account, jid, session, form)
def handle_event_privacy_lists_received(self, account, data):
# ('PRIVACY_LISTS_RECEIVED', account, list)

View File

@ -37,8 +37,6 @@ class MessageControl:
self.hide_chat_buttons_current = False
self.resource = resource
self.thread_id = self.contact.get_session()
gajim.last_message_time[self.account][self.get_full_jid()] = 0
self.xml = gtkgui_helpers.get_glade('message_window.glade', widget_name)
@ -112,14 +110,6 @@ class MessageControl:
def get_specific_unread(self):
return len(gajim.events.get_events(self.account, self.contact.jid))
def set_thread_id(self, thread_id):
if thread_id == self.thread_id:
return
if self.thread_id:
print "starting a new session, forgetting about the old one!"
self.thread_id = thread_id
self.contact.sessions[self.contact.get_full_jid()] = thread_id
def send_message(self, message, keyID = '', type = 'chat',
chatstate = None, msg_id = None, composing_jep = None, resource = None,
user_nick = None):
@ -127,11 +117,8 @@ class MessageControl:
'''
jid = self.contact.jid
if not self.thread_id:
self.thread_id = self.contact.new_session()
# Send and update history
return gajim.connections[self.account].send_message(jid, message, keyID,
type = type, chatstate = chatstate, msg_id = msg_id,
composing_jep = composing_jep, resource = self.resource,
user_nick = user_nick, thread = self.thread_id)
user_nick = user_nick, session = self.session)

View File

@ -123,8 +123,9 @@ class MessageWindow:
def get_num_controls(self):
n = 0
for dict in self._controls.values():
n += len(dict)
for sess_dict in self._controls.values():
for dict in sess_dict.values():
n += len(dict)
return n
def _on_window_focus(self, widget, event):
@ -165,7 +166,9 @@ class MessageWindow:
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.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 ?
@ -292,11 +295,11 @@ class MessageWindow:
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, jid, acct, thread_id):
ctrl = self._controls[acct][jid][thread_id]
ctrl_page = self.notebook.page_num(ctrl.widget)
self.notebook.set_current_page(ctrl_page)
def remove_tab(self, ctrl, method, reason = None, force = False):
'''reason is only for gc (offline status message)
if force is True, do not ask any confirmation'''
@ -317,7 +320,9 @@ class MessageWindow:
self.notebook.remove_page(self.notebook.page_num(ctrl.widget))
fjid = ctrl.get_full_jid()
del self._controls[ctrl.account][fjid]
del self._controls[ctrl.account][fjid][ctrl.session.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]
@ -415,7 +420,19 @@ class MessageWindow:
for ctrl in self.controls():
ctrl.update_tags()
def get_control(self, key, acct):
def has_control(self, jid, acct, thread_id = None):
try:
if thread_id:
return (thread_id in self._controls[acct][jid])
else:
return (jid in self._controls[acct])
except KeyError:
return False
def get_controls(self, jid, acct):
return self._controls[acct][jid].values()
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'''
if isinstance(key, str):
@ -424,7 +441,7 @@ class MessageWindow:
if isinstance(key, unicode):
jid = key
try:
return self._controls[acct][jid]
return self._controls[acct][jid][thread_id]
except:
return None
else:
@ -436,9 +453,10 @@ class MessageWindow:
return self._widget_to_control(nth_child)
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 sess_dict in jid_dict.values():
for ctrl in sess_dict.values():
yield ctrl
def move_to_next_unread_tab(self, forward):
ind = self.notebook.get_current_page()
@ -495,7 +513,7 @@ class MessageWindow:
if old_no >= 0:
old_ctrl = self._widget_to_control(notebook.get_nth_page(old_no))
old_ctrl.set_control_active(False)
new_ctrl = self._widget_to_control(notebook.get_nth_page(page_num))
new_ctrl.set_control_active(True)
self.show_title(control = new_ctrl)
@ -569,11 +587,11 @@ class MessageWindow:
source_child = self.notebook.get_nth_page(source_page_num)
if dest_page_num != source_page_num:
self.notebook.reorder_child(source_child, dest_page_num)
def get_tab_at_xy(self, x, y):
'''Thanks to Gaim
Return the tab under xy and
if its nearer from left or right side of the tab
if its nearer from left or right side of the tab
'''
page_num = -1
to_right = False
@ -594,7 +612,7 @@ class MessageWindow:
if (y >= tab_alloc.y) and \
(y <= (tab_alloc.y + tab_alloc.height)):
page_num = i
if y > tab_alloc.y + (tab_alloc.height / 2.0):
to_right = True
break
@ -659,14 +677,22 @@ class MessageWindowMgr:
return w
return None
def get_window(self, jid, acct):
def get_window(self, jid, acct, thread_id):
for win in self.windows():
if win.get_control(jid, acct):
if win.has_control(jid, acct, thread_id):
return win
return None
def has_window(self, jid, acct):
return self.get_window(jid, acct) != None
def get_windows(self, jid, acct):
for win in self.windows():
if win.has_control(jid, acct):
yield win
def has_window(self, jid, acct, thread_id = None):
for win in self.windows():
if win.has_control(jid, acct, thread_id):
return True
return False
def one_window_opened(self, contact, acct, type):
try:
@ -678,7 +704,7 @@ class MessageWindowMgr:
'''Resizes window according to config settings'''
if not gajim.config.get('saveposition'):
return
if self.mode == self.ONE_MSG_WINDOW_ALWAYS:
size = (gajim.config.get('msgwin-width'),
gajim.config.get('msgwin-height'))
@ -695,7 +721,7 @@ class MessageWindowMgr:
return
gtkgui_helpers.resize_window(win.window, size[0], size[1])
def _position_window(self, win, acct, type):
'''Moves window according to config settings'''
if not gajim.config.get('saveposition') or\
@ -773,18 +799,20 @@ class MessageWindowMgr:
del self._windows[k]
return
def get_control(self, jid, acct):
def get_control(self, jid, acct, thread_id):
'''Amongst all windows, return the MessageControl for jid'''
win = self.get_window(jid, acct)
win = self.get_window(jid, acct, thread_id)
if win:
return win.get_control(jid, acct)
return win.get_control(jid, acct, thread_id)
return None
def get_controls(self, type = None, acct = None):
def get_controls(self, type = None, acct = None, jid = None):
ctrls = []
for c in self.controls():
if acct and c.account != acct:
continue
if jid and c.get_full_jid() != jid:
continue
if not type or c.type_id == type:
ctrls.append(c)
return ctrls
@ -808,7 +836,7 @@ class MessageWindowMgr:
def save_state(self, msg_win):
if not gajim.config.get('saveposition'):
return
# Save window size and position
pos_x_key = 'msgwin-x-position'
pos_y_key = 'msgwin-y-position'
@ -843,11 +871,11 @@ class MessageWindowMgr:
if self.mode != self.ONE_MSG_WINDOW_NEVER:
gajim.config.set_per('accounts', acct, pos_x_key, x)
gajim.config.set_per('accounts', acct, pos_y_key, y)
else:
gajim.config.set(size_width_key, width)
gajim.config.set(size_height_key, height)
if self.mode != self.ONE_MSG_WINDOW_NEVER:
gajim.config.set(pos_x_key, x)
gajim.config.set(pos_y_key, y)

View File

@ -7,11 +7,11 @@ from common import xmpp
class FeatureNegotiationWindow:
'''FeatureNegotiotionWindow class'''
def __init__(self, account, jid, thread_id, form):
def __init__(self, account, jid, session, form):
self.account = account
self.jid = jid
self.form = form
self.thread_id = thread_id
self.session = session
self.xml = gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_window')
self.window = self.xml.get_widget('data_form_window')
@ -27,7 +27,7 @@ class FeatureNegotiationWindow:
def on_ok_button_clicked(self, widget):
acceptance = xmpp.Message(self.jid)
acceptance.setThread(self.thread_id)
acceptance.setThread(self.session.thread_id)
feature = acceptance.NT.feature
feature.setNamespace(xmpp.NS_FEATURE)
@ -44,7 +44,7 @@ class FeatureNegotiationWindow:
# XXX determine whether to reveal presence
rejection = xmpp.Message(self.jid)
rejection.setThread(self.thread_id)
rejection.setThread(self.session.thread_id)
feature = rejection.NT.feature
feature.setNamespace(xmpp.NS_FEATURE)

View File

@ -40,7 +40,7 @@ try:
except ImportError:
USER_HAS_PYNOTIFY = False
def get_show_in_roster(event, account, contact):
def get_show_in_roster(event, account, contact, session):
'''Return True if this event must be shown in roster, else False'''
if event == 'gc_message_received':
return True
@ -51,7 +51,7 @@ 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)
chat_control = helpers.get_chat_control(account, contact, session)
if chat_control:
return False
return True

View File

@ -1190,10 +1190,14 @@ class RosterWindow:
'''reads from db the unread messages, and fire them up'''
for jid in gajim.contacts.get_jid_list(account):
results = gajim.logger.get_unread_msgs_for_jid(jid)
# XXX results should contain sessions anyways
session = gajim.connections[account].make_new_session(jid)
for result in results:
tim = time.localtime(float(result[2]))
self.on_message(jid, result[1], tim, account, msg_type = 'chat',
msg_id = result[0])
msg_id = result[0], session = session)
def fill_contacts_and_groups_dicts(self, array, account):
'''fill gajim.contacts and gajim.groups'''
@ -1255,8 +1259,8 @@ class RosterWindow:
gajim.transport_avatar[account][host].append(contact1.jid)
# If we already have a chat window opened, update it with new contact
# instance
chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
if chat_control:
chat_controls = gajim.interface.msg_win_mgr.get_controls(jid=ji, acct=account)
for chat_control in chat_controls:
chat_control.contact = contact1
def chg_contact_status(self, contact, show, status, account):
@ -1283,14 +1287,14 @@ class RosterWindow:
jid_list = [contact.jid]
for jid in jid_list:
if gajim.interface.msg_win_mgr.has_window(jid, account):
win = gajim.interface.msg_win_mgr.get_window(jid, account)
ctrl = win.get_control(jid, account)
ctrl.contact = gajim.contacts.get_contact_with_highest_priority(
account, contact.jid)
ctrl.update_ui()
win.redraw_tab(ctrl)
for win in gajim.interface.msg_win_mgr.get_windows(jid, account):
for ctrl in win.get_controls(jid=jid, acct=account):
ctrl.contact = gajim.contacts.get_contact_with_highest_priority(
account, contact.jid)
ctrl.update_ui()
win.redraw_tab(ctrl)
name = contact.get_shown_name()
name = contact.get_shown_name()
# if multiple resources (or second one disconnecting)
if (len(contact_instances) > 1 or (len(contact_instances) == 1 and \
@ -3477,18 +3481,22 @@ class RosterWindow:
# We call this here to avoid race conditions with widget validation
chat_control.read_queue()
def new_chat(self, contact, account, resource = None):
def new_chat(self, contact, account, resource = None, session = None):
# Get target window, create a control, and associate it with the window
type_ = message_control.TYPE_CHAT
fjid = contact.jid
if resource:
fjid += '/' + resource
mw = gajim.interface.msg_win_mgr.get_window(fjid, account)
if not session:
session = gajim.connections[account].make_new_session(fjid)
mw = gajim.interface.msg_win_mgr.get_window(fjid, account, session.thread_id)
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
chat_control = ChatControl(mw, contact, account, resource)
chat_control = ChatControl(mw, contact, account, session, resource)
mw.new_tab(chat_control)
@ -3496,6 +3504,8 @@ class RosterWindow:
# We call this here to avoid race conditions with widget validation
chat_control.read_queue()
return session
def new_chat_from_jid(self, account, fjid):
jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
if resource:
@ -3531,7 +3541,7 @@ class RosterWindow:
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, thread = None):
user_nick = '', advanced_notif_num = None, xhtml = None, session = None):
'''when we receive a message'''
contact = None
# if chat window will be for specific resource
@ -3569,16 +3579,18 @@ class RosterWindow:
path = self.get_path(jid, account) # Try to get line of contact in roster
ctrl = session.get_control(advanced_notif_num)
# Look for a chat control that has the given resource
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, account)
if not ctrl:
# 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
# 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
@ -3588,7 +3600,7 @@ class RosterWindow:
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, thread = thread)
message = msg, resource = resource, session = session)
return
# We print if window is opened and it's not a single message
@ -3596,8 +3608,6 @@ class RosterWindow:
typ = ''
if msg_type == 'error':
typ = 'status'
if thread:
ctrl.set_thread_id(thread)
ctrl.print_conversation(msg, typ, tim = tim, encrypted = encrypted,
subject = subject, xhtml = xhtml)
if msg_id:
@ -3610,26 +3620,26 @@ class RosterWindow:
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_roster = notify.get_show_in_roster(event_type, account, contact, session)
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, thread), show_in_roster = show_in_roster,
encrypted, resource, msg_id, xhtml, session), show_in_roster = show_in_roster,
show_in_systray = show_in_systray)
gajim.events.add_event(account, fjid, event)
if popup:
if not ctrl:
self.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 OR he
# is not in the roster at all.
# if popup:
# if not ctrl:
# self.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 OR he
# # is not in the roster at all.
# 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:
# 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)
if not popup:
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]
@ -3875,7 +3885,7 @@ class RosterWindow:
if event.type_ == 'normal':
dialogs.SingleMessageWindow(account, jid,
action = 'receive', from_whom = jid, subject = data[1],
message = data[0], resource = data[5], thread = data[8])
message = data[0], resource = data[5], session = data[8])
gajim.interface.remove_first_event(account, jid, event.type_)
return True
elif event.type_ == 'file-request':
@ -3912,22 +3922,22 @@ class RosterWindow:
jid = jid + u'/' + resource
adhoc_commands.CommandWindow(account, jid)
def on_open_chat_window(self, widget, contact, account, resource = 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 = gajim.interface.msg_win_mgr.get_window(fjid, account)
if not win:
self.new_chat(contact, account, resource = resource)
win = gajim.interface.msg_win_mgr.get_window(fjid, account)
ctrl = win.get_control(fjid, account)
# last message is long time ago
gajim.last_message_time[account][ctrl.get_full_jid()] = 0
win.set_active_tab(fjid, account)
session = self.new_chat(contact, account, resource=resource, session=session)
win = gajim.interface.msg_win_mgr.get_window(fjid, account, session.thread_id)
ctrl = win.get_control(fjid, account, session.thread_id)
# last message is long time ago
gajim.last_message_time[account][ctrl.get_full_jid()] = 0
win.set_active_tab(fjid, account, session.thread_id)
if gajim.connections[account].is_zeroconf and \
gajim.connections[account].status in ('offline', 'invisible'):
win.get_control(fjid, account).got_disconnected()
win.get_control(fjid, account, session.thread_id).got_disconnected()
win.window.present()
@ -3967,7 +3977,10 @@ class RosterWindow:
jid = child_jid
else:
child_iter = model.iter_next(child_iter)
session = None
if first_ev:
session = first_ev.parameters[8]
fjid = jid
if resource:
fjid += '/' + resource
@ -3978,7 +3991,7 @@ class RosterWindow:
c = gajim.contacts.get_contact_with_highest_priority(account, jid)
if jid == gajim.get_jid_from_account(account):
resource = c.resource
self.on_open_chat_window(widget, c, account, resource = resource)
self.on_open_chat_window(widget, c, account, resource = resource, session=session)
def on_roster_treeview_row_activated(self, widget, path, col = 0):
'''When an iter is double clicked: open the first event window'''