diff --git a/src/chat_control.py b/src/chat_control.py
index 6cfd56226..1989fb20f 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -2108,7 +2108,7 @@ class ChatControl(ChatControlBase):
if (not show_transports and gajim.jid_is_transport(jid)) or \
(not show_offline and typ == 'chat' and \
len(gajim.contacts.get_contacts(self.account, jid)) < 2):
- gajim.interface.roster.really_remove_contact(self.contact,
+ gajim.interface.roster.remove_to_be_removed(self.contact.jid,
self.account)
elif typ == 'pm':
control.remove_contact(nick)
diff --git a/src/common/contacts.py b/src/common/contacts.py
index 33ca5adde..1b9937e55 100644
--- a/src/common/contacts.py
+++ b/src/common/contacts.py
@@ -245,6 +245,11 @@ class Contacts:
return c
return None
+ def iter_contacts(self, account):
+ for jid in self._contacts[account]:
+ for contact in self._contacts[account][jid]:
+ yield contact
+
def get_contact_from_full_jid(self, account, fjid):
''' Get Contact object for specific resource of given jid'''
barejid, resource = common.gajim.get_room_and_nick_from_fjid(fjid)
@@ -292,10 +297,6 @@ class Contacts:
for account in accounts:
our_jid = common.gajim.get_jid_from_account(account)
for jid in self.get_jid_list(account):
- if self.has_brother(account, jid) and not \
- self.is_big_brother(account, jid):
- # count metacontacts only once
- continue
if jid == our_jid:
continue
if common.gajim.jid_is_transport(jid) and not \
diff --git a/src/dialogs.py b/src/dialogs.py
index 8ebfc0c44..b13113488 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -91,63 +91,21 @@ class EditGroupsDialog:
if response_id == gtk.RESPONSE_CLOSE:
self.dialog.destroy()
- def update_contact(self):
- for (contact, account) in self.list_:
- tag = gajim.contacts.get_metacontacts_tag(account, contact.jid)
- if not tag:
- gajim.interface.roster.remove_contact(contact, account)
- gajim.interface.roster.add_contact_to_roster(contact.jid, account)
- gajim.connections[account].update_contact(contact.jid, contact.name,
- contact.groups)
- continue
- all_jid = gajim.contacts.get_metacontacts_jids(tag)
- for _account in all_jid:
- if not gajim.interface.roster.regroup and _account != account:
- continue
- for _jid in all_jid[_account]:
- c = gajim.contacts.get_first_contact_from_jid(_account, _jid)
- if not c:
- continue
- gajim.interface.roster.remove_contact(c, _account)
- gajim.interface.roster.add_contact_to_roster(_jid, _account)
- gajim.connections[_account].update_contact(_jid, c.name,
- c.groups)
-
def remove_group(self, group):
'''remove group group from all contacts and all their brothers'''
for (contact, account) in self.list_:
- tag = gajim.contacts.get_metacontacts_tag(account, contact.jid)
- if not tag:
- if group in contact.groups:
- contact.groups.remove(group)
- continue
- all_jid = gajim.contacts.get_metacontacts_jids(tag)
- for _account in all_jid:
- if not gajim.interface.roster.regroup and _account != account:
- continue
- for _jid in all_jid[_account]:
- contacts = gajim.contacts.get_contacts(_account, _jid)
- for c in contacts:
- if group in c.groups:
- c.groups.remove(group)
+ gajim.interface.roster.remove_contact_from_groups(contact.jid, account, [group])
+
+ # FIXME: Ugly workaround.
+ gajim.interface.roster.draw_group(_('General'), account)
def add_group(self, group):
'''add group group to all contacts and all their brothers'''
for (contact, account) in self.list_:
- tag = gajim.contacts.get_metacontacts_tag(account, contact.jid)
- if not tag:
- if group not in contact.groups:
- contact.groups.append(group)
- continue
- all_jid = gajim.contacts.get_metacontacts_jids(tag)
- for _account in all_jid:
- if not gajim.interface.roster.regroup and _account != account:
- continue
- for _jid in all_jid[_account]:
- contacts = gajim.contacts.get_contacts(_account, _jid)
- for c in contacts:
- if not group in c.groups:
- c.groups.append(group)
+ gajim.interface.roster.add_contact_to_groups(contact.jid, account, [group])
+
+ # FIXME: Ugly workaround. Maybe we haven't been in any group (defaults to General)
+ gajim.interface.roster.draw_group(_('General'), account)
def on_add_button_clicked(self, widget):
group = self.xml.get_widget('group_entry').get_text().decode('utf-8')
@@ -166,7 +124,6 @@ class EditGroupsDialog:
self.changes_made = True
model.append((group, True, False))
self.add_group(group)
- self.update_contact()
self.init_list() # Re-draw list to sort new item
def group_toggled_cb(self, cell, path):
@@ -182,7 +139,6 @@ class EditGroupsDialog:
self.add_group(group)
else:
self.remove_group(group)
- self.update_contact()
def init_list(self):
store = gtk.ListStore(str, bool, bool)
@@ -200,7 +156,11 @@ class EditGroupsDialog:
if g in groups:
continue
groups[g] = 0
- for g in contact.groups:
+ c_groups = contact.groups
+ # FIXME: Move to backend
+ if not c_groups:
+ c_groups = [_('General')]
+ for g in c_groups:
groups[g] += 1
group_list = []
# Remove special groups if they are empty
diff --git a/src/gajim.py b/src/gajim.py
index e6d4e7df4..5e3187510 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -644,7 +644,6 @@ class Interface:
old_show = 0
gajim.contacts.add_contact(account, contact1)
lcontact.append(contact1)
- self.roster.add_self_contact(account)
elif contact1.show in statuss:
old_show = statuss.index(contact1.show)
if (resources != [''] and (len(lcontact) != 1 or
@@ -653,6 +652,9 @@ class Interface:
contact1 = gajim.contacts.copy_contact(contact1)
lcontact.append(contact1)
contact1.resource = resource
+ # FIXME ugly workaround for self contact
+ self.roster.add_contact(contact1.jid, account)
+
if contact1.jid.find('@') > 0 and len(lcontact) == 1:
# It's not an agent
if old_show == 0 and new_show > 1:
@@ -669,8 +671,8 @@ class Interface:
if contact1.jid in gajim.newly_added[account]:
gajim.newly_added[account].remove(contact1.jid)
self.roster.draw_contact(contact1.jid, account)
- gobject.timeout_add_seconds(5, self.roster.really_remove_contact,
- contact1, account)
+ gobject.timeout_add_seconds(5, self.roster.remove_to_be_removed,
+ contact1.jid, account)
contact1.show = array[1]
contact1.status = status_message
contact1.priority = priority
@@ -686,6 +688,7 @@ class Interface:
# It must be an agent
if ji in jid_list:
# Update existing iter
+ self.roster.modelfilter.refilter()
self.roster.draw_contact(ji, account)
self.roster.draw_group(_('Transports'), account)
if new_show > 1 and ji in gajim.transport_avatar[account]:
@@ -942,10 +945,10 @@ class Interface:
if jid in gajim.contacts.get_jid_list(account):
c = gajim.contacts.get_first_contact_from_jid(account, jid)
c.resource = array[1]
- self.roster.remove_contact(c, account)
+ self.roster.remove_contact(c.jid, account)
if _('Not in Roster') in c.groups:
c.groups.remove(_('Not in Roster'))
- self.roster.add_contact_to_roster(c.jid, account)
+ self.roster.add_contact(c.jid, account)
else:
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
@@ -958,7 +961,7 @@ class Interface:
groups = [], show = 'online', status = 'online',
ask = 'to', resource = array[1], keyID = keyID)
gajim.contacts.add_contact(account, contact1)
- self.roster.add_contact_to_roster(jid, account)
+ self.roster.add_contact(jid, account)
dialogs.InformationDialog(_('Authorization accepted'),
_('The contact "%s" has authorized you to see his or her status.')
% jid)
@@ -1013,7 +1016,7 @@ class Interface:
# This way we'll really remove it
gajim.to_be_removed[account].remove(c.jid)
gajim.contacts.remove_jid(account, c.jid)
- self.roster.remove_contact(c, account)
+ self.roster.remove_contact(c.jid, account)
def handle_event_register_agent_info(self, account, array):
# ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
@@ -1476,7 +1479,7 @@ class Interface:
not name and not groups:
if contacts:
c = contacts[0]
- self.roster.remove_contact(c, account)
+ self.roster.remove_contact(c.jid, account)
gajim.contacts.remove_jid(account, jid)
self.roster.draw_account(account)
if gajim.events.get_events(account, c.jid):
@@ -1490,7 +1493,7 @@ class Interface:
show = 'not in roster', status = '', sub = 'none',
keyID = keyID)
gajim.contacts.add_contact(account, contact)
- self.roster.add_contact_to_roster(contact.jid, account)
+ self.roster.add_contact(contact.jid, account)
#FIXME if it was the only one in its group, remove the group
return
elif not contacts:
@@ -1500,12 +1503,12 @@ class Interface:
contact = gajim.contacts.create_contact(jid = jid, name = name,
groups = groups, show = 'offline', sub = sub, ask = ask)
gajim.contacts.add_contact(account, contact)
- self.roster.add_contact_to_roster(jid, account)
+ self.roster.add_contact(jid, account)
else:
re_add = False
# if sub changed: remove and re-add, maybe observer status changed
if contacts[0].sub != sub:
- self.roster.remove_contact(contacts[0], account)
+ self.roster.remove_contact(contacts[0].jid, account)
re_add = True
for contact in contacts:
if not name:
@@ -1516,7 +1519,7 @@ class Interface:
if groups:
contact.groups = groups
if re_add:
- self.roster.add_contact_to_roster(jid, account)
+ self.roster.add_contact(jid, account)
self.roster.draw_contact(jid, account)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('RosterInfo', (account, array))
@@ -1666,7 +1669,7 @@ class Interface:
if no_queue: # We didn't have a queue: we change icons
if not gajim.contacts.get_contact_with_highest_priority(account, jid):
if type_ == 'gc-invitation':
- self.roster.add_groupchat_to_roster(account, jid,
+ self.roster.add_groupchat(account, jid,
status='offline')
else:
# add contact to roster ("Not In The Roster") if he is not
@@ -1674,8 +1677,7 @@ class Interface:
self.roster.draw_contact(jid, account)
# Show contact in roster (if he is invisible for example) and select line
- path = self.roster.get_path(jid, account)
- self.roster.show_and_select_path(path, jid, account)
+ self.roster.show_and_select_contact_if_having_events(jid, account)
def remove_first_event(self, account, jid, type_ = None):
event = gajim.events.get_first_event(account, jid, type_)
@@ -1693,7 +1695,7 @@ class Interface:
if contact and (contact.show in ('error', 'offline') and \
not gajim.config.get('showoffline') or (
gajim.jid_is_transport(jid) and not show_transport)):
- self.roster.really_remove_contact(contact, account)
+ self.roster.remove_contact(contact.jid, account)
self.roster.show_title()
self.roster.draw_contact(jid, account)
@@ -1739,7 +1741,7 @@ class Interface:
groups = [_('Not in Roster')], show = 'not in roster', status = '',
sub = 'none', keyID = keyID)
gajim.contacts.add_contact(account, contact)
- self.roster.add_contact_to_roster(contact.jid, account)
+ self.roster.add_contact(contact.jid, account)
file_props = array[1]
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
diff --git a/src/groupchat_control.py b/src/groupchat_control.py
index 740f9f444..003c06b74 100644
--- a/src/groupchat_control.py
+++ b/src/groupchat_control.py
@@ -1587,7 +1587,7 @@ class GroupchatControl(ChatControlBase):
del win._controls[self.account][self.contact.jid]
- gajim.interface.roster.add_groupchat_to_roster(self.account,
+ gajim.interface.roster.add_groupchat(self.account,
self.contact.jid, status = self.subject)
def shutdown(self, status='offline'):
diff --git a/src/roster_window.py b/src/roster_window.py
index fda80adfd..0587cf4c3 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -64,8 +64,7 @@ from lastfm_track_listener import LastFMTrackListener
if sys.platform == 'darwin':
from osx import syncmenu
-
-
+
#(icon, name, type, jid, account, editable, second pixbuf)
(
C_IMG, # image to show state (online, new message etc)
@@ -80,25 +79,41 @@ C_PADLOCK_PIXBUF, # use for account row only
class RosterWindow:
'''Class for main window of the GTK+ interface'''
- def get_account_iter(self, name):
- ''' Returns a gtk.TreeIter of accounts in roster data model or None '''
- model = self.tree.get_model()
+ def _get_account_iter(self, name, model = None):
+ ''' Return the gtk.TreeIter of the given account or None if not found.
+
+ Keyword arguments:
+ name -- the account name
+ model -- the data model (default TreeFilterModel)
+
+ '''
+ if not model:
+ model = self.tree.get_model()
if model is None:
return
account_iter = model.get_iter_root()
if self.regroup:
return account_iter
while account_iter:
- account_name = model[account_iter][C_ACCOUNT].decode('utf-8')
- if name == account_name:
+ account_name = model[account_iter][C_ACCOUNT]
+ if account_name and name == account_name.decode('utf-8'):
break
account_iter = model.iter_next(account_iter)
return account_iter
- def get_group_iter(self, name, account):
- ''' Returns a gtk.TreeIter of groups in roster data model or None '''
- model = self.tree.get_model()
- root = self.get_account_iter(account)
+
+ def _get_group_iter(self, name, account, model = None):
+ ''' Return the gtk.TreeIter of the given group or None if not found.
+
+ Keyword arguments:
+ name -- the group name
+ account -- the account name
+ model -- the data model (default TreeFilterModel)
+
+ '''
+ if not model:
+ model = self.tree.get_model()
+ root = self._get_account_iter(account, model)
group_iter = model.iter_children(root)
# C_NAME column contacts the pango escaped group name
while group_iter:
@@ -107,16 +122,44 @@ class RosterWindow:
break
group_iter = model.iter_next(group_iter)
return group_iter
+
- def get_contact_iter(self, jid, account):
+ def _get_self_contact_iter(self, account, model = None):
+ ''' Return the gtk.TreeIter of SelfContact or None if not found.
+
+ Keyword arguments:
+ account -- the account of SelfContact
+ model -- the data model (default TreeFilterModel)
+
+ '''
+
+ if not model:
+ model = self.tree.get_model()
+ iterAcct = self._get_account_iter(account, model)
+ iter = model.iter_children(iterAcct)
+ if iter and model[iter][C_TYPE] == 'self_contact':
+ return iter
+ return None
+
+
+ def _get_contact_iter(self, jid, account, model = None):
+ ''' Return a list of gtk.TreeIter of the given jid.
+
+ Keyword arguments:
+ jid -- the jid without resource
+ account -- the account
+ model -- the data model (default TreeFilterModel)
+
+ '''
+ if not model:
+ model = self.tree.get_model()
if jid == gajim.get_jid_from_account(account):
- iter = self.get_self_contact_iter(account)
+ iter = self._get_self_contact_iter(account, model)
if iter:
return [iter]
else:
return []
- model = self.tree.get_model()
- acct = self.get_account_iter(account)
+ acct = self._get_account_iter(account, model)
found = []
if model is None: # when closing Gajim model can be none (async pbs?)
return found
@@ -124,7 +167,8 @@ class RosterWindow:
while group_iter:
contact_iter = model.iter_children(group_iter)
while contact_iter:
- if jid == model[contact_iter][C_JID].decode('utf-8') and \
+ iter_jid = model[contact_iter][C_JID]
+ if iter_jid and jid == iter_jid.decode('utf-8') and \
account == model[contact_iter][C_ACCOUNT].decode('utf-8'):
found.append(contact_iter)
# find next contact iter
@@ -152,74 +196,612 @@ class RosterWindow:
contact_iter = next_contact_iter
group_iter = model.iter_next(group_iter)
return found
+
- def get_path(self, jid, account):
- ''' Try to get line of contact in roster '''
- iters = self.get_contact_iter(jid, account)
- if iters:
- path = self.tree.get_model().get_path(iters[0])
- else:
- path = None
- return path
+ def _iter_is_separator(self, model, iter):
+ ''' Return True if the given iter is a separator.
+
+ Keyword arguments:
+ model -- the data model
+ iter -- the gtk.TreeIter to test
+ '''
+ if model[iter][0] == 'SEPARATOR':
+ return True
+ return False
- def show_and_select_path(self, path, jid, account):
- '''Show contact in roster (if he is invisible for example)
- and select line'''
- if not path:
- # contact is in roster but we curently don't see him online
- # show him
- self.add_contact_to_roster(jid, account)
- iters = self.get_contact_iter(jid, account)
- path = self.tree.get_model().get_path(iters[0])
- if self.dragging or not gajim.config.get('scroll_roster_to_last_message'):
- # do not change selection while DND'ing
+
+ def _iter_contact_rows(self, model = None):
+ '''Iterate over all contact rows in given model.
+
+ Keyword arguments:
+ model -- the data model (default TreeFilterModel)
+ '''
+ if not model:
+ model = self.tree.get_model()
+ account_iter = model.get_iter_root()
+ while account_iter:
+ group_iter = model.iter_children(account_iter)
+ while group_iter:
+ contact_iter = model.iter_children(group_iter)
+ while contact_iter:
+ yield model[contact_iter]
+ contact_iter = model.iter_next(contact_iter)
+ group_iter = model.iter_next(group_iter)
+ account_iter = model.iter_next(account_iter)
+
+
+################################################################################
+### Methods for adding and removing roster window items
+################################################################################
+
+ def add_account(self, account):
+ '''Add account to roster but do nothing if it is already in.'''
+ # no redraw, add only
+ if self._get_account_iter(account):
+ # Will happen on reconnect or for merged accounts
return
- # popup == False so we show awaiting event in roster
- # show and select contact line in roster (even if he is not in roster)
- 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)
-
- def add_account_to_roster(self, account):
- ''' Add an account to roster data model. '''
- model = self.tree.get_model()
- if self.get_account_iter(account):
- return
-
- # if we merge accounts...
+
if self.regroup:
+ # Merged accounts view
show = helpers.get_global_show()
- model.append(None, [gajim.interface.jabber_state_images['16'][show],
+ self.model.append(None, [gajim.interface.jabber_state_images['16'][show],
_('Merged accounts'), 'account', '', 'all', None, None])
- self.draw_account(account)
+ else:
+ show = gajim.SHOW_LIST[gajim.connections[account].connected]
+ our_jid = gajim.get_jid_from_account(account)
+
+ tls_pixbuf = None
+ if gajim.account_is_securely_connected(account):
+ tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
+ gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock
+
+ self.model.append(None, [gajim.interface.jabber_state_images['16'][show],
+ gobject.markup_escape_text(account),
+ 'account', our_jid, account, None, tls_pixbuf])
+
+
+ def add_account_contacts(self, account):
+ '''Add all contacts and groups of the given account to roster
+ and draw them.
+ '''
+ c1 = time.clock()
+ jids = gajim.contacts.get_jid_list(account)
+
+ self.starting = True # don't draw contacts
+ self.tree.freeze_child_notify()
+ c5 = time.clock()
+ for jid in jids:
+ self.add_contact(jid, account)
+ c6 = time.clock()
+ self.tree.thaw_child_notify()
+ self.starting = False
+
+ c9 = time.clock()
+ # Draw all known groups
+ for group in gajim.groups[account].keys():
+ self.draw_group(group, account)
+ self.draw_account(account)
+ c10 = time.clock()
+
+ # Do not freeze the GUI when drawing the contacts
+ if jids:
+ # Overhead is big, only invoke when needed
+ self._idle_draw_jids_of_account(jids, account)
+
+ c4 = time.clock()
+
+ print ""
+ print "--- Add account contacts -----------------------------"
+ print "Total Time", c4-c1
+ print "Add contact without draw", c6-c5
+ print "Draw groups and account", c10-c9
+ print "--- contacts added -----------------------------"
+ print ""
+
+
+ def _add_entity(self, contact, account, groups = None, big_brother_contact = None):
+ '''Add the given contact to roster data model.
+
+ Contact is added regardless if he is already in roster or not.
+ Return list of newly added iters.
+
+ Keyword arguments:
+ contact -- the contact to add
+ account -- the contacts account
+ groups -- list of groups to add the contact to. (default groups in contact.groups).
+ Parameter ignored when big_brother_contact is specified.
+ big_brother_contact -- if specified contact is added as child big_brother_contact. (default None)
+ '''
+ added_iters = []
+ if big_brother_contact:
+ # Add contact under big brother
+
+ parent_iters = self._get_contact_iter(big_brother_contact.jid,
+ account, self.model)
+ assert len(parent_iters) > 0,\
+ "Big brother is not yet in roster!"
+
+ for child_iter in parent_iters:
+ it = self.model.append(child_iter, (None, contact.get_shown_name(),
+ 'contact', contact.jid, account, None, None))
+ added_iters.append(it)
+
+ else:
+ # We are a normal contact. Add us to our groups.
+ if not groups:
+ if contact.is_transport():
+ contact.groups = [_('Transports')]
+ if contact.is_observer():
+ contact.groups = [_('Observers')]
+ groups = contact.groups
+ if not groups:
+ groups = [_('General')]
+ for group in groups:
+ child_iterG = self._get_group_iter(group, account, self.model)
+ if not child_iterG:
+ # Group is not yet in roster, add it!
+ child_iterA = self._get_account_iter(account, self.model)
+ child_iterG = self.model.append(child_iterA, [
+ gajim.interface.jabber_state_images['16']['closed'],
+ gobject.markup_escape_text(group), 'group',
+ group, account, None, None])
+
+ if contact.is_transport():
+ typestr = 'agent'
+ elif contact.is_groupchat():
+ typestr = 'groupchat'
+ else:
+ typestr = 'contact'
+
+ # we add some values here. see draw_contact for more
+ i_ = self.model.append(child_iterG, (None, contact.get_shown_name(),
+ typestr, contact.jid, account, None, None))
+ added_iters.append(i_)
+
+ assert len(added_iters), "%s has not been added to roster!" % contact.jid
+ return added_iters
+
+
+ def _remove_entity(self, contact, account, groups = None):
+ '''Remove the given contact from roster data model.
+
+ Empty groups after contact removal are removed too.
+ Return False if contact still has children and deletion was not performed.
+ Return True on success.
+
+ Keyword arguments:
+ contact -- the contact to add
+ account -- the contacts account
+ groups -- list of groups to remove the contact from. (default groups in contact.groups).
+
+ '''
+ iters = self._get_contact_iter(contact.jid, account, self.model)
+ assert iters, "%s shall be removed but is not in roster" % contact.jid
+
+ parent_iter = self.model.iter_parent(iters[0])
+ parent_type = self.model[parent_iter][C_TYPE]
+
+ if groups:
+ # Only remove from specified groups
+ all_iters = iters[:]
+ group_iters = [self._get_group_iter(group, account) for group in groups]
+ iters = [iter for iter in all_iters if self.model.iter_parent(iter) in group_iters]
+
+ iter_children = self.model.iter_children(iters[0])
+
+ if iter_children:
+ # We have children. We cannot be removed!
+ return False
+ else:
+ # Remove us and empty groups from the model
+ for i in iters:
+ parent_i = self.model.iter_parent(i)
+ self.model.remove(i)
+
+ if parent_type == 'group' and \
+ self.model.iter_n_children(parent_i) == 0:
+ group = self.model[parent_i][C_JID].decode('utf-8')
+ if gajim.groups[account].has_key(group):
+ del gajim.groups[account][group]
+ self.model.remove(parent_i)
+ return True
+
+
+ def _add_metacontact_family(self, family):
+ '''Add the give Metacontact family to roster data model.
+
+ Add Big Brother to his groups and all others under him.
+ Return list of all added (contact, account) tuples with Big Brother
+ as first element.
+
+ Keyword arguments:
+ family -- the family, see Contacts.get_metacontacts_family()
+ '''
+ big_brother_data = gajim.contacts.get_metacontacts_big_brother(family)
+ big_brother_jid = big_brother_data['jid']
+ big_brother_account = big_brother_data['account']
+ big_brother_contact = gajim.contacts.get_first_contact_from_jid(big_brother_account, big_brother_jid)
+
+ assert len(self._get_contact_iter(big_brother_jid, big_brother_account, self.model)) == 0,\
+ "Big brother %s already in roster" % big_brother_jid
+ self._add_entity(big_brother_contact, big_brother_account)
+
+ brothers = []
+ for data in family:
+ if data == big_brother_data:
+ continue # already added
+
+ _account = data['account']
+ _jid = data['jid']
+ _contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
+
+ if not _contact:
+ # Corresponding account is not connected
+ continue
+
+ assert len(self._get_contact_iter(_jid, _account, self.model)) == 0,\
+ "%s already in roster" % _jid
+ self._add_entity(_contact, _account, big_brother_contact = big_brother_contact)
+ brothers.append((_contact, _account))
+
+ brothers.insert(0, (big_brother_contact, big_brother_account))
+ return brothers
+
+
+ def _remove_metacontact_family(self, family):
+ '''Remove the give Metacontact family from roster data model.
+
+ See Contacts.get_metacontacts_family() and RosterWindow._remove_entity()
+ '''
+ # Family might has changed (actual big brother not on top).
+ # Remove in correct order: Childs first
+ for data in family:
+ _account = data['account']
+ _jid = data['jid']
+ _contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
+
+ if not _contact:
+ # Corresponding account is not online
+ continue
+
+ iters = self._get_contact_iter(_jid, _account, self.model)
+ assert iters, "%s shall be removed but is not in roster" % _jid
+
+ parent_iter = self.model.iter_parent(iters[0])
+ parent_type = self.model[parent_iter][C_TYPE]
+
+ if parent_type != 'contact':
+ # The contact on top
+ old_big_account = _account
+ old_big_contact = _contact
+ old_big_jid = _jid
+ continue
+
+ ok = self._remove_entity(_contact, _account)
+ assert ok, "%s was not removed" % _jid
+ assert len(self._get_contact_iter(_jid, _account, self.model)) == 0,\
+ "%s is removed but still in roster" % _jid
+
+ iters = self._get_contact_iter(old_big_jid, old_big_account, self.model)
+ assert len(iters) > 0, "Old Big Brother %s is not in roster anymore" % old_big_jid
+ assert not self.model.iter_children(iters[0]),\
+ "Old Big Brother still has children" % old_big_jid
+
+ # This one is strange but necessary:
+ # Refilter filtered model to not crash hard. It thinks it still has children.
+ self.refilter_shown_roster_items()
+
+ ok = self._remove_entity(old_big_contact, old_big_account)
+ assert ok, "Old Big Brother %s not removed" % old_big_jid
+ assert len(self._get_contact_iter(old_big_jid, old_big_account, self.model)) == 0,\
+ "Old Big Brother %s is removed but still in roster" % old_big_jid
+
+ return True
+
+
+ def _add_self_contact(self, account):
+ '''Add account's SelfContact to roster and draw it and the account.
+
+ Return the SelfContact contact instance
+ '''
+ jid = gajim.get_jid_from_account(account)
+ contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+
+ assert len(self._get_contact_iter(jid, account, self.model)) == 0,\
+ "Self contact %s already in roster" % jid
+
+ child_iterA = self._get_account_iter(account, self.model)
+ self.model.append(child_iterA, (None, gajim.nicks[account],
+ 'self_contact', jid, account, None, None))
+
+ self.draw_contact(jid, account)
+ self.draw_avatar(jid, account)
+ self.draw_account(account)
+
+ return contact
+
+
+ def add_contact(self, jid, account):
+ '''Add contact to roster and draw him.
+
+ Add contact to all its group and redraw the groups, the contact and the
+ account. If it's a Metacontact, add and draw the whole family.
+ Do nothing if the contact is already in roster.
+
+ Return the added contact instance. If it is a Metacontact return
+ Big Brother.
+
+ Keyword arguments:
+ jid -- the contact's jid or SelfJid to add SelfContact
+ account -- the corresponding account.
+
+ '''
+ if len(self._get_contact_iter(jid, account, self.model)):
+ # If contact already in roster, do nothing
return
- show = gajim.SHOW_LIST[gajim.connections[account].connected]
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- tls_pixbuf = None
- if gajim.account_is_securely_connected(account):
- tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
- gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock
+ if jid == gajim.get_jid_from_account(account):
+ if contact.resource != gajim.connections[account].server_resource:
+ return self._add_self_contact(account)
+ #FIXME When does this happen?
+ assert False
+ return
+
+ if contact.is_observer():
+ # if he has a tag, remove it
+ tag = gajim.contacts.get_metacontacts_tag(account, jid)
+ if tag:
+ gajim.contacts.remove_metacontact(account, jid)
+
+ # Add contact to roster
+ family = gajim.contacts.get_metacontacts_family(account, jid)
+ contacts = []
+ if family:
+ # We have a family. So we are a metacontact.
+ # Add our whole family
+ contacts = self._add_metacontact_family(family)
+ else:
+ # We are a normal contact
+ contacts = [(contact, account),]
+ self._add_entity(contact, account)
- our_jid = gajim.get_jid_from_account(account)
+ # Draw all groups of the contact
+ if contact.is_transport():
+ contact.groups = [_('Transports')]
+ if contact.is_observer():
+ contact.groups = [_('Observers')]
+ groups = contact.groups
+ if not groups:
+ groups = [_('General')]
+ for group in groups:
+ # Restore the group expand state
+ if group not in gajim.groups[account]:
+ #if account + group in self.collapsed_rows:
+ #ishidden = False
+ #else:
+ ishidden = True
+ gajim.groups[account][group] = {'expand': ishidden}
- model.append(None, [gajim.interface.jabber_state_images['16'][show],
- gobject.markup_escape_text(account),
- 'account', our_jid, account, None, tls_pixbuf])
+ #if not self.starting:
+ self.draw_group(group, account)
+
+ if not self.starting:
+ for c, acc in contacts:
+ self.draw_contact(c.jid, acc)
+ self.draw_avatar(c.jid, acc)
+ self.draw_account(account)
+
+ return contacts[0][0] # it's contact/big brother with highest priority
+
+
+ def remove_contact(self, jid, account):
+ '''Remove contact from roster.
+
+ Remove contact from all its group. Remove empty groups or redraw otherwise.
+ Draw the account.
+ If it's a Metacontact, remove the whole family.
+ Do nothing if the contact is not in roster.
+
+ Keyword arguments:
+ jid -- the contact's jid or SelfJid to remove SelfContact
+ account -- the corresponding account.
+
+ '''
+ iters = self._get_contact_iter(jid, account, self.model)
+ if not iters:
+ return
+
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+
+ # Remove contact from roster
+ family = gajim.contacts.get_metacontacts_family(account, jid)
+ if family:
+ # We have a family. So we are a metacontact.
+ self._remove_metacontact_family(family)
+ else:
+ self._remove_entity(contact, account)
+
+ # Draw all groups of the contact
+ groups = contact.groups
+ if contact.is_transport():
+ contact.groups = [_('Transports')]
+ if contact.is_observer():
+ contact.groups = [_('Observers')]
+ if not groups:
+ groups = [_('General')]
+ for group in groups:
+ self.draw_group(group, account)
+
+ self.draw_account(account)
+
+ return True
+
+
+ def add_groupchat(self, account, jid, resource = '', status = ''):
+ '''Add groupchat to roster and draw it.
+ Return the added contact instance.
+ '''
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ if contact == None:
+ contact = gajim.contacts.create_contact(jid = jid, name = jid,
+ groups = [_('Groupchats')], show = 'online',
+ status = status, sub = 'none',
+ resource = resource)
+ gajim.contacts.add_contact(account, contact)
+ else:
+ contact.show = 'online'
+ self.add_contact(jid, account)
+ return contact
+
+
+ def remove_groupchat(self, jid, account):
+ '''Remove groupchat from roster and redraw account and group.'''
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ self.remove_contact(jid, account)
+ gajim.contacts.remove_contact(account, contact)
+ #FIXME: stupid hack.
+ # When we redraw the group in remove_contact the
+ # contact does still exist and so the group is still showing
+ # the old numbers.
+ # Maybe use timeout to draw groups so that there is enough time
+ # to remove the contact instance.
+ self.draw_group(_('Groupchats'), account)
+ return True
+
+
+ def add_transport(self, account, transport):
+ '''Add transport to roster and draw it.
+ Return the added contact instance.'''
+ contact = gajim.contacts.create_contact(jid = transport, name = transport,
+ groups = [_('Transports')], show = 'offline',
+ status = 'offline', sub = 'from')
+ gajim.contacts.add_contact(account, contact)
+ self.add_contact(transport, account)
+ return contact
+
+ # FIXME: remove_transport method is missing
+
+ #FIXME: Better never even remove....
+ def _readd_contact_to_roster_if_needed(self, contact, account):
+ # FIXME What am I REALLY needed for
+ need_readd = False
+ if len(gajim.events.get_events(account, contact.jid)):
+ need_readd = True
+ elif gajim.interface.msg_win_mgr.has_window(contact.jid, account):
+ if _('Not in Roster') in contact.groups:
+ # Close chat window
+ msg_win = gajim.interface.msg_win_mgr.get_window(contact.jid,
+ account)
+ ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account)
+ msg_win.remove_tab(ctrl, msg_win.CLOSE_CLOSE_BUTTON)
+ else:
+ need_readd = True
+ if need_readd:
+ c = gajim.contacts.create_contact(jid = contact.jid,
+ name = '', groups = [_('Not in Roster')],
+ show = 'not in roster', status = '', ask = 'none',
+ keyID = contact.keyID)
+ gajim.contacts.add_contact(account, c)
+ self.add_contact(contact.jid, account)
+
+
+ def add_contact_to_groups(self, jid, account, groups):
+ '''Add contact to given groups and redraw them.
+
+ Contact on server is updated too. When the contact has a family,
+ the action will be performed for all members.
+
+ Keyword Arguments:
+ jid -- the jid
+ account -- the corresponding account
+ groups -- list of Groups to add the contact too.
+
+ '''
+ self.remove_contact(jid, account)
+
+ for contact in gajim.contacts.get_contacts(account, jid):
+ for group in groups:
+ if group not in contact.groups:
+ # statement needed for drap from meta to group
+ contact.groups.append(group)
+ gajim.connections[account].update_contact(jid, contact.name,
+ contact.groups)
+
+ self.add_contact(jid, account)
+
+
+ def remove_contact_from_groups(self, jid, account, groups):
+ '''Remove contact from given groups and redraw them.
+
+ Contact on server is updated too. When the contact has a family,
+ the action will be performed for all members.
+
+ Keyword Arguments:
+ jid -- the jid
+ account -- the corresponding account
+ groups -- list of Groups to remove the contact from
+
+ '''
+ self.remove_contact(jid, account)
+
+ for contact in gajim.contacts.get_contacts(account, jid):
+ for group in groups:
+ if group in contact.groups:
+ # Needed when we remove from "General"
+ contact.groups.remove(group)
+ gajim.connections[account].update_contact(jid, contact.name,
+ contact.groups)
+
+ self.add_contact(jid, account)
+
+ for group in groups:
+ self.draw_group(group, account)
+
+
+ # FIXME: maybe move to gajim.py
+ def remove_newly_added(self, jid, account):
+ if jid in gajim.newly_added[account]:
+ gajim.newly_added[account].remove(jid)
+ self.draw_contact(jid, account)
+
+
+ # FIXME: maybe move to gajim.py
+ def remove_to_be_removed(self, jid, account):
+ if not gajim.interface.instances.has_key(account):
+ # Account has been deleted during the timeout that called us
+ return
+ if jid in gajim.newly_added[account]:
+ return
+ if jid in gajim.to_be_removed[account]:
+ gajim.to_be_removed[account].remove(jid)
+ self.draw_contact(jid, account)
+ self.refilter_shown_roster_items()
+
+
+ #FIXME: integrate into add_contact()
+ def add_to_not_in_the_roster(self, account, jid, nick = '', resource = ''):
+ keyID = ''
+ attached_keys = gajim.config.get_per('accounts', account,
+ 'attached_gpg_keys').split()
+ if jid in attached_keys:
+ keyID = attached_keys[attached_keys.index(jid) + 1]
+ contact = gajim.contacts.create_contact(jid = jid, name = nick,
+ groups = [_('Not in Roster')], show = 'not in roster', status = '',
+ sub = 'none', resource = resource, keyID = keyID)
+ gajim.contacts.add_contact(account, contact)
+ self.add_contact(contact.jid, account)
+ return contact
+
+
+################################################################################
+### Methods for adding and removing roster window items
+################################################################################
def draw_account(self, account):
- if account in self.draw_account_id:
- return
- self.draw_account_id[account] = gobject.timeout_add(500,
- self.really_draw_account, account)
+ child_iter = self._get_account_iter(account, self.model)
- def really_draw_account(self, account):
- if account in self.draw_account_id:
- del self.draw_account_id[account]
- model = self.tree.get_model()
- iter = self.get_account_iter(account)
-
num_of_accounts = gajim.get_number_of_connected_accounts()
num_of_secured = gajim.get_number_of_securely_connected_accounts()
@@ -227,17 +809,17 @@ class RosterWindow:
self.regroup and num_of_secured and num_of_secured == num_of_accounts:
tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock
- model[iter][C_PADLOCK_PIXBUF] = tls_pixbuf
+ self.model[child_iter][C_PADLOCK_PIXBUF] = tls_pixbuf
else:
- model[iter][C_PADLOCK_PIXBUF] = None
- path = model.get_path(iter)
+ self.model[child_iter][C_PADLOCK_PIXBUF] = None
+ path = self.model.get_path(child_iter)
account_name = account
accounts = [account]
if self.regroup:
account_name = _('Merged accounts')
accounts = []
- if not self.tree.row_expanded(path) and model.iter_has_child(iter):
- # account row not expanded
+ iter = self._get_account_iter(account)
+ if not self.tree.row_expanded(path):
account_name = '[%s]' % account_name
if (gajim.account_is_connected(account) or (self.regroup and \
gajim.get_number_of_connected_accounts())) and gajim.config.get(
@@ -245,176 +827,18 @@ class RosterWindow:
nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
accounts = accounts)
account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
- model[iter][C_NAME] = account_name
-
- def remove_newly_added(self, jid, account):
- if jid in gajim.newly_added[account]:
- gajim.newly_added[account].remove(jid)
- self.draw_contact(jid, account)
-
- def add_contact_to_roster(self, jid, account):
- '''Add a contact to the roster and add groups if they aren't in roster
- force is about force to add it, even if it is offline and show offline
- is False, because it has online children, so we need to show it.
- If add_children is True, we also add all children, even if they were not
- already drawn'''
- showOffline = gajim.config.get('showoffline')
- model = self.tree.get_model()
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- if not contact:
- return
- nb_events = gajim.events.get_nb_roster_events(account, contact.jid)
- # count events from all resources
- for contact_ in gajim.contacts.get_contacts(account, jid):
- if contact_.resource:
- nb_events += gajim.events.get_nb_roster_events(account,
- contact_.get_full_jid())
- # If contact already in roster, do not add it
- if len(self.get_contact_iter(jid, account)):
- return
- if jid == gajim.get_jid_from_account(account):
- if contact.resource != gajim.connections[account].server_resource:
- self.add_self_contact(account)
- return
- if gajim.jid_is_transport(contact.jid):
- # if jid is transport, check if we wanna show it in roster
- if not gajim.config.get('show_transports_group') and not nb_events:
- return
- contact.groups = [_('Transports')]
- elif not showOffline and not gajim.account_is_connected(account) and \
- nb_events == 0:
- return
-
- # XEP-0162
- hide = contact.is_hidden_from_roster()
- if hide and contact.sub != 'from':
- return
- observer = contact.is_observer()
-
- if observer:
- # if he has a tag, remove it
- tag = gajim.contacts.get_metacontacts_tag(account, jid)
- if tag:
- gajim.contacts.remove_metacontact(account, jid)
-
- # family is [{'account': acct, 'jid': jid, 'priority': prio}, ]
- # 'priority' is optional
- family = gajim.contacts.get_metacontacts_family(account, jid)
-
- # family members that are in roster and belong to the same account.
- shown_family = []
- if family:
- for data in family:
- _account = data['account']
- # Metacontacts over different accounts only in merged mode
- if _account != account and not self.regroup:
- continue
- _jid = data['jid']
-
- if self.get_contact_iter(_jid, _account):
- shown_family.append(data)
- if _jid == jid and _account == account:
- our_data = data
- shown_family.append(our_data)
- big_brother_data = gajim.contacts.get_metacontacts_big_brother(
- shown_family)
- big_brother_jid = big_brother_data['jid']
- big_brother_account = big_brother_data['account']
- if big_brother_jid != jid or big_brother_account != account:
- # We are adding a child contact
- if contact.show in ('offline', 'error') and \
- not showOffline and len(gajim.events.get_events(account, jid)) == 0:
- return
- parent_iters = self.get_contact_iter(big_brother_jid,
- big_brother_account)
- name = contact.get_shown_name()
- for i in parent_iters:
- # we add some values here. see draw_contact for more
- model.append(i, (None, name, 'contact', jid, account, None,
- None))
- self.draw_contact(jid, account)
- self.draw_avatar(jid, account)
- self.draw_account(account)
- # Redraw parent to change icon
- self.draw_contact(big_brother_jid, big_brother_account)
- return
-
- if (contact.show in ('offline', 'error') or hide) and \
- not showOffline and (not _('Transports') in contact.groups or \
- gajim.connections[account].connected < 2) and \
- len(gajim.contacts.get_contacts(account, jid)) == 1 and nb_events == 0 and\
- not _('Not in Roster') in contact.groups:
- return
-
- # Remove brother contacts that are already in roster to add them
- # under this iter
- for data in shown_family:
- contacts = gajim.contacts.get_contacts(data['account'],
- data['jid'])
- for c in contacts:
- self.remove_contact(c, data['account'])
- groups = contact.groups
- if observer:
- groups = [_('Observers')]
- elif not groups:
- groups = [_('General')]
- for group in groups:
- self.draw_group(group, account)
- iterG = self.get_group_iter(group, account)
- if not iterG:
- IterAcct = self.get_account_iter(account)
- iterG = model.append(IterAcct, [
- gajim.interface.jabber_state_images['16']['closed'],
- gobject.markup_escape_text(group), 'group',
- group, account, None, None])
- self.draw_group(group, account)
- if model.iter_n_children(IterAcct) == 1: # We added the first one
- self.draw_account(account)
- if group not in gajim.groups[account]: # It can probably never append
- if account + group in self.collapsed_rows:
- ishidden = False
- else:
- ishidden = True
- gajim.groups[account][group] = {'expand': ishidden}
- if not account in self.collapsed_rows:
- self.tree.expand_row((model.get_path(iterG)[0]), False)
-
- typestr = 'contact'
- if group == _('Transports'):
- typestr = 'agent'
- if gajim.gc_connected[account].has_key(jid):
- typestr = 'groupchat'
-
- name = contact.get_shown_name()
- # we add some values here. see draw_contact for more
- model.append(iterG, (None, name, typestr, contact.jid, account, None,
- None))
-
- if gajim.groups[account][group]['expand']:
- self.tree.expand_row(model.get_path(iterG), False)
- self.draw_contact(jid, account)
- self.draw_avatar(jid, account)
- self.draw_account(account)
- # put the children under this iter
- for data in shown_family:
- contacts = gajim.contacts.get_contacts(data['account'],
- data['jid'])
- self.add_contact_to_roster(data['jid'], data['account'])
-
+ self.model[child_iter][C_NAME] = account_name
+ return False
+
def draw_group(self, group, account):
- key = (group, account)
- if key in self.draw_group_id:
+ child_iter = self._get_group_iter(group, account, self.model)
+ if not child_iter:
+ # Eg. We redraw groups after we removed a entitiy
+ # and its empty groups
return
- self.draw_group_id[key] = gobject.timeout_add(500,
- self.really_draw_group, group, account)
-
- def really_draw_group(self, group, account):
key = (group, account)
if key in self.draw_group_id:
del self.draw_group_id[key]
- iter = self.get_group_iter(group, account)
- if not iter:
- return
if self.regroup:
accounts = []
else:
@@ -426,201 +850,35 @@ class RosterWindow:
nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
accounts = accounts, groups = [group])
text += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
- model = self.tree.get_model()
- model.set_value(iter, 1 , text)
-
- def add_to_not_in_the_roster(self, account, jid, nick = '', resource = ''):
- ''' add jid to group "not in the roster", he MUST not be in roster yet,
- return contact '''
- keyID = ''
- attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
- if jid in attached_keys:
- keyID = attached_keys[attached_keys.index(jid) + 1]
- contact = gajim.contacts.create_contact(jid = jid, name = nick,
- groups = [_('Not in Roster')], show = 'not in roster', status = '',
- sub = 'none', resource = resource, keyID = keyID)
- gajim.contacts.add_contact(account, contact)
- self.add_contact_to_roster(contact.jid, account)
- return contact
-
- def add_groupchat_to_roster(self, account, jid, resource = '', status = ''):
- ''' add groupchat to roster '''
- contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- if contact == None:
- contact = gajim.contacts.create_contact(jid = jid, name = jid,
- groups = [_('Groupchats')], show = 'online',
- status = status, sub = 'none',
- resource = resource)
- gajim.contacts.add_contact(account, contact)
- self.add_contact_to_roster(jid, account)
- self.draw_group(_('Groupchats'), account)
- else:
- contact.show = 'online'
- self.draw_contact(jid, account)
- self.add_contact_to_roster(jid, account)
- for group in contact.groups:
- self.draw_group(group, account)
- self.draw_account(account)
- return contact
-
- def get_self_contact_iter(self, account):
- model = self.tree.get_model()
- iterAcct = self.get_account_iter(account)
- iter = model.iter_children(iterAcct)
- if not iter:
- return None
- if model[iter][C_TYPE] == 'self_contact':
- return iter
- return None
-
- def add_self_contact(self, account):
- jid = gajim.get_jid_from_account(account)
- if self.get_self_contact_iter(account):
- self.draw_contact(jid, account)
- self.draw_avatar(jid, account)
+
+ self.model.set_value(child_iter, 1 , gobject.markup_escape_text(text))
+ return False
+
+ def draw_parent_contact(self, jid, account):
+ child_iters = self._get_contact_iter(jid, account, self.model)
+ parent_iter = self.model.iter_parent(child_iters[0])
+ if self.model[parent_iter][C_TYPE] != 'contact':
+ # parent is not a contact
return
-
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- if not contact:
- return
- showOffline = gajim.config.get('showoffline')
- if (contact.show in ('offline', 'error')) and not showOffline and \
- len(gajim.events.get_events(account, jid)) == 0:
- return
-
- model = self.tree.get_model()
- iterAcct = self.get_account_iter(account)
- model.append(iterAcct, (None, gajim.nicks[account], 'self_contact', jid,
- account, None, None))
- self.draw_contact(jid, account)
- self.draw_avatar(jid, account)
-
- def add_transport_to_roster(self, account, transport):
- c = gajim.contacts.create_contact(jid = transport, name = transport,
- groups = [_('Transports')], show = 'offline', status = 'offline',
- sub = 'from')
- gajim.contacts.add_contact(account, c)
- self.add_contact_to_roster(transport, account)
-
- def really_remove_contact(self, contact, account):
- if not gajim.interface.instances.has_key(account):
- # Account has been deleted during the timeout that called us
- return
- if contact.jid in gajim.newly_added[account]:
- return
- if gajim.jid_is_transport(contact.jid) and gajim.account_is_connected(
- account) and gajim.config.get('show_transports_group'):
- # It's an agent and we show them
- return
- if contact.jid in gajim.to_be_removed[account]:
- gajim.to_be_removed[account].remove(contact.jid)
-
-
- hide = contact.is_hidden_from_roster()
-
- show_offline = gajim.config.get('showoffline')
- show_transports = gajim.config.get('show_transports_group')
-
- nb_events = 0
- jid_list = [contact.jid]
- if contact.get_full_jid() != contact.jid:
- jid_list.append(contact.get_full_jid())
- for jid in jid_list:
- # dont't count printed_chat messages
- nb_events += gajim.events.get_nb_roster_events(account, jid, ['chat'])
-
- if (_('Transports') in contact.groups and not show_transports) or \
- ((contact.show in ('offline', 'error') or hide) and not show_offline and \
- (not _('Transports') in contact.groups or \
- gajim.account_is_disconnected(account))) and nb_events == 0:
- self.remove_contact(contact, account)
- else:
- # If it's a metacontact, big brother may have changed, so remove and
- # re-add
- model = self.tree.get_model()
- iters = self.get_contact_iter(contact.jid, account)
- if iters and model.iter_has_child(iters[0]):
- self.remove_contact(contact, account)
- self.add_contact_to_roster(contact.jid, account)
- else:
- self.draw_contact(contact.jid, account)
-
- def remove_contact(self, contact, account):
- '''Remove a contact from the roster'''
- if contact.jid in gajim.to_be_removed[account]:
- return
- model = self.tree.get_model()
- iters = self.get_contact_iter(contact.jid, account)
- if not iters:
- return
- parent_iter = model.iter_parent(iters[0])
- parent_type = model[parent_iter][C_TYPE]
- # remember children to re-add them
- children = []
- child_iter = model.iter_children(iters[0])
- while child_iter:
- c_jid = model[child_iter][C_JID].decode('utf-8')
- c_account = model[child_iter][C_ACCOUNT].decode('utf-8')
- children.append((c_jid, c_account))
- child_iter = model.iter_next(child_iter)
-
- # Remove iters and group iter if they are empty
- for i in iters:
- parent_i = model.iter_parent(i)
- model.remove(i)
- if parent_type == 'group':
- group = model[parent_i][C_JID].decode('utf-8')
- if model.iter_n_children(parent_i) == 0:
- model.remove(parent_i)
- # We need to check all contacts, even offline contacts
- for jid in gajim.contacts.get_jid_list(account):
- if group in gajim.contacts.get_contact_with_highest_priority(
- account, jid).groups:
- break
- else:
- if gajim.groups[account].has_key(group):
- del gajim.groups[account][group]
- else:
- self.draw_group(group, account)
-
- # re-add children
- for child in children:
- self.add_contact_to_roster(child[0], child[1])
- # redraw parent
- if parent_type == 'contact':
- parent_jid = model[parent_iter][C_JID].decode('utf-8')
- parent_account = model[parent_iter][C_ACCOUNT].decode('utf-8')
- self.draw_contact(parent_jid, parent_account)
-
- def get_appropriate_state_images(self, jid, size = '16',
- icon_name = 'online'):
- '''check jid and return the appropriate state images dict for
- the demanded size. icon_name is taken into account when jid is from
- transport: transport iconset doesn't contain all icons, so we fall back
- to jabber one'''
- transport = gajim.get_transport_name_from_jid(jid)
- if transport and self.transports_state_images.has_key(size):
- if not self.transports_state_images[size].has_key(transport):
- # we don't have iconset for this transport loaded yet. Let's do it
- self.make_transport_state_images(transport)
- if self.transports_state_images[size].has_key(transport) and \
- icon_name in self.transports_state_images[size][transport]:
- return self.transports_state_images[size][transport]
- return gajim.interface.jabber_state_images[size]
-
+ parent_jid = self.model[parent_iter][C_JID].decode('utf-8')
+ parent_account = self.model[parent_iter][C_ACCOUNT].decode('utf-8')
+ self.draw_contact(parent_jid, parent_account)
+
def draw_contact(self, jid, account, selected = False, focus = False):
'''draw the correct state image, name BUT not avatar'''
# focus is about if the roster window has toplevel-focus or not
- model = self.tree.get_model()
- iters = self.get_contact_iter(jid, account)
- if len(iters) == 0:
- return
+
+ # FIXME Remove functionality but check before if all cases
+ # are handled by GTK
+ selected = False
+
+ child_iters = self._get_contact_iter(jid, account, self.model)
+ if not child_iters:
+ return False
+
contact_instances = gajim.contacts.get_contacts(account, jid)
contact = gajim.contacts.get_highest_prio_contact_from_contacts(
contact_instances)
- if not contact:
- return
name = gobject.markup_escape_text(contact.get_shown_name())
# gets number of unread gc marked messages
@@ -634,7 +892,8 @@ class RosterWindow:
name = '%s *' % name
elif nb_unread > 1:
name = '%s [%s]' % (name, str(nb_unread))
-
+
+ # Strike name if blocked
strike = False
if jid in gajim.connections[account].blocked_contacts:
strike = True
@@ -651,6 +910,7 @@ class RosterWindow:
if strike:
name = '%s' % name
+ # Show resource counter
nb_connected_contact = 0
for c in contact_instances:
if c.show not in ('error', 'offline'):
@@ -662,16 +922,15 @@ class RosterWindow:
if self.regroup:
add_acct = False
# look through all contacts of all accounts
- for account_iter in gajim.connections:
- if account_iter == account: # useless to add accout name
+ for account_ in gajim.connections:
+ if account_ == account: # useless to add accout name
continue
- for jid_iter in gajim.contacts.get_jid_list(account_iter):
+ for jid_ in gajim.contacts.get_jid_list(account_):
# [0] cause it'fster than highest_prio
- contact_iter = gajim.contacts.\
- get_first_contact_from_jid(account_iter, jid_iter)
- if contact_iter.get_shown_name() == \
- contact.get_shown_name() and\
- (jid_iter, account_iter) != (jid, account):
+ contact_ = gajim.contacts.get_first_contact_from_jid(account_,
+ jid_)
+ if contact_.get_shown_name() == contact.get_shown_name() and \
+ (jid_, account_) != (jid, account):
add_acct = True
break
if add_acct:
@@ -691,12 +950,34 @@ class RosterWindow:
name += \
'\n%s' \
% (colorstring, gobject.markup_escape_text(status))
+
+ # Check if our metacontacts familiy has changed
+ brothers = []
+ family = gajim.contacts.get_metacontacts_family(account, jid)
+ if family: # Are we a metacontact (have a familiy)
+ big_brother_data = gajim.contacts.get_metacontacts_big_brother(family)
+ big_brother_jid = big_brother_data['jid']
+ big_brother_account = big_brother_data['account']
- iter = iters[0] # choose the icon with the first iter
-
- if gajim.gc_connected[account].has_key(jid):
- contact.show = 'online'
- model[iter][C_TYPE] = 'groupchat'
+ if big_brother_jid == jid and big_brother_account == account:
+ # We are the big brother. But have also been before?
+ iters = self._get_contact_iter(jid, account, self.model)
+ if self.model.iter_has_child(iters[0]):
+ # We have children. We must have been
+ # big brother even before
+ pass
+ else:
+ # We are the new big brother but haven't been before
+ # Remove us and all our brothers and then re-add us so that
+ self._remove_metacontact_family(family)
+ brothers = self._add_metacontact_family(family)
+ big_brother_c, big_brother_acc = brothers[0]
+ brothers = brothers[1:]
+ self.draw_avatar(big_brother_c.jid, big_brother_acc)
+ else:
+ # We are a simple brother
+ # Let our big brother know of our existence (and possible events)
+ self.draw_contact(big_brother_jid, big_brother_account)
icon_name = helpers.get_icon_name_to_show(contact, account)
# look if another resource has awaiting events
@@ -705,29 +986,36 @@ class RosterWindow:
if c_icon_name in ('event', 'muc_active', 'muc_inactive'):
icon_name = c_icon_name
break
- path = model.get_path(iter)
- if model.iter_has_child(iter):
+
+ iters = self._get_contact_iter(jid, account)
+ if iters and self.modelfilter.iter_has_child(iters[0]):
+ # We are big brother contact and visible in the roster
+ iter = iters[0]
+ path = self.modelfilter.get_path(iter)
+
if not self.tree.row_expanded(path) and \
icon_name not in ('event', 'muc_active', 'muc_inactive'):
- child_iter = model.iter_children(iter)
+
+ iterC = self.modelfilter.iter_children(iter)
if icon_name in ('error', 'offline'):
# get the icon from the first child as they are sorted by show
- child_jid = model[child_iter][C_JID].decode('utf-8')
- child_account = model[child_iter][C_ACCOUNT].decode('utf-8')
- child_contact = gajim.contacts.get_contact_with_highest_priority(
- child_account, child_jid)
- child_icon_name = helpers.get_icon_name_to_show(child_contact,
- child_account)
- if child_icon_name not in ('error', 'not in roster'):
- icon_name = child_icon_name
- while child_iter:
+ jidC = self.modelfilter[iterC][C_JID].decode('utf-8')
+ accountC = self.modelfilter[iterC][C_ACCOUNT].decode(
+ 'utf-8')
+ contactC = gajim.contacts.get_contact_with_highest_priority(
+ accountC, jidC)
+ icon_nameC = helpers.get_icon_name_to_show(contactC, accountC)
+ if icon_nameC not in ('error', 'not in roster'):
+ icon_name = icon_nameC
+ while iterC:
# a child has awaiting messages ?
- child_jid = model[child_iter][C_JID].decode('utf-8')
- child_account = model[child_iter][C_ACCOUNT].decode('utf-8')
- if len(gajim.events.get_events(child_account, child_jid)):
+ jidC = self.modelfilter[iterC][C_JID].decode('utf-8')
+ accountC = self.modelfilter[iterC][C_ACCOUNT].decode('utf-8')
+ if len(gajim.events.get_events(accountC, jidC)):
icon_name = 'event'
break
- child_iter = model.iter_next(child_iter)
+ iterC = self.modelfilter.iter_next(iterC)
+
if self.tree.row_expanded(path):
state_images = self.get_appropriate_state_images(jid,
size = 'opened', icon_name = icon_name)
@@ -735,47 +1023,815 @@ class RosterWindow:
state_images = self.get_appropriate_state_images(jid,
size = 'closed', icon_name = icon_name)
else:
- # redraw parent
- self.draw_parent_contact(jid, account)
+ # We might be a normal contact or a little brother
+ #self.draw_parent_contact(jid, account) # simply call, it's save
state_images = self.get_appropriate_state_images(jid,
icon_name = icon_name)
img = state_images[icon_name]
+ child_iters = self._get_contact_iter(jid, account, self.model)
+ for child_iter in child_iters:
+ self.model[child_iter][C_IMG] = img
+ self.model[child_iter][C_NAME] = name
- for iter in iters:
- model[iter][C_IMG] = img
- model[iter][C_NAME] = name
+ for c, acc in brothers:
+ self.draw_contact(c.jid, acc)
+ self.draw_avatar(c.jid, acc)
+ return False
- def draw_parent_contact(self, jid, account):
- model = self.tree.get_model()
- iters = self.get_contact_iter(jid, account)
- if not len(iters):
- return
- parent_iter = model.iter_parent(iters[0])
- if model[parent_iter][C_TYPE] != 'contact':
- # parent is not a contact
- return
- parent_jid = model[parent_iter][C_JID].decode('utf-8')
- parent_account = model[parent_iter][C_ACCOUNT].decode('utf-8')
- self.draw_contact(parent_jid, parent_account)
def draw_avatar(self, jid, account):
- '''draw the avatar'''
+ iters = self._get_contact_iter(jid, account, self.model)
+ self.draw_avatar_from_iters(iters)
+
+ def draw_avatar_from_iters(self, iters):
+ '''draw the avatar
+ iters are iters in treemodel, not treemodelfilter'''
if not gajim.config.get('show_avatars_in_roster'):
return
- model = self.tree.get_model()
- iters = self.get_contact_iter(jid, account)
- if gajim.config.get('show_avatars_in_roster'):
- pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid)
- if pixbuf in ('ask', None):
- scaled_pixbuf = None
- else:
- scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'roster')
- else:
+ jid = self.model[iters[0]][C_JID]
+ jid = jid.decode('utf-8')
+ pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid)
+ if pixbuf is None or pixbuf == 'ask':
scaled_pixbuf = None
- for iter in iters:
- model[iter][C_AVATAR_PIXBUF] = scaled_pixbuf
+ else:
+ scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'roster')
+ for child_iter in iters:
+ self.model[child_iter][C_AVATAR_PIXBUF] = scaled_pixbuf
+ return False
+ 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
+ '''
+ contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+ self.draw_contact(jid, account)
+
+ groups = contact.groups
+ if contact.is_transport():
+ contact.groups = [_('Transports')]
+ if contact.is_observer():
+ contact.groups = [_('Observers')]
+ if not groups:
+ groups = [_('General')]
+ for group in groups:
+ self.draw_group(group, account)
+
+ self.draw_account(account)
+ self.draw_contact(jid, account)
+ self.refilter_shown_roster_items()
+
+ def _idle_draw_jids_of_account(self, jids, account):
+ '''Draw given contacts and their avatars in a lazy fashion.
+
+ Keyword arguments:
+ jids -- a list of jids to draw
+ account -- the corresponding account
+ '''
+ def _draw_all_contacts(jids, account, t):
+ for jid in jids:
+ self.draw_contact(jid, account)
+ self.draw_avatar(jid, account)
+ yield True
+ print "--- Idle draw -----------------"
+ print "Draw contact and avatar", time.clock() - t
+ print "-------------------------------"
+ yield False
+
+ t = time.clock()
+ task = _draw_all_contacts(jids, account, t)
+ gobject.idle_add(task.next)
+
+ def draw_roster(self):
+ '''clear and draw roster'''
+ # clear the model, only if it is not empty
+ if self.model:
+ self.model.clear()
+ for acct in gajim.connections:
+ self.add_account(acct)
+ self.add_account_contacts(acct)
+ # Recalculate column width for ellipsizing
+ self.tree.columns_autosize()
+
+
+ def show_and_select_contact_if_having_events(self, jid, account):
+ '''Select contact in roster. If contact is hidden but has eventsi,
+ show him.'''
+ self.refilter_shown_roster_items()
+ iters = self._get_contact_iter(jid, account)
+ if not iters:
+ # Not visible in roster
+ return
+ path = self.tree.get_model().get_path(iters[0])
+ if self.dragging or not gajim.config.get('scroll_roster_to_last_message'):
+ # do not change selection while DND'ing
+ return
+ 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)
+
+################################################################################
+### Roster and Modelfilter handling
+################################################################################
+
+ def _search_roster_func(self, model, column, key, iter):
+ if model[iter][C_NAME].decode('utf-8').lower().startswith(
+ gobject.markup_escape_text(key.lower())):
+ return False
+ return True
+
+ def refilter_shown_roster_items(self):
+ self.filtering = True
+ self.modelfilter.refilter()
+ self.filtering = False
+
+ def contact_is_visible(self, contact, account):
+ # show it if pending events
+ if gajim.events.get_nb_roster_events(account, contact.jid) > 0:
+ return True
+ # count events from all resources
+ for contact_ in gajim.contacts.get_contacts(account, contact.jid):
+ if contact_.resource and gajim.events.get_nb_roster_events(account,
+ contact_.get_full_jid()) > 0:
+ return True
+ # XEP-0162
+ hide = contact.is_hidden_from_roster()
+ if hide and contact.sub != 'from':
+ return False
+
+ showOffline = gajim.config.get('showoffline')
+ if (contact.show in ('offline', 'error') or hide) and not showOffline:
+ if contact.jid in gajim.to_be_removed[account]:
+ return True
+ return False
+ return True
+
+ def _visible_func(self, model, iter):
+ '''Determine whether iter should be visible in the treeview'''
+ type_ = model[iter][C_TYPE]
+ if not type_:
+ return False
+ if type_ == 'account':
+ return True
+ account = model[iter][C_ACCOUNT]
+ if not account:
+ return False
+ account = account.decode('utf-8')
+ jid = model[iter][C_JID]
+ if not jid:
+ return False
+ jid = jid.decode('utf-8')
+
+ if type_ == 'group':
+ group = jid
+ if group == _('Transports'):
+ return gajim.config.get('show_transports_group')
+ for contact in gajim.contacts.iter_contacts(account):
+ # Is this contact in this group ?
+ if group in contact.groups or (group == _('General') and not \
+ contact.groups):
+ if self.contact_is_visible(contact, account):
+ return True
+ return False
+ if type_ == 'contact':
+ contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+ return self.contact_is_visible(contact, account)
+ if type_ == 'agent':
+ return gajim.config.get('show_transports_group')
+ return True
+
+ def _compareIters(self, model, iter1, iter2, data = None):
+ '''Compare two iters to sort them'''
+ name1 = model[iter1][C_NAME]
+ name2 = model[iter2][C_NAME]
+ if not name1 or not name2:
+ return 0
+ name1 = name1.decode('utf-8')
+ name2 = name2.decode('utf-8')
+ type1 = model[iter1][C_TYPE]
+ type2 = model[iter2][C_TYPE]
+ if type1 == 'self_contact':
+ return -1
+ if type2 == 'self_contact':
+ return 1
+ if type1 == 'group':
+ name1 = model[iter1][C_JID]
+ name2 = model[iter2][C_JID]
+ if name1 == _('Transports'):
+ return 1
+ if name2 == _('Transports'):
+ return -1
+ if name1 == _('Not in Roster'):
+ return 1
+ if name2 == _('Not in Roster'):
+ return -1
+ if name1 == _('Groupchats'):
+ return 1
+ if name2 == _('Groupchats'):
+ return -1
+ account1 = model[iter1][C_ACCOUNT]
+ account2 = model[iter2][C_ACCOUNT]
+ if not account1 or not account2:
+ return 0
+ account1 = account1.decode('utf-8')
+ account2 = account2.decode('utf-8')
+ if type1 == 'account':
+ if account1 < account2:
+ return -1
+ return 1
+ jid1 = model[iter1][C_JID].decode('utf-8')
+ jid2 = model[iter2][C_JID].decode('utf-8')
+ if type1 == 'contact':
+ lcontact1 = gajim.contacts.get_contacts(account1, jid1)
+ contact1 = gajim.contacts.get_first_contact_from_jid(account1, jid1)
+ if not contact1:
+ return 0
+ name1 = contact1.get_shown_name()
+ if type2 == 'contact':
+ lcontact2 = gajim.contacts.get_contacts(account2, jid2)
+ contact2 = gajim.contacts.get_first_contact_from_jid(account2, jid2)
+ if not contact2:
+ return 0
+ name2 = contact2.get_shown_name()
+ # We first compare by show if sort_by_show is True or if it's a child
+ # contact
+ if type1 == 'contact' and type2 == 'contact' and \
+ gajim.config.get('sort_by_show'):
+ cshow = {'online':0, 'chat': 1, 'away': 2, 'xa': 3, 'dnd': 4,
+ 'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
+ s = self.get_show(lcontact1)
+ if s in cshow:
+ show1 = cshow[s]
+ else:
+ show1 = 9
+ s = self.get_show(lcontact2)
+ if s in cshow:
+ show2 = cshow[s]
+ else:
+ show2 = 9
+ removing1 = False
+ removing2 = False
+ if show1 == 6 and jid1 in gajim.to_be_removed[account1]:
+ removing1 = True
+ if show2 == 6 and jid2 in gajim.to_be_removed[account2]:
+ removing2 = True
+ if removing1 and not removing2:
+ return -1
+ if removing2 and not removing1:
+ return 1
+ if show1 < show2:
+ return -1
+ elif show1 > show2:
+ return 1
+ # We compare names
+ if name1.lower() < name2.lower():
+ return -1
+ if name2.lower() < name1.lower():
+ return 1
+ if type1 == 'contact' and type2 == 'contact':
+ # We compare account names
+ if account1.lower() < account2.lower():
+ return -1
+ if account2.lower() < account1.lower():
+ return 1
+ # We compare jids
+ if jid1.lower() < jid2.lower():
+ return -1
+ if jid2.lower() < jid1.lower():
+ return 1
+ return 0
+
+################################################################################
+### FIXME: Methods that don't belong to roster window...
+### ... atleast not in there current form
+################################################################################
+
+ def fire_up_unread_messages_events(self, account):
+ '''reads from db the unread messages, and fire them up, and
+ if we find very old unread messages, delete them from unread table'''
+ results = gajim.logger.get_unread_msgs()
+ for result in results:
+ jid = result[4]
+ if gajim.contacts.get_first_contact_from_jid(account, jid):
+ # We have this jid in our contacts list
+ # XXX unread messages should probably have their session saved with
+ # them
+ 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)
+
+ 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
+ # roster for accounts we usually launch, so we will delete this id
+ # from unread message tables.
+ gajim.logger.set_read_messages([result[0]])
+
+ def fill_contacts_and_groups_dicts(self, array, account):
+ '''fill gajim.contacts and gajim.groups'''
+ if account not in gajim.contacts.get_accounts():
+ gajim.contacts.add_account(account)
+ if not gajim.groups.has_key(account):
+ gajim.groups[account] = {}
+ for jid in array.keys():
+ # Remove old Contact instances
+ gajim.contacts.remove_jid(account, jid, remove_meta=False)
+ jids = jid.split('/')
+ # get jid
+ ji = jids[0]
+ # get resource
+ resource = ''
+ if len(jids) > 1:
+ resource = '/'.join(jids[1:])
+ # get name
+ name = array[jid]['name']
+ if not name:
+ name = ''
+ show = 'offline' # show is offline by default
+ status = '' # no status message by default
+
+ keyID = ''
+ attached_keys = gajim.config.get_per('accounts', account,
+ 'attached_gpg_keys').split()
+ if jid in attached_keys:
+ keyID = attached_keys[attached_keys.index(jid) + 1]
+ contact1 = gajim.contacts.create_contact(jid = ji, name = name,
+ groups = array[jid]['groups'], show = show, status = status,
+ sub = array[jid]['subscription'], ask = array[jid]['ask'],
+ resource = resource, keyID = keyID)
+ gajim.contacts.add_contact(account, contact1)
+
+ # when we draw the roster, we avoid having the same contact
+ # more than once (f.e. we avoid showing it twice when 2 resources)
+ for g in array[jid]['groups']:
+ if g in gajim.groups[account].keys():
+ continue
+
+ #if account + g in self.collapsed_rows:
+ #ishidden = False
+ #else:
+ ishidden = True
+ gajim.groups[account][g] = { 'expand': ishidden }
+ if gajim.config.get('ask_avatars_on_startup'):
+ pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(ji)
+ if pixbuf == 'ask':
+ transport = gajim.get_transport_name_from_jid(contact1.jid)
+ if not transport or gajim.jid_is_transport(contact1.jid):
+ jid_with_resource = contact1.jid
+ if contact1.resource:
+ jid_with_resource += '/' + contact1.resource
+ gajim.connections[account].request_vcard(jid_with_resource)
+ else:
+ host = gajim.get_server_from_jid(contact1.jid)
+ if not gajim.transport_avatar[account].has_key(host):
+ 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
+ # instance
+ chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
+ if chat_control:
+ chat_control.contact = contact1
+
+ def enable_syncing_status_msg_from_current_music_track(self, enabled):
+ '''enable setting status msg from currently playing music track
+
+ if enabled is True, we listen to events from music players about
+ currently played music track, and we update our
+ status message accordinly'''
+ if not dbus_support.supported:
+ # do nothing if user doesn't have D-Bus bindings
+ return
+ if enabled:
+ listener = MusicTrackListener.get()
+ if self._music_track_changed_signal is None:
+ self._music_track_changed_signal = listener.connect(
+ 'music-track-changed', self._music_track_changed)
+ track = listener.get_playing_track()
+ self._music_track_changed(listener, track)
+ else:
+ if self._music_track_changed_signal is not None:
+ listener = MusicTrackListener.get()
+ listener.disconnect(self._music_track_changed_signal)
+ self._music_track_changed_signal = None
+ self._music_track_changed(None, None)
+
+ def enable_syncing_status_msg_from_lastfm(self, enabled):
+ ''' enable setting status msg from a Last.fm account
+
+ if enabled is True, we start polling the Last.fm server,
+ and we update our status message accordinly'''
+ if enabled:
+ if self._music_track_changed_signal is None:
+ listener = LastFMTrackListener.get(
+ gajim.config.get('lastfm_username'))
+ self._music_track_changed_signal = listener.connect(
+ 'music-track-changed', self._music_track_changed)
+ track = listener.get_playing_track()
+ self._music_track_changed(listener, track)
+ else:
+ if self._music_track_changed_signal is not None:
+ listener = LastFMTrackListener.get(
+ gajim.config.get('lastfm_username'))
+ listener.disconnect(self._music_track_changed_signal)
+ self._music_track_changed_signal = None
+ self._music_track_changed(None, None)
+
+ def _change_awn_icon_status(self, status):
+ if not dbus_support.supported:
+ # do nothing if user doesn't have D-Bus bindings
+ return
+ try:
+ bus = dbus.SessionBus()
+ if not 'com.google.code.Awn' in bus.list_names():
+ # Awn is not installed
+ return
+ except:
+ return
+ iconset = gajim.config.get('iconset')
+ prefix = os.path.join(helpers.get_iconset_path(iconset), '32x32')
+ if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'):
+ status = status + '.png'
+ elif status == 'online':
+ prefix = os.path.join(gajim.DATA_DIR, 'pixmaps')
+ status = 'gajim.png'
+ path = os.path.join(prefix, status)
+ try:
+ obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn')
+ awn = dbus.Interface(obj, 'com.google.code.Awn')
+ awn.SetTaskIconByName('Gajim', os.path.abspath(path))
+ except Exception, e:
+ pass
+
+ def _music_track_changed(self, unused_listener, music_track_info,
+ account=''):
+ from common import pep
+ if account == '':
+ accounts = gajim.connections.keys()
+ if music_track_info is None:
+ artist = ''
+ title = ''
+ source = ''
+ track = ''
+ length = ''
+ elif hasattr(music_track_info, 'paused') and music_track_info.paused == 0:
+ artist = ''
+ title = ''
+ source = ''
+ track = ''
+ length = ''
+ else:
+ artist = music_track_info.artist
+ title = music_track_info.title
+ source = music_track_info.album
+ if account == '':
+ for account in accounts:
+ if not gajim.account_is_connected(account):
+ continue
+ if not gajim.connections[account].pep_supported:
+ continue
+ pep.user_send_tune(account, artist, title, source)
+ elif gajim.connections[account].pep_supported:
+ pep.user_send_tune(account, artist, title, source)
+
+
+ def connected_rooms(self, account):
+ if account in gajim.gc_connected[account].values():
+ return True
+ return False
+
+ def auto_join_bookmarks(self, account):
+ '''autojoin bookmarks that have 'auto join' on for this account'''
+ for bm in gajim.connections[account].bookmarks:
+ if bm['autojoin'] in ('1', 'true'):
+ jid = bm['jid']
+ if not gajim.gc_connected[account].has_key(jid) or\
+ not gajim.gc_connected[account][jid]:
+ # we are not already connected
+ minimize = bm['minimize'] in ('1', 'true')
+ self.join_gc_room(account, jid, bm['nick'],
+ bm['password'], minimize = minimize)
+
+ def open_event(self, account, jid, event):
+ '''If an event was handled, return True, else return False'''
+ data = event.parameters
+ ft = gajim.interface.instances['file_transfers']
+ if event.type_ == 'normal':
+ dialogs.SingleMessageWindow(account, jid,
+ action='receive', from_whom=jid, subject=data[1], message=data[0],
+ resource=data[5], session=data[8], form_node=data[9])
+ gajim.interface.remove_first_event(account, jid, event.type_)
+ return True
+ elif event.type_ == 'file-request':
+ contact = gajim.contacts.get_contact_with_highest_priority(account,
+ jid)
+ gajim.interface.remove_first_event(account, jid, event.type_)
+ ft.show_file_request(account, contact, data)
+ return True
+ elif event.type_ in ('file-request-error', 'file-send-error'):
+ gajim.interface.remove_first_event(account, jid, event.type_)
+ ft.show_send_error(data)
+ return True
+ elif event.type_ in ('file-error', 'file-stopped'):
+ gajim.interface.remove_first_event(account, jid, event.type_)
+ ft.show_stopped(jid, data)
+ return True
+ elif event.type_ == 'file-completed':
+ gajim.interface.remove_first_event(account, jid, event.type_)
+ ft.show_completed(jid, data)
+ return True
+ elif event.type_ == 'gc-invitation':
+ dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
+ data[1])
+ gajim.interface.remove_first_event(account, jid, event.type_)
+ return True
+ return False
+
+################################################################################
+### This and that... random.
+################################################################################
+
+ def show_roster_vbox(self, active):
+ if active:
+ self.xml.get_widget('roster_vbox2').show()
+ else:
+ self.xml.get_widget('roster_vbox2').hide()
+
+
+ def show_tooltip(self, contact):
+ pointer = self.tree.get_pointer()
+ props = self.tree.get_path_at_pos(pointer[0], pointer[1])
+ # check if the current pointer is at the same path
+ # as it was before setting the timeout
+ if props and self.tooltip.id == props[0]:
+ # bounding rectangle of coordinates for the cell within the treeview
+ rect = self.tree.get_cell_area(props[0], props[1])
+
+ # position of the treeview on the screen
+ position = self.tree.window.get_origin()
+ self.tooltip.show_tooltip(contact, rect.height, position[1] + rect.y)
+ else:
+ self.tooltip.hide_tooltip()
+
+
+ def authorize(self, widget, jid, account):
+ '''Authorize a contact (by re-sending auth menuitem)'''
+ gajim.connections[account].send_authorization(jid)
+ dialogs.InformationDialog(_('Authorization has been sent'),
+ _('Now "%s" will know your status.') %jid)
+
+ def req_sub(self, widget, jid, txt, account, groups = [], nickname = None,
+ auto_auth = False):
+ '''Request subscription to a contact'''
+ gajim.connections[account].request_subscription(jid, txt, nickname,
+ groups, auto_auth, gajim.nicks[account])
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ if not contact:
+ keyID = ''
+ attached_keys = gajim.config.get_per('accounts', account,
+ 'attached_gpg_keys').split()
+ if jid in attached_keys:
+ keyID = attached_keys[attached_keys.index(jid) + 1]
+ contact = gajim.contacts.create_contact(jid = jid, name = nickname,
+ groups = groups, show = 'requested', status = '', ask = 'none',
+ sub = 'subscribe', keyID = keyID)
+ gajim.contacts.add_contact(account, contact)
+ else:
+ if not _('Not in Roster') in contact.groups:
+ dialogs.InformationDialog(_('Subscription request has been sent'),
+ _('If "%s" accepts this request you will know his or her status.'
+ ) % jid)
+ return
+ contact.groups = groups
+ if nickname:
+ contact.name = nickname
+ self.remove_contact(contact.jid, account)
+ self.add_contact(jid, account)
+
+ def revoke_auth(self, widget, jid, account):
+ '''Revoke a contact's authorization'''
+ gajim.connections[account].refuse_authorization(jid)
+ dialogs.InformationDialog(_('Authorization has been removed'),
+ _('Now "%s" will always see you as offline.') %jid)
+
+ def set_connecting_state(self, account):
+ model = self.tree.get_model()
+ child_model = model.get_model()
+ IterA = self._get_account_iter(account)
+ if IterA:
+ child_iterA = model.convert_iter_to_child_iter(IterA)
+ child_model[child_iterA][0] = \
+ gajim.interface.jabber_state_images['16']['connecting']
+ if gajim.interface.systray_enabled:
+ gajim.interface.systray.change_status('connecting')
+
+ def send_status(self, account, status, txt, auto = False, to = None):
+ model = self.tree.get_model()
+ child_model = model.get_model()
+ iterA = self._get_account_iter(account)
+ if status != 'offline':
+ if gajim.connections[account].connected < 2:
+ self.set_connecting_state(account)
+
+ if not gajim.connections[account].password:
+ passphrase = ''
+ text = _('Enter your password for account %s') % account
+ if passwords.USER_HAS_GNOMEKEYRING and \
+ not passwords.USER_USES_GNOMEKEYRING:
+ text += '\n' + _('Gnome Keyring is installed but not correctly started\
+ (environment variable probably not correctly set)')
+ w = dialogs.PassphraseDialog(_('Password Required'), text,
+ _('Save password'))
+ passphrase, save = w.run()
+ if passphrase == -1:
+ if iterA:
+ child_iterA = model.convert_iter_to_child_iter(iterA)
+ child_model[child_iterA][0] = gajim.interface.jabber_state_images[
+ '16']['offline']
+ if gajim.interface.systray_enabled:
+ gajim.interface.systray.change_status('offline')
+ self.update_status_combobox()
+ return
+ gajim.connections[account].password = passphrase
+ if save:
+ gajim.config.set_per('accounts', account, 'savepass', True)
+ passwords.save_password(account, passphrase)
+
+ keyid = gajim.config.get_per('accounts', account, 'keyid')
+ if keyid and not gajim.connections[account].gpg:
+ dialog = dialogs.WarningDialog(_('GPG is not usable'),
+ _('You will be connected to %s without OpenPGP.') % account)
+
+ if gajim.account_is_connected(account):
+ if status == 'online' and gajim.interface.sleeper.getState() != \
+ common.sleepy.STATE_UNKNOWN:
+ gajim.sleeper_state[account] = 'online'
+ elif gajim.sleeper_state[account] not in ('autoaway', 'autoxa'):
+ gajim.sleeper_state[account] = 'off'
+ if to:
+ gajim.connections[account].send_custom_status(status, txt, to)
+ else:
+ was_invisible = gajim.connections[account].connected == \
+ gajim.SHOW_LIST.index('invisible')
+ gajim.connections[account].change_status(status, txt, auto)
+
+ if gajim.interface.status_sent_to_users.has_key(account):
+ gajim.interface.status_sent_to_users[account] = {}
+ if gajim.interface.status_sent_to_groups.has_key(account):
+ gajim.interface.status_sent_to_groups[account] = {}
+ for gc_control in gajim.interface.msg_win_mgr.get_controls(
+ message_control.TYPE_GC) + \
+ gajim.interface.minimized_controls[account].values():
+ if gc_control.account == account:
+ if gajim.gc_connected[account][gc_control.room_jid]:
+ gajim.connections[account].send_gc_status(gc_control.nick,
+ gc_control.room_jid, status, txt)
+ else:
+ # for some reason, we are not connected to the room even if
+ # tab is opened, send initial join_gc()
+ gajim.connections[account].join_gc(gc_control.nick,
+ gc_control.room_jid, None)
+ if was_invisible and status != 'offline':
+ # We come back from invisible, join bookmarks
+ self.auto_join_bookmarks(account)
+
+ def chg_contact_status(self, contact, show, status, account):
+ '''When a contact changes his or her status'''
+ contact_instances = gajim.contacts.get_contacts(account, contact.jid)
+ contact.show = show
+ contact.status = status
+ # name is to show in conversation window
+ name = contact.get_shown_name()
+
+ if len(contact_instances) > 1:
+ if contact.resource != '':
+ name += '/' + contact.resource
+ if show in ('offline', 'error') and \
+ len(gajim.events.get_events(account, contact.get_full_jid())) == 0:
+ jid_with_resource = contact.jid + '/' + contact.resource
+ if gajim.interface.msg_win_mgr.has_window(jid_with_resource,
+ 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)
+ gajim.contacts.remove_contact(account, contact)
+ elif contact.jid == gajim.get_jid_from_account(account) and show == 'offline':
+ # Our SelfContact went offline. Remove him
+ self.remove_contact(contact.jid, account)
+
+ # 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)
+
+ # unset custom status
+ if gajim.interface.status_sent_to_users.has_key(account) and \
+ contact.jid in gajim.interface.status_sent_to_users[account]:
+ del gajim.interface.status_sent_to_users[account][contact.jid]
+
+ # Redraw everything and select the sender
+ self.draw_completely_and_show_if_needed(contact.jid, account)
+
+
+ def on_status_changed(self, account, status):
+ '''the core tells us that our status has changed'''
+ if account not in gajim.contacts.get_accounts():
+ return
+ child_iterA = self._get_account_iter(account, self.model)
+ self.set_account_status_icon(account)
+ if status == 'offline':
+ if self.quit_on_next_offline > -1:
+ # we want to quit, we are waiting for all accounts to be offline
+ self.quit_on_next_offline -= 1
+ if self.quit_on_next_offline < 1:
+ # all accounts offline, quit
+ self.quit_gtkgui_interface()
+ else:
+ # No need to redraw contacts if we're quitting
+ if child_iterA:
+ self.model[child_iterA][C_AVATAR_PIXBUF] = None
+ if gajim.con_types.has_key(account):
+ gajim.con_types[account] = None
+ for jid in gajim.contacts.get_jid_list(account):
+ lcontact = gajim.contacts.get_contacts(account, jid)
+ for contact in [c for c in lcontact if (c.show != 'offline' or \
+ c.is_transport())]:
+ self.chg_contact_status(contact, 'offline', '', account)
+ # Remove SelfContact from roster. It might be gone when we return
+ self.remove_contact(gajim.get_jid_from_account(account), account)
+
+ self.actions_menu_needs_rebuild = True
+ self.update_status_combobox()
+ # Force the rebuild now since the on_activates on the menu itself does
+ # not work with the os/x top level menubar
+ if sys.platform == 'darwin':
+ self.make_menu(force = True)
+
+ def get_status_message(self, show):
+ if show in gajim.config.get_per('defaultstatusmsg'):
+ if gajim.config.get_per('defaultstatusmsg', show, 'enabled'):
+ return gajim.config.get_per('defaultstatusmsg', show, 'message')
+ if (show == 'online' and not gajim.config.get('ask_online_status')) or \
+ (show in ('offline', 'invisible')
+ and not gajim.config.get('ask_offline_status')):
+ return ''
+ dlg = dialogs.ChangeStatusMessageDialog(show)
+ dlg.window.present() # show it on current workspace
+ message = dlg.run()
+ return message
+
+ def change_status(self, widget, account, status):
+ def change(account, status):
+ message = self.get_status_message(status)
+ if message is None:
+ # user pressed Cancel to change status message dialog
+ return
+ self.send_status(account, status, message)
+
+ if status == 'invisible' and self.connected_rooms(account):
+ dialogs.ConfirmationDialog(
+ _('You are participating in one or more group chats'),
+ _('Changing your status to invisible will result in disconnection '
+ 'from those group chats. Are you sure you want to go invisible?'),
+ on_response_ok = (change, account, status))
+ else:
+ change(account, status)
+
+ 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, session = session)
+ 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)
+ if gajim.connections[account].is_zeroconf and \
+ gajim.connections[account].status in ('offline', 'invisible'):
+ win.get_control(fjid, account).got_disconnected()
+ win.window.present()
+
def join_gc_room(self, account, room_jid, nick, password, minimize=False,
is_continued=False):
'''joins the room immediatelly'''
@@ -802,7 +1858,7 @@ class RosterWindow:
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)
+ self.add_groupchat(account, room_jid)
return
if not minimized_control_exists and \
not gajim.interface.msg_win_mgr.has_window(room_jid, account):
@@ -817,7 +1873,346 @@ class RosterWindow:
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)
+ self.add_groupchat(account, room_jid)
+
+ def update_status_combobox(self):
+ # table to change index in connection.connected to index in combobox
+ table = {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
+ 'xa':3, 'dnd':4, 'invisible':5}
+
+ # we check if there are more options in the combobox that it should
+ # if yes, we remove the first ones
+ while len(self.status_combobox.get_model()) > len(table)+2:
+ self.status_combobox.remove_text(0)
+
+ show = helpers.get_global_show()
+ # temporarily block signal in order not to send status that we show
+ # in the combobox
+ self.combobox_callback_active = False
+ if helpers.statuses_unified():
+ self.status_combobox.set_active(table[show])
+ else:
+ uf_show = helpers.get_uf_show(show)
+ liststore = self.status_combobox.get_model()
+ liststore.prepend(['SEPARATOR', None, '', True])
+ liststore.prepend([uf_show + ' ' +"(desync'ed)",
+ gajim.interface.jabber_state_images['16'][show], show, False])
+ self.status_combobox.set_active(0)
+ self._change_awn_icon_status(show)
+ self.combobox_callback_active = True
+ if gajim.interface.systray_enabled:
+ gajim.interface.systray.change_status(show)
+
+ def new_private_chat(self, gc_contact, account, session = None):
+ contact = gajim.contacts.contact_from_gc_contact(gc_contact)
+ type_ = message_control.TYPE_PM
+ fjid = gc_contact.room_jid + '/' + gc_contact.name
+ mw = gajim.interface.msg_win_mgr.get_window(fjid, account)
+ if not mw:
+ mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
+
+ chat_control = PrivateChatControl(mw, gc_contact, contact, account, session)
+ mw.new_tab(chat_control)
+ if len(gajim.events.get_events(account, fjid)):
+ # We call this here to avoid race conditions with widget validation
+ chat_control.read_queue()
+
+ 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 mw:
+ mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
+
+ chat_control = ChatControl(mw, contact, account, session, resource)
+
+ 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()
+
+ def new_chat_from_jid(self, account, fjid):
+ jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
+ contact = gajim.contacts.get_contact(account, jid, resource)
+ added_to_roster = False
+ if not contact:
+ added_to_roster = True
+ contact = self.add_to_not_in_the_roster(account, jid,
+ resource = resource)
+
+ if not gajim.interface.msg_win_mgr.has_window(fjid, account):
+ self.new_chat(contact, account, resource = resource)
+ mw = gajim.interface.msg_win_mgr.get_window(fjid, account)
+ mw.set_active_tab(fjid, account)
+ mw.window.present()
+ # For JEP-0172
+ if added_to_roster:
+ mc = mw.get_control(fjid, account)
+ mc.user_nick = gajim.nicks[account]
+
+ def new_room(self, room_jid, nick, account, is_continued=False):
+ # Get target window, create a control, and associate it with the window
+ contact = gajim.contacts.create_contact(jid = room_jid, name = nick)
+ mw = gajim.interface.msg_win_mgr.get_window(contact.jid, account)
+ if not mw:
+ mw = gajim.interface.msg_win_mgr.create_window(contact, account,
+ GroupchatControl.TYPE_ID)
+ gc_control = GroupchatControl(mw, contact, account,
+ is_continued=is_continued)
+ mw.new_tab(gc_control)
+
+ def get_show(self, lcontact):
+ prio = lcontact[0].priority
+ show = lcontact[0].show
+ for u in lcontact:
+ if u.priority > prio:
+ prio = u.priority
+ show = u.show
+ return show
+
+ def on_message_window_delete(self, win_mgr, msg_win):
+ if gajim.config.get('one_message_window') == 'always_with_roster':
+ self.show_roster_vbox(True)
+ gtkgui_helpers.resize_window(self.window,
+ 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
+ iters = self._get_contact_iter(jid, account)
+ 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:
+ # FIXME: What is happening here. What does "OR he is not in the roster at all" mean
+ 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:
+ 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():
+ if type(w) == type({}):
+ self.close_all_from_dict(w)
+ else:
+ w.window.destroy()
+
+ def close_all(self, account, force = False):
+ '''close all the windows from an account
+ if force is True, do not ask confirmation before closing chat/gc windows
+ '''
+ if account in gajim.interface.instances:
+ self.close_all_from_dict(gajim.interface.instances[account])
+ for ctrl in gajim.interface.msg_win_mgr.get_controls(acct = account):
+ ctrl.parent_win.remove_tab(ctrl, ctrl.parent_win.CLOSE_CLOSE_BUTTON,
+ force = force)
+
+ def on_roster_window_delete_event(self, widget, event):
+ '''Main window X button was clicked'''
+ if gajim.interface.systray_enabled and not gajim.config.get(
+ 'quit_on_roster_x_button'):
+ self.tooltip.hide_tooltip()
+ self.window.hide()
+ else:
+ self.on_quit_request()
+ return True # do NOT destroy the window
+
+ def quit_gtkgui_interface(self):
+ '''When we quit the gtk interface :
+ tell that to the core and exit gtk'''
+ msgwin_width_adjust = 0
+
+ # in case show_roster_on_start is False and roster is never shown
+ # window.window is None
+ if self.window.window is not None:
+ x, y = self.window.window.get_root_origin()
+ gajim.config.set('roster_x-position', x)
+ gajim.config.set('roster_y-position', y)
+ width, height = self.window.get_size()
+ # For the width use the size of the vbox containing the tree and
+ # status combo, this will cancel out any hpaned width
+ width = self.xml.get_widget('roster_vbox2').allocation.width
+ gajim.config.set('roster_width', width)
+ gajim.config.set('roster_height', height)
+ if not self.xml.get_widget('roster_vbox2').get_property('visible'):
+ # The roster vbox is hidden, so the message window is larger
+ # then we want to save (i.e. the window will grow every startup)
+ # so adjust.
+ msgwin_width_adjust = -1 * width
+ gajim.config.set('show_roster_on_startup',
+ self.window.get_property('visible'))
+ gajim.interface.msg_win_mgr.shutdown(msgwin_width_adjust)
+
+ gajim.config.set('collapsed_rows', '\t'.join(self.collapsed_rows))
+ gajim.interface.save_config()
+ for account in gajim.connections:
+ gajim.connections[account].quit(True)
+ self.close_all(account)
+ if gajim.interface.systray_enabled:
+ gajim.interface.hide_systray()
+ gtk.main_quit()
+
+ 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 '''
+ accounts = gajim.connections.keys()
+ get_msg = False
+ for acct in accounts:
+ if gajim.connections[acct].connected:
+ get_msg = True
+ break
+ if get_msg:
+ message = self.get_status_message('offline')
+ if message is None:
+ # user pressed Cancel to change status message dialog
+ return
+
+ # check if we have unread messages
+ 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
+
+ # check if we have recent messages
+ recent = False
+ for win in gajim.interface.msg_win_mgr.windows():
+ for ctrl in win.controls():
+ fjid = ctrl.get_full_jid()
+ if gajim.last_message_time[ctrl.account].has_key(fjid):
+ if time.time() - gajim.last_message_time[ctrl.account][fjid] < 2:
+ recent = True
+ break
+ if recent:
+ break
+
+ if unread or recent:
+ dialog = dialogs.ConfirmationDialog(_('You have unread messages'),
+ _('Messages will only be available for reading them later if you'
+ ' have history enabled and contact is in your roster.'))
+ if dialog.get_response() != gtk.RESPONSE_OK:
+ return
+
+ self.quit_on_next_offline = 0
+ for acct in accounts:
+ 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...
+################################################################################
def on_actions_menuitem_activate(self, widget):
self.make_menu()
@@ -872,61 +2267,1986 @@ class RosterWindow:
helpers.exec_command('python history_manager.py')
else: # Unix user
helpers.exec_command('python history_manager.py &')
-
- def get_and_connect_advanced_menuitem_menu(self, account):
- '''adds FOR ACCOUNT options'''
- xml = gtkgui_helpers.get_glade('advanced_menuitem_menu.glade')
- advanced_menuitem_menu = xml.get_widget('advanced_menuitem_menu')
-
- xml_console_menuitem = xml.get_widget('xml_console_menuitem')
- privacy_lists_menuitem = xml.get_widget('privacy_lists_menuitem')
- administrator_menuitem = xml.get_widget('administrator_menuitem')
- send_server_message_menuitem = xml.get_widget(
- 'send_server_message_menuitem')
- set_motd_menuitem = xml.get_widget('set_motd_menuitem')
- update_motd_menuitem = xml.get_widget('update_motd_menuitem')
- delete_motd_menuitem = xml.get_widget('delete_motd_menuitem')
-
- xml_console_menuitem.connect('activate',
- self.on_xml_console_menuitem_activate, account)
-
- if gajim.connections[account] and gajim.connections[account].\
- privacy_rules_supported:
- privacy_lists_menuitem.connect('activate',
- self.on_privacy_lists_menuitem_activate, account)
- else:
- privacy_lists_menuitem.set_sensitive(False)
-
+
+ def on_info(self, widget, contact, account):
+ '''Call vcard_information_window class to display contact's information'''
if gajim.connections[account].is_zeroconf:
- administrator_menuitem.set_sensitive(False)
- send_server_message_menuitem.set_sensitive(False)
- set_motd_menuitem.set_sensitive(False)
- update_motd_menuitem.set_sensitive(False)
- delete_motd_menuitem.set_sensitive(False)
+ self.on_info_zeroconf(widget, contact, account)
+ return
+
+ info = gajim.interface.instances[account]['infos']
+ if info.has_key(contact.jid):
+ info[contact.jid].window.present()
else:
- send_server_message_menuitem.connect('activate',
- self.on_send_server_message_menuitem_activate, account)
+ info[contact.jid] = vcard.VcardWindow(contact, account)
- set_motd_menuitem.connect('activate',
- self.on_set_motd_menuitem_activate, account)
+ def on_info_zeroconf(self, widget, contact, account):
+ info = gajim.interface.instances[account]['infos']
+ if info.has_key(contact.jid):
+ info[contact.jid].window.present()
+ else:
+ contact = gajim.contacts.get_first_contact_from_jid(account,
+ contact.jid)
+ if contact.show in ('offline', 'error'):
+ # don't show info on offline contacts
+ return
+ info[contact.jid] = vcard.ZeroconfVcardWindow(contact, account)
- update_motd_menuitem.connect('activate',
- self.on_update_motd_menuitem_activate, account)
+ def on_roster_treeview_leave_notify_event(self, widget, event):
+ props = widget.get_path_at_pos(int(event.x), int(event.y))
+ if self.tooltip.timeout > 0:
+ if not props or self.tooltip.id == props[0]:
+ self.tooltip.hide_tooltip()
- delete_motd_menuitem.connect('activate',
- self.on_delete_motd_menuitem_activate, account)
+ def on_roster_treeview_motion_notify_event(self, widget, event):
+ model = widget.get_model()
+ props = widget.get_path_at_pos(int(event.x), int(event.y))
+ if self.tooltip.timeout > 0:
+ if not props or self.tooltip.id != props[0]:
+ self.tooltip.hide_tooltip()
+ if props:
+ [row, col, x, y] = props
+ iter = None
+ try:
+ iter = model.get_iter(row)
+ except:
+ self.tooltip.hide_tooltip()
+ return
+ if model[iter][C_TYPE] in ('contact', 'self_contact'):
+ # we're on a contact entry in the roster
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ jid = model[iter][C_JID].decode('utf-8')
+ if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
+ self.tooltip.id = row
+ contacts = gajim.contacts.get_contacts(account, jid)
+ connected_contacts = []
+ for c in contacts:
+ if c.show not in ('offline', 'error'):
+ connected_contacts.append(c)
+ if not connected_contacts:
+ # no connected contacts, show the ofline one
+ connected_contacts = contacts
+ self.tooltip.account = account
+ self.tooltip.timeout = gobject.timeout_add(500,
+ self.show_tooltip, connected_contacts)
+ elif model[iter][C_TYPE] == 'groupchat':
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ jid = model[iter][C_JID].decode('utf-8')
+ if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
+ self.tooltip.id = row
+ contact = gajim.contacts.get_contacts(account, jid)
+ self.tooltip.account = account
+ self.tooltip.timeout = gobject.timeout_add(500,
+ self.show_tooltip, contact)
+ elif model[iter][C_TYPE] == 'account':
+ # we're on an account entry in the roster
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ if account == 'all':
+ if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
+ self.tooltip.id = row
+ self.tooltip.account = None
+ self.tooltip.timeout = gobject.timeout_add(500,
+ self.show_tooltip, [])
+ return
+ jid = gajim.get_jid_from_account(account)
+ contacts = []
+ connection = gajim.connections[account]
+ # get our current contact info
- advanced_menuitem_menu.show_all()
+ nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
+ accounts = [account])
+ account_name = account
+ if gajim.account_is_connected(account):
+ account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
+ contact = gajim.contacts.create_contact(jid = jid,
+ name = account_name, show = connection.get_status(), sub = '',
+ status = connection.status,
+ resource = connection.server_resource,
+ priority = connection.priority,
+ mood = connection.mood,
+ tune = connection.tune,
+ activity = connection.activity)
+ if gajim.connections[account].gpg:
+ contact.keyID = gajim.config.get_per('accounts', connection.name,
+ 'keyid')
+ contacts.append(contact)
+ # if we're online ...
+ if connection.connection:
+ roster = connection.connection.getRoster()
+ # in threadless connection when no roster stanza is sent,
+ # 'roster' is None
+ if roster and roster.getItem(jid):
+ resources = roster.getResources(jid)
+ # ...get the contact info for our other online resources
+ for resource in resources:
+ # Check if we already have this resource
+ found = False
+ for contact_ in contacts:
+ if contact_.resource == resource:
+ found = True
+ break
+ if found:
+ continue
+ show = roster.getShow(jid+'/'+resource)
+ if not show:
+ show = 'online'
+ contact = gajim.contacts.create_contact(jid = jid,
+ name = account, show = show,
+ status = roster.getStatus(jid+'/'+resource),
+ resource = resource,
+ priority = roster.getPriority(jid+'/'+resource))
+ contacts.append(contact)
+ if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
+ self.tooltip.id = row
+ self.tooltip.account = None
+ self.tooltip.timeout = gobject.timeout_add(500,
+ self.show_tooltip, contacts)
- return advanced_menuitem_menu
+ def on_agent_logging(self, widget, jid, state, account):
+ '''When an agent is requested to log in or off'''
+ gajim.connections[account].send_agent_status(jid, state)
- def set_actions_menu_needs_rebuild(self):
- self.actions_menu_needs_rebuild = True
- # Force the rebuild now since the on_activates on the menu itself does
- # not work with the os/x top level menubar
- if sys.platform == 'darwin':
- self.make_menu(force = True)
- return
+ def on_edit_agent(self, widget, contact, account):
+ '''When we want to modify the agent registration'''
+ gajim.connections[account].request_register_agent_info(contact.jid)
+
+ def on_remove_agent(self, widget, list_):
+ '''When an agent is requested to be removed. list_ is a list of
+ (contact, account) tuple'''
+ for (contact, account) in list_:
+ if gajim.config.get_per('accounts', account, 'hostname') == \
+ contact.jid:
+ # We remove the server contact
+ # remove it from treeview
+ gajim.connections[account].unsubscribe(contact.jid)
+ self.remove_contact(contact.jid, account)
+ gajim.contacts.remove_contact(account, contact)
+ return
+
+ def remove(list_):
+ for (contact, account) in list_:
+ full_jid = contact.get_full_jid()
+ gajim.connections[account].unsubscribe_agent(full_jid)
+ # remove transport from treeview
+ self.remove_contact(contact.jid, account)
+ gajim.contacts.remove_jid(account, contact.jid)
+ gajim.contacts.remove_contact(account, contact)
+
+ # Check if there are unread events from some contacts
+ has_unread_events = False
+ for (contact, account) in list_:
+ for jid in gajim.events.get_events(account):
+ if jid.endswith(contact.jid):
+ has_unread_events = True
+ break
+ if has_unread_events:
+ dialogs.ErrorDialog(_('You have unread messages'),
+ _('You must read them before removing this transport.'))
+ return
+ if len(list_) == 1:
+ pritext = _('Transport "%s" will be removed') % contact.jid
+ sectext = _('You will no longer be able to send and receive messages '
+ 'from contacts using this transport.')
+ else:
+ pritext = _('Transports will be removed')
+ jids = ''
+ for (contact, account) in list_:
+ jids += '\n ' + contact.get_shown_name() + ','
+ jids = jids[:-1] + '.'
+ sectext = _('You will no longer be able to send and receive messages '
+ 'to contacts from these transports: %s') % jids
+ dialogs.ConfirmationDialog(pritext, sectext,
+ on_response_ok = (remove, list_))
+
+ def on_block(self, widget, iter, group_list):
+ ''' When clicked on the 'block' button in context menu. '''
+ model = self.tree.get_model()
+ accounts = []
+ msg = self.get_status_message('offline')
+ if group_list == None:
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ accounts.append(account)
+ self.send_status(account, 'offline', msg, to = jid)
+ new_rule = {'order': u'1', 'type': u'jid', 'action': u'deny',
+ 'value' : jid, 'child': [u'message', u'iq', u'presence-out']}
+ gajim.connections[account].blocked_list.append(new_rule)
+ # needed for draw_contact:
+ gajim.connections[account].blocked_contacts.append(jid)
+ self.draw_contact(jid, account)
+ else:
+ if iter == None:
+ for (contact, account) in group_list:
+ if account not in accounts:
+ if not gajim.connections[account].privacy_rules_supported:
+ continue
+ accounts.append(account)
+ self.send_status(account, 'offline', msg, to=contact.jid)
+ new_rule = {'order': u'1', 'type': u'jid',
+ 'action': u'deny', 'value' : contact.jid,
+ 'child': [u'message', u'iq', u'presence-out']}
+ gajim.connections[account].blocked_list.append(new_rule)
+ # needed for draw_contact:
+ gajim.connections[account].blocked_contacts.append(contact.jid)
+ self.draw_contact(contact.jid, account)
+ else:
+ group = model[iter][C_JID].decode('utf-8')
+ for (contact, account) in group_list:
+ if account not in accounts:
+ if not gajim.connections[account].privacy_rules_supported:
+ continue
+ accounts.append(account)
+ # needed for draw_group:
+ gajim.connections[account].blocked_groups.append(group)
+ self.draw_group(group, account)
+ self.send_status(account, 'offline', msg, to=contact.jid)
+ self.draw_contact(contact.jid, account)
+ new_rule = {'order': u'1', 'type': u'group', 'action': u'deny',
+ 'value' : group, 'child': [u'message', u'iq', u'presence-out']}
+ gajim.connections[account].blocked_list.append(new_rule)
+ for account in accounts:
+ gajim.connections[account].set_privacy_list(
+ 'block', gajim.connections[account].blocked_list)
+ if len(gajim.connections[account].blocked_list) == 1:
+ gajim.connections[account].set_active_list('block')
+ gajim.connections[account].set_default_list('block')
+ gajim.connections[account].get_privacy_list('block')
+
+ def on_unblock(self, widget, iter, group_list):
+ ''' When clicked on the 'unblock' button in context menu. '''
+ model = self.tree.get_model()
+ accounts = []
+ if group_list == None:
+ jid = model[iter][C_JID].decode('utf-8')
+ jid_account = model[iter][C_ACCOUNT].decode('utf-8')
+ accounts.append(jid_account)
+ gajim.connections[jid_account].new_blocked_list = []
+ for rule in gajim.connections[jid_account].blocked_list:
+ if rule['action'] != 'deny' or rule['type'] != 'jid' \
+ or rule['value'] != jid:
+ gajim.connections[jid_account].new_blocked_list.append(rule)
+ # needed for draw_contact:
+ if jid in gajim.connections[jid_account].blocked_contacts:
+ gajim.connections[jid_account].blocked_contacts.remove(jid)
+ self.draw_contact(jid, jid_account)
+ else:
+ if iter == None:
+ for (contact, account) in group_list:
+ if account not in accounts:
+ if gajim.connections[account].privacy_rules_supported:
+ accounts.append(account)
+ gajim.connections[account].new_blocked_list = []
+ gajim.connections[account].to_unblock = []
+ gajim.connections[account].to_unblock.append(contact.jid)
+ else:
+ gajim.connections[account].to_unblock.append(contact.jid)
+ # needed for draw_contact:
+ if contact.jid in gajim.connections[account].blocked_contacts:
+ gajim.connections[account].blocked_contacts.remove(
+ contact.jid)
+ self.draw_contact(contact.jid, account)
+ for account in accounts:
+ for rule in gajim.connections[account].blocked_list:
+ if rule['action'] != 'deny' or rule['type'] != 'jid' \
+ or rule['value'] not in gajim.connections[account].to_unblock:
+ gajim.connections[account].new_blocked_list.append(rule)
+ else:
+ group = model[iter][C_JID].decode('utf-8')
+ for (contact, account) in group_list:
+ if account not in accounts:
+ if gajim.connections[account].privacy_rules_supported:
+ accounts.append(account)
+ # needed for draw_group:
+ if group in gajim.connections[account].blocked_groups:
+ gajim.connections[account].blocked_groups.remove(group)
+ self.draw_group(group, account)
+ gajim.connections[account].new_blocked_list = []
+ for rule in gajim.connections[account].blocked_list:
+ if rule['action'] != 'deny' or rule['type'] != 'group' \
+ or rule['value'] != group:
+ gajim.connections[account].new_blocked_list.append(
+ rule)
+ self.draw_contact(contact.jid, account)
+ for account in accounts:
+ gajim.connections[account].set_privacy_list(
+ 'block', gajim.connections[account].new_blocked_list)
+ gajim.connections[account].get_privacy_list('block')
+ if len(gajim.connections[account].new_blocked_list) == 0:
+ gajim.connections[account].blocked_list = []
+ gajim.connections[account].blocked_contacts = []
+ gajim.connections[account].blocked_groups = []
+ gajim.connections[account].set_default_list('')
+ gajim.connections[account].set_active_list('')
+ gajim.connections[account].del_privacy_list('block')
+ if gajim.interface.instances[account].has_key('blocked_contacts'):
+ gajim.interface.instances[account]['blocked_contacts'].\
+ privacy_list_received([])
+ if group_list == None:
+ status = gajim.connections[jid_account].connected
+ msg = gajim.connections[jid_account].status
+ if not self.regroup:
+ show = gajim.SHOW_LIST[status]
+ else: # accounts merged
+ show = helpers.get_global_show()
+ self.send_status(jid_account, show, msg, to=jid)
+ else:
+ for (contact, account) in group_list:
+ if not self.regroup:
+ show = gajim.SHOW_LIST[gajim.connections[account].connected]
+ else: # accounts merged
+ show = helpers.get_global_show()
+ if account not in accounts:
+ if gajim.connections[account].privacy_rules_supported:
+ accounts.append(account)
+ self.send_status(account, show,
+ gajim.connections[account].status, to=contact.jid)
+ else:
+ self.send_status(account, show,
+ gajim.connections[account].status, to=contact.jid)
+
+ def on_rename(self, widget, iter, path):
+ # this function is called either by F2 or by Rename menuitem
+ if gajim.interface.instances.has_key('rename'):
+ gajim.interface.instances['rename'].dialog.present()
+ return
+ model = self.tree.get_model()
+
+ row_type = model[iter][C_TYPE]
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ # account is offline, don't allow to rename
+ if gajim.connections[account].connected < 2:
+ return
+ if row_type in ('contact', 'agent'):
+ # it's jid
+ title = _('Rename Contact')
+ message = _('Enter a new nickname for contact %s') % jid
+ old_text = gajim.contacts.get_contact_with_highest_priority(account,
+ jid).name
+ elif row_type == 'group':
+ if jid in helpers.special_groups + (_('General'),):
+ return
+ old_text = model[iter][C_JID].decode('utf-8')
+ title = _('Rename Group')
+ message = _('Enter a new name for group %s') % old_text
+
+ def on_renamed(new_text, account, row_type, jid, old_text):
+ if gajim.interface.instances.has_key('rename'):
+ del gajim.interface.instances['rename']
+ if row_type in ('contact', 'agent'):
+ if old_text == new_text:
+ return
+ for u in gajim.contacts.get_contacts(account, jid):
+ 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:
+ ctrl.update_ui()
+ win = gajim.interface.msg_win_mgr.get_window(jid, account)
+ win.redraw_tab(ctrl)
+ win.show_title()
+ elif row_type == 'group':
+ # in C_JID column, we hold the group name (which is not escaped)
+ if old_text == new_text:
+ return
+ # Groups may not change name from or to a special groups
+ for g in helpers.special_groups:
+ if g in (new_text, old_text):
+ return
+ # get all contacts in that group
+ for jid in gajim.contacts.get_jid_list(account):
+ contact = gajim.contacts.get_contact_with_highest_priority(
+ account, jid)
+ if old_text in contact.groups:
+ # set them in the new one and remove it from the old
+ contact.groups.remove(old_text)
+ self.remove_contact(contact.jid, account)
+ if new_text not in contact.groups:
+ contact.groups.append(new_text)
+ self.add_contact(contact.jid, account)
+ gajim.connections[account].update_contact(contact.jid,
+ contact.name, contact.groups)
+ # If last removed iter was not visible, gajim.groups is not cleaned
+ if gajim.groups[account].has_key(old_text):
+ del gajim.groups[account][old_text]
+ self.draw_group(new_text, account)
+
+ def on_canceled():
+ if gajim.interface.instances.has_key('rename'):
+ del gajim.interface.instances['rename']
+
+ gajim.interface.instances['rename'] = dialogs.InputDialog(title, message,
+ old_text, False, (on_renamed, account, row_type, jid, old_text),
+ on_canceled)
+
+ def on_remove_group_item_activated(self, widget, group, account):
+ dlg = dialogs.ConfirmationDialogCheck(_('Remove Group'),
+ _('Do you want to remove group %s from the roster?' % group),
+ _('Remove also all contacts in this group from your roster'))
+ dlg.set_default_response(gtk.BUTTONS_OK_CANCEL)
+ response = dlg.run()
+ if response == gtk.RESPONSE_OK:
+ for contact in gajim.contacts.get_contacts_from_group(account, group):
+ if not dlg.is_checked():
+ self.remove_contact_from_groups(contact.jid,account, [group])
+ gajim.connections[account].update_contact(contact.jid,
+ contact.name, contact.groups)
+ self.add_contact(contact.jid, account)
+ else:
+ gajim.connections[account].unsubscribe(contact.jid)
+ for c in gajim.contacts.get_contacts(account, contact.jid):
+ self.remove_contact(c.jid, account)
+ gajim.contacts.remove_jid(account, c.jid)
+ self._readd_contact_to_roster_if_needed(contact, account)
+ self.draw_account(account)
+
+ def on_assign_pgp_key(self, widget, contact, account):
+ attached_keys = gajim.config.get_per('accounts', account,
+ 'attached_gpg_keys').split()
+ keys = {}
+ #GPG Key
+ keyID = _('None')
+ for i in xrange(len(attached_keys)/2):
+ keys[attached_keys[2*i]] = attached_keys[2*i+1]
+ if attached_keys[2*i] == contact.jid:
+ keyID = attached_keys[2*i+1]
+ public_keys = gajim.connections[account].ask_gpg_keys()
+ #GPG Key
+ public_keys[_('None')] = _('None')
+ instance = dialogs.ChooseGPGKeyDialog(_('Assign OpenPGP Key'),
+ _('Select a key to apply to the contact'), public_keys, keyID)
+ keyID = instance.run()
+ if keyID is None:
+ return
+ #GPG Key
+ if keyID[0] == _('None'):
+ if contact.jid in keys:
+ del keys[contact.jid]
+ keyID = ''
+ else:
+ 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)
+ ctrl.update_ui()
+ keys_str = ''
+ for jid in keys:
+ keys_str += jid + ' ' + keys[jid] + ' '
+ gajim.config.set_per('accounts', account, 'attached_gpg_keys', keys_str)
+ for u in gajim.contacts.get_contacts(account, contact.jid):
+ u.keyID = helpers.prepare_and_validate_gpg_keyID(account,
+ contact.jid, keyID)
+
+ def on_set_custom_avatar_activate(self, widget, contact, account):
+ def on_ok(widget, path_to_file):
+ filesize = os.path.getsize(path_to_file) # in bytes
+ invalid_file = False
+ msg = ''
+ if os.path.isfile(path_to_file):
+ stat = os.stat(path_to_file)
+ if stat[6] == 0:
+ invalid_file = True
+ msg = _('File is empty')
+ else:
+ invalid_file = True
+ msg = _('File does not exist')
+ if invalid_file:
+ dialogs.ErrorDialog(_('Could not load image'), msg)
+ return
+ try:
+ pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
+ if filesize > 16384: # 16 kb
+ # get the image at 'tooltip size'
+ # and hope that user did not specify in ACE crazy size
+ pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip')
+ except gobject.GError, msg: # unknown format
+ # msg should be string, not object instance
+ msg = str(msg)
+ dialogs.ErrorDialog(_('Could not load image'), msg)
+ return
+ gajim.interface.save_avatar_files(contact.jid, pixbuf, local = True)
+ dlg.destroy()
+ self.update_avatar_in_gui(contact.jid, account)
+
+ def on_clear(widget):
+ dlg.destroy()
+ # Delete file:
+ gajim.interface.remove_avatar_files(contact.jid, local = True)
+ self.update_avatar_in_gui(contact.jid, account)
+
+ dlg = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
+ on_response_clear = on_clear)
+
+ def on_edit_groups(self, widget, list_):
+ dlg = dialogs.EditGroupsDialog(list_)
+ dlg.run()
+
+ def on_history(self, widget, contact, account):
+ '''When history menuitem is activated: call log window'''
+ if gajim.interface.instances.has_key('logs'):
+ gajim.interface.instances['logs'].window.present()
+ gajim.interface.instances['logs'].open_history(contact.jid, account)
+ else:
+ gajim.interface.instances['logs'] = history_window.\
+ HistoryWindow(contact.jid, account)
+
+ def on_disconnect(self, widget, jid, account):
+ '''When disconnect menuitem is activated: disconect from room'''
+ ctrl = gajim.interface.minimized_controls[account][jid]
+ del gajim.interface.minimized_controls[account][jid]
+ ctrl.shutdown()
+
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ if not contact:
+ return
+ if contact.groups == [_('Groupchats')]:
+ # FIXME: use proper API
+ gajim.contacts.remove_contact(account, contact)
+ self.remove_contact(contact.jid, account)
+
+ def on_send_single_message_menuitem_activate(self, widget, account,
+ contact = None):
+ if contact is None:
+ dialogs.SingleMessageWindow(account, action = 'send')
+ elif type(contact) == type([]):
+ dialogs.SingleMessageWindow(account, contact, 'send')
+ else:
+ jid = contact.jid
+ if contact.jid == gajim.get_jid_from_account(account):
+ jid += '/' + contact.resource
+ dialogs.SingleMessageWindow(account, jid, 'send')
+
+ def on_send_file_menuitem_activate(self, widget, contact, account,
+ resource=None):
+ gajim.interface.instances['file_transfers'].show_file_send_request(
+ account, contact)
+
+ def on_add_special_notification_menuitem_activate(self, widget, jid):
+ dialogs.AddSpecialNotificationDialog(jid)
+
+ def on_invite_to_new_room(self, widget, list_, resource = None):
+ ''' resource parameter MUST NOT be used if more than one contact in
+ list '''
+ account_list = []
+ jid_list = []
+ for (contact, account) in list_:
+ if contact.jid not in jid_list:
+ if resource: # we MUST have one contact only in list_
+ fjid = contact.jid + '/' + resource
+ jid_list.append(fjid)
+ else:
+ jid_list.append(contact.jid)
+ if account not in account_list:
+ account_list.append(account)
+ # transform None in 'jabber'
+ type_ = gajim.get_transport_name_from_jid(jid_list[0]) or 'jabber'
+ for account in account_list:
+ if gajim.connections[account].muc_jid[type_]:
+ # create the room on this muc server
+ if gajim.interface.instances[account].has_key('join_gc'):
+ gajim.interface.instances[account]['join_gc'].window.destroy()
+ try:
+ gajim.interface.instances[account]['join_gc'] = \
+ dialogs.JoinGroupchatWindow(account,
+ gajim.connections[account].muc_jid[type_],
+ automatic = {'invities': jid_list})
+ except GajimGeneralException:
+ continue
+ break
+
+ def on_invite_to_room(self, widget, list_, room_jid, room_account,
+ resource = None):
+ ''' resource parameter MUST NOT be used if more than one contact in
+ list '''
+ for (contact, acct) in list_:
+ contact_jid = contact.jid
+ if resource: # we MUST have one contact only in list_
+ contact_jid += '/' + resource
+ gajim.connections[room_account].send_invite(room_jid, contact_jid)
+
+ def on_all_groupchat_maximized(self, widget, group_list):
+ for (contact, account) in group_list:
+ self.on_groupchat_maximized(widget, contact.jid, account)
+
+ def on_groupchat_maximized(self, widget, jid, account):
+ '''When a groupchat is maximised'''
+ ctrl = gajim.interface.minimized_controls[account][jid]
+ mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid, ctrl.account)
+ if not mw:
+ mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact,
+ ctrl.account, ctrl.type_id)
+ ctrl.parent_win = mw
+ mw.new_tab(ctrl)
+ mw.set_active_tab(jid, account)
+ mw.window.present()
+ del gajim.interface.minimized_controls[account][jid]
+
+ self.remove_groupchat(jid, account)
+
+ def on_edit_account(self, widget, account):
+ if gajim.interface.instances.has_key('accounts'):
+ gajim.interface.instances['accounts'].window.present()
+ else:
+ gajim.interface.instances['accounts'] = config.AccountsWindow()
+ gajim.interface.instances['accounts'].select_account(account)
+
+ def on_zeroconf_properties(self, widget, account):
+ if gajim.interface.instances.has_key('accounts'):
+ gajim.interface.instances['accounts'].window.present()
+ else:
+ gajim.interface.instances['accounts'] = config.AccountsWindow()
+ gajim.interface.instances['accounts'].select_account(account)
+
+ def on_open_gmail_inbox(self, widget, account):
+ url = gajim.connections[account].gmail_url
+ if url:
+ helpers.launch_browser_mailer('url', url)
+
+ def on_change_activity_activate(self, widget, account):
+ dlg = dialogs.ChangeActivityDialog(account)
+
+ def on_change_mood_activate(self, widget, account):
+ dlg = dialogs.ChangeMoodDialog(account)
+
+ def on_change_status_message_activate(self, widget, account):
+ show = gajim.SHOW_LIST[gajim.connections[account].connected]
+ dlg = dialogs.ChangeStatusMessageDialog(show)
+ message = dlg.run()
+ if message is not None: # None is if user pressed Cancel
+ self.send_status(account, show, message)
+
+ def on_add_to_roster(self, widget, contact, account):
+ dialogs.AddNewContactWindow(account, contact.jid, contact.name)
+
+
+ def on_roster_treeview_scroll_event(self, widget, event):
+ self.tooltip.hide_tooltip()
+
+ def on_roster_treeview_key_press_event(self, widget, event):
+ '''when a key is pressed in the treeviews'''
+ self.tooltip.hide_tooltip()
+ if event.keyval == gtk.keysyms.Escape:
+ self.tree.get_selection().unselect_all()
+ elif event.keyval == gtk.keysyms.F2:
+ treeselection = self.tree.get_selection()
+ model, list_of_paths = treeselection.get_selected_rows()
+ if len(list_of_paths) != 1:
+ return
+ path = list_of_paths[0]
+ type_ = model[path][C_TYPE]
+ if type_ in ('contact', 'group', 'agent'):
+ iter = model.get_iter(path)
+ self.on_rename(widget, iter, path)
+
+ elif event.keyval == gtk.keysyms.Delete:
+ treeselection = self.tree.get_selection()
+ model, list_of_paths = treeselection.get_selected_rows()
+ if not len(list_of_paths):
+ return
+ type_ = model[list_of_paths[0]][C_TYPE]
+ account = model[list_of_paths[0]][C_ACCOUNT]
+ list_ = []
+ for path in list_of_paths:
+ if model[path][C_TYPE] != type_:
+ return
+ jid = model[path][C_JID].decode('utf-8')
+ account = model[path][C_ACCOUNT].decode('utf-8')
+ contact = gajim.contacts.get_contact_with_highest_priority(account,
+ jid)
+ list_.append((contact, account))
+ if type_ in ('account', 'group', 'self_contact') or \
+ account == gajim.ZEROCONF_ACC_NAME:
+ return
+ if type_ == 'contact':
+ self.on_req_usub(widget, list_)
+ elif type_ == 'agent':
+ self.on_remove_agent(widget, list_)
+
+ def on_roster_treeview_button_release_event(self, widget, event):
+ try:
+ path, column, x, y = self.tree.get_path_at_pos(int(event.x),
+ int(event.y))
+ except TypeError:
+ return False
+
+ if event.button == 1: # Left click
+ if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
+ not event.state & gtk.gdk.CONTROL_MASK:
+ # Check if button has been pressed on the same row
+ if self.clicked_path == path:
+ self.on_row_activated(widget, path)
+ self.clicked_path = None
+
+ def on_roster_treeview_button_press_event(self, widget, event):
+ # hide tooltip, no matter the button is pressed
+ self.tooltip.hide_tooltip()
+ try:
+ path, column, x, y = self.tree.get_path_at_pos(int(event.x),
+ int(event.y))
+ except TypeError:
+ self.tree.get_selection().unselect_all()
+ return False
+
+ if event.button == 3: # Right click
+ try:
+ model, list_of_paths = self.tree.get_selection().get_selected_rows()
+ except TypeError:
+ list_of_paths = []
+ if path not in list_of_paths:
+ self.tree.get_selection().unselect_all()
+ self.tree.get_selection().select_path(path)
+ return self.show_treeview_menu(event)
+
+ elif event.button == 2: # Middle click
+ try:
+ model, list_of_paths = self.tree.get_selection().get_selected_rows()
+ except TypeError:
+ list_of_paths = []
+ if list_of_paths != [path]:
+ self.tree.get_selection().unselect_all()
+ self.tree.get_selection().select_path(path)
+ type_ = model[path][C_TYPE]
+ if type_ in ('agent', 'contact', 'self_contact', 'groupchat'):
+ self.on_row_activated(widget, path)
+ elif type_ == 'account':
+ account = model[path][C_ACCOUNT].decode('utf-8')
+ if account != 'all':
+ show = gajim.connections[account].connected
+ if show > 1: # We are connected
+ self.on_change_status_message_activate(widget, account)
+ return True
+ show = helpers.get_global_show()
+ if show == 'offline':
+ return True
+ dlg = dialogs.ChangeStatusMessageDialog(show)
+ message = dlg.run()
+ if not message:
+ return True
+ for acct in gajim.connections:
+ if not gajim.config.get_per('accounts', acct,
+ 'sync_with_global_status'):
+ continue
+ current_show = gajim.SHOW_LIST[gajim.connections[acct].connected]
+ self.send_status(acct, current_show, message)
+ return True
+
+ elif event.button == 1: # Left click
+ model = self.tree.get_model()
+ type_ = model[path][C_TYPE]
+ # x_min is the x start position of status icon column
+ if gajim.config.get('avatar_position_in_roster') == 'left':
+ x_min = gajim.config.get('roster_avatar_width')
+ else:
+ 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
+ iter = model.get_iter(path)
+ if x > x_min and x < x_min + 27 and type_ == 'contact' and \
+ model.iter_has_child(iter):
+ account = model[path][C_ACCOUNT].decode('utf-8')
+ jid = model[path][C_JID].decode('utf-8')
+ # first cell in 1st column (the arrow SINGLE clicked)
+ iters = self._get_contact_iter(jid, account)
+ for iter in iters:
+ path = model.get_path(iter)
+ if (self.tree.row_expanded(path)):
+ self.tree.collapse_row(path)
+ else:
+ self.tree.expand_row(path, False)
+ return
+ # We just save on which row we press button, and open chat window on
+ # button release to be able to do DND without opening chat window
+ self.clicked_path = path
+ return
+ else:
+ if type_ == 'group' and x < 27:
+ # first cell in 1st column (the arrow SINGLE clicked)
+ if (self.tree.row_expanded(path)):
+ self.tree.collapse_row(path)
+ else:
+ self.tree.expand_row(path, False)
+
+ elif type_ == 'contact' and x > x_min and x < x_min + 27:
+ account = model[path][C_ACCOUNT].decode('utf-8')
+ jid = model[path][C_JID].decode('utf-8')
+ # first cell in 1st column (the arrow SINGLE clicked)
+ iters = self._get_contact_iter(jid, account)
+ for iter in iters:
+ path = model.get_path(iter)
+ if (self.tree.row_expanded(path)):
+ self.tree.collapse_row(path)
+ else:
+ self.tree.expand_row(path, False)
+
+ def on_req_usub(self, widget, list_):
+ '''Remove a contact. list_ is a list of (contact, account) tuples'''
+ def on_ok(is_checked, list_):
+ remove_auth = True
+ if len(list_) == 1:
+ contact = list_[0][0]
+ if contact.sub != 'to' and is_checked:
+ remove_auth = False
+ for (contact, account) in list_:
+ gajim.connections[account].unsubscribe(contact.jid, remove_auth)
+ for c in gajim.contacts.get_contacts(account, contact.jid):
+ self.remove_contact(c.jid, account)
+ gajim.contacts.remove_jid(account, contact.jid)
+ self.draw_account(account)
+ if not remove_auth and contact.sub == 'both':
+ contact.name = ''
+ contact.groups = []
+ contact.sub = 'from'
+ gajim.contacts.add_contact(account, contact)
+ self.add_contact(contact.jid, account)
+ else:
+ if _('Not in Roster') in contact.groups:
+ gajim.events.remove_events(account, contact.jid)
+ self._readd_contact_to_roster_if_needed(contact, account)
+
+ def on_ok2(list_):
+ on_ok(False, list_)
+
+ if len(list_) == 1:
+ contact = list_[0][0]
+ account = list_[0][1]
+ pritext = _('Contact "%s" will be removed from your roster') % \
+ contact.get_shown_name()
+ if contact.sub == 'to':
+ dialogs.ConfirmationDialog(pritext,
+ _('By removing this contact you also remove authorization '
+ 'resulting in him or her always seeing you as offline.'),
+ on_response_ok = (on_ok2, list_))
+ else:
+ dialogs.ConfirmationDialogCheck(pritext,
+ _('By removing this contact you also by default remove '
+ 'authorization resulting in him or her always seeing you as '
+ 'offline.'),
+ _('I want this contact to know my status after removal'),
+ on_response_ok = (on_ok, list_))
+ else:
+ # several contact to remove at the same time
+ pritext = _('Contacts will be removed from your roster')
+ jids = ''
+ for (contact, account) in list_:
+ jids += '\n ' + contact.get_shown_name() + ','
+ sectext = _('By removing these contacts:%s\nyou also remove '
+ 'authorization resulting in them always seeing you as offline.') % \
+ jids
+ dialogs.ConfirmationDialog(pritext, sectext,
+ on_response_ok = (on_ok2, list_))
+
+ def on_send_custom_status(self, widget, contact_list, show, group=None):
+ '''send custom status'''
+ dlg = dialogs.ChangeStatusMessageDialog(show)
+ message = dlg.run()
+ if message is not None: # None if user pressed Cancel
+ for (contact, account) in contact_list:
+ our_jid = gajim.get_jid_from_account(account)
+ accounts = []
+ if group and account not in accounts:
+ if not gajim.interface.status_sent_to_groups.has_key(account):
+ gajim.interface.status_sent_to_groups[account] = {}
+ gajim.interface.status_sent_to_groups[account][group] = show
+ accounts.append(group)
+ jid = contact.jid
+ if jid == our_jid:
+ jid += '/' + contact.resource
+ self.send_status(account, show, message, to=jid)
+ if not gajim.interface.status_sent_to_users.has_key(account):
+ gajim.interface.status_sent_to_users[account] = {}
+ gajim.interface.status_sent_to_users[account][contact.jid] = show
+
+ def on_status_combobox_changed(self, widget):
+ '''When we change our status via the combobox'''
+ model = self.status_combobox.get_model()
+ active = self.status_combobox.get_active()
+ if active == -1: # no active item
+ return
+ if not self.combobox_callback_active:
+ self.previous_status_combobox_active = active
+ return
+ accounts = gajim.connections.keys()
+ if len(accounts) == 0:
+ dialogs.ErrorDialog(_('No account available'),
+ _('You must create an account before you can chat with other contacts.'))
+ self.update_status_combobox()
+ return
+ status = model[active][2].decode('utf-8')
+ statuses_unified = helpers.statuses_unified() # status "desync'ed" or not
+ if (active == 7 and statuses_unified) or (active == 9 and not statuses_unified):
+ # 'Change status message' selected:
+ # do not change show, just show change status dialog
+ status = model[self.previous_status_combobox_active][2].decode('utf-8')
+ dlg = dialogs.ChangeStatusMessageDialog(status)
+ message = dlg.run()
+ if message is not None: # None if user pressed Cancel
+ for account in accounts:
+ if not gajim.config.get_per('accounts', account,
+ 'sync_with_global_status'):
+ continue
+ current_show = gajim.SHOW_LIST[
+ gajim.connections[account].connected]
+ self.send_status(account, current_show, message)
+ self.combobox_callback_active = False
+ self.status_combobox.set_active(
+ self.previous_status_combobox_active)
+ self.combobox_callback_active = True
+ return
+ # we are about to change show, so save this new show so in case
+ # after user chooses "Change status message" menuitem
+ # we can return to this show
+ self.previous_status_combobox_active = active
+ connected_accounts = gajim.get_number_of_connected_accounts()
+ if status == 'invisible':
+ bug_user = False
+ for account in accounts:
+ if connected_accounts < 1 or gajim.account_is_connected(account):
+ if not gajim.config.get_per('accounts', account,
+ 'sync_with_global_status'):
+ continue
+ # We're going to change our status to invisible
+ if self.connected_rooms(account):
+ bug_user = True
+ break
+ if bug_user:
+ dialog = dialogs.ConfirmationDialog(
+ _('You are participating in one or more group chats'),
+ _('Changing your status to invisible will result in '
+ 'disconnection from those group chats. Are you sure you want to '
+ 'go invisible?'))
+ if dialog.get_response() != gtk.RESPONSE_OK:
+ self.update_status_combobox()
+ return
+ message = self.get_status_message(status)
+ if message is None: # user pressed Cancel to change status message dialog
+ self.update_status_combobox()
+ return
+ global_sync_accounts = []
+ for acct in accounts:
+ if gajim.config.get_per('accounts', acct, 'sync_with_global_status'):
+ global_sync_accounts.append(acct)
+ global_sync_connected_accounts = gajim.get_number_of_connected_accounts(
+ global_sync_accounts)
+ for account in accounts:
+ if not gajim.config.get_per('accounts', account,
+ 'sync_with_global_status'):
+ continue
+ # we are connected (so we wanna change show and status)
+ # or no account is connected and we want to connect with new show and
+ # status
+
+ if not global_sync_connected_accounts > 0 or \
+ gajim.connections[account].connected > 0:
+ self.send_status(account, status, message)
+ self.update_status_combobox()
+
+ def on_preferences_menuitem_activate(self, widget):
+ if gajim.interface.instances.has_key('preferences'):
+ gajim.interface.instances['preferences'].window.present()
+ else:
+ gajim.interface.instances['preferences'] = config.PreferencesWindow()
+
+ def on_pep_services_menuitem_activate(self, widget, account):
+ if gajim.interface.instances[account].has_key('pep_services'):
+ gajim.interface.instances[account]['pep_services'].window.present()
+ else:
+ gajim.interface.instances[account]['pep_services'] = \
+ config.ManagePEPServicesWindow(account)
+
+ def on_add_new_contact(self, widget, account):
+ dialogs.AddNewContactWindow(account)
+
+ def on_join_gc_activate(self, widget, account):
+ '''when the join gc menuitem is clicked, show the join gc window'''
+ 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 gajim.interface.instances[account].has_key('join_gc'):
+ gajim.interface.instances[account]['join_gc'].window.present()
+ else:
+ # c http://nkour.blogspot.com/2005/05/pythons-init-return-none-doesnt-return.html
+ try:
+ gajim.interface.instances[account]['join_gc'] = \
+ dialogs.JoinGroupchatWindow(account)
+ except GajimGeneralException:
+ pass
+
+ def on_new_chat_menuitem_activate(self, widget, account):
+ dialogs.NewChatDialog(account)
+
+ def on_contents_menuitem_activate(self, widget):
+ helpers.launch_browser_mailer('url', 'http://trac.gajim.org/wiki')
+
+ def on_faq_menuitem_activate(self, widget):
+ helpers.launch_browser_mailer('url',
+ 'http://trac.gajim.org/wiki/GajimFaq')
+
+ def on_features_menuitem_activate(self, widget):
+ features_window.FeaturesWindow()
+
+ def on_about_menuitem_activate(self, widget):
+ dialogs.AboutDialog()
+
+ def on_accounts_menuitem_activate(self, widget):
+ if gajim.interface.instances.has_key('accounts'):
+ gajim.interface.instances['accounts'].window.present()
+ else:
+ gajim.interface.instances['accounts'] = config.AccountsWindow()
+
+ def on_file_transfers_menuitem_activate(self, widget):
+ if gajim.interface.instances['file_transfers'].window.get_property(
+ 'visible'):
+ gajim.interface.instances['file_transfers'].window.present()
+ else:
+ gajim.interface.instances['file_transfers'].window.show_all()
+
+ def on_history_menuitem_activate(self, widget):
+ if gajim.interface.instances.has_key('logs'):
+ gajim.interface.instances['logs'].window.present()
+ else:
+ gajim.interface.instances['logs'] = history_window.\
+ HistoryWindow()
+
+ def on_show_transports_menuitem_activate(self, widget):
+ gajim.config.set('show_transports_group', widget.get_active())
+ self.refilter_shown_roster_items()
+
+ def on_manage_bookmarks_menuitem_activate(self, widget):
+ config.ManageBookmarksWindow()
+
+ def on_profile_avatar_menuitem_activate(self, widget, account):
+ gajim.interface.edit_own_details(account)
+
+ 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
+ able to make this a callback.'''
+ jid = contact.jid
+ if resource is not None:
+ jid = jid + u'/' + resource
+ adhoc_commands.CommandWindow(account, jid)
+
+ def on_roster_window_focus_in_event(self, widget, event):
+ # roster received focus, so if we had urgency REMOVE IT
+ # NOTE: we do not have to read the message to remove urgency
+ # so this functions does that
+ gtkgui_helpers.set_unset_urgency_hint(widget, False)
+
+ # if a contact row is selected, update colors (eg. for status msg)
+ # because gtk engines may differ in bg when window is selected
+ # or not
+ if len(self._last_selected_contact):
+ for (jid, account) in self._last_selected_contact:
+ self.draw_contact(jid, account, selected = True,
+ focus = True)
+
+ def on_roster_window_focus_out_event(self, widget, event):
+ # if a contact row is selected, update colors (eg. for status msg)
+ # because gtk engines may differ in bg when window is selected
+ # or not
+ if len(self._last_selected_contact):
+ for (jid, account) in self._last_selected_contact:
+ self.draw_contact(jid, account, selected = True,
+ focus = False)
+
+ def on_roster_window_key_press_event(self, widget, event):
+ if event.keyval == gtk.keysyms.Escape:
+ if gajim.interface.msg_win_mgr.mode == \
+ MessageWindowMgr.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER and \
+ gajim.interface.msg_win_mgr.one_window_opened():
+ # let message window close the tab
+ return
+ model, list_of_paths = self.tree.get_selection().get_selected_rows()
+ if not len(list_of_paths) and gajim.interface.systray_enabled and \
+ not gajim.config.get('quit_on_roster_x_button'):
+ self.tooltip.hide_tooltip()
+ self.window.hide()
+
+ def on_roster_window_popup_menu(self, widget):
+ event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
+ 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'''
+ model = self.tree.get_model()
+ account = model[path][C_ACCOUNT].decode('utf-8')
+ type_ = model[path][C_TYPE]
+ jid = model[path][C_JID].decode('utf-8')
+ resource = None
+ contact = None
+ iter = model.get_iter(path)
+ if type_ in ('group', 'account'):
+ if self.tree.row_expanded(path):
+ self.tree.collapse_row(path)
+ else:
+ self.tree.expand_row(path, False)
+ elif jid in gajim.interface.minimized_controls[account]:
+ self.on_groupchat_maximized(None, jid, account)
+ else:
+ first_ev = gajim.events.get_first_event(account, jid)
+ if not first_ev:
+ # look in other resources
+ for c in gajim.contacts.get_contacts(account, jid):
+ fjid = c.get_full_jid()
+ first_ev = gajim.events.get_first_event(account, fjid)
+ if first_ev:
+ resource = c.resource
+ break
+ if not first_ev and model.iter_has_child(iter):
+ child_iter = model.iter_children(iter)
+ while not first_ev and child_iter:
+ child_jid = model[child_iter][C_JID].decode('utf-8')
+ first_ev = gajim.events.get_first_event(account, child_jid)
+ if first_ev:
+ jid = child_jid
+ else:
+ child_iter = model.iter_next(child_iter)
+ session = None
+ if first_ev:
+ if first_ev.type_ in ('chat', 'normal'):
+ session = first_ev.parameters[8]
+ fjid = jid
+ if resource:
+ fjid += '/' + resource
+ if self.open_event(account, fjid, first_ev):
+ return
+ # else
+ contact = gajim.contacts.get_contact(account, jid, resource)
+ if not contact or isinstance(contact, list):
+ contact = \
+ gajim.contacts.get_contact_with_highest_priority(account, jid)
+ if jid == gajim.get_jid_from_account(account):
+ resource = contact.resource
+ self.on_open_chat_window(widget, contact, 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'''
+ if not gajim.single_click:
+ self.on_row_activated(widget, path)
+
+ def on_roster_treeview_row_expanded(self, widget, iter, path):
+ '''When a row is expanded change the icon of the arrow'''
+ self._toggeling_row = True
+ model = self.tree.get_model()
+ child_model = model.get_model()
+ child_iter = model.convert_iter_to_child_iter(iter)
+ if self.regroup: # merged accounts
+ accounts = gajim.connections.keys()
+ else:
+ accounts = [model[iter][C_ACCOUNT].decode('utf-8')]
+ type_ = model[iter][C_TYPE]
+ if type_ == 'group':
+ child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images['16'][
+ 'opened']
+ jid = model[iter][C_JID].decode('utf-8')
+ for account in accounts:
+ if gajim.groups[account].has_key(jid): # This account has this group
+ gajim.groups[account][jid]['expand'] = True
+ #if account + jid in self.collapsed_rows:
+ # self.collapsed_rows.remove(account + jid)
+ elif type_ == 'account':
+ account = accounts[0] # There is only one cause we don't use merge
+ #if account in self.collapsed_rows:
+ # self.collapsed_rows.remove(account)
+ for g in gajim.groups[account]:
+ groupIter = self._get_group_iter(g, account)
+ if groupIter and gajim.groups[account][g]['expand']:
+ pathG = model.get_path(groupIter)
+ self.tree.expand_row(pathG, False)
+ self.draw_account(account)
+ elif type_ == 'contact':
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ self.draw_contact(jid, account)
+
+ self._toggeling_row = False
+
+ def on_roster_treeview_row_collapsed(self, widget, iter, path):
+ '''When a row is collapsed change the icon of the arrow'''
+ self._toggeling_row = True
+ model = self.tree.get_model()
+ child_model = model.get_model()
+ child_iter = model.convert_iter_to_child_iter(iter)
+ if self.regroup: # merged accounts
+ accounts = gajim.connections.keys()
+ else:
+ accounts = [model[iter][C_ACCOUNT].decode('utf-8')]
+ type_ = model[iter][C_TYPE]
+ if type_ == 'group':
+ child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images['16'][
+ 'closed']
+ jid = model[iter][C_JID].decode('utf-8')
+ for account in accounts:
+ if gajim.groups[account].has_key(jid): # This account has this group
+ gajim.groups[account][jid]['expand'] = False
+ #if not account + jid in self.collapsed_rows:
+ # self.collapsed_rows.append(account + jid)
+ elif type_ == 'account':
+ account = accounts[0] # There is only one cause we don't use merge
+ #if not account in self.collapsed_rows:
+ # self.collapsed_rows.append(account)
+ self.draw_account(account)
+ elif type_ == 'contact':
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ self.draw_contact(jid, account)
+
+ self._toggeling_row = False
+
+
+ def on_model_row_has_child_toggled(self, model, path, iter):
+ '''This signal is emitted when a row has gotten the first child row or lost its last child row.
+ Expand Parent if necessary.
+ '''
+ if self._toggeling_row:
+ # Ugly hack: (workaround)
+ # Signal is emitted when we write to our model
+ return
+
+ type_ = model[iter][C_TYPE]
+ account = model[iter][C_ACCOUNT]
+ if not account:
+ print "Yet another thing that SHOULD NOT happend....", model,path,iter,account,type_
+ return
+
+ account = account.decode('utf-8')
+ child_iter = model.convert_iter_to_child_iter(iter)
+
+ if type_ == 'account':
+ if not self.filtering:
+ # Only expand row when we create and add the account to the roster
+ self.tree.expand_row(path, False)
+
+ if type_ == 'group':
+ group = model[iter][C_JID].decode('utf-8')
+ if not group:
+ return
+ if gajim.groups[account].has_key(group) and gajim.groups[account][group]['expand']:
+ # Ugly workaround. When we expand the last groupchat the entry is removd from the dict
+ self.tree.expand_row(path, False)
+ else:
+ self.tree.collapse_row(path)
+
+ #if account in self.collapsed_rows or\
+ #account + group in self.collapsed_rows:
+ # print "collapsing group", group
+ # self.tree.collapse_row(path, False)
+ #else:
+ # #self.tree.collapse_row(path[:-1], False)
+ # print "expanding group", group
+ # self.tree.expand_to_path(path)
+
+ if type_ == 'contact' and self.model.iter_has_child(child_iter):
+ # we are a bigbrother metacontact
+ # redraw us to show/hide expand icon
+ jid = model[iter][C_JID].decode('utf-8')
+ if self.filtering:
+ # Prevent endless loops
+ gobject.idle_add(self.draw_contact, jid, account)
+
+
+ # FIXME: Looks like it is unneeded: If yes, remove it
+
+ #def on_treeview_selection_changed(self, selection):
+ # if self.filtering:
+ # print "async problem"
+ # return
+ # model, list_of_paths = selection.get_selected_rows()
+ # if len(self._last_selected_contact):
+ # # update unselected rows
+ # for (jid, account) in self._last_selected_contact:
+ # #try:
+ # self.draw_contact(jid, account)
+ # #except:
+ # # This can fail when last selected row was on an account we just
+ # # removed. So we don't care if that fail
+ # # pass
+ # self._last_selected_contact = []
+ # if len(list_of_paths) == 0:
+ # return
+ # for path in list_of_paths:
+ # row = model[path]
+ # if row[C_TYPE] != 'contact':
+ # self._last_selected_contact = []
+ # return
+ # jid = row[C_JID].decode('utf-8')
+ # account = row[C_ACCOUNT].decode('utf-8')
+ # self._last_selected_contact.append((jid, account))
+ # self.draw_contact(jid, account, selected = True)
+
+ def on_service_disco_menuitem_activate(self, widget, account):
+ server_jid = gajim.config.get_per('accounts', account, 'hostname')
+ if gajim.interface.instances[account]['disco'].has_key(server_jid):
+ gajim.interface.instances[account]['disco'][server_jid].\
+ window.present()
+ else:
+ try:
+ # Object will add itself to the window dict
+ disco.ServiceDiscoveryWindow(account, address_entry = True)
+ except GajimGeneralException:
+ pass
+
+ def on_show_offline_contacts_menuitem_activate(self, widget):
+ '''when show offline option is changed:
+ redraw the treeview'''
+ gajim.config.set('showoffline', not gajim.config.get('showoffline'))
+ self.refilter_shown_roster_items()
+
+ def on_view_menu_activate(self, widget):
+ # Hide the show roster menu if we are not in the right windowing mode.
+ if self.hpaned.get_child2() is not None:
+ self.xml.get_widget('show_roster_menuitem').show()
+ else:
+ self.xml.get_widget('show_roster_menuitem').hide()
+
+ def on_show_roster_menuitem_toggled(self, widget):
+ # when num controls is 0 this menuitem is hidden, but still need to
+ # disable keybinding
+ if self.hpaned.get_child2() is not None:
+ self.show_roster_vbox(widget.get_active())
+
+################################################################################
+### Drag and Drop handling
+################################################################################
+
+ def drag_data_get_data(self, treeview, context, selection, target_id, etime):
+ model, list_of_paths = self.tree.get_selection().get_selected_rows()
+ if len(list_of_paths) != 1:
+ return
+ path = list_of_paths[0]
+ data = ''
+ if len(path) >= 3:
+ data = model[path][C_JID]
+ selection.set(selection.target, 8, data)
+
+ def drag_begin(self, treeview, context):
+ self.dragging = True
+
+ def drag_end(self, treeview, context):
+ self.dragging = False
+
+ def on_drop_in_contact(self, widget, account_source, c_source, account_dest,
+ c_dest, was_big_brother, context, etime):
+
+ if not gajim.connections[account_source].private_storage_supported or not\
+ gajim.connections[account_dest].private_storage_supported:
+ dialogs.WarningDialog(_('Metacontacts storage not supported by your '
+ 'server'),
+ _('Your server does not support storing metacontacts information. '
+ 'So those information will not be saved on next reconnection.'))
+
+ def merge_contacts(is_checked=None):
+ if is_checked != None: # dialog has been shown
+ if is_checked: # user does not want to be asked again
+ gajim.config.set('confirm_metacontacts', 'no')
+ else:
+ gajim.config.set('confirm_metacontacts', 'yes')
+
+ # We might have dropped on a metacontact. Readd it.
+ dest_family = gajim.contacts.get_metacontacts_family(account_dest,
+ c_dest.jid)
+ if dest_family:
+ self._remove_metacontact_family(dest_family)
+ else:
+ self._remove_entity(c_dest, account_dest)
+
+ old_family = gajim.contacts.get_metacontacts_family(account_source,
+ c_source.jid)
+ old_groups = c_source.groups
+
+ # Remove old source contact(s)
+ if was_big_brother:
+ # We have got little brothers. Readd them all
+ self._remove_metacontact_family(old_family)
+ else:
+ # We are only a litle brother. Simply remove us from our big brother
+ self._remove_entity(c_source, account_source)
+
+ own_data = {}
+ own_data['jid'] = c_source.jid
+ own_data['account'] = account_source
+ old_family.append(own_data)
+
+ # Apply new tag and update contact
+ for data in old_family:
+ _account = data['account']
+ _jid = data['jid']
+ _contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
+
+ _contact.groups = c_dest.groups[:]
+ gajim.contacts.add_metacontact(account_dest, c_dest.jid,
+ _account, _contact.jid)
+ gajim.connections[account_source].update_contact(_contact.jid,
+ _contact.name, _contact.groups)
+
+ # Re-add all and update GUI
+ new_family = gajim.contacts.get_metacontacts_family(account_source,
+ c_source.jid)
+ brothers = self._add_metacontact_family(new_family)
+
+ for c, acc in brothers:
+ self.draw_contact(c.jid, acc)
+ self.draw_avatar(c.jid, acc)
+
+ old_groups.extend(c_dest.groups)
+ for g in old_groups:
+ self.draw_group(g, account_source)
+
+ self.draw_account(account_source)
+ context.finish(True, True, etime)
+
+ confirm_metacontacts = gajim.config.get('confirm_metacontacts')
+ if confirm_metacontacts == 'no':
+ merge_contacts()
+ return
+ pritext = _('You are about to create a metacontact. Are you sure you want'
+ ' to continue?')
+ sectext = _('Metacontacts are a way to regroup several contacts in one '
+ 'line. Generally it is used when the same person has several Jabber '
+ 'accounts or transport accounts.')
+ dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
+ _('Do _not ask me again'), on_response_ok = merge_contacts)
+ if not confirm_metacontacts: # First time we see this window
+ dlg.checkbutton.set_active(True)
+
+
+ def on_drop_in_group(self, widget, account, c_source, grp_dest, is_big_brother,
+ context, etime, grp_source = None):
+ if is_big_brother:
+ # add whole metacontact to new group
+ self.remove_contact_from_groups(c_source.jid, account, [grp_source,])
+ self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
+ else:
+ # Normal contact or little brother
+ family = gajim.contacts.get_metacontacts_family(account,
+ c_source.jid)
+ if family:
+ # Little brother
+ # Remove whole family. Remove us from the family.
+ # Then re-add other family members.
+ self._remove_metacontact_family(family)
+ gajim.contacts.remove_metacontact(account, c_source.jid)
+ for data in family:
+ if data['jid'] == c_source.jid:
+ continue
+ self.add_contact(data['jid'], data['account'])
+ break;
+
+ self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
+
+ else:
+ # Simple contact
+ self.remove_contact_from_groups(c_source.jid, account, [grp_source,])
+ self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
+
+ if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY):
+ context.finish(True, True, etime)
+
+
+ def drag_drop(self, treeview, context, x, y, timestamp):
+ target_list = treeview.drag_dest_get_target_list()
+ target = treeview.drag_dest_find_target(context, target_list)
+ selection = treeview.drag_get_data(context, target)
+ context.finish(False, True)
+ return True
+
+ def drag_data_received_data(self, treeview, context, x, y, selection, info,
+ etime):
+ treeview.stop_emission('drag_data_received')
+ drop_info = treeview.get_dest_row_at_pos(x, y)
+ if not drop_info:
+ return
+ if not selection.data:
+ return # prevents tb when several entrys are dragged
+ model = treeview.get_model()
+ data = selection.data
+ path_dest, position = drop_info
+
+ if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2 \
+ and path_dest[1] == 0: # dropped before the first group
+ return
+ if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
+ # dropped before a group: we drop it in the previous group every time
+ path_dest = (path_dest[0], path_dest[1]-1)
+ # destination: the row something got dropped on
+ iter_dest = model.get_iter(path_dest)
+ type_dest = model[iter_dest][C_TYPE].decode('utf-8')
+ jid_dest = model[iter_dest][C_JID].decode('utf-8')
+ account_dest = model[iter_dest][C_ACCOUNT].decode('utf-8')
+
+ # drop on account row in merged mode, we cannot know the desired account
+ if account_dest == 'all':
+ return
+ # nothing can be done, if destination account is offline
+ if gajim.connections[account_dest].connected < 2:
+ return
+
+ # A file got dropped on the roster
+ if info == self.TARGET_TYPE_URI_LIST:
+ if len(path_dest) < 3:
+ return
+ if type_dest != 'contact':
+ return
+ c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
+ jid_dest)
+ uri = data.strip()
+ uri_splitted = uri.split() # we may have more than one file dropped
+ nb_uri = len(uri_splitted)
+ # Check the URIs
+ bad_uris = []
+ for a_uri in uri_splitted:
+ path = helpers.get_file_path_from_dnd_dropped_uri(a_uri)
+ if not os.path.isfile(path):
+ bad_uris.append(a_uri)
+ if len(bad_uris):
+ dialogs.ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris))
+ return
+ def _on_send_files(account, jid, uris):
+ c = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ for uri in uris:
+ path = helpers.get_file_path_from_dnd_dropped_uri(uri)
+ if os.path.isfile(path): # is it file?
+ gajim.interface.instances['file_transfers'].send_file(
+ account, c, path)
+ # Popup dialog to confirm sending
+ prim_text = 'Send file?'
+ sec_text = i18n.ngettext('Do you want to send this file to %s:',
+ 'Do you want to send those files to %s:', nb_uri) %\
+ c_dest.get_shown_name()
+ for uri in uri_splitted:
+ path = helpers.get_file_path_from_dnd_dropped_uri(uri)
+ sec_text += '\n' + os.path.basename(path)
+ dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
+ on_response_ok = (_on_send_files, account_dest, jid_dest,
+ uri_splitted))
+ dialog.popup()
+ return
+
+ # a roster entry was dragged and dropped somewhere in the roster
+
+ # source: the row that was dragged
+ path_source = treeview.get_selection().get_selected_rows()[1][0]
+ iter_source = model.get_iter(path_source)
+ type_source = model[iter_source][C_TYPE]
+ account_source = model[iter_source][C_ACCOUNT].decode('utf-8')
+
+ # Only normal contacts can be dragged
+ if type_source != 'contact':
+ return
+ if gajim.config.get_per('accounts', account_source, 'is_zeroconf'):
+ return
+
+ # A contact was dropped
+ if gajim.config.get_per('accounts', account_dest, 'is_zeroconf'):
+ # drop on zeroconf account, adding not possible
+ return
+ if type_dest == 'self_contact':
+ # drop on self contact row
+ return
+ if type_dest == 'account' and account_source == account_dest:
+ # drop on the account it was dragged from
+ return
+ if type_dest == 'groupchat':
+ # drop on a minimized groupchat
+ # TODO: Invite to groupchat
+ return
+
+ # Get valid source group, jid and contact
+ it = iter_source
+ while model[it][C_TYPE] == 'contact':
+ it = model.iter_parent(it)
+ grp_source = model[it][C_JID].decode('utf-8')
+ if grp_source in helpers.special_groups and \
+ grp_source not in ('Not in Roster', 'Observers'):
+ # a transport or a minimized groupchat was dragged
+ # we can add it to other accounts but not move it to another group, see below
+ return
+ jid_source = data.decode('utf-8')
+ c_source = gajim.contacts.get_contact_with_highest_priority(
+ account_source, jid_source)
+
+ # Get destination group
+ grp_dest = None
+ if type_dest == 'group':
+ grp_dest = model[iter_dest][C_JID].decode('utf-8')
+ elif type_dest in ('contact', 'agent'):
+ it = iter_dest
+ while model[it][C_TYPE] != 'group':
+ it = model.iter_parent(it)
+ grp_dest = model[it][C_JID].decode('utf-8')
+ if grp_dest in helpers.special_groups:
+ return
+
+ if jid_source == jid_dest:
+ if grp_source == grp_dest and account_source == account_dest:
+ # Drop on self
+ return
+
+ # contact drop somewhere in or on a foreign account
+ if (type_dest == 'account' or not self.regroup) and \
+ account_source != account_dest:
+ # add to account in specified group
+ dialogs.AddNewContactWindow(account = account_dest, jid = jid_source,
+ user_nick = c_source.name, group = grp_dest)
+ return
+
+ # we may not add contacts from special_groups
+ if grp_source in helpers.special_groups :
+ return
+
+ # Is the contact we drag a meta contact?
+ is_big_brother = gajim.contacts.is_big_brother(account_source, jid_source)
+
+ # Contact drop on group row or between two contacts
+ if type_dest == 'group' or position == gtk.TREE_VIEW_DROP_BEFORE or \
+ position == gtk.TREE_VIEW_DROP_AFTER:
+ self.on_drop_in_group(None, account_source, c_source, grp_dest,
+ is_big_brother, context, etime, grp_source)
+ return
+
+ # Contact drop on another contact, make meta contacts
+ if position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or \
+ position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE:
+ c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
+ jid_dest)
+ if not c_dest:
+ # c_dest is None if jid_dest doesn't belong to account
+ return
+ self.on_drop_in_contact(treeview, account_source, c_source,
+ account_dest, c_dest, is_big_brother, context, etime)
+ return
+
+################################################################################
+### Everything about images and icons....
+### Cleanup assigned to Jim++ :-)
+################################################################################
+
+ def get_appropriate_state_images(self, jid, size = '16',
+ icon_name = 'online'):
+ '''check jid and return the appropriate state images dict for
+ the demanded size. icon_name is taken into account when jid is from
+ transport: transport iconset doesn't contain all icons, so we fall back
+ to jabber one'''
+ transport = gajim.get_transport_name_from_jid(jid)
+ if transport and self.transports_state_images.has_key(size):
+ if not self.transports_state_images[size].has_key(transport):
+ # we don't have iconset for this transport loaded yet. Let's do it
+ self.make_transport_state_images(transport)
+ if self.transports_state_images[size].has_key(transport) and \
+ icon_name in self.transports_state_images[size][transport]:
+ return self.transports_state_images[size][transport]
+ return gajim.interface.jabber_state_images[size]
+
+
+ def make_transport_state_images(self, transport):
+ '''initialise opened and closed 'transport' iconset dict'''
+ if gajim.config.get('use_transports_iconsets'):
+ folder = os.path.join(helpers.get_transport_path(transport),
+ '16x16')
+ pixo, pixc = gtkgui_helpers.load_icons_meta()
+ self.transports_state_images['opened'][transport] = \
+ gtkgui_helpers.load_iconset(folder, pixo, transport = True)
+ self.transports_state_images['closed'][transport] = \
+ gtkgui_helpers.load_iconset(folder, pixc, transport = True)
+ folder = os.path.join(helpers.get_transport_path(transport), '32x32')
+ self.transports_state_images['32'][transport] = gtkgui_helpers.load_iconset(
+ folder, transport = True)
+ folder = os.path.join(helpers.get_transport_path(transport), '16x16')
+ self.transports_state_images['16'][transport] = gtkgui_helpers.load_iconset(
+ folder, transport = True)
+
+ def update_jabber_state_images(self):
+ # Update the roster
+ self.draw_roster()
+ # Update the status combobox
+ model = self.status_combobox.get_model()
+ iter = model.get_iter_root()
+ while iter:
+ if model[iter][2] != '':
+ # If it's not change status message iter
+ # eg. if it has show parameter not ''
+ model[iter][1] = gajim.interface.jabber_state_images['16'][model[iter][2]]
+ iter = model.iter_next(iter)
+ # Update the systray
+ if gajim.interface.systray_enabled:
+ gajim.interface.systray.set_img()
+
+ for win in gajim.interface.msg_win_mgr.windows():
+ for ctrl in win.controls():
+ ctrl.update_ui()
+ win.redraw_tab(ctrl)
+
+ self.update_status_combobox()
+
+ def set_account_status_icon(self, account):
+ status = gajim.connections[account].connected
+ model = self.tree.get_model()
+ child_model = model.get_model()
+ iterA = self._get_account_iter(account)
+ if not iterA:
+ return
+ child_iterA = model.convert_iter_to_child_iter(iterA)
+ if not self.regroup:
+ show = gajim.SHOW_LIST[status]
+ else: # accounts merged
+ show = helpers.get_global_show()
+ child_model[child_iterA][C_IMG] = gajim.interface.jabber_state_images['16'][show]
+
+################################################################################
+### Style and theme related methods
+################################################################################
+
+ def show_title(self):
+ change_title_allowed = gajim.config.get('change_roster_title')
+ if not change_title_allowed:
+ return
+
+ if gajim.config.get('one_message_window') == 'always_with_roster':
+ # always_with_roster mode defers to the MessageWindow
+ if not gajim.interface.msg_win_mgr.one_window_opened():
+ # No MessageWindow to defer to
+ self.window.set_title('Gajim')
+ return
+
+ nb_unread = 0
+ start = ''
+ for account in gajim.connections:
+ # Count events in roster title only if we don't auto open them
+ if not helpers.allow_popup_window(account):
+ nb_unread += gajim.events.get_nb_events(['chat', 'normal',
+ 'file-request', 'file-error', 'file-completed',
+ 'file-request-error', 'file-send-error', 'file-stopped',
+ 'printed_chat'], account)
+ if nb_unread > 1:
+ 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)
+
+ def _change_style(self, model, path, iter, option):
+ if option is None or model[iter][C_TYPE] == option:
+ # We changed style for this type of row
+ model[iter][C_NAME] = model[iter][C_NAME]
+
+ def change_roster_style(self, option):
+ model = self.tree.get_model()
+ child_model = model.get_model()
+ child_model.foreach(self._change_style, option)
+ for win in gajim.interface.msg_win_mgr.windows():
+ win.repaint_themed_widgets()
+
+ def repaint_themed_widgets(self):
+ '''Notify windows that contain themed widgets to repaint them'''
+ for win in gajim.interface.msg_win_mgr.windows():
+ win.repaint_themed_widgets()
+ for account in gajim.connections:
+ for addr in gajim.interface.instances[account]['disco']:
+ gajim.interface.instances[account]['disco'][addr].paint_banner()
+ for ctrl in gajim.interface.minimized_controls[account].values():
+ ctrl.repaint_themed_widgets()
+
+ def update_avatar_in_gui(self, jid, account):
+ # Update roster
+ self.draw_avatar(jid, account)
+ # Update chat window
+ 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)
+ if win and ctrl.type_id != message_control.TYPE_GC:
+ ctrl.show_avatar()
+
+ def on_roster_treeview_style_set(self, treeview, style):
+ '''When style (theme) changes, redraw all contacts'''
+ for contact in self._iter_contact_rows():
+ self.draw_contact(contact[C_JID].decode('utf-8'),
+ contact[C_ACCOUNT].decode('utf-8'))
+
+ def set_renderer_color(self, renderer, style, set_background = True):
+ '''set style for treeview cell, using PRELIGHT system color'''
+ if set_background:
+ bgcolor = self.tree.style.bg[style]
+ renderer.set_property('cell-background-gdk', bgcolor)
+ else:
+ fgcolor = self.tree.style.fg[style]
+ renderer.set_property('foreground-gdk', fgcolor)
+
+ def _iconCellDataFunc(self, column, renderer, model, iter, data = None):
+ '''When a row is added, set properties for icon renderer'''
+ theme = gajim.config.get('roster_theme')
+ type_ = model[iter][C_TYPE]
+ if type_ == 'account':
+ color = gajim.config.get_per('themes', theme, 'accountbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
+ renderer.set_property('xalign', 0)
+ elif type_ == 'group':
+ color = gajim.config.get_per('themes', theme, 'groupbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_PRELIGHT)
+ renderer.set_property('xalign', 0.2)
+ elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
+ if not model[iter][C_JID] or not model[iter][C_ACCOUNT]:
+ # This can append when at the moment we add the row
+ return
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ if jid in gajim.newly_added[account]:
+ renderer.set_property('cell-background', gajim.config.get(
+ 'just_connected_bg_color'))
+ elif jid in gajim.to_be_removed[account]:
+ renderer.set_property('cell-background', gajim.config.get(
+ 'just_disconnected_bg_color'))
+ else:
+ color = gajim.config.get_per('themes', theme, 'contactbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ renderer.set_property('cell-background', None)
+ parent_iter = model.iter_parent(iter)
+ if model[parent_iter][C_TYPE] == 'contact':
+ renderer.set_property('xalign', 1)
+ else:
+ renderer.set_property('xalign', 0.4)
+ renderer.set_property('width', 26)
+
+ def _nameCellDataFunc(self, column, renderer, model, iter, data = None):
+ '''When a row is added, set properties for name renderer'''
+ theme = gajim.config.get('roster_theme')
+ type_ = model[iter][C_TYPE]
+ if type_ == 'account':
+ color = gajim.config.get_per('themes', theme, 'accounttextcolor')
+ if color:
+ renderer.set_property('foreground', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_ACTIVE, False)
+ color = gajim.config.get_per('themes', theme, 'accountbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
+ renderer.set_property('font',
+ gtkgui_helpers.get_theme_font_for_option(theme, 'accountfont'))
+ renderer.set_property('xpad', 0)
+ renderer.set_property('width', 3)
+ elif type_ == 'group':
+ color = gajim.config.get_per('themes', theme, 'grouptextcolor')
+ if color:
+ renderer.set_property('foreground', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_PRELIGHT, False)
+ color = gajim.config.get_per('themes', theme, 'groupbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_PRELIGHT)
+ renderer.set_property('font',
+ gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
+ renderer.set_property('xpad', 4)
+ elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
+ if not model[iter][C_JID] or not model[iter][C_ACCOUNT]:
+ # This can append when at the moment we add the row
+ return
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ color = gajim.config.get_per('themes', theme, 'contacttextcolor')
+ if color:
+ renderer.set_property('foreground', color)
+ else:
+ renderer.set_property('foreground', None)
+ if jid in gajim.newly_added[account]:
+ renderer.set_property('cell-background', gajim.config.get(
+ 'just_connected_bg_color'))
+ elif jid in gajim.to_be_removed[account]:
+ renderer.set_property('cell-background', gajim.config.get(
+ 'just_disconnected_bg_color'))
+ else:
+ color = gajim.config.get_per('themes', theme, 'contactbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ renderer.set_property('cell-background', None)
+ renderer.set_property('font',
+ gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
+ parent_iter = model.iter_parent(iter)
+ if model[parent_iter][C_TYPE] == 'contact':
+ renderer.set_property('xpad', 16)
+ else:
+ renderer.set_property('xpad', 8)
+
+ def _fill_avatar_pixbuf_rederer(self, column, renderer, model, iter,
+ data = None):
+ '''When a row is added, set properties for avatar renderer'''
+ theme = gajim.config.get('roster_theme')
+ type_ = model[iter][C_TYPE]
+ if type_ in ('group', 'account'):
+ renderer.set_property('visible', False)
+ return
+
+ # allocate space for the icon only if needed
+ if model[iter][C_AVATAR_PIXBUF] or \
+ gajim.config.get('avatar_position_in_roster') == 'left':
+ renderer.set_property('visible', True)
+ else:
+ renderer.set_property('visible', False)
+ if type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
+ if not model[iter][C_JID] or not model[iter][C_ACCOUNT]:
+ # This can append at the moment we add the row
+ return
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ if jid in gajim.newly_added[account]:
+ renderer.set_property('cell-background', gajim.config.get(
+ 'just_connected_bg_color'))
+ elif jid in gajim.to_be_removed[account]:
+ renderer.set_property('cell-background', gajim.config.get(
+ 'just_disconnected_bg_color'))
+ else:
+ color = gajim.config.get_per('themes', theme, 'contactbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ renderer.set_property('cell-background', None)
+ if gajim.config.get('avatar_position_in_roster') == 'left':
+ renderer.set_property('width', gajim.config.get('roster_avatar_width'))
+ renderer.set_property('xalign', 0.5)
+ else:
+ renderer.set_property('xalign', 1) # align pixbuf to the right
+
+ def _fill_padlock_pixbuf_rederer(self, column, renderer, model, iter,
+ data = None):
+ '''When a row is added, set properties for padlock renderer'''
+ theme = gajim.config.get('roster_theme')
+ type_ = model[iter][C_TYPE]
+ # allocate space for the icon only if needed
+ if type_ == 'account' and model[iter][C_PADLOCK_PIXBUF]:
+ renderer.set_property('visible', True)
+ color = gajim.config.get_per('themes', theme, 'accountbgcolor')
+ if color:
+ renderer.set_property('cell-background', color)
+ else:
+ self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
+ renderer.set_property('xalign', 1) # align pixbuf to the right
+ else:
+ renderer.set_property('visible', False)
+
+################################################################################
+### Everything about building menus
+### FIXME: We really need to make it simpler! 1465 lines are a few to much....
+################################################################################
def make_menu(self, force = False):
'''create the main window\'s menus'''
@@ -1172,7 +4492,7 @@ class RosterWindow:
account)
self.advanced_menus.append(advanced_menuitem_menu)
- self._add_history_manager_menuitem(advanced_menuitem_menu)
+ self.add_history_manager_menuitem(advanced_menuitem_menu)
advanced_menuitem.set_submenu(advanced_menuitem_menu)
advanced_menuitem_menu.show_all()
@@ -1190,7 +4510,7 @@ class RosterWindow:
self.advanced_menus.append(advanced_menuitem_menu)
advanced_item.set_submenu(advanced_menuitem_menu)
- self._add_history_manager_menuitem(advanced_sub_menu)
+ self.add_history_manager_menuitem(advanced_sub_menu)
advanced_menuitem.set_submenu(advanced_sub_menu)
advanced_sub_menu.show_all()
@@ -1198,1784 +4518,8 @@ class RosterWindow:
if sys.platform == 'darwin':
syncmenu.takeover_menu(self.xml.get_widget('menubar'))
- self.actions_menu_needs_rebuild = False
-
- def _add_history_manager_menuitem(self, menu):
- '''adds a seperator and History Manager menuitem BELOW for account
- menuitems'''
- item = gtk.SeparatorMenuItem() # separator
- menu.append(item)
-
- # History manager
- item = gtk.ImageMenuItem(_('History Manager'))
- icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL,
- gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- menu.append(item)
- item.connect('activate', self.on_history_manager_menuitem_activate)
-
- def add_bookmarks_list(self, gc_sub_menu, account):
- '''Show join new group chat item and bookmarks list for an account'''
- item = gtk.ImageMenuItem(_('_Join New Group Chat'))
- icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- item.connect('activate', self.on_join_gc_activate, account)
- gc_sub_menu.append(item)
-
- # user has at least one bookmark
- if len(gajim.connections[account].bookmarks) > 0:
- item = gtk.SeparatorMenuItem() # separator
- gc_sub_menu.append(item)
-
- for bookmark in gajim.connections[account].bookmarks:
- item = gtk.MenuItem(bookmark['name'], False) # Do not use underline
- item.connect('activate', self.on_bookmark_menuitem_activate,
- account, bookmark)
- gc_sub_menu.append(item)
-
- def _change_style(self, model, path, iter, option):
- if option is None or model[iter][C_TYPE] == option:
- # We changed style for this type of row
- model[iter][C_NAME] = model[iter][C_NAME]
-
- def change_roster_style(self, option):
- model = self.tree.get_model()
- model.foreach(self._change_style, option)
- for win in gajim.interface.msg_win_mgr.windows():
- win.repaint_themed_widgets()
-
- def draw_roster(self):
- '''clear and draw roster'''
- # clear the model, only if it is not empty
- model = self.tree.get_model()
- if model:
- model.clear()
- for acct in gajim.connections:
- self.add_account_to_roster(acct)
- self.add_account_contacts(acct)
- # Recalculate column width for ellipsizing
- self.tree.columns_autosize()
-
- def add_account_contacts(self, account):
- '''adds contacts of group to roster treeview'''
- for jid in gajim.contacts.get_jid_list(account):
- self.add_contact_to_roster(jid, account)
- self.draw_account(account)
-
- def fire_up_unread_messages_events(self, account):
- '''reads from db the unread messages, and fire them up, and
- if we find very old unread messages, delete them from unread table'''
- results = gajim.logger.get_unread_msgs()
- for result in results:
- jid = result[4]
- if gajim.contacts.get_first_contact_from_jid(account, jid):
- # We have this jid in our contacts list
- # XXX unread messages should probably have their session saved with
- # them
- 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)
-
- 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
- # roster for accounts we usually launch, so we will delete this id
- # from unread message tables.
- gajim.logger.set_read_messages([result[0]])
-
- def fill_contacts_and_groups_dicts(self, array, account):
- '''fill gajim.contacts and gajim.groups'''
- if account not in gajim.contacts.get_accounts():
- gajim.contacts.add_account(account)
- if not gajim.groups.has_key(account):
- gajim.groups[account] = {}
- for jid in array.keys():
- # Remove old Contact instances
- gajim.contacts.remove_jid(account, jid, remove_meta=False)
- jids = jid.split('/')
- # get jid
- ji = jids[0]
- # get resource
- resource = ''
- if len(jids) > 1:
- resource = '/'.join(jids[1:])
- # get name
- name = array[jid]['name']
- if not name:
- name = ''
- show = 'offline' # show is offline by default
- status = '' # no status message by default
-
- keyID = ''
- attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
- if jid in attached_keys:
- keyID = attached_keys[attached_keys.index(jid) + 1]
- contact1 = gajim.contacts.create_contact(jid = ji, name = name,
- groups = array[jid]['groups'], show = show, status = status,
- sub = array[jid]['subscription'], ask = array[jid]['ask'],
- resource = resource, keyID = keyID)
- gajim.contacts.add_contact(account, contact1)
-
- # when we draw the roster, we avoid having the same contact
- # more than once (f.e. we avoid showing it twice when 2 resources)
- for g in array[jid]['groups']:
- if g in gajim.groups[account].keys():
- continue
-
- if account + g in self.collapsed_rows:
- ishidden = False
- else:
- ishidden = True
- gajim.groups[account][g] = { 'expand': ishidden }
- if gajim.config.get('ask_avatars_on_startup'):
- pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(ji)
- if pixbuf == 'ask':
- transport = gajim.get_transport_name_from_jid(contact1.jid)
- if not transport or gajim.jid_is_transport(contact1.jid):
- jid_with_resource = contact1.jid
- if contact1.resource:
- jid_with_resource += '/' + contact1.resource
- gajim.connections[account].request_vcard(jid_with_resource)
- else:
- host = gajim.get_server_from_jid(contact1.jid)
- if not gajim.transport_avatar[account].has_key(host):
- 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
- # instance
- chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
- if chat_control:
- chat_control.contact = contact1
-
- def chg_contact_status(self, contact, show, status, account):
- '''When a contact changes his or her status'''
- contact_instances = gajim.contacts.get_contacts(account, contact.jid)
- contact.show = show
- contact.status = status
- # name is to show in conversation window
- name = contact.get_shown_name()
-
- if len(contact_instances) > 1:
- if contact.resource != '':
- name += '/' + contact.resource
- if show in ('offline', 'error') and \
- len(gajim.events.get_events(account, contact.get_full_jid())) == 0:
- jid_with_resource = contact.jid + '/' + contact.resource
- if gajim.interface.msg_win_mgr.has_window(jid_with_resource,
- 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)
- gajim.contacts.remove_contact(account, contact)
- self.remove_contact(contact, account)
- self.add_contact_to_roster(contact.jid, account)
- # 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)
-
- # unset custom status
- if gajim.interface.status_sent_to_users.has_key(account) and \
- contact.jid in gajim.interface.status_sent_to_users[account]:
- del gajim.interface.status_sent_to_users[account][contact.jid]
-
- if not contact.groups:
- self.draw_group(_('General'), account)
- else:
- for group in contact.groups:
- self.draw_group(group, account)
-
- self.draw_account(account)
-
- def on_info(self, widget, contact, account):
- '''Call vcard_information_window class to display contact's information'''
- if gajim.connections[account].is_zeroconf:
- self.on_info_zeroconf(widget, contact, account)
- return
-
- info = gajim.interface.instances[account]['infos']
- if info.has_key(contact.jid):
- info[contact.jid].window.present()
- else:
- info[contact.jid] = vcard.VcardWindow(contact, account)
-
- def on_info_zeroconf(self, widget, contact, account):
- info = gajim.interface.instances[account]['infos']
- if info.has_key(contact.jid):
- info[contact.jid].window.present()
- else:
- contact = gajim.contacts.get_first_contact_from_jid(account,
- contact.jid)
- if contact.show in ('offline', 'error'):
- # don't show info on offline contacts
- return
- info[contact.jid] = vcard.ZeroconfVcardWindow(contact, account)
-
- def show_tooltip(self, contact):
- pointer = self.tree.get_pointer()
- props = self.tree.get_path_at_pos(pointer[0], pointer[1])
- # check if the current pointer is at the same path
- # as it was before setting the timeout
- if props and self.tooltip.id == props[0]:
- # bounding rectangle of coordinates for the cell within the treeview
- rect = self.tree.get_cell_area(props[0], props[1])
-
- # position of the treeview on the screen
- position = self.tree.window.get_origin()
- self.tooltip.show_tooltip(contact, rect.height, position[1] + rect.y)
- else:
- self.tooltip.hide_tooltip()
-
- def on_roster_treeview_leave_notify_event(self, widget, event):
- props = widget.get_path_at_pos(int(event.x), int(event.y))
- if self.tooltip.timeout > 0:
- if not props or self.tooltip.id == props[0]:
- self.tooltip.hide_tooltip()
-
- def on_roster_treeview_motion_notify_event(self, widget, event):
- model = widget.get_model()
- props = widget.get_path_at_pos(int(event.x), int(event.y))
- if self.tooltip.timeout > 0:
- if not props or self.tooltip.id != props[0]:
- self.tooltip.hide_tooltip()
- if props:
- [row, col, x, y] = props
- iter = None
- try:
- iter = model.get_iter(row)
- except:
- self.tooltip.hide_tooltip()
- return
- if model[iter][C_TYPE] in ('contact', 'self_contact'):
- # we're on a contact entry in the roster
- account = model[iter][C_ACCOUNT].decode('utf-8')
- jid = model[iter][C_JID].decode('utf-8')
- if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
- self.tooltip.id = row
- contacts = gajim.contacts.get_contacts(account, jid)
- connected_contacts = []
- for c in contacts:
- if c.show not in ('offline', 'error'):
- connected_contacts.append(c)
- if not connected_contacts:
- # no connected contacts, show the ofline one
- connected_contacts = contacts
- self.tooltip.account = account
- self.tooltip.timeout = gobject.timeout_add(500,
- self.show_tooltip, connected_contacts)
- elif model[iter][C_TYPE] == 'groupchat':
- account = model[iter][C_ACCOUNT].decode('utf-8')
- jid = model[iter][C_JID].decode('utf-8')
- if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
- self.tooltip.id = row
- contact = gajim.contacts.get_contacts(account, jid)
- self.tooltip.account = account
- self.tooltip.timeout = gobject.timeout_add(500,
- self.show_tooltip, contact)
- elif model[iter][C_TYPE] == 'account':
- # we're on an account entry in the roster
- account = model[iter][C_ACCOUNT].decode('utf-8')
- if account == 'all':
- if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
- self.tooltip.id = row
- self.tooltip.account = None
- self.tooltip.timeout = gobject.timeout_add(500,
- self.show_tooltip, [])
- return
- jid = gajim.get_jid_from_account(account)
- contacts = []
- connection = gajim.connections[account]
- # get our current contact info
-
- nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
- accounts = [account])
- account_name = account
- if gajim.account_is_connected(account):
- account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
- contact = gajim.contacts.create_contact(jid = jid,
- name = account_name, show = connection.get_status(), sub = '',
- status = connection.status,
- resource = connection.server_resource,
- priority = connection.priority,
- mood = connection.mood,
- tune = connection.tune,
- activity = connection.activity)
- if gajim.connections[account].gpg:
- contact.keyID = gajim.config.get_per('accounts', connection.name,
- 'keyid')
- contacts.append(contact)
- # if we're online ...
- if connection.connection:
- roster = connection.connection.getRoster()
- # in threadless connection when no roster stanza is sent,
- # 'roster' is None
- if roster and roster.getItem(jid):
- resources = roster.getResources(jid)
- # ...get the contact info for our other online resources
- for resource in resources:
- # Check if we already have this resource
- found = False
- for contact_ in contacts:
- if contact_.resource == resource:
- found = True
- break
- if found:
- continue
- show = roster.getShow(jid+'/'+resource)
- if not show:
- show = 'online'
- contact = gajim.contacts.create_contact(jid = jid,
- name = account, show = show,
- status = roster.getStatus(jid+'/'+resource),
- resource = resource,
- priority = roster.getPriority(jid+'/'+resource))
- contacts.append(contact)
- if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
- self.tooltip.id = row
- self.tooltip.account = None
- self.tooltip.timeout = gobject.timeout_add(500,
- self.show_tooltip, contacts)
-
- def on_agent_logging(self, widget, jid, state, account):
- '''When an agent is requested to log in or off'''
- gajim.connections[account].send_agent_status(jid, state)
-
- def on_edit_agent(self, widget, contact, account):
- '''When we want to modify the agent registration'''
- gajim.connections[account].request_register_agent_info(contact.jid)
-
- def on_remove_agent(self, widget, list_):
- '''When an agent is requested to be removed. list_ is a list of
- (contact, account) tuple'''
- for (contact, account) in list_:
- if gajim.config.get_per('accounts', account, 'hostname') == \
- contact.jid:
- # We remove the server contact
- # remove it from treeview
- gajim.connections[account].unsubscribe(contact.jid)
- self.remove_contact(contact, account)
- gajim.contacts.remove_contact(account, contact)
- return
-
- def remove(list_):
- for (contact, account) in list_:
- full_jid = contact.get_full_jid()
- gajim.connections[account].unsubscribe_agent(full_jid)
- # remove transport from treeview
- self.remove_contact(contact, account)
- gajim.contacts.remove_jid(account, contact.jid)
- gajim.contacts.remove_contact(account, contact)
-
- # Check if there are unread events from some contacts
- has_unread_events = False
- for (contact, account) in list_:
- for jid in gajim.events.get_events(account):
- if jid.endswith(contact.jid):
- has_unread_events = True
- break
- if has_unread_events:
- dialogs.ErrorDialog(_('You have unread messages'),
- _('You must read them before removing this transport.'))
- return
- if len(list_) == 1:
- pritext = _('Transport "%s" will be removed') % contact.jid
- sectext = _('You will no longer be able to send and receive messages '
- 'from contacts using this transport.')
- else:
- pritext = _('Transports will be removed')
- jids = ''
- for (contact, account) in list_:
- jids += '\n ' + contact.get_shown_name() + ','
- jids = jids[:-1] + '.'
- sectext = _('You will no longer be able to send and receive messages '
- 'to contacts from these transports: %s') % jids
- dialogs.ConfirmationDialog(pritext, sectext,
- on_response_ok = (remove, list_))
-
- def on_block(self, widget, iter, group_list):
- ''' When clicked on the 'block' button in context menu. '''
- model = self.tree.get_model()
- accounts = []
- msg = self.get_status_message('offline')
- if group_list == None:
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- accounts.append(account)
- self.send_status(account, 'offline', msg, to = jid)
- new_rule = {'order': u'1', 'type': u'jid', 'action': u'deny',
- 'value' : jid, 'child': [u'message', u'iq', u'presence-out']}
- gajim.connections[account].blocked_list.append(new_rule)
- # needed for draw_contact:
- gajim.connections[account].blocked_contacts.append(jid)
- self.draw_contact(jid, account)
- else:
- if iter == None:
- for (contact, account) in group_list:
- if account not in accounts:
- if not gajim.connections[account].privacy_rules_supported:
- continue
- accounts.append(account)
- self.send_status(account, 'offline', msg, to=contact.jid)
- new_rule = {'order': u'1', 'type': u'jid',
- 'action': u'deny', 'value' : contact.jid,
- 'child': [u'message', u'iq', u'presence-out']}
- gajim.connections[account].blocked_list.append(new_rule)
- # needed for draw_contact:
- gajim.connections[account].blocked_contacts.append(contact.jid)
- self.draw_contact(contact.jid, account)
- else:
- group = model[iter][C_JID].decode('utf-8')
- for (contact, account) in group_list:
- if account not in accounts:
- if not gajim.connections[account].privacy_rules_supported:
- continue
- accounts.append(account)
- # needed for draw_group:
- gajim.connections[account].blocked_groups.append(group)
- self.draw_group(group, account)
- self.send_status(account, 'offline', msg, to=contact.jid)
- self.draw_contact(contact.jid, account)
- new_rule = {'order': u'1', 'type': u'group', 'action': u'deny',
- 'value' : group, 'child': [u'message', u'iq', u'presence-out']}
- gajim.connections[account].blocked_list.append(new_rule)
- for account in accounts:
- gajim.connections[account].set_privacy_list(
- 'block', gajim.connections[account].blocked_list)
- if len(gajim.connections[account].blocked_list) == 1:
- gajim.connections[account].set_active_list('block')
- gajim.connections[account].set_default_list('block')
- gajim.connections[account].get_privacy_list('block')
-
- def on_unblock(self, widget, iter, group_list):
- ''' When clicked on the 'unblock' button in context menu. '''
- model = self.tree.get_model()
- accounts = []
- if group_list == None:
- jid = model[iter][C_JID].decode('utf-8')
- jid_account = model[iter][C_ACCOUNT].decode('utf-8')
- accounts.append(jid_account)
- gajim.connections[jid_account].new_blocked_list = []
- for rule in gajim.connections[jid_account].blocked_list:
- if rule['action'] != 'deny' or rule['type'] != 'jid' \
- or rule['value'] != jid:
- gajim.connections[jid_account].new_blocked_list.append(rule)
- # needed for draw_contact:
- if jid in gajim.connections[jid_account].blocked_contacts:
- gajim.connections[jid_account].blocked_contacts.remove(jid)
- self.draw_contact(jid, jid_account)
- else:
- if iter == None:
- for (contact, account) in group_list:
- if account not in accounts:
- if gajim.connections[account].privacy_rules_supported:
- accounts.append(account)
- gajim.connections[account].new_blocked_list = []
- gajim.connections[account].to_unblock = []
- gajim.connections[account].to_unblock.append(contact.jid)
- else:
- gajim.connections[account].to_unblock.append(contact.jid)
- # needed for draw_contact:
- if contact.jid in gajim.connections[account].blocked_contacts:
- gajim.connections[account].blocked_contacts.remove(
- contact.jid)
- self.draw_contact(contact.jid, account)
- for account in accounts:
- for rule in gajim.connections[account].blocked_list:
- if rule['action'] != 'deny' or rule['type'] != 'jid' \
- or rule['value'] not in gajim.connections[account].to_unblock:
- gajim.connections[account].new_blocked_list.append(rule)
- else:
- group = model[iter][C_JID].decode('utf-8')
- for (contact, account) in group_list:
- if account not in accounts:
- if gajim.connections[account].privacy_rules_supported:
- accounts.append(account)
- # needed for draw_group:
- if group in gajim.connections[account].blocked_groups:
- gajim.connections[account].blocked_groups.remove(group)
- self.draw_group(group, account)
- gajim.connections[account].new_blocked_list = []
- for rule in gajim.connections[account].blocked_list:
- if rule['action'] != 'deny' or rule['type'] != 'group' \
- or rule['value'] != group:
- gajim.connections[account].new_blocked_list.append(
- rule)
- self.draw_contact(contact.jid, account)
- for account in accounts:
- gajim.connections[account].set_privacy_list(
- 'block', gajim.connections[account].new_blocked_list)
- gajim.connections[account].get_privacy_list('block')
- if len(gajim.connections[account].new_blocked_list) == 0:
- gajim.connections[account].blocked_list = []
- gajim.connections[account].blocked_contacts = []
- gajim.connections[account].blocked_groups = []
- gajim.connections[account].set_default_list('')
- gajim.connections[account].set_active_list('')
- gajim.connections[account].del_privacy_list('block')
- if gajim.interface.instances[account].has_key('blocked_contacts'):
- gajim.interface.instances[account]['blocked_contacts'].\
- privacy_list_received([])
- if group_list == None:
- status = gajim.connections[jid_account].connected
- msg = gajim.connections[jid_account].status
- if not self.regroup:
- show = gajim.SHOW_LIST[status]
- else: # accounts merged
- show = helpers.get_global_show()
- self.send_status(jid_account, show, msg, to=jid)
- else:
- for (contact, account) in group_list:
- if not self.regroup:
- show = gajim.SHOW_LIST[gajim.connections[account].connected]
- else: # accounts merged
- show = helpers.get_global_show()
- if account not in accounts:
- if gajim.connections[account].privacy_rules_supported:
- accounts.append(account)
- self.send_status(account, show,
- gajim.connections[account].status, to=contact.jid)
- else:
- self.send_status(account, show,
- gajim.connections[account].status, to=contact.jid)
-
- def on_rename(self, widget, iter, path):
- # this function is called either by F2 or by Rename menuitem
- if gajim.interface.instances.has_key('rename'):
- gajim.interface.instances['rename'].dialog.present()
- return
- model = self.tree.get_model()
-
- row_type = model[iter][C_TYPE]
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- # account is offline, don't allow to rename
- if gajim.connections[account].connected < 2:
- return
- if row_type in ('contact', 'agent'):
- # it's jid
- title = _('Rename Contact')
- message = _('Enter a new nickname for contact %s') % jid
- old_text = gajim.contacts.get_contact_with_highest_priority(account,
- jid).name
- elif row_type == 'group':
- if jid in helpers.special_groups + (_('General'),):
- return
- old_text = model[iter][C_JID].decode('utf-8')
- title = _('Rename Group')
- message = _('Enter a new name for group %s') % old_text
-
- def on_renamed(new_text, account, row_type, jid, old_text):
- if gajim.interface.instances.has_key('rename'):
- del gajim.interface.instances['rename']
- if row_type in ('contact', 'agent'):
- if old_text == new_text:
- return
- for u in gajim.contacts.get_contacts(account, jid):
- 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:
- ctrl.update_ui()
- win = gajim.interface.msg_win_mgr.get_window(jid, account)
- win.redraw_tab(ctrl)
- win.show_title()
- elif row_type == 'group':
- # in C_JID column, we hold the group name (which is not escaped)
- if old_text == new_text:
- return
- # Groups may not change name from or to a special groups
- for g in helpers.special_groups:
- if g in (new_text, old_text):
- return
- # get all contacts in that group
- for jid in gajim.contacts.get_jid_list(account):
- contact = gajim.contacts.get_contact_with_highest_priority(
- account, jid)
- if old_text in contact.groups:
- # set them in the new one and remove it from the old
- contact.groups.remove(old_text)
- self.remove_contact(contact, account)
- if new_text not in contact.groups:
- contact.groups.append(new_text)
- self.add_contact_to_roster(contact.jid, account)
- gajim.connections[account].update_contact(contact.jid,
- contact.name, contact.groups)
- # If last removed iter was not visible, gajim.groups is not cleaned
- if gajim.groups[account].has_key(old_text):
- del gajim.groups[account][old_text]
- self.draw_group(new_text, account)
-
- def on_canceled():
- if gajim.interface.instances.has_key('rename'):
- del gajim.interface.instances['rename']
-
- gajim.interface.instances['rename'] = dialogs.InputDialog(title, message,
- old_text, False, (on_renamed, account, row_type, jid, old_text),
- on_canceled)
-
- def readd_if_needed(self, contact, account):
- need_readd = False
- if len(gajim.events.get_events(account, contact.jid)):
- need_readd = True
- elif gajim.interface.msg_win_mgr.has_window(contact.jid, account):
- if _('Not in Roster') in contact.groups:
- # Close chat window
- msg_win = gajim.interface.msg_win_mgr.get_window(contact.jid,
- account)
- ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account)
- msg_win.remove_tab(ctrl, msg_win.CLOSE_CLOSE_BUTTON)
- else:
- need_readd = True
- if need_readd:
- c = gajim.contacts.create_contact(jid = contact.jid,
- name = '', groups = [_('Not in Roster')],
- show = 'not in roster', status = '', ask = 'none',
- keyID = contact.keyID)
- gajim.contacts.add_contact(account, c)
- self.add_contact_to_roster(contact.jid, account)
-
- def on_remove_group_item_activated(self, widget, group, account):
- dlg = dialogs.ConfirmationDialogCheck(_('Remove Group'),
- _('Do you want to remove group %s from the roster?' % group),
- _('Remove also all contacts in this group from your roster'))
- dlg.set_default_response(gtk.BUTTONS_OK_CANCEL)
- response = dlg.run()
- if response == gtk.RESPONSE_OK:
- for contact in gajim.contacts.get_contacts_from_group(account, group):
- if not dlg.is_checked():
- self.remove_contact_from_group(account, contact, group)
- gajim.connections[account].update_contact(contact.jid,
- contact.name, contact.groups)
- self.add_contact_to_roster(contact.jid, account)
- else:
- gajim.connections[account].unsubscribe(contact.jid)
- for c in gajim.contacts.get_contacts(account, contact.jid):
- self.remove_contact(c, account)
- gajim.contacts.remove_jid(account, c.jid)
- self.readd_if_needed(contact, account)
- self.draw_account(account)
-
- def on_assign_pgp_key(self, widget, contact, account):
- attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
- keys = {}
- #GPG Key
- keyID = _('None')
- for i in xrange(len(attached_keys)/2):
- keys[attached_keys[2*i]] = attached_keys[2*i+1]
- if attached_keys[2*i] == contact.jid:
- keyID = attached_keys[2*i+1]
- public_keys = gajim.connections[account].ask_gpg_keys()
- #GPG Key
- public_keys[_('None')] = _('None')
- instance = dialogs.ChooseGPGKeyDialog(_('Assign OpenPGP Key'),
- _('Select a key to apply to the contact'), public_keys, keyID)
- keyID = instance.run()
- if keyID is None:
- return
- #GPG Key
- if keyID[0] == _('None'):
- if contact.jid in keys:
- del keys[contact.jid]
- keyID = ''
- else:
- 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)
- ctrl.update_ui()
- keys_str = ''
- for jid in keys:
- keys_str += jid + ' ' + keys[jid] + ' '
- gajim.config.set_per('accounts', account, 'attached_gpg_keys', keys_str)
- for u in gajim.contacts.get_contacts(account, contact.jid):
- u.keyID = helpers.prepare_and_validate_gpg_keyID(account,
- contact.jid, keyID)
-
- def update_avatar_in_gui(self, jid, account):
- # Update roster
- self.draw_avatar(jid, account)
- # Update chat window
- 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)
- if win and ctrl.type_id != message_control.TYPE_GC:
- ctrl.show_avatar()
-
- def on_set_custom_avatar_activate(self, widget, contact, account):
- def on_ok(widget, path_to_file):
- filesize = os.path.getsize(path_to_file) # in bytes
- invalid_file = False
- msg = ''
- if os.path.isfile(path_to_file):
- stat = os.stat(path_to_file)
- if stat[6] == 0:
- invalid_file = True
- msg = _('File is empty')
- else:
- invalid_file = True
- msg = _('File does not exist')
- if invalid_file:
- dialogs.ErrorDialog(_('Could not load image'), msg)
- return
- try:
- pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
- if filesize > 16384: # 16 kb
- # get the image at 'tooltip size'
- # and hope that user did not specify in ACE crazy size
- pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip')
- except gobject.GError, msg: # unknown format
- # msg should be string, not object instance
- msg = str(msg)
- dialogs.ErrorDialog(_('Could not load image'), msg)
- return
- gajim.interface.save_avatar_files(contact.jid, pixbuf, local = True)
- dlg.destroy()
- self.update_avatar_in_gui(contact.jid, account)
-
- def on_clear(widget):
- dlg.destroy()
- # Delete file:
- gajim.interface.remove_avatar_files(contact.jid, local = True)
- self.update_avatar_in_gui(contact.jid, account)
-
- dlg = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
- on_response_clear = on_clear)
-
- def on_edit_groups(self, widget, list_):
- dlg = dialogs.EditGroupsDialog(list_)
- dlg.run()
-
- def on_history(self, widget, contact, account):
- '''When history menuitem is activated: call log window'''
- if gajim.interface.instances.has_key('logs'):
- gajim.interface.instances['logs'].window.present()
- gajim.interface.instances['logs'].open_history(contact.jid, account)
- else:
- gajim.interface.instances['logs'] = history_window.\
- HistoryWindow(contact.jid, account)
-
- def on_disconnect(self, widget, jid, account):
- '''When disconnect menuitem is activated: disconect from room'''
- ctrl = gajim.interface.minimized_controls[account][jid]
- del gajim.interface.minimized_controls[account][jid]
- ctrl.shutdown()
-
- contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- if not contact:
- return
- if contact.groups == [_('Groupchats')]:
- self.remove_contact(contact, account)
- gajim.contacts.remove_contact(account, contact)
- self.draw_group(_('Groupchats'), account)
- self.draw_account(account)
-
- def on_send_single_message_menuitem_activate(self, widget, account,
- contact = None):
- if contact is None:
- dialogs.SingleMessageWindow(account, action = 'send')
- elif type(contact) == type([]):
- dialogs.SingleMessageWindow(account, contact, 'send')
- else:
- jid = contact.jid
- if contact.jid == gajim.get_jid_from_account(account):
- jid += '/' + contact.resource
- dialogs.SingleMessageWindow(account, jid, 'send')
-
- def on_send_file_menuitem_activate(self, widget, contact, account,
- resource=None):
- gajim.interface.instances['file_transfers'].show_file_send_request(
- account, contact)
-
- def on_add_special_notification_menuitem_activate(self, widget, jid):
- dialogs.AddSpecialNotificationDialog(jid)
-
- def build_resources_submenu(self, contacts, account, action, room_jid=None,
- room_account=None):
- ''' Build a submenu with contact's resources.
- room_jid and room_account are for action self.on_invite_to_room '''
- sub_menu = gtk.Menu()
-
- iconset = gajim.config.get('iconset')
- if not iconset:
- iconset = gajim.config.DEFAULT_ICONSET
- path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
- for c in contacts:
- # icon MUST be different instance for every item
- state_images = gtkgui_helpers.load_iconset(path)
- item = gtk.ImageMenuItem('%s (%s)' % (c.resource, str(c.priority)))
- icon_name = helpers.get_icon_name_to_show(c, account)
- icon = state_images[icon_name]
- item.set_image(icon)
- sub_menu.append(item)
- if action == self.on_invite_to_room:
- item.connect('activate', action, [(c, account)],
- room_jid, room_account, c.resource)
- elif action == self.on_invite_to_new_room:
- item.connect('activate', action, [(c, account)], c.resource)
- else: # start_chat, execute_command, send_file
- item.connect('activate', action, c, account, c.resource)
- return sub_menu
-
- def build_invite_submenu(self, invite_menuitem, list_):
- '''list_ in a list of (contact, account)'''
- # used if we invite only one contact with several resources
- contact_list = []
- if len(list_) == 1:
- contact, account = list_[0]
- contact_list = gajim.contacts.get_contacts(account, contact.jid)
- contacts_transport = -1
- connected_accounts = []
- # -1 is at start, False when not from the same, None when jabber
- for (contact, account) in list_:
- if not account in connected_accounts:
- connected_accounts.append(account)
- transport = gajim.get_transport_name_from_jid(contact.jid)
- if contacts_transport == -1:
- contacts_transport = transport
- elif contacts_transport != transport:
- contacts_transport = False
-
- if contacts_transport == False:
- # they are not all from the same transport
- invite_menuitem.set_sensitive(False)
- return
- invite_to_submenu = gtk.Menu()
- invite_menuitem.set_submenu(invite_to_submenu)
- invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New Group Chat'))
- icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
- invite_to_new_room_menuitem.set_image(icon)
- if len(contact_list) > 1: # several resources
- invite_to_new_room_menuitem.set_submenu(self.build_resources_submenu(
- contact_list, account, self.on_invite_to_new_room))
- else:
- invite_to_new_room_menuitem.connect('activate',
- self.on_invite_to_new_room, list_)
- # transform None in 'jabber'
- c_t = contacts_transport or 'jabber'
- muc_jid = {}
- for account in connected_accounts:
- for t in gajim.connections[account].muc_jid:
- muc_jid[t] = gajim.connections[account].muc_jid[t]
- if not muc_jid.has_key(c_t):
- invite_to_new_room_menuitem.set_sensitive(False)
- rooms = [] # a list of (room_jid, account) tuple
- invite_to_submenu.append(invite_to_new_room_menuitem)
- rooms = [] # a list of (room_jid, account) tuple
- minimized_controls = []
- for account in connected_accounts:
- minimized_controls += \
- gajim.interface.minimized_controls[account].values()
- for gc_control in gajim.interface.msg_win_mgr.get_controls(
- message_control.TYPE_GC) + minimized_controls:
- acct = gc_control.account
- room_jid = gc_control.room_jid
- if gajim.gc_connected[acct].has_key(room_jid) and \
- gajim.gc_connected[acct][room_jid] and \
- contacts_transport == gajim.get_transport_name_from_jid(room_jid):
- rooms.append((room_jid, acct))
- if len(rooms):
- item = gtk.SeparatorMenuItem() # separator
- invite_to_submenu.append(item)
- for (room_jid, account) in rooms:
- menuitem = gtk.MenuItem(room_jid.split('@')[0])
- if len(contact_list) > 1: # several resources
- menuitem.set_submenu(self.build_resources_submenu(
- contact_list, account, self.on_invite_to_room, room_jid,
- account))
- else:
- menuitem.connect('activate', self.on_invite_to_room, list_,
- room_jid, account)
- invite_to_submenu.append(menuitem)
-
- def make_contact_menu(self, event, iter):
- '''Make contact\'s popup menu'''
- model = self.tree.get_model()
- jid = model[iter][C_JID].decode('utf-8')
- tree_path = model.get_path(iter)
- account = model[iter][C_ACCOUNT].decode('utf-8')
- our_jid = jid == gajim.get_jid_from_account(account)
- contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- if not contact:
- return
-
- # Zeroconf Account
- if gajim.config.get_per('accounts', account, 'is_zeroconf'):
- xml = gtkgui_helpers.get_glade('zeroconf_contact_context_menu.glade')
- zeroconf_contact_context_menu = xml.get_widget(
- 'zeroconf_contact_context_menu')
-
- start_chat_menuitem = xml.get_widget('start_chat_menuitem')
- rename_menuitem = xml.get_widget('rename_menuitem')
- edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
- send_file_menuitem = xml.get_widget('send_file_menuitem')
- assign_openpgp_key_menuitem = xml.get_widget(
- 'assign_openpgp_key_menuitem')
- add_special_notification_menuitem = xml.get_widget(
- 'add_special_notification_menuitem')
-
- if not our_jid:
- # add a special img for rename menuitem
- path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
- 'kbd_input.png')
- img = gtk.Image()
- img.set_from_file(path_to_kbd_input_img)
- rename_menuitem.set_image(img)
-
- above_information_separator = xml.get_widget(
- 'above_information_separator')
-
- information_menuitem = xml.get_widget('information_menuitem')
- history_menuitem = xml.get_widget('history_menuitem')
-
- contacts = gajim.contacts.get_contacts(account, jid)
- if len(contacts) > 1: # several resources
- sub_menu = gtk.Menu()
- start_chat_menuitem.set_submenu(sub_menu)
-
- iconset = gajim.config.get('iconset')
- path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
- for c in contacts:
- # icon MUST be different instance for every item
- state_images = gtkgui_helpers.load_iconset(path)
- item = gtk.ImageMenuItem('%s (%s)' % (c.resource,
- str(c.priority)))
- icon_name = helpers.get_icon_name_to_show(c, account)
- icon = state_images[icon_name]
- item.set_image(icon)
- sub_menu.append(item)
- item.connect('activate', self.on_open_chat_window, c, account,
- c.resource)
-
- else: # one resource
- start_chat_menuitem.connect('activate',
- self.on_roster_treeview_row_activated, tree_path)
-
- if contact.resource:
- send_file_menuitem.connect('activate',
- self.on_send_file_menuitem_activate, contact, account)
- else: # if we do no have resource we cannot do much
- send_file_menuitem.set_sensitive(False)
-
- rename_menuitem.connect('activate', self.on_rename, iter, tree_path)
- if contact.show in ('offline', 'error'):
- information_menuitem.set_sensitive(False)
- send_file_menuitem.set_sensitive(False)
- else:
- information_menuitem.connect('activate', self.on_info_zeroconf,
- contact, account)
- history_menuitem.connect('activate', self.on_history, contact,
- account)
-
- if _('Not in Roster') not in contact.groups:
- # contact is in normal group
- edit_groups_menuitem.set_no_show_all(False)
- assign_openpgp_key_menuitem.set_no_show_all(False)
- edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
- contact,account)])
-
- if gajim.connections[account].gpg:
- assign_openpgp_key_menuitem.connect('activate',
- self.on_assign_pgp_key, contact, account)
- else:
- assign_openpgp_key_menuitem.set_sensitive(False)
-
- else: # contact is in group 'Not in Roster'
- edit_groups_menuitem.set_sensitive(False)
- edit_groups_menuitem.set_no_show_all(True)
- assign_openpgp_key_menuitem.set_sensitive(False)
-
- # Remove many items when it's self contact row
- if our_jid:
- for menuitem in (rename_menuitem, edit_groups_menuitem,
- above_information_separator):
- menuitem.set_no_show_all(True)
- menuitem.hide()
-
- # Unsensitive many items when account is offline
- if gajim.connections[account].connected < 2:
- for widget in [start_chat_menuitem, rename_menuitem,
- edit_groups_menuitem, send_file_menuitem]:
- widget.set_sensitive(False)
-
- event_button = gtkgui_helpers.get_possible_button_event(event)
-
- zeroconf_contact_context_menu.attach_to_widget(self.tree, None)
- zeroconf_contact_context_menu.connect('selection-done',
- gtkgui_helpers.destroy_widget)
- zeroconf_contact_context_menu.show_all()
- zeroconf_contact_context_menu.popup(None, None, None, event_button,
- event.time)
- return
-
-
- # normal account
- xml = gtkgui_helpers.get_glade('roster_contact_context_menu.glade')
- roster_contact_context_menu = xml.get_widget(
- 'roster_contact_context_menu')
-
- start_chat_menuitem = xml.get_widget('start_chat_menuitem')
- send_custom_status_menuitem = xml.get_widget(
- 'send_custom_status_menuitem')
- send_single_message_menuitem = xml.get_widget(
- 'send_single_message_menuitem')
- invite_menuitem = xml.get_widget('invite_menuitem')
- block_menuitem = xml.get_widget('block_menuitem')
- unblock_menuitem = xml.get_widget('unblock_menuitem')
- rename_menuitem = xml.get_widget('rename_menuitem')
- edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
- send_file_menuitem = xml.get_widget('send_file_menuitem')
- assign_openpgp_key_menuitem = xml.get_widget(
- 'assign_openpgp_key_menuitem')
- set_custom_avatar_menuitem = xml.get_widget('set_custom_avatar_menuitem')
- add_special_notification_menuitem = xml.get_widget(
- 'add_special_notification_menuitem')
- execute_command_menuitem = xml.get_widget(
- 'execute_command_menuitem')
-
- # send custom status icon
- blocked = False
- if jid in gajim.connections[account].blocked_contacts:
- blocked = True
- else:
- groups = contact.groups
- if contact.is_observer():
- groups = [_('Observers')]
- elif not groups:
- groups = [_('General')]
- for group in groups:
- if group in gajim.connections[account].blocked_groups:
- blocked = True
- break
- if blocked:
- send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- 'offline'))
- send_custom_status_menuitem.set_sensitive(False)
- elif gajim.interface.status_sent_to_users.has_key(account) and \
- jid in gajim.interface.status_sent_to_users[account]:
- send_custom_status_menuitem.set_image(
- gtkgui_helpers.load_icon(gajim.interface.status_sent_to_users[
- account][jid]))
- else:
- icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_MENU)
- send_custom_status_menuitem.set_image(icon)
-
- if not our_jid:
- # add a special img for rename menuitem
- path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
- 'kbd_input.png')
- img = gtk.Image()
- img.set_from_file(path_to_kbd_input_img)
- rename_menuitem.set_image(img)
-
- muc_icon = gtkgui_helpers.load_icon('muc_active')
- if muc_icon:
- invite_menuitem.set_image(muc_icon)
-
- self.build_invite_submenu(invite_menuitem, [(contact, account)])
-
- # Subscription submenu
- subscription_menuitem = xml.get_widget('subscription_menuitem')
- send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem =\
- subscription_menuitem.get_submenu().get_children()
- add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem')
- remove_from_roster_menuitem = xml.get_widget(
- 'remove_from_roster_menuitem')
-
- information_menuitem = xml.get_widget('information_menuitem')
- history_menuitem = xml.get_widget('history_menuitem')
-
- contacts = gajim.contacts.get_contacts(account, jid)
-
- # One or several resource, we do the same for send_custom_status
- status_menuitems = gtk.Menu()
- send_custom_status_menuitem.set_submenu(status_menuitems)
- iconset = gajim.config.get('iconset')
- path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
- for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
- # icon MUST be different instance for every item
- state_images = gtkgui_helpers.load_iconset(path)
- status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
- status_menuitem.connect('activate', self.on_send_custom_status,
- [(contact, account)], s)
- icon = state_images[s]
- status_menuitem.set_image(icon)
- status_menuitems.append(status_menuitem)
- if len(contacts) > 1: # several resources
- start_chat_menuitem.set_submenu(self.build_resources_submenu(contacts,
- account, self.on_open_chat_window))
- send_file_menuitem.set_submenu(self.build_resources_submenu(contacts,
- account, self.on_send_file_menuitem_activate))
- execute_command_menuitem.set_submenu(self.build_resources_submenu(
- contacts, account, self.on_execute_command))
-
- else: # one resource
- start_chat_menuitem.connect('activate',
- self.on_open_chat_window, contact, account)
- execute_command_menuitem.connect('activate', self.on_execute_command,
- contact, account, contact.resource)
-
- our_jid_other_resource = None
- if our_jid:
- # It's another resource of us, be sure to send invite to her
- our_jid_other_resource = contact.resource
- #Â Else this var is useless but harmless in next connect calls
-
- if contact.resource:
- send_file_menuitem.connect('activate',
- self.on_send_file_menuitem_activate, contact, account)
- else: # if we do not have resource we cannot send file
- send_file_menuitem.set_sensitive(False)
-
- send_single_message_menuitem.connect('activate',
- self.on_send_single_message_menuitem_activate, account, contact)
-
- rename_menuitem.connect('activate', self.on_rename, iter, tree_path)
- remove_from_roster_menuitem.connect('activate', self.on_req_usub,
- [(contact, account)])
- information_menuitem.connect('activate', self.on_info, contact,
- account)
- history_menuitem.connect('activate', self.on_history, contact,
- account)
-
- if _('Not in Roster') not in contact.groups:
- # contact is in normal group
- add_to_roster_menuitem.hide()
- add_to_roster_menuitem.set_no_show_all(True)
- edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
- contact,account)])
-
- if gajim.connections[account].gpg:
- assign_openpgp_key_menuitem.connect('activate',
- self.on_assign_pgp_key, contact, account)
- else:
- assign_openpgp_key_menuitem.set_sensitive(False)
-
- if contact.sub in ('from', 'both'):
- send_auth_menuitem.set_sensitive(False)
- else:
- send_auth_menuitem.connect('activate', self.authorize, jid, account)
- if contact.sub in ('to', 'both'):
- ask_auth_menuitem.set_sensitive(False)
- add_special_notification_menuitem.connect('activate',
- self.on_add_special_notification_menuitem_activate, jid)
- else:
- ask_auth_menuitem.connect('activate', self.req_sub, jid,
- _('I would like to add you to my roster'), account,
- contact.groups, contact.name)
- if contact.sub in ('to', 'none'):
- revoke_auth_menuitem.set_sensitive(False)
- else:
- revoke_auth_menuitem.connect('activate', self.revoke_auth, jid,
- account)
-
- else: # contact is in group 'Not in Roster'
- add_to_roster_menuitem.set_no_show_all(False)
- edit_groups_menuitem.set_sensitive(False)
- assign_openpgp_key_menuitem.set_sensitive(False)
- subscription_menuitem.set_sensitive(False)
-
- add_to_roster_menuitem.connect('activate',
- self.on_add_to_roster, contact, account)
-
- set_custom_avatar_menuitem.connect('activate',
- self.on_set_custom_avatar_activate, contact, account)
- # Hide items when it's self contact row
- if our_jid:
- menuitem = xml.get_widget('manage_contact')
- menuitem.set_sensitive(False)
-
- # Unsensitive many items when account is offline
- if gajim.connections[account].connected < 2:
- for widget in [start_chat_menuitem, send_single_message_menuitem,
- rename_menuitem, edit_groups_menuitem, send_file_menuitem,
- subscription_menuitem, add_to_roster_menuitem,
- remove_from_roster_menuitem, execute_command_menuitem,
- send_custom_status_menuitem]:
- widget.set_sensitive(False)
-
- if gajim.connections[account] and gajim.connections[account].\
- privacy_rules_supported:
- if jid in gajim.connections[account].blocked_contacts:
- block_menuitem.set_no_show_all(True)
- unblock_menuitem.connect('activate', self.on_unblock, iter, None)
- block_menuitem.hide()
- else:
- unblock_menuitem.set_no_show_all(True)
- block_menuitem.connect('activate', self.on_block, iter, None)
- unblock_menuitem.hide()
- else:
- unblock_menuitem.set_no_show_all(True)
- block_menuitem.set_sensitive(False)
- unblock_menuitem.hide()
-
- event_button = gtkgui_helpers.get_possible_button_event(event)
-
- roster_contact_context_menu.attach_to_widget(self.tree, None)
- roster_contact_context_menu.connect('selection-done',
- gtkgui_helpers.destroy_widget)
- roster_contact_context_menu.show_all()
- roster_contact_context_menu.popup(None, None, None, event_button,
- event.time)
-
- def on_invite_to_new_room(self, widget, list_, resource = None):
- ''' resource parameter MUST NOT be used if more than one contact in
- list '''
- account_list = []
- jid_list = []
- for (contact, account) in list_:
- if contact.jid not in jid_list:
- if resource: # we MUST have one contact only in list_
- fjid = contact.jid + '/' + resource
- jid_list.append(fjid)
- else:
- jid_list.append(contact.jid)
- if account not in account_list:
- account_list.append(account)
- # transform None in 'jabber'
- type_ = gajim.get_transport_name_from_jid(jid_list[0]) or 'jabber'
- for account in account_list:
- if gajim.connections[account].muc_jid[type_]:
- # create the room on this muc server
- if gajim.interface.instances[account].has_key('join_gc'):
- gajim.interface.instances[account]['join_gc'].window.destroy()
- try:
- gajim.interface.instances[account]['join_gc'] = \
- dialogs.JoinGroupchatWindow(account,
- gajim.connections[account].muc_jid[type_],
- automatic = {'invities': jid_list})
- except GajimGeneralException:
- continue
- break
-
- def on_invite_to_room(self, widget, list_, room_jid, room_account,
- resource = None):
- ''' resource parameter MUST NOT be used if more than one contact in
- list '''
- for (contact, acct) in list_:
- contact_jid = contact.jid
- if resource: # we MUST have one contact only in list_
- contact_jid += '/' + resource
- gajim.connections[room_account].send_invite(room_jid, contact_jid)
-
-
- def make_multiple_contact_menu(self, event, iters):
- '''Make group's popup menu'''
- model = self.tree.get_model()
- list_ = [] # list of (jid, account) tuples
- one_account_offline = False
- is_blocked = True
- for iter in iters:
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- if gajim.connections[account].connected < 2:
- one_account_offline = True
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
- if jid not in gajim.connections[account].blocked_contacts:
- is_blocked = False
- list_.append((contact, account))
-
- menu = gtk.Menu()
- account = None
- for (contact, current_account) in list_:
- # check that we use the same account for every sender
- if account is not None and account != current_account:
- account = None
- break
- account = current_account
- if account is not None:
- send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
- icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
- send_group_message_item.set_image(icon)
- menu.append(send_group_message_item)
- send_group_message_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, list_)
-
- # Invite to Groupchat
- invite_item = gtk.ImageMenuItem(_('In_vite to'))
- muc_icon = gtkgui_helpers.load_icon('muc_active')
- if muc_icon:
- invite_item.set_image(muc_icon)
-
- self.build_invite_submenu(invite_item, list_)
- menu.append(invite_item)
-
- item = gtk.SeparatorMenuItem() # separator
- menu.append(item)
-
- # Manage Transport submenu
- item = gtk.ImageMenuItem(_('_Manage Contacts'))
- icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- manage_contacts_submenu = gtk.Menu()
- item.set_submenu(manage_contacts_submenu)
- menu.append(item)
-
- # Edit Groups
- edit_groups_item = gtk.ImageMenuItem(_('Edit _Groups'))
- icon = gtk.image_new_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_MENU)
- edit_groups_item.set_image(icon)
- manage_contacts_submenu.append(edit_groups_item)
- edit_groups_item.connect('activate', self.on_edit_groups, list_)
-
- item = gtk.SeparatorMenuItem() # separator
- manage_contacts_submenu.append(item)
-
- # Block
- if is_blocked and gajim.connections[account].privacy_rules_supported:
- unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
- unblock_menuitem.set_image(icon)
- unblock_menuitem.connect('activate', self.on_unblock, None, list_)
- manage_contacts_submenu.append(unblock_menuitem)
- else:
- block_menuitem = gtk.ImageMenuItem(_('_Block'))
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
- block_menuitem.set_image(icon)
- block_menuitem.connect('activate', self.on_block, None, list_)
- manage_contacts_submenu.append(block_menuitem)
-
- if not gajim.connections[account].privacy_rules_supported:
- block_menuitem.set_sensitive(False)
-
- # Remove
- remove_item = gtk.ImageMenuItem(_('_Remove'))
- icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
- remove_item.set_image(icon)
- manage_contacts_submenu.append(remove_item)
- remove_item.connect('activate', self.on_req_usub, list_)
- # unsensitive remove if one account is not connected
- if one_account_offline:
- remove_item.set_sensitive(False)
-
- event_button = gtkgui_helpers.get_possible_button_event(event)
-
- menu.attach_to_widget(self.tree, None)
- menu.connect('selection-done', gtkgui_helpers.destroy_widget)
- menu.show_all()
- menu.popup(None, None, None, event_button, event.time)
-
- def make_groupchat_menu(self, event, iter):
- model = self.tree.get_model()
-
- path = model.get_path(iter)
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- menu = gtk.Menu()
-
- if jid in gajim.interface.minimized_controls[account]:
- maximize_menuitem = gtk.ImageMenuItem(_('_Maximize'))
- icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
- maximize_menuitem.set_image(icon)
- maximize_menuitem.connect('activate', self.on_groupchat_maximized, \
- jid, account)
- menu.append(maximize_menuitem)
-
- disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect'))
- disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \
- gtk.ICON_SIZE_MENU)
- disconnect_menuitem.set_image(disconnect_icon)
- disconnect_menuitem .connect('activate', self.on_disconnect, jid, account)
- menu.append(disconnect_menuitem)
-
- item = gtk.SeparatorMenuItem() # separator
- menu.append(item)
-
- history_menuitem = gtk.ImageMenuItem(_('_History'))
- history_icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL, \
- gtk.ICON_SIZE_MENU)
- history_menuitem.set_image(history_icon)
- history_menuitem .connect('activate', self.on_history, \
- contact, account)
- menu.append(history_menuitem)
-
- event_button = gtkgui_helpers.get_possible_button_event(event)
-
- menu.attach_to_widget(self.tree, None)
- menu.connect('selection-done', gtkgui_helpers.destroy_widget)
- menu.show_all()
- menu.popup(None, None, None, event_button, event.time)
-
- def on_all_groupchat_maximized(self, widget, group_list):
- for (contact, account) in group_list:
- self.on_groupchat_maximized(widget, contact.jid, account)
-
-
- def on_groupchat_maximized(self, widget, jid, account):
- '''When a groupchat is maximised'''
- if not gajim.interface.minimized_controls[account].has_key(jid):
- return
-
-
- ctrl = gajim.interface.minimized_controls[account][jid]
- mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid, ctrl.account)
- if not mw:
- mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact,
- ctrl.account, ctrl.type_id)
- ctrl.parent_win = mw
- mw.new_tab(ctrl)
- mw.set_active_tab(jid, account)
- mw.window.present()
- del gajim.interface.minimized_controls[account][jid]
-
- contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- if not contact:
- return
- if contact.groups == [_('Groupchats')]:
- self.remove_contact(contact, account)
- gajim.contacts.remove_contact(account, contact)
- self.draw_group(_('Groupchats'), account)
- self.draw_account(account)
-
- def make_group_menu(self, event, iter):
- '''Make group's popup menu'''
- model = self.tree.get_model()
- path = model.get_path(iter)
- group = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
-
- list_ = [] # list of (jid, account) tuples
- list_online = [] # list of (jid, account) tuples
-
- group = model[iter][C_JID]
- for jid in gajim.contacts.get_jid_list(account):
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
- if group in contact.groups or (contact.groups == [] and group == \
- _('General')):
- if contact.show not in ('offline', 'error'):
- list_online.append((contact, account))
- list_.append((contact, account))
- menu = gtk.Menu()
-
- # Make special context menu if group is Groupchats
- if group == _('Groupchats'):
- maximize_menuitem = gtk.ImageMenuItem(_('_Maximize All'))
- icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
- maximize_menuitem.set_image(icon)
- maximize_menuitem.connect('activate', self.on_all_groupchat_maximized,\
- list_)
- menu.append(maximize_menuitem)
- else:
- # Send Group Message
- send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
- icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
- send_group_message_item.set_image(icon)
-
- send_group_message_submenu = gtk.Menu()
- send_group_message_item.set_submenu(send_group_message_submenu)
- menu.append(send_group_message_item)
-
- group_message_to_all_item = gtk.MenuItem(_('To all users'))
- send_group_message_submenu.append(group_message_to_all_item)
-
- group_message_to_all_online_item = gtk.MenuItem(_('To all online users'))
- send_group_message_submenu.append(group_message_to_all_online_item)
-
- group_message_to_all_online_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, list_online)
- group_message_to_all_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, list_)
-
- # Invite to
- invite_menuitem = gtk.ImageMenuItem(_('In_vite to'))
- muc_icon = gtkgui_helpers.load_icon('muc_active')
- if muc_icon:
- invite_menuitem.set_image(muc_icon)
-
- self.build_invite_submenu(invite_menuitem, list_online)
- menu.append(invite_menuitem)
-
- # Send Custom Status
- send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
- # add a special img for this menuitem
- if group in gajim.connections[account].blocked_groups:
- send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- 'offline'))
- send_custom_status_menuitem.set_sensitive(False)
- else:
- icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
- gtk.ICON_SIZE_MENU)
- send_custom_status_menuitem.set_image(icon)
- status_menuitems = gtk.Menu()
- send_custom_status_menuitem.set_submenu(status_menuitems)
- iconset = gajim.config.get('iconset')
- path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
- for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
- # icon MUST be different instance for every item
- state_images = gtkgui_helpers.load_iconset(path)
- status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
- status_menuitem.connect('activate', self.on_send_custom_status, list_,
- s, group)
- icon = state_images[s]
- status_menuitem.set_image(icon)
- status_menuitems.append(status_menuitem)
- menu.append(send_custom_status_menuitem)
-
- # there is no singlemessage and custom status for zeroconf
- if gajim.config.get_per('accounts', account, 'is_zeroconf'):
- send_custom_status_menuitem.set_sensitive(False)
- send_group_message_item.set_sensitive(False)
-
- if not group in helpers.special_groups:
- item = gtk.SeparatorMenuItem() # separator
- menu.append(item)
-
- # Rename
- rename_item = gtk.ImageMenuItem(_('Re_name'))
- # add a special img for rename menuitem
- path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
- 'kbd_input.png')
- img = gtk.Image()
- img.set_from_file(path_to_kbd_input_img)
- rename_item.set_image(img)
- menu.append(rename_item)
- rename_item.connect('activate', self.on_rename, iter, path)
-
- # Block group
- is_blocked = False
- if self.regroup:
- for g_account in gajim.connections:
- if group in gajim.connections[g_account].blocked_groups:
- is_blocked = True
- else:
- if group in gajim.connections[account].blocked_groups:
- is_blocked = True
-
- if is_blocked and gajim.connections[account].privacy_rules_supported:
- unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
- unblock_menuitem.set_image(icon)
- unblock_menuitem.connect('activate', self.on_unblock, iter, list_)
- menu.append(unblock_menuitem)
- else:
- block_menuitem = gtk.ImageMenuItem(_('_Block'))
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
- block_menuitem.set_image(icon)
- block_menuitem.connect('activate', self.on_block, iter, list_)
- menu.append(block_menuitem)
- if not gajim.connections[account].privacy_rules_supported:
- block_menuitem.set_sensitive(False)
-
- # Remove group
- remove_item = gtk.ImageMenuItem(_('_Remove'))
- icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
- remove_item.set_image(icon)
- menu.append(remove_item)
- remove_item.connect('activate', self.on_remove_group_item_activated,
- group, account)
-
- # unsensitive if account is not connected
- if gajim.connections[account].connected < 2:
- rename_item.set_sensitive(False)
-
- # General group cannot be changed
- if group == _('General'):
- rename_item.set_sensitive(False)
- block_menuitem.set_sensitive(False)
- remove_item.set_sensitive(False)
-
- event_button = gtkgui_helpers.get_possible_button_event(event)
-
- menu.attach_to_widget(self.tree, None)
- menu.connect('selection-done', gtkgui_helpers.destroy_widget)
- menu.show_all()
- menu.popup(None, None, None, event_button, event.time)
-
- def make_transport_menu(self, event, iter):
- '''Make transport\'s popup menu'''
- model = self.tree.get_model()
- jid = model[iter][C_JID].decode('utf-8')
- path = model.get_path(iter)
- account = model[iter][C_ACCOUNT].decode('utf-8')
- contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
- menu = gtk.Menu()
-
- # Send single message
- item = gtk.ImageMenuItem(_('Send Single Message'))
- icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, contact)
- menu.append(item)
-
- blocked = False
- if jid in gajim.connections[account].blocked_contacts:
- blocked = True
-
- # Send Custom Status
- send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
- # add a special img for this menuitem
- if blocked:
- send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- 'offline'))
- send_custom_status_menuitem.set_sensitive(False)
- else:
- if gajim.interface.status_sent_to_users.has_key(account) and \
- jid in gajim.interface.status_sent_to_users[account]:
- send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- gajim.interface.status_sent_to_users[account][jid]))
- else:
- icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
- gtk.ICON_SIZE_MENU)
- send_custom_status_menuitem.set_image(icon)
- status_menuitems = gtk.Menu()
- send_custom_status_menuitem.set_submenu(status_menuitems)
- iconset = gajim.config.get('iconset')
- path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
- for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
- # icon MUST be different instance for every item
- state_images = gtkgui_helpers.load_iconset(path)
- status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
- status_menuitem.connect('activate', self.on_send_custom_status,
- [(contact, account)], s)
- icon = state_images[s]
- status_menuitem.set_image(icon)
- status_menuitems.append(status_menuitem)
- menu.append(send_custom_status_menuitem)
-
- item = gtk.SeparatorMenuItem() # separator
- menu.append(item)
-
- # Execute Command
- item = gtk.ImageMenuItem(_('Execute Command...'))
- icon = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- menu.append(item)
- item.connect('activate', self.on_execute_command, contact, account,
- contact.resource)
- if gajim.account_is_disconnected(account):
- item.set_sensitive(False)
-
- # Manage Transport submenu
- item = gtk.ImageMenuItem(_('_Manage Transport'))
- icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- manage_transport_submenu = gtk.Menu()
- item.set_submenu(manage_transport_submenu)
- menu.append(item)
-
- # Modify Transport
- item = gtk.ImageMenuItem(_('_Modify Transport'))
- icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- manage_transport_submenu.append(item)
- item.connect('activate', self.on_edit_agent, contact, account)
- if gajim.account_is_disconnected(account):
- item.set_sensitive(False)
-
- # Rename
- item = gtk.ImageMenuItem(_('_Rename'))
- # add a special img for rename menuitem
- path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
- 'kbd_input.png')
- img = gtk.Image()
- img.set_from_file(path_to_kbd_input_img)
- item.set_image(img)
- manage_transport_submenu.append(item)
- item.connect('activate', self.on_rename, iter, path)
- if gajim.account_is_disconnected(account):
- item.set_sensitive(False)
-
- item = gtk.SeparatorMenuItem() # separator
- manage_transport_submenu.append(item)
-
- # Block
- if blocked:
- item = gtk.ImageMenuItem(_('_Unblock'))
- item.connect('activate', self.on_unblock, iter, None)
- else:
- item = gtk.ImageMenuItem(_('_Block'))
- item.connect('activate', self.on_block, iter, None)
-
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- manage_transport_submenu.append(item)
- if gajim.account_is_disconnected(account):
- item.set_sensitive(False)
-
- # Remove
- item = gtk.ImageMenuItem(_('_Remove'))
- icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
- item.set_image(icon)
- manage_transport_submenu.append(item)
- item.connect('activate', self.on_remove_agent, [(contact, account)])
- if gajim.account_is_disconnected(account):
- item.set_sensitive(False)
-
- item = gtk.SeparatorMenuItem() # separator
- menu.append(item)
-
- # Information
- information_menuitem = gtk.ImageMenuItem(_('_Information'))
- icon = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU)
- information_menuitem.set_image(icon)
- menu.append(information_menuitem)
- information_menuitem.connect('activate', self.on_info, contact, account)
-
-
- event_button = gtkgui_helpers.get_possible_button_event(event)
-
- menu.attach_to_widget(self.tree, None)
- menu.connect('selection-done', gtkgui_helpers.destroy_widget)
- menu.show_all()
- menu.popup(None, None, None, event_button, event.time)
-
- def on_edit_account(self, widget, account):
- if gajim.interface.instances.has_key('accounts'):
- gajim.interface.instances['accounts'].window.present()
- else:
- gajim.interface.instances['accounts'] = config.AccountsWindow()
- gajim.interface.instances['accounts'].select_account(account)
-
- def on_zeroconf_properties(self, widget, account):
- if gajim.interface.instances.has_key('accounts'):
- gajim.interface.instances['accounts'].window.present()
- else:
- gajim.interface.instances['accounts'] = config.AccountsWindow()
- gajim.interface.instances['accounts'].select_account(account)
-
- def on_open_gmail_inbox(self, widget, account):
- url = gajim.connections[account].gmail_url
- if url:
- helpers.launch_browser_mailer('url', url)
-
- def on_change_activity_activate(self, widget, account):
- dlg = dialogs.ChangeActivityDialog(account)
-
- def on_change_mood_activate(self, widget, account):
- dlg = dialogs.ChangeMoodDialog(account)
-
- def on_change_status_message_activate(self, widget, account):
- show = gajim.SHOW_LIST[gajim.connections[account].connected]
- dlg = dialogs.ChangeStatusMessageDialog(show)
- message = dlg.run()
- if message is not None: # None is if user pressed Cancel
- self.send_status(account, show, message)
-
+ self.actions_menu_needs_rebuild = False
+
def build_account_menu(self, account):
# we have to create our own set of icons for the menu
# using self.jabber_status_images is poopoo
@@ -3185,93 +4729,951 @@ class RosterWindow:
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
menu.show_all()
menu.popup(None, None, None, event_button, event.time)
+
+ def make_group_menu(self, event, iter):
+ '''Make group's popup menu'''
+ model = self.tree.get_model()
+ path = model.get_path(iter)
+ group = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
- def on_add_to_roster(self, widget, contact, account):
- dialogs.AddNewContactWindow(account, contact.jid, contact.name)
+ list_ = [] # list of (jid, account) tuples
+ list_online = [] # list of (jid, account) tuples
- def authorize(self, widget, jid, account):
- '''Authorize a contact (by re-sending auth menuitem)'''
- gajim.connections[account].send_authorization(jid)
- dialogs.InformationDialog(_('Authorization has been sent'),
- _('Now "%s" will know your status.') %jid)
+ group = model[iter][C_JID]
+ for jid in gajim.contacts.get_jid_list(account):
+ contact = gajim.contacts.get_contact_with_highest_priority(account,
+ jid)
+ if group in contact.groups or (contact.groups == [] and group == \
+ _('General')):
+ if contact.show not in ('offline', 'error'):
+ list_online.append((contact, account))
+ list_.append((contact, account))
+ menu = gtk.Menu()
- def req_sub(self, widget, jid, txt, account, groups = [], nickname = None,
- auto_auth = False):
- '''Request subscription to a contact'''
- gajim.connections[account].request_subscription(jid, txt, nickname,
- groups, auto_auth, gajim.nicks[account])
+ # Make special context menu if group is Groupchats
+ if group == _('Groupchats'):
+ maximize_menuitem = gtk.ImageMenuItem(_('_Maximize All'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
+ maximize_menuitem.set_image(icon)
+ maximize_menuitem.connect('activate', self.on_all_groupchat_maximized,\
+ list_)
+ menu.append(maximize_menuitem)
+ else:
+ # Send Group Message
+ send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
+ send_group_message_item.set_image(icon)
+
+ send_group_message_submenu = gtk.Menu()
+ send_group_message_item.set_submenu(send_group_message_submenu)
+ menu.append(send_group_message_item)
+
+ group_message_to_all_item = gtk.MenuItem(_('To all users'))
+ send_group_message_submenu.append(group_message_to_all_item)
+
+ group_message_to_all_online_item = gtk.MenuItem(_('To all online users'))
+ send_group_message_submenu.append(group_message_to_all_online_item)
+
+ group_message_to_all_online_item.connect('activate',
+ self.on_send_single_message_menuitem_activate, account, list_online)
+ group_message_to_all_item.connect('activate',
+ self.on_send_single_message_menuitem_activate, account, list_)
+
+ # Invite to
+ invite_menuitem = gtk.ImageMenuItem(_('In_vite to'))
+ muc_icon = gtkgui_helpers.load_icon('muc_active')
+ if muc_icon:
+ invite_menuitem.set_image(muc_icon)
+
+ self.build_invite_submenu(invite_menuitem, list_online)
+ menu.append(invite_menuitem)
+
+ # Send Custom Status
+ send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
+ # add a special img for this menuitem
+ if group in gajim.connections[account].blocked_groups:
+ send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
+ send_custom_status_menuitem.set_sensitive(False)
+ else:
+ icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
+ gtk.ICON_SIZE_MENU)
+ send_custom_status_menuitem.set_image(icon)
+ status_menuitems = gtk.Menu()
+ send_custom_status_menuitem.set_submenu(status_menuitems)
+ iconset = gajim.config.get('iconset')
+ path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
+ for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
+ # icon MUST be different instance for every item
+ state_images = gtkgui_helpers.load_iconset(path)
+ status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
+ status_menuitem.connect('activate', self.on_send_custom_status, list_,
+ s, group)
+ icon = state_images[s]
+ status_menuitem.set_image(icon)
+ status_menuitems.append(status_menuitem)
+ menu.append(send_custom_status_menuitem)
+
+ # there is no singlemessage and custom status for zeroconf
+ if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+ send_custom_status_menuitem.set_sensitive(False)
+ send_group_message_item.set_sensitive(False)
+
+ if not group in helpers.special_groups:
+ item = gtk.SeparatorMenuItem() # separator
+ menu.append(item)
+
+ # Rename
+ rename_item = gtk.ImageMenuItem(_('Re_name'))
+ # add a special img for rename menuitem
+ path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
+ 'kbd_input.png')
+ img = gtk.Image()
+ img.set_from_file(path_to_kbd_input_img)
+ rename_item.set_image(img)
+ menu.append(rename_item)
+ rename_item.connect('activate', self.on_rename, iter, path)
+
+ # Block group
+ is_blocked = False
+ if self.regroup:
+ for g_account in gajim.connections:
+ if group in gajim.connections[g_account].blocked_groups:
+ is_blocked = True
+ else:
+ if group in gajim.connections[account].blocked_groups:
+ is_blocked = True
+
+ if is_blocked and gajim.connections[account].privacy_rules_supported:
+ unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ unblock_menuitem.set_image(icon)
+ unblock_menuitem.connect('activate', self.on_unblock, iter, list_)
+ menu.append(unblock_menuitem)
+ else:
+ block_menuitem = gtk.ImageMenuItem(_('_Block'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ block_menuitem.set_image(icon)
+ block_menuitem.connect('activate', self.on_block, iter, list_)
+ menu.append(block_menuitem)
+ if not gajim.connections[account].privacy_rules_supported:
+ block_menuitem.set_sensitive(False)
+
+ # Remove group
+ remove_item = gtk.ImageMenuItem(_('_Remove'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
+ remove_item.set_image(icon)
+ menu.append(remove_item)
+ remove_item.connect('activate', self.on_remove_group_item_activated,
+ group, account)
+
+ # unsensitive if account is not connected
+ if gajim.connections[account].connected < 2:
+ rename_item.set_sensitive(False)
+
+ # General group cannot be changed
+ if group == _('General'):
+ rename_item.set_sensitive(False)
+ block_menuitem.set_sensitive(False)
+ remove_item.set_sensitive(False)
+
+ event_button = gtkgui_helpers.get_possible_button_event(event)
+
+ menu.attach_to_widget(self.tree, None)
+ 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, iter):
+ '''Make contact\'s popup menu'''
+ model = self.tree.get_model()
+ jid = model[iter][C_JID].decode('utf-8')
+ tree_path = model.get_path(iter)
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ our_jid = jid == gajim.get_jid_from_account(account)
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if not contact:
- keyID = ''
- attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
- if jid in attached_keys:
- keyID = attached_keys[attached_keys.index(jid) + 1]
- contact = gajim.contacts.create_contact(jid = jid, name = nickname,
- groups = groups, show = 'requested', status = '', ask = 'none',
- sub = 'subscribe', keyID = keyID)
- gajim.contacts.add_contact(account, contact)
+ return
+
+ # Zeroconf Account
+ if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+ xml = gtkgui_helpers.get_glade('zeroconf_contact_context_menu.glade')
+ zeroconf_contact_context_menu = xml.get_widget(
+ 'zeroconf_contact_context_menu')
+
+ start_chat_menuitem = xml.get_widget('start_chat_menuitem')
+ rename_menuitem = xml.get_widget('rename_menuitem')
+ edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
+ send_file_menuitem = xml.get_widget('send_file_menuitem')
+ assign_openpgp_key_menuitem = xml.get_widget(
+ 'assign_openpgp_key_menuitem')
+ add_special_notification_menuitem = xml.get_widget(
+ 'add_special_notification_menuitem')
+
+ if not our_jid:
+ # add a special img for rename menuitem
+ path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
+ 'kbd_input.png')
+ img = gtk.Image()
+ img.set_from_file(path_to_kbd_input_img)
+ rename_menuitem.set_image(img)
+
+ above_information_separator = xml.get_widget(
+ 'above_information_separator')
+
+ information_menuitem = xml.get_widget('information_menuitem')
+ history_menuitem = xml.get_widget('history_menuitem')
+
+ contacts = gajim.contacts.get_contacts(account, jid)
+ if len(contacts) > 1: # several resources
+ sub_menu = gtk.Menu()
+ start_chat_menuitem.set_submenu(sub_menu)
+
+ iconset = gajim.config.get('iconset')
+ path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
+ for c in contacts:
+ # icon MUST be different instance for every item
+ state_images = gtkgui_helpers.load_iconset(path)
+ item = gtk.ImageMenuItem('%s (%s)' % (c.resource,
+ str(c.priority)))
+ icon_name = helpers.get_icon_name_to_show(c, account)
+ icon = state_images[icon_name]
+ item.set_image(icon)
+ sub_menu.append(item)
+ item.connect('activate', self.on_open_chat_window, c, account,
+ c.resource)
+
+ else: # one resource
+ start_chat_menuitem.connect('activate',
+ self.on_roster_treeview_row_activated, tree_path)
+
+ if contact.resource:
+ send_file_menuitem.connect('activate',
+ self.on_send_file_menuitem_activate, contact, account)
+ else: # if we do no have resource we cannot do much
+ send_file_menuitem.set_sensitive(False)
+
+ rename_menuitem.connect('activate', self.on_rename, iter, tree_path)
+ if contact.show in ('offline', 'error'):
+ information_menuitem.set_sensitive(False)
+ send_file_menuitem.set_sensitive(False)
+ else:
+ information_menuitem.connect('activate', self.on_info_zeroconf,
+ contact, account)
+ history_menuitem.connect('activate', self.on_history, contact,
+ account)
+
+ if _('Not in Roster') not in contact.groups:
+ # contact is in normal group
+ edit_groups_menuitem.set_no_show_all(False)
+ assign_openpgp_key_menuitem.set_no_show_all(False)
+ edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
+ contact,account)])
+
+ if gajim.connections[account].gpg:
+ assign_openpgp_key_menuitem.connect('activate',
+ self.on_assign_pgp_key, contact, account)
+ else:
+ assign_openpgp_key_menuitem.set_sensitive(False)
+
+ else: # contact is in group 'Not in Roster'
+ edit_groups_menuitem.set_sensitive(False)
+ edit_groups_menuitem.set_no_show_all(True)
+ assign_openpgp_key_menuitem.set_sensitive(False)
+
+ # Remove many items when it's self contact row
+ if our_jid:
+ for menuitem in (rename_menuitem, edit_groups_menuitem,
+ above_information_separator):
+ menuitem.set_no_show_all(True)
+ menuitem.hide()
+
+ # Unsensitive many items when account is offline
+ if gajim.connections[account].connected < 2:
+ for widget in [start_chat_menuitem, rename_menuitem,
+ edit_groups_menuitem, send_file_menuitem]:
+ widget.set_sensitive(False)
+
+ event_button = gtkgui_helpers.get_possible_button_event(event)
+
+ zeroconf_contact_context_menu.attach_to_widget(self.tree, None)
+ zeroconf_contact_context_menu.connect('selection-done',
+ gtkgui_helpers.destroy_widget)
+ zeroconf_contact_context_menu.show_all()
+ zeroconf_contact_context_menu.popup(None, None, None, event_button,
+ event.time)
+ return
+
+ # normal account
+ xml = gtkgui_helpers.get_glade('roster_contact_context_menu.glade')
+ roster_contact_context_menu = xml.get_widget(
+ 'roster_contact_context_menu')
+
+ start_chat_menuitem = xml.get_widget('start_chat_menuitem')
+ send_custom_status_menuitem = xml.get_widget(
+ 'send_custom_status_menuitem')
+ send_single_message_menuitem = xml.get_widget(
+ 'send_single_message_menuitem')
+ invite_menuitem = xml.get_widget('invite_menuitem')
+ block_menuitem = xml.get_widget('block_menuitem')
+ unblock_menuitem = xml.get_widget('unblock_menuitem')
+ rename_menuitem = xml.get_widget('rename_menuitem')
+ edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
+ send_file_menuitem = xml.get_widget('send_file_menuitem')
+ assign_openpgp_key_menuitem = xml.get_widget(
+ 'assign_openpgp_key_menuitem')
+ set_custom_avatar_menuitem = xml.get_widget('set_custom_avatar_menuitem')
+ add_special_notification_menuitem = xml.get_widget(
+ 'add_special_notification_menuitem')
+ execute_command_menuitem = xml.get_widget(
+ 'execute_command_menuitem')
+
+ # send custom status icon
+ blocked = False
+ if jid in gajim.connections[account].blocked_contacts:
+ blocked = True
else:
- if not _('Not in Roster') in contact.groups:
- dialogs.InformationDialog(_('Subscription request has been sent'),
- _('If "%s" accepts this request you will know his or her status.'
- ) % jid)
- return
- contact.groups = groups
- if nickname:
- contact.name = nickname
- self.remove_contact(contact, account)
- self.add_contact_to_roster(jid, account)
+ groups = contact.groups
+ if contact.is_observer():
+ groups = [_('Observers')]
+ elif not groups:
+ groups = [_('General')]
+ for group in groups:
+ if group in gajim.connections[account].blocked_groups:
+ blocked = True
+ break
+ if blocked:
+ send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
+ send_custom_status_menuitem.set_sensitive(False)
+ elif gajim.interface.status_sent_to_users.has_key(account) and \
+ jid in gajim.interface.status_sent_to_users[account]:
+ send_custom_status_menuitem.set_image(
+ gtkgui_helpers.load_icon(gajim.interface.status_sent_to_users[account][jid]))
+ else:
+ icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_MENU)
+ send_custom_status_menuitem.set_image(icon)
- def revoke_auth(self, widget, jid, account):
- '''Revoke a contact's authorization'''
- gajim.connections[account].refuse_authorization(jid)
- dialogs.InformationDialog(_('Authorization has been removed'),
- _('Now "%s" will always see you as offline.') %jid)
+ if not our_jid:
+ # add a special img for rename menuitem
+ path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
+ 'kbd_input.png')
+ img = gtk.Image()
+ img.set_from_file(path_to_kbd_input_img)
+ rename_menuitem.set_image(img)
- def on_roster_treeview_scroll_event(self, widget, event):
- self.tooltip.hide_tooltip()
+ muc_icon = gtkgui_helpers.load_icon('muc_active')
+ if muc_icon:
+ invite_menuitem.set_image(muc_icon)
- def on_roster_treeview_key_press_event(self, widget, event):
- '''when a key is pressed in the treeviews'''
- self.tooltip.hide_tooltip()
- if event.keyval == gtk.keysyms.Escape:
- self.tree.get_selection().unselect_all()
- elif event.keyval == gtk.keysyms.F2:
- treeselection = self.tree.get_selection()
- model, list_of_paths = treeselection.get_selected_rows()
- if len(list_of_paths) != 1:
- return
- path = list_of_paths[0]
- type_ = model[path][C_TYPE]
- if type_ in ('contact', 'group', 'agent'):
- iter = model.get_iter(path)
- self.on_rename(widget, iter, path)
+ self.build_invite_submenu(invite_menuitem, [(contact, account)])
- elif event.keyval == gtk.keysyms.Delete:
- treeselection = self.tree.get_selection()
- model, list_of_paths = treeselection.get_selected_rows()
- if not len(list_of_paths):
- return
- type_ = model[list_of_paths[0]][C_TYPE]
- account = model[list_of_paths[0]][C_ACCOUNT]
- list_ = []
- for path in list_of_paths:
- if model[path][C_TYPE] != type_:
- return
- jid = model[path][C_JID].decode('utf-8')
- account = model[path][C_ACCOUNT].decode('utf-8')
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
- list_.append((contact, account))
- if type_ in ('account', 'group', 'self_contact') or \
- account == gajim.ZEROCONF_ACC_NAME:
- return
- if type_ == 'contact':
- self.on_req_usub(widget, list_)
- elif type_ == 'agent':
- self.on_remove_agent(widget, list_)
+ # Subscription submenu
+ subscription_menuitem = xml.get_widget('subscription_menuitem')
+ send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem =\
+ subscription_menuitem.get_submenu().get_children()
+ add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem')
+ remove_from_roster_menuitem = xml.get_widget(
+ 'remove_from_roster_menuitem')
+ information_menuitem = xml.get_widget('information_menuitem')
+ history_menuitem = xml.get_widget('history_menuitem')
+
+ contacts = gajim.contacts.get_contacts(account, jid)
+
+ # One or several resource, we do the same for send_custom_status
+ status_menuitems = gtk.Menu()
+ send_custom_status_menuitem.set_submenu(status_menuitems)
+ iconset = gajim.config.get('iconset')
+ path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
+ for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
+ # icon MUST be different instance for every item
+ state_images = gtkgui_helpers.load_iconset(path)
+ status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
+ status_menuitem.connect('activate', self.on_send_custom_status,
+ [(contact, account)], s)
+ icon = state_images[s]
+ status_menuitem.set_image(icon)
+ status_menuitems.append(status_menuitem)
+ if len(contacts) > 1: # several resources
+ start_chat_menuitem.set_submenu(self.build_resources_submenu(contacts,
+ account, self.on_open_chat_window))
+ send_file_menuitem.set_submenu(self.build_resources_submenu(contacts,
+ account, self.on_send_file_menuitem_activate))
+ execute_command_menuitem.set_submenu(self.build_resources_submenu(
+ contacts, account, self.on_execute_command))
+
+ else: # one resource
+ start_chat_menuitem.connect('activate',
+ self.on_open_chat_window, contact, account)
+ execute_command_menuitem.connect('activate', self.on_execute_command,
+ contact, account, contact.resource)
+
+ our_jid_other_resource = None
+ if our_jid:
+ # It's another resource of us, be sure to send invite to her
+ our_jid_other_resource = contact.resource
+ #Â Else this var is useless but harmless in next connect calls
+
+ if contact.resource:
+ send_file_menuitem.connect('activate',
+ self.on_send_file_menuitem_activate, contact, account)
+ else: # if we do not have resource we cannot send file
+ send_file_menuitem.set_sensitive(False)
+
+ send_single_message_menuitem.connect('activate',
+ self.on_send_single_message_menuitem_activate, account, contact)
+
+ rename_menuitem.connect('activate', self.on_rename, iter, tree_path)
+ remove_from_roster_menuitem.connect('activate', self.on_req_usub,
+ [(contact, account)])
+ information_menuitem.connect('activate', self.on_info, contact,
+ account)
+ history_menuitem.connect('activate', self.on_history, contact,
+ account)
+
+ if _('Not in Roster') not in contact.groups:
+ # contact is in normal group
+ add_to_roster_menuitem.hide()
+ add_to_roster_menuitem.set_no_show_all(True)
+ edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
+ contact,account)])
+
+ if gajim.connections[account].gpg:
+ assign_openpgp_key_menuitem.connect('activate',
+ self.on_assign_pgp_key, contact, account)
+ else:
+ assign_openpgp_key_menuitem.set_sensitive(False)
+
+ if contact.sub in ('from', 'both'):
+ send_auth_menuitem.set_sensitive(False)
+ else:
+ send_auth_menuitem.connect('activate', self.authorize, jid, account)
+ if contact.sub in ('to', 'both'):
+ ask_auth_menuitem.set_sensitive(False)
+ add_special_notification_menuitem.connect('activate',
+ self.on_add_special_notification_menuitem_activate, jid)
+ else:
+ ask_auth_menuitem.connect('activate', self.req_sub, jid,
+ _('I would like to add you to my roster'), account,
+ contact.groups, contact.name)
+ if contact.sub in ('to', 'none'):
+ revoke_auth_menuitem.set_sensitive(False)
+ else:
+ revoke_auth_menuitem.connect('activate', self.revoke_auth, jid,
+ account)
+
+ else: # contact is in group 'Not in Roster'
+ add_to_roster_menuitem.set_no_show_all(False)
+ edit_groups_menuitem.set_sensitive(False)
+ assign_openpgp_key_menuitem.set_sensitive(False)
+ subscription_menuitem.set_sensitive(False)
+
+ add_to_roster_menuitem.connect('activate',
+ self.on_add_to_roster, contact, account)
+
+ set_custom_avatar_menuitem.connect('activate',
+ self.on_set_custom_avatar_activate, contact, account)
+ # Hide items when it's self contact row
+ if our_jid:
+ menuitem = xml.get_widget('manage_contact')
+ menuitem.set_sensitive(False)
+
+ # Unsensitive many items when account is offline
+ if gajim.connections[account].connected < 2:
+ for widget in [start_chat_menuitem, send_single_message_menuitem,
+ rename_menuitem, edit_groups_menuitem, send_file_menuitem,
+ subscription_menuitem, add_to_roster_menuitem,
+ remove_from_roster_menuitem, execute_command_menuitem,
+ send_custom_status_menuitem]:
+ widget.set_sensitive(False)
+
+ if gajim.connections[account] and gajim.connections[account].\
+ privacy_rules_supported:
+ if jid in gajim.connections[account].blocked_contacts:
+ block_menuitem.set_no_show_all(True)
+ unblock_menuitem.connect('activate', self.on_unblock, iter, None)
+ block_menuitem.hide()
+ else:
+ unblock_menuitem.set_no_show_all(True)
+ block_menuitem.connect('activate', self.on_block, iter, None)
+ unblock_menuitem.hide()
+ else:
+ unblock_menuitem.set_no_show_all(True)
+ block_menuitem.set_sensitive(False)
+ unblock_menuitem.hide()
+
+ event_button = gtkgui_helpers.get_possible_button_event(event)
+
+ roster_contact_context_menu.attach_to_widget(self.tree, None)
+ roster_contact_context_menu.connect('selection-done',
+ gtkgui_helpers.destroy_widget)
+ roster_contact_context_menu.show_all()
+ roster_contact_context_menu.popup(None, None, None, event_button,
+ event.time)
+
+ def make_multiple_contact_menu(self, event, iters):
+ '''Make group's popup menu'''
+ model = self.tree.get_model()
+ list_ = [] # list of (jid, account) tuples
+ one_account_offline = False
+ is_blocked = True
+ for iter in iters:
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ if gajim.connections[account].connected < 2:
+ one_account_offline = True
+ contact = gajim.contacts.get_contact_with_highest_priority(account,
+ jid)
+ if jid not in gajim.connections[account].blocked_contacts:
+ is_blocked = False
+ list_.append((contact, account))
+
+ menu = gtk.Menu()
+ account = None
+ for (contact, current_account) in list_:
+ # check that we use the same account for every sender
+ if account is not None and account != current_account:
+ account = None
+ break
+ account = current_account
+ if account is not None:
+ send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
+ send_group_message_item.set_image(icon)
+ menu.append(send_group_message_item)
+ send_group_message_item.connect('activate',
+ self.on_send_single_message_menuitem_activate, account, list_)
+
+ # Invite to Groupchat
+ invite_item = gtk.ImageMenuItem(_('In_vite to'))
+ muc_icon = gtkgui_helpers.load_icon('muc_active')
+ if muc_icon:
+ invite_item.set_image(muc_icon)
+
+ self.build_invite_submenu(invite_item, list_)
+ menu.append(invite_item)
+
+ item = gtk.SeparatorMenuItem() # separator
+ menu.append(item)
+
+ # Manage Transport submenu
+ item = gtk.ImageMenuItem(_('_Manage Contacts'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ manage_contacts_submenu = gtk.Menu()
+ item.set_submenu(manage_contacts_submenu)
+ menu.append(item)
+
+ # Edit Groups
+ edit_groups_item = gtk.ImageMenuItem(_('Edit _Groups'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_MENU)
+ edit_groups_item.set_image(icon)
+ manage_contacts_submenu.append(edit_groups_item)
+ edit_groups_item.connect('activate', self.on_edit_groups, list_)
+
+ item = gtk.SeparatorMenuItem() # separator
+ manage_contacts_submenu.append(item)
+
+ # Block
+ if is_blocked and gajim.connections[account].privacy_rules_supported:
+ unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ unblock_menuitem.set_image(icon)
+ unblock_menuitem.connect('activate', self.on_unblock, None, list_)
+ manage_contacts_submenu.append(unblock_menuitem)
+ else:
+ block_menuitem = gtk.ImageMenuItem(_('_Block'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ block_menuitem.set_image(icon)
+ block_menuitem.connect('activate', self.on_block, None, list_)
+ manage_contacts_submenu.append(block_menuitem)
+
+ if not gajim.connections[account].privacy_rules_supported:
+ block_menuitem.set_sensitive(False)
+
+ # Remove
+ remove_item = gtk.ImageMenuItem(_('_Remove'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
+ remove_item.set_image(icon)
+ manage_contacts_submenu.append(remove_item)
+ remove_item.connect('activate', self.on_req_usub, list_)
+ # unsensitive remove if one account is not connected
+ if one_account_offline:
+ remove_item.set_sensitive(False)
+
+ event_button = gtkgui_helpers.get_possible_button_event(event)
+
+ menu.attach_to_widget(self.tree, None)
+ menu.connect('selection-done', gtkgui_helpers.destroy_widget)
+ menu.show_all()
+ menu.popup(None, None, None, event_button, event.time)
+
+ def make_transport_menu(self, event, iter):
+ '''Make transport\'s popup menu'''
+ model = self.tree.get_model()
+ jid = model[iter][C_JID].decode('utf-8')
+ path = model.get_path(iter)
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ menu = gtk.Menu()
+
+ # Send single message
+ item = gtk.ImageMenuItem(_('Send Single Message'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ item.connect('activate',
+ self.on_send_single_message_menuitem_activate, account, contact)
+ menu.append(item)
+
+ blocked = False
+ if jid in gajim.connections[account].blocked_contacts:
+ blocked = True
+
+ # Send Custom Status
+ send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
+ # add a special img for this menuitem
+ if blocked:
+ send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
+ send_custom_status_menuitem.set_sensitive(False)
+ else:
+ if gajim.interface.status_sent_to_users.has_key(account) and \
+ jid in gajim.interface.status_sent_to_users[account]:
+ send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
+ gajim.interface.status_sent_to_users[account][jid]))
+ else:
+ icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
+ gtk.ICON_SIZE_MENU)
+ send_custom_status_menuitem.set_image(icon)
+ status_menuitems = gtk.Menu()
+ send_custom_status_menuitem.set_submenu(status_menuitems)
+ iconset = gajim.config.get('iconset')
+ path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
+ for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
+ # icon MUST be different instance for every item
+ state_images = gtkgui_helpers.load_iconset(path)
+ status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
+ status_menuitem.connect('activate', self.on_send_custom_status,
+ [(contact, account)], s)
+ icon = state_images[s]
+ status_menuitem.set_image(icon)
+ status_menuitems.append(status_menuitem)
+ menu.append(send_custom_status_menuitem)
+
+ item = gtk.SeparatorMenuItem() # separator
+ menu.append(item)
+
+ # Execute Command
+ item = gtk.ImageMenuItem(_('Execute Command...'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ menu.append(item)
+ item.connect('activate', self.on_execute_command, contact, account,
+ contact.resource)
+ if gajim.account_is_disconnected(account):
+ item.set_sensitive(False)
+
+ # Manage Transport submenu
+ item = gtk.ImageMenuItem(_('_Manage Transport'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ manage_transport_submenu = gtk.Menu()
+ item.set_submenu(manage_transport_submenu)
+ menu.append(item)
+
+ # Modify Transport
+ item = gtk.ImageMenuItem(_('_Modify Transport'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ manage_transport_submenu.append(item)
+ item.connect('activate', self.on_edit_agent, contact, account)
+ if gajim.account_is_disconnected(account):
+ item.set_sensitive(False)
+
+ # Rename
+ item = gtk.ImageMenuItem(_('_Rename'))
+ # add a special img for rename menuitem
+ path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
+ 'kbd_input.png')
+ img = gtk.Image()
+ img.set_from_file(path_to_kbd_input_img)
+ item.set_image(img)
+ manage_transport_submenu.append(item)
+ item.connect('activate', self.on_rename, iter, path)
+ if gajim.account_is_disconnected(account):
+ item.set_sensitive(False)
+
+ item = gtk.SeparatorMenuItem() # separator
+ manage_transport_submenu.append(item)
+
+ # Block
+ if blocked:
+ item = gtk.ImageMenuItem(_('_Unblock'))
+ item.connect('activate', self.on_unblock, iter, None)
+ else:
+ item = gtk.ImageMenuItem(_('_Block'))
+ item.connect('activate', self.on_block, iter, None)
+
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ manage_transport_submenu.append(item)
+ if gajim.account_is_disconnected(account):
+ item.set_sensitive(False)
+
+ # Remove
+ item = gtk.ImageMenuItem(_('_Remove'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ manage_transport_submenu.append(item)
+ item.connect('activate', self.on_remove_agent, [(contact, account)])
+ if gajim.account_is_disconnected(account):
+ item.set_sensitive(False)
+
+ item = gtk.SeparatorMenuItem() # separator
+ menu.append(item)
+
+ # Information
+ information_menuitem = gtk.ImageMenuItem(_('_Information'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU)
+ information_menuitem.set_image(icon)
+ menu.append(information_menuitem)
+ information_menuitem.connect('activate', self.on_info, contact, account)
+
+
+ event_button = gtkgui_helpers.get_possible_button_event(event)
+
+ menu.attach_to_widget(self.tree, None)
+ menu.connect('selection-done', gtkgui_helpers.destroy_widget)
+ menu.show_all()
+ menu.popup(None, None, None, event_button, event.time)
+
+ def make_groupchat_menu(self, event, iter):
+ model = self.tree.get_model()
+
+ path = model.get_path(iter)
+ jid = model[iter][C_JID].decode('utf-8')
+ account = model[iter][C_ACCOUNT].decode('utf-8')
+ contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ menu = gtk.Menu()
+
+ if jid in gajim.interface.minimized_controls[account]:
+ maximize_menuitem = gtk.ImageMenuItem(_('_Maximize'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
+ maximize_menuitem.set_image(icon)
+ maximize_menuitem.connect('activate', self.on_groupchat_maximized, \
+ jid, account)
+ menu.append(maximize_menuitem)
+
+ disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect'))
+ disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \
+ gtk.ICON_SIZE_MENU)
+ disconnect_menuitem.set_image(disconnect_icon)
+ disconnect_menuitem .connect('activate', self.on_disconnect, jid, account)
+ menu.append(disconnect_menuitem)
+
+ item = gtk.SeparatorMenuItem() # separator
+ menu.append(item)
+
+ history_menuitem = gtk.ImageMenuItem(_('_History'))
+ history_icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL, \
+ gtk.ICON_SIZE_MENU)
+ history_menuitem.set_image(history_icon)
+ history_menuitem .connect('activate', self.on_history, \
+ contact, account)
+ menu.append(history_menuitem)
+
+ event_button = gtkgui_helpers.get_possible_button_event(event)
+
+ menu.attach_to_widget(self.tree, None)
+ menu.connect('selection-done', gtkgui_helpers.destroy_widget)
+ menu.show_all()
+ menu.popup(None, None, None, event_button, event.time)
+
+ def build_resources_submenu(self, contacts, account, action, room_jid=None,
+ room_account=None):
+ ''' Build a submenu with contact's resources.
+ room_jid and room_account are for action self.on_invite_to_room '''
+ sub_menu = gtk.Menu()
+
+ iconset = gajim.config.get('iconset')
+ if not iconset:
+ iconset = gajim.config.DEFAULT_ICONSET
+ path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
+ for c in contacts:
+ # icon MUST be different instance for every item
+ state_images = gtkgui_helpers.load_iconset(path)
+ item = gtk.ImageMenuItem('%s (%s)' % (c.resource, str(c.priority)))
+ icon_name = helpers.get_icon_name_to_show(c, account)
+ icon = state_images[icon_name]
+ item.set_image(icon)
+ sub_menu.append(item)
+ if action == self.on_invite_to_room:
+ item.connect('activate', action, [(c, account)],
+ room_jid, room_account, c.resource)
+ elif action == self.on_invite_to_new_room:
+ item.connect('activate', action, [(c, account)], c.resource)
+ else: # start_chat, execute_command, send_file
+ item.connect('activate', action, c, account, c.resource)
+ return sub_menu
+
+ def build_invite_submenu(self, invite_menuitem, list_):
+ '''list_ in a list of (contact, account)'''
+ # used if we invite only one contact with several resources
+ contact_list = []
+ if len(list_) == 1:
+ contact, account = list_[0]
+ contact_list = gajim.contacts.get_contacts(account, contact.jid)
+ contacts_transport = -1
+ connected_accounts = []
+ # -1 is at start, False when not from the same, None when jabber
+ for (contact, account) in list_:
+ if not account in connected_accounts:
+ connected_accounts.append(account)
+ transport = gajim.get_transport_name_from_jid(contact.jid)
+ if contacts_transport == -1:
+ contacts_transport = transport
+ elif contacts_transport != transport:
+ contacts_transport = False
+
+ if contacts_transport == False:
+ # they are not all from the same transport
+ invite_menuitem.set_sensitive(False)
+ return
+ invite_to_submenu = gtk.Menu()
+ invite_menuitem.set_submenu(invite_to_submenu)
+ invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New Group Chat'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
+ invite_to_new_room_menuitem.set_image(icon)
+ if len(contact_list) > 1: # several resources
+ invite_to_new_room_menuitem.set_submenu(self.build_resources_submenu(
+ contact_list, account, self.on_invite_to_new_room))
+ else:
+ invite_to_new_room_menuitem.connect('activate',
+ self.on_invite_to_new_room, list_)
+ # transform None in 'jabber'
+ c_t = contacts_transport or 'jabber'
+ muc_jid = {}
+ for account in connected_accounts:
+ for t in gajim.connections[account].muc_jid:
+ muc_jid[t] = gajim.connections[account].muc_jid[t]
+ if not muc_jid.has_key(c_t):
+ invite_to_new_room_menuitem.set_sensitive(False)
+ rooms = [] # a list of (room_jid, account) tuple
+ invite_to_submenu.append(invite_to_new_room_menuitem)
+ rooms = [] # a list of (room_jid, account) tuple
+ minimized_controls = []
+ for account in connected_accounts:
+ minimized_controls += \
+ gajim.interface.minimized_controls[account].values()
+ for gc_control in gajim.interface.msg_win_mgr.get_controls(
+ message_control.TYPE_GC) + minimized_controls:
+ acct = gc_control.account
+ room_jid = gc_control.room_jid
+ if gajim.gc_connected[acct].has_key(room_jid) and \
+ gajim.gc_connected[acct][room_jid] and \
+ contacts_transport == gajim.get_transport_name_from_jid(room_jid):
+ rooms.append((room_jid, acct))
+ if len(rooms):
+ item = gtk.SeparatorMenuItem() # separator
+ invite_to_submenu.append(item)
+ for (room_jid, account) in rooms:
+ menuitem = gtk.MenuItem(room_jid.split('@')[0])
+ if len(contact_list) > 1: # several resources
+ menuitem.set_submenu(self.build_resources_submenu(
+ contact_list, account, self.on_invite_to_room, room_jid,
+ account))
+ else:
+ menuitem.connect('activate', self.on_invite_to_room, list_,
+ room_jid, account)
+ invite_to_submenu.append(menuitem)
+
+ def get_and_connect_advanced_menuitem_menu(self, account):
+ '''adds FOR ACCOUNT options'''
+ xml = gtkgui_helpers.get_glade('advanced_menuitem_menu.glade')
+ advanced_menuitem_menu = xml.get_widget('advanced_menuitem_menu')
+
+ xml_console_menuitem = xml.get_widget('xml_console_menuitem')
+ privacy_lists_menuitem = xml.get_widget('privacy_lists_menuitem')
+ administrator_menuitem = xml.get_widget('administrator_menuitem')
+ send_server_message_menuitem = xml.get_widget(
+ 'send_server_message_menuitem')
+ set_motd_menuitem = xml.get_widget('set_motd_menuitem')
+ update_motd_menuitem = xml.get_widget('update_motd_menuitem')
+ delete_motd_menuitem = xml.get_widget('delete_motd_menuitem')
+
+ xml_console_menuitem.connect('activate',
+ self.on_xml_console_menuitem_activate, account)
+
+ if gajim.connections[account] and gajim.connections[account].\
+ privacy_rules_supported:
+ privacy_lists_menuitem.connect('activate',
+ self.on_privacy_lists_menuitem_activate, account)
+ else:
+ privacy_lists_menuitem.set_sensitive(False)
+
+ if gajim.connections[account].is_zeroconf:
+ administrator_menuitem.set_sensitive(False)
+ send_server_message_menuitem.set_sensitive(False)
+ set_motd_menuitem.set_sensitive(False)
+ update_motd_menuitem.set_sensitive(False)
+ delete_motd_menuitem.set_sensitive(False)
+ else:
+ send_server_message_menuitem.connect('activate',
+ self.on_send_server_message_menuitem_activate, account)
+
+ set_motd_menuitem.connect('activate',
+ self.on_set_motd_menuitem_activate, account)
+
+ update_motd_menuitem.connect('activate',
+ self.on_update_motd_menuitem_activate, account)
+
+ delete_motd_menuitem.connect('activate',
+ self.on_delete_motd_menuitem_activate, account)
+
+ advanced_menuitem_menu.show_all()
+
+ return advanced_menuitem_menu
+
+ def add_history_manager_menuitem(self, menu):
+ '''adds a seperator and History Manager menuitem BELOW for account
+ menuitems'''
+ item = gtk.SeparatorMenuItem() # separator
+ menu.append(item)
+
+ # History manager
+ item = gtk.ImageMenuItem(_('History Manager'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL,
+ gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ menu.append(item)
+ item.connect('activate', self.on_history_manager_menuitem_activate)
+
+ def add_bookmarks_list(self, gc_sub_menu, account):
+ '''Show join new group chat item and bookmarks list for an account'''
+ item = gtk.ImageMenuItem(_('_Join New Group Chat'))
+ icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
+ item.set_image(icon)
+ item.connect('activate', self.on_join_gc_activate, account)
+ gc_sub_menu.append(item)
+
+ # user has at least one bookmark
+ if len(gajim.connections[account].bookmarks) > 0:
+ item = gtk.SeparatorMenuItem() # separator
+ gc_sub_menu.append(item)
+
+ for bookmark in gajim.connections[account].bookmarks:
+ item = gtk.MenuItem(bookmark['name'], False) # Do not use underline
+ item.connect('activate', self.on_bookmark_menuitem_activate,
+ account, bookmark)
+ gc_sub_menu.append(item)
+
+ def set_actions_menu_needs_rebuild(self):
+ self.actions_menu_needs_rebuild = True
+ # Force the rebuild now since the on_activates on the menu itself does
+ # not work with the os/x top level menubar
+ if sys.platform == 'darwin':
+ self.make_menu(force = True)
+ return
+
def show_appropriate_context_menu(self, event, iters):
# iters must be all of the same type
model = self.tree.get_model()
@@ -3311,1914 +5713,10 @@ class RosterWindow:
self.show_appropriate_context_menu(event, iters)
return True
-
- def on_roster_treeview_button_release_event(self, widget, event):
- try:
- path, column, x, y = self.tree.get_path_at_pos(int(event.x),
- int(event.y))
- except TypeError:
- return False
-
- if event.button == 1: # Left click
- if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
- not event.state & gtk.gdk.CONTROL_MASK:
- # Check if button has been pressed on the same row
- if self.clicked_path == path:
- self.on_row_activated(widget, path)
- self.clicked_path = None
-
- def on_roster_treeview_button_press_event(self, widget, event):
- # hide tooltip, no matter the button is pressed
- self.tooltip.hide_tooltip()
- try:
- path, column, x, y = self.tree.get_path_at_pos(int(event.x),
- int(event.y))
- except TypeError:
- self.tree.get_selection().unselect_all()
- return False
-
- if event.button == 3: # Right click
- try:
- model, list_of_paths = self.tree.get_selection().get_selected_rows()
- except TypeError:
- list_of_paths = []
- if path not in list_of_paths:
- self.tree.get_selection().unselect_all()
- self.tree.get_selection().select_path(path)
- return self.show_treeview_menu(event)
-
- elif event.button == 2: # Middle click
- try:
- model, list_of_paths = self.tree.get_selection().get_selected_rows()
- except TypeError:
- list_of_paths = []
- if list_of_paths != [path]:
- self.tree.get_selection().unselect_all()
- self.tree.get_selection().select_path(path)
- type_ = model[path][C_TYPE]
- if type_ in ('agent', 'contact', 'self_contact', 'groupchat'):
- self.on_row_activated(widget, path)
- elif type_ == 'account':
- account = model[path][C_ACCOUNT].decode('utf-8')
- if account != 'all':
- show = gajim.connections[account].connected
- if show > 1: # We are connected
- self.on_change_status_message_activate(widget, account)
- return True
- show = helpers.get_global_show()
- if show == 'offline':
- return True
- dlg = dialogs.ChangeStatusMessageDialog(show)
- message = dlg.run()
- if not message:
- return True
- for acct in gajim.connections:
- if not gajim.config.get_per('accounts', acct,
- 'sync_with_global_status'):
- continue
- current_show = gajim.SHOW_LIST[gajim.connections[acct].connected]
- self.send_status(acct, current_show, message)
- return True
-
- elif event.button == 1: # Left click
- model = self.tree.get_model()
- type_ = model[path][C_TYPE]
- # x_min is the x start position of status icon column
- if gajim.config.get('avatar_position_in_roster') == 'left':
- x_min = gajim.config.get('roster_avatar_width')
- else:
- 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
- iter = model.get_iter(path)
- if x > x_min and x < x_min + 27 and type_ == 'contact' and \
- model.iter_has_child(iter):
- account = model[path][C_ACCOUNT].decode('utf-8')
- jid = model[path][C_JID].decode('utf-8')
- # first cell in 1st column (the arrow SINGLE clicked)
- iters = self.get_contact_iter(jid, account)
- for iter in iters:
- path = model.get_path(iter)
- if (self.tree.row_expanded(path)):
- self.tree.collapse_row(path)
- else:
- self.tree.expand_row(path, False)
- return
- # We just save on which row we press button, and open chat window on
- # button release to be able to do DND without opening chat window
- self.clicked_path = path
- return
- else:
- if type_ == 'group' and x < 27:
- # first cell in 1st column (the arrow SINGLE clicked)
- if (self.tree.row_expanded(path)):
- self.tree.collapse_row(path)
- else:
- self.tree.expand_row(path, False)
-
- elif type_ == 'contact' and x > x_min and x < x_min + 27:
- account = model[path][C_ACCOUNT].decode('utf-8')
- jid = model[path][C_JID].decode('utf-8')
- # first cell in 1st column (the arrow SINGLE clicked)
- iters = self.get_contact_iter(jid, account)
- for iter in iters:
- path = model.get_path(iter)
- if (self.tree.row_expanded(path)):
- self.tree.collapse_row(path)
- else:
- self.tree.expand_row(path, False)
-
- def on_req_usub(self, widget, list_):
- '''Remove a contact. list_ is a list of (contact, account) tuples'''
- def on_ok(is_checked, list_):
- remove_auth = True
- if len(list_) == 1:
- contact = list_[0][0]
- if contact.sub != 'to' and is_checked:
- remove_auth = False
- for (contact, account) in list_:
- gajim.connections[account].unsubscribe(contact.jid, remove_auth)
- for c in gajim.contacts.get_contacts(account, contact.jid):
- self.remove_contact(c, account)
- gajim.contacts.remove_jid(account, contact.jid)
- self.draw_account(account)
- if not remove_auth and contact.sub == 'both':
- contact.name = ''
- contact.groups = []
- contact.sub = 'from'
- gajim.contacts.add_contact(account, contact)
- self.add_contact_to_roster(contact.jid, account)
- else:
- if _('Not in Roster') in contact.groups:
- gajim.events.remove_events(account, contact.jid)
- self.readd_if_needed(contact, account)
-
- def on_ok2(list_):
- on_ok(False, list_)
-
- if len(list_) == 1:
- contact = list_[0][0]
- account = list_[0][1]
- pritext = _('Contact "%s" will be removed from your roster') % \
- contact.get_shown_name()
- if contact.sub == 'to':
- dialogs.ConfirmationDialog(pritext,
- _('By removing this contact you also remove authorization '
- 'resulting in him or her always seeing you as offline.'),
- on_response_ok = (on_ok2, list_))
- else:
- dialogs.ConfirmationDialogCheck(pritext,
- _('By removing this contact you also by default remove '
- 'authorization resulting in him or her always seeing you as '
- 'offline.'),
- _('I want this contact to know my status after removal'),
- on_response_ok = (on_ok, list_))
- else:
- # several contact to remove at the same time
- pritext = _('Contacts will be removed from your roster')
- jids = ''
- for (contact, account) in list_:
- jids += '\n ' + contact.get_shown_name() + ','
- sectext = _('By removing these contacts:%s\nyou also remove '
- 'authorization resulting in them always seeing you as offline.') % \
- jids
- dialogs.ConfirmationDialog(pritext, sectext,
- on_response_ok = (on_ok2, list_))
-
-
- def set_connecting_state(self, account):
- model = self.tree.get_model()
- accountIter = self.get_account_iter(account)
- if accountIter:
- model[accountIter][0] = gajim.interface.jabber_state_images['16'][
- 'connecting']
- if gajim.interface.systray_enabled:
- gajim.interface.systray.change_status('connecting')
-
- def send_status(self, account, status, txt, auto = False, to = None):
- model = self.tree.get_model()
- accountIter = self.get_account_iter(account)
- if status != 'offline':
- if gajim.connections[account].connected < 2:
- self.set_connecting_state(account)
-
- if not gajim.connections[account].password:
- passphrase = ''
- text = _('Enter your password for account %s') % account
- if passwords.USER_HAS_GNOMEKEYRING and \
- not passwords.USER_USES_GNOMEKEYRING:
- text += '\n' + _('Gnome Keyring is installed but not correctly started\
- (environment variable probably not correctly set)')
- w = dialogs.PassphraseDialog(_('Password Required'), text,
- _('Save password'))
- passphrase, save = w.run()
- if passphrase == -1:
- if accountIter:
- model[accountIter][0] = gajim.interface.\
- jabber_state_images['16']['offline']
- if gajim.interface.systray_enabled:
- gajim.interface.systray.change_status('offline')
- self.update_status_combobox()
- return
- gajim.connections[account].password = passphrase
- if save:
- gajim.config.set_per('accounts', account, 'savepass', True)
- passwords.save_password(account, passphrase)
-
- keyid = gajim.config.get_per('accounts', account, 'keyid')
- if keyid and not gajim.connections[account].gpg:
- dialog = dialogs.WarningDialog(_('GPG is not usable'),
- _('You will be connected to %s without OpenPGP.') % account)
-
- if gajim.account_is_connected(account):
- if status == 'online' and gajim.interface.sleeper.getState() != \
- common.sleepy.STATE_UNKNOWN:
- gajim.sleeper_state[account] = 'online'
- elif gajim.sleeper_state[account] not in ('autoaway', 'autoxa'):
- gajim.sleeper_state[account] = 'off'
- if to:
- gajim.connections[account].send_custom_status(status, txt, to)
- else:
- was_invisible = gajim.connections[account].connected == \
- gajim.SHOW_LIST.index('invisible')
- gajim.connections[account].change_status(status, txt, auto)
-
- if gajim.interface.status_sent_to_users.has_key(account):
- gajim.interface.status_sent_to_users[account] = {}
- if gajim.interface.status_sent_to_groups.has_key(account):
- gajim.interface.status_sent_to_groups[account] = {}
- for gc_control in gajim.interface.msg_win_mgr.get_controls(
- message_control.TYPE_GC) + \
- gajim.interface.minimized_controls[account].values():
- if gc_control.account == account:
- if gajim.gc_connected[account][gc_control.room_jid]:
- gajim.connections[account].send_gc_status(gc_control.nick,
- gc_control.room_jid, status, txt)
- else:
- # for some reason, we are not connected to the room even if
- # tab is opened, send initial join_gc()
- gajim.connections[account].join_gc(gc_control.nick,
- gc_control.room_jid, None)
- if was_invisible and status != 'offline':
- # We come back from invisible, join bookmarks
- self.auto_join_bookmarks(account)
-
- def get_status_message(self, show):
- if show in gajim.config.get_per('defaultstatusmsg'):
- if gajim.config.get_per('defaultstatusmsg', show, 'enabled'):
- return gajim.config.get_per('defaultstatusmsg', show, 'message')
- if (show == 'online' and not gajim.config.get('ask_online_status')) or \
- (show in ('offline', 'invisible')
- and not gajim.config.get('ask_offline_status')):
- return ''
- dlg = dialogs.ChangeStatusMessageDialog(show)
- dlg.window.present() # show it on current workspace
- message = dlg.run()
- return message
-
- def auto_join_bookmarks(self, account):
- '''autojoin bookmarks that have 'auto join' on for this account'''
- for bm in gajim.connections[account].bookmarks:
- if bm['autojoin'] in ('1', 'true'):
- jid = bm['jid']
- if not gajim.gc_connected[account].has_key(jid) or \
- not gajim.gc_connected[account][jid]:
- # we are not already connected
- minimize = bm['minimize'] in ('1', 'true')
- self.join_gc_room(account, jid, bm['nick'],
- bm['password'], minimize = minimize)
-
- def connected_rooms(self, account):
- if True in gajim.gc_connected[account].values():
- return True
- return False
-
- def change_status(self, widget, account, status):
- def change(account, status):
- message = self.get_status_message(status)
- if message is None:
- # user pressed Cancel to change status message dialog
- return
- self.send_status(account, status, message)
-
- if status == 'invisible' and self.connected_rooms(account):
- dialogs.ConfirmationDialog(
- _('You are participating in one or more group chats'),
- _('Changing your status to invisible will result in disconnection '
- 'from those group chats. Are you sure you want to go invisible?'),
- on_response_ok = (change, account, status))
- else:
- change(account, status)
-
- def on_send_custom_status(self, widget, contact_list, show, group=None):
- '''send custom status'''
- dlg = dialogs.ChangeStatusMessageDialog(show)
- message = dlg.run()
- if message is not None: # None if user pressed Cancel
- for (contact, account) in contact_list:
- our_jid = gajim.get_jid_from_account(account)
- accounts = []
- if group and account not in accounts:
- if not gajim.interface.status_sent_to_groups.has_key(account):
- gajim.interface.status_sent_to_groups[account] = {}
- gajim.interface.status_sent_to_groups[account][group] = show
- accounts.append(group)
- jid = contact.jid
- if jid == our_jid:
- jid += '/' + contact.resource
- self.send_status(account, show, message, to=jid)
- if not gajim.interface.status_sent_to_users.has_key(account):
- gajim.interface.status_sent_to_users[account] = {}
- gajim.interface.status_sent_to_users[account][contact.jid] = show
-
- def on_status_combobox_changed(self, widget):
- '''When we change our status via the combobox'''
- model = self.status_combobox.get_model()
- active = self.status_combobox.get_active()
- if active == -1: # no active item
- return
- if not self.combobox_callback_active:
- self.previous_status_combobox_active = active
- return
- accounts = gajim.connections.keys()
- if len(accounts) == 0:
- dialogs.ErrorDialog(_('No account available'),
- _('You must create an account before you can chat with other contacts.'))
- self.update_status_combobox()
- return
- status = model[active][2].decode('utf-8')
- statuses_unified = helpers.statuses_unified() # status "desync'ed" or not
- if (active == 7 and statuses_unified) or (active == 9 and not statuses_unified):
- # 'Change status message' selected:
- # do not change show, just show change status dialog
- status = model[self.previous_status_combobox_active][2].decode('utf-8')
- dlg = dialogs.ChangeStatusMessageDialog(status)
- message = dlg.run()
- if message is not None: # None if user pressed Cancel
- for account in accounts:
- if not gajim.config.get_per('accounts', account,
- 'sync_with_global_status'):
- continue
- current_show = gajim.SHOW_LIST[
- gajim.connections[account].connected]
- self.send_status(account, current_show, message)
- self.combobox_callback_active = False
- self.status_combobox.set_active(
- self.previous_status_combobox_active)
- self.combobox_callback_active = True
- return
- # we are about to change show, so save this new show so in case
- # after user chooses "Change status message" menuitem
- # we can return to this show
- self.previous_status_combobox_active = active
- connected_accounts = gajim.get_number_of_connected_accounts()
- if status == 'invisible':
- bug_user = False
- for account in accounts:
- if connected_accounts < 1 or gajim.account_is_connected(account):
- if not gajim.config.get_per('accounts', account,
- 'sync_with_global_status'):
- continue
- # We're going to change our status to invisible
- if self.connected_rooms(account):
- bug_user = True
- break
- if bug_user:
- dialog = dialogs.ConfirmationDialog(
- _('You are participating in one or more group chats'),
- _('Changing your status to invisible will result in '
- 'disconnection from those group chats. Are you sure you want to '
- 'go invisible?'))
- if dialog.get_response() != gtk.RESPONSE_OK:
- self.update_status_combobox()
- return
- message = self.get_status_message(status)
- if message is None: # user pressed Cancel to change status message dialog
- self.update_status_combobox()
- return
- global_sync_accounts = []
- for acct in accounts:
- if gajim.config.get_per('accounts', acct, 'sync_with_global_status'):
- global_sync_accounts.append(acct)
- global_sync_connected_accounts = gajim.get_number_of_connected_accounts(
- global_sync_accounts)
- for account in accounts:
- if not gajim.config.get_per('accounts', account,
- 'sync_with_global_status'):
- continue
- # we are connected (so we wanna change show and status)
- # or no account is connected and we want to connect with new show and
- # status
-
- if not global_sync_connected_accounts > 0 or \
- gajim.connections[account].connected > 0:
- self.send_status(account, status, message)
- self.update_status_combobox()
-
- ## enable setting status msg from currently playing music track
- def enable_syncing_status_msg_from_current_music_track(self, enabled):
- '''if enabled is True, we listen to events from music players about
- currently played music track, and we update our
- status message accordinly'''
- if not dbus_support.supported:
- # do nothing if user doesn't have D-Bus bindings
- return
- if enabled:
- listener = MusicTrackListener.get()
- if self._music_track_changed_signal is None:
- self._music_track_changed_signal = listener.connect(
- 'music-track-changed', self._music_track_changed)
- track = listener.get_playing_track()
- self._music_track_changed(listener, track)
- else:
- if self._music_track_changed_signal is not None:
- listener = MusicTrackListener.get()
- listener.disconnect(self._music_track_changed_signal)
- self._music_track_changed_signal = None
- self._music_track_changed(None, None)
-
- ## enable setting status msg from a Last.fm account
- def enable_syncing_status_msg_from_lastfm(self, enabled):
- '''if enabled is True, we start polling the Last.fm server,
- and we update our status message accordinly'''
- if enabled:
- if self._music_track_changed_signal is None:
- listener = LastFMTrackListener.get(
- gajim.config.get('lastfm_username'))
- self._music_track_changed_signal = listener.connect(
- 'music-track-changed', self._music_track_changed)
- track = listener.get_playing_track()
- self._music_track_changed(listener, track)
- else:
- if self._music_track_changed_signal is not None:
- listener = LastFMTrackListener.get(
- gajim.config.get('lastfm_username'))
- listener.disconnect(self._music_track_changed_signal)
- self._music_track_changed_signal = None
- self._music_track_changed(None, None)
-
- def _change_awn_icon_status(self, status):
- if not dbus_support.supported:
- # do nothing if user doesn't have D-Bus bindings
- return
- try:
- bus = dbus.SessionBus()
- if not 'com.google.code.Awn' in bus.list_names():
- # Awn is not installed
- return
- except:
- return
- iconset = gajim.config.get('iconset')
- prefix = os.path.join(helpers.get_iconset_path(iconset), '32x32')
- if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'):
- status = status + '.png'
- elif status == 'online':
- prefix = os.path.join(gajim.DATA_DIR, 'pixmaps')
- status = 'gajim.png'
- path = os.path.join(prefix, status)
- try:
- obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn')
- awn = dbus.Interface(obj, 'com.google.code.Awn')
- awn.SetTaskIconByName('Gajim', os.path.abspath(path))
- except Exception, e:
- pass
-
- def _music_track_changed(self, unused_listener, music_track_info,
- account=''):
- from common import pep
- if account == '':
- accounts = gajim.connections.keys()
- if music_track_info is None:
- artist = ''
- title = ''
- source = ''
- track = ''
- length = ''
- elif hasattr(music_track_info, 'paused') and music_track_info.paused == 0:
- artist = ''
- title = ''
- source = ''
- track = ''
- length = ''
- else:
- artist = music_track_info.artist
- title = music_track_info.title
- source = music_track_info.album
- if account == '':
- for account in accounts:
- if not gajim.account_is_connected(account):
- continue
- if not gajim.connections[account].pep_supported:
- continue
- pep.user_send_tune(account, artist, title, source)
- elif gajim.connections[account].pep_supported:
- pep.user_send_tune(account, artist, title, source)
-
- def update_status_combobox(self):
- # table to change index in connection.connected to index in combobox
- table = {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
- 'xa':3, 'dnd':4, 'invisible':5}
-
- # we check if there are more options in the combobox that it should
- # if yes, we remove the first ones
- while len(self.status_combobox.get_model()) > len(table)+2:
- self.status_combobox.remove_text(0)
-
- show = helpers.get_global_show()
- # temporarily block signal in order not to send status that we show
- # in the combobox
- self.combobox_callback_active = False
- if helpers.statuses_unified():
- self.status_combobox.set_active(table[show])
- else:
- uf_show = helpers.get_uf_show(show)
- liststore = self.status_combobox.get_model()
- liststore.prepend(['SEPARATOR', None, '', True])
- liststore.prepend([uf_show + ' ' + "(desync'ed)",
- gajim.interface.jabber_state_images['16'][show], show, False])
- self.status_combobox.set_active(0)
- self._change_awn_icon_status(show)
- self.combobox_callback_active = True
- if gajim.interface.systray_enabled:
- gajim.interface.systray.change_status(show)
-
- def set_account_status_icon(self, account):
- status = gajim.connections[account].connected
- model = self.tree.get_model()
- accountIter = self.get_account_iter(account)
- if not accountIter:
- return
- if not self.regroup:
- show = gajim.SHOW_LIST[status]
- else: # accounts merged
- show = helpers.get_global_show()
- model[accountIter][C_IMG] = gajim.interface.jabber_state_images['16'][
- show]
-
- def on_status_changed(self, account, status):
- '''the core tells us that our status has changed'''
- if account not in gajim.contacts.get_accounts():
- return
- model = self.tree.get_model()
- accountIter = self.get_account_iter(account)
- self.set_account_status_icon(account)
- if status == 'offline':
- if self.quit_on_next_offline > -1:
- # we want to quit, we are waiting for all accounts to be offline
- self.quit_on_next_offline -= 1
- if self.quit_on_next_offline < 1:
- # all accounts offline, quit
- self.quit_gtkgui_interface()
- else:
- # No need to redraw contacts if we're quitting
- if accountIter:
- model[accountIter][C_AVATAR_PIXBUF] = None
- if gajim.con_types.has_key(account):
- gajim.con_types[account] = None
- for jid in gajim.contacts.get_jid_list(account):
- lcontact = gajim.contacts.get_contacts(account, jid)
- for contact in [c for c in lcontact if (c.show != 'offline' or \
- c.is_transport())]:
- self.chg_contact_status(contact, 'offline', '', account)
- self.actions_menu_needs_rebuild = True
- self.update_status_combobox()
- # Force the rebuild now since the on_activates on the menu itself does
- # not work with the os/x top level menubar
- if sys.platform == 'darwin':
- self.make_menu(force = True)
-
- def new_private_chat(self, gc_contact, account, session = None):
- contact = gajim.contacts.contact_from_gc_contact(gc_contact)
- type_ = message_control.TYPE_PM
- fjid = gc_contact.room_jid + '/' + gc_contact.name
- mw = gajim.interface.msg_win_mgr.get_window(fjid, account)
- if not mw:
- mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
-
- chat_control = PrivateChatControl(mw, gc_contact, contact, account, session)
- mw.new_tab(chat_control)
- if len(gajim.events.get_events(account, fjid)):
- # We call this here to avoid race conditions with widget validation
- chat_control.read_queue()
-
- 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 mw:
- mw = gajim.interface.msg_win_mgr.create_window(contact, account, type_)
-
- chat_control = ChatControl(mw, contact, account, session, resource)
-
- 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()
-
- def new_chat_from_jid(self, account, fjid):
- jid, resource = gajim.get_room_and_nick_from_fjid(fjid)
- if resource:
- contact = gajim.contacts.get_contact(account, jid, resource)
- else:
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
- added_to_roster = False
- if not contact:
- added_to_roster = True
- contact = self.add_to_not_in_the_roster(account, jid,
- resource = resource)
-
- if not gajim.interface.msg_win_mgr.has_window(fjid, account):
- self.new_chat(contact, account, resource = resource)
- mw = gajim.interface.msg_win_mgr.get_window(fjid, account)
- mw.set_active_tab(fjid, account)
- mw.window.present()
- # For JEP-0172
- if added_to_roster:
- mc = mw.get_control(fjid, account)
- mc.user_nick = gajim.nicks[account]
-
- def new_room(self, room_jid, nick, account, is_continued=False):
- # Get target window, create a control, and associate it with the window
- contact = gajim.contacts.create_contact(jid = room_jid, name = nick)
- mw = gajim.interface.msg_win_mgr.get_window(contact.jid, account)
- if not mw:
- mw = gajim.interface.msg_win_mgr.create_window(contact, account,
- GroupchatControl.TYPE_ID)
- gc_control = GroupchatControl(mw, contact, account,
- is_continued=is_continued)
- mw.new_tab(gc_control)
-
- 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)
-
- path = self.get_path(jid, account) # Try to get line of contact in roster
-
- # 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:
- 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:
- 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_path(path, jid, account)
-
- def on_preferences_menuitem_activate(self, widget):
- if gajim.interface.instances.has_key('preferences'):
- gajim.interface.instances['preferences'].window.present()
- else:
- gajim.interface.instances['preferences'] = config.PreferencesWindow()
-
- def on_pep_services_menuitem_activate(self, widget, account):
- if gajim.interface.instances[account].has_key('pep_services'):
- gajim.interface.instances[account]['pep_services'].window.present()
- else:
- gajim.interface.instances[account]['pep_services'] = \
- config.ManagePEPServicesWindow(account)
-
- def on_add_new_contact(self, widget, account):
- dialogs.AddNewContactWindow(account)
-
- def on_join_gc_activate(self, widget, account):
- '''when the join gc menuitem is clicked, show the join gc window'''
- 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 gajim.interface.instances[account].has_key('join_gc'):
- gajim.interface.instances[account]['join_gc'].window.present()
- else:
- # c http://nkour.blogspot.com/2005/05/pythons-init-return-none-doesnt-return.html
- try:
- gajim.interface.instances[account]['join_gc'] = \
- dialogs.JoinGroupchatWindow(account)
- except GajimGeneralException:
- pass
-
- def on_new_chat_menuitem_activate(self, widget, account):
- dialogs.NewChatDialog(account)
-
- def on_contents_menuitem_activate(self, widget):
- helpers.launch_browser_mailer('url', 'http://trac.gajim.org/wiki')
-
- def on_faq_menuitem_activate(self, widget):
- helpers.launch_browser_mailer('url',
- 'http://trac.gajim.org/wiki/GajimFaq')
-
- def on_features_menuitem_activate(self, widget):
- features_window.FeaturesWindow()
-
- def on_about_menuitem_activate(self, widget):
- dialogs.AboutDialog()
-
- def on_accounts_menuitem_activate(self, widget):
- if gajim.interface.instances.has_key('accounts'):
- gajim.interface.instances['accounts'].window.present()
- else:
- gajim.interface.instances['accounts'] = config.AccountsWindow()
-
- def on_file_transfers_menuitem_activate(self, widget):
- if gajim.interface.instances['file_transfers'].window.get_property(
- 'visible'):
- gajim.interface.instances['file_transfers'].window.present()
- else:
- gajim.interface.instances['file_transfers'].window.show_all()
-
- def on_history_menuitem_activate(self, widget):
- if gajim.interface.instances.has_key('logs'):
- gajim.interface.instances['logs'].window.present()
- else:
- gajim.interface.instances['logs'] = history_window.\
- HistoryWindow()
-
- def on_show_transports_menuitem_activate(self, widget):
- gajim.config.set('show_transports_group', widget.get_active())
- self.draw_roster()
-
- def on_manage_bookmarks_menuitem_activate(self, widget):
- config.ManageBookmarksWindow()
-
- def on_profile_avatar_menuitem_activate(self, widget, account):
- gajim.interface.edit_own_details(account)
-
- def close_all_from_dict(self, dic):
- '''close all the windows in the given dictionary'''
- for w in dic.values():
- if type(w) == type({}):
- self.close_all_from_dict(w)
- else:
- w.window.destroy()
-
- def close_all(self, account, force = False):
- '''close all the windows from an account
- if force is True, do not ask confirmation before closing chat/gc windows
- '''
- if account in gajim.interface.instances:
- self.close_all_from_dict(gajim.interface.instances[account])
- for ctrl in gajim.interface.msg_win_mgr.get_controls(acct = account):
- ctrl.parent_win.remove_tab(ctrl, ctrl.parent_win.CLOSE_CLOSE_BUTTON,
- force = force)
-
- def on_roster_window_delete_event(self, widget, event):
- '''Main window X button was clicked'''
- if gajim.interface.systray_enabled and not gajim.config.get(
- 'quit_on_roster_x_button'):
- self.tooltip.hide_tooltip()
- self.window.hide()
- else:
- self.on_quit_request()
- return True # do NOT destroy the window
-
- def on_roster_window_focus_in_event(self, widget, event):
- # roster received focus, so if we had urgency REMOVE IT
- # NOTE: we do not have to read the message to remove urgency
- # so this functions does that
- gtkgui_helpers.set_unset_urgency_hint(widget, False)
-
- # if a contact row is selected, update colors (eg. for status msg)
- # because gtk engines may differ in bg when window is selected
- # or not
- if len(self._last_selected_contact):
- for (jid, account) in self._last_selected_contact:
- self.draw_contact(jid, account, selected = True,
- focus = True)
-
- def on_roster_window_focus_out_event(self, widget, event):
- # if a contact row is selected, update colors (eg. for status msg)
- # because gtk engines may differ in bg when window is selected
- # or not
- if len(self._last_selected_contact):
- for (jid, account) in self._last_selected_contact:
- self.draw_contact(jid, account, selected = True,
- focus = False)
-
- def on_roster_window_key_press_event(self, widget, event):
- if event.keyval == gtk.keysyms.Escape:
- if gajim.interface.msg_win_mgr.mode == \
- MessageWindowMgr.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER and \
- gajim.interface.msg_win_mgr.one_window_opened():
- # let message window close the tab
- return
- model, list_of_paths = self.tree.get_selection().get_selected_rows()
- if not len(list_of_paths) and gajim.interface.systray_enabled and \
- not gajim.config.get('quit_on_roster_x_button'):
- self.tooltip.hide_tooltip()
- self.window.hide()
-
- def on_roster_window_popup_menu(self, widget):
- event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
- self.show_treeview_menu(event)
-
- def quit_gtkgui_interface(self):
- '''When we quit the gtk interface :
- tell that to the core and exit gtk'''
- msgwin_width_adjust = 0
-
- # in case show_roster_on_start is False and roster is never shown
- # window.window is None
- if self.window.window is not None:
- x, y = self.window.window.get_root_origin()
- gajim.config.set('roster_x-position', x)
- gajim.config.set('roster_y-position', y)
- width, height = self.window.get_size()
- # For the width use the size of the vbox containing the tree and
- # status combo, this will cancel out any hpaned width
- width = self.xml.get_widget('roster_vbox2').allocation.width
- gajim.config.set('roster_width', width)
- gajim.config.set('roster_height', height)
- if not self.xml.get_widget('roster_vbox2').get_property('visible'):
- # The roster vbox is hidden, so the message window is larger
- # then we want to save (i.e. the window will grow every startup)
- # so adjust.
- msgwin_width_adjust = -1 * width
-
-
- gajim.config.set('show_roster_on_startup',
- self.window.get_property('visible'))
- gajim.interface.msg_win_mgr.shutdown(msgwin_width_adjust)
-
- gajim.config.set('collapsed_rows', '\t'.join(self.collapsed_rows))
- gajim.interface.save_config()
- for account in gajim.connections:
- gajim.connections[account].quit(True)
- self.close_all(account)
- if gajim.interface.systray_enabled:
- gajim.interface.hide_systray()
- gtk.main_quit()
-
- 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 '''
- accounts = gajim.connections.keys()
- get_msg = False
- for acct in accounts:
- if gajim.connections[acct].connected:
- get_msg = True
- break
- if get_msg:
- message = self.get_status_message('offline')
- if message is None:
- # user pressed Cancel to change status message dialog
- return
-
- # check if we have unread messages
- 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
-
- # check if we have recent messages
- recent = False
- for win in gajim.interface.msg_win_mgr.windows():
- for ctrl in win.controls():
- fjid = ctrl.get_full_jid()
- if gajim.last_message_time[ctrl.account].has_key(fjid):
- if time.time() - gajim.last_message_time[ctrl.account][fjid] < 2:
- recent = True
- break
- if recent:
- break
-
- if unread or recent:
- dialog = dialogs.ConfirmationDialog(_('You have unread messages'),
- _('Messages will only be available for reading them later if you'
- ' have history enabled and contact is in your roster.'))
- if dialog.get_response() != gtk.RESPONSE_OK:
- return
-
- self.quit_on_next_offline = 0
- for acct in accounts:
- 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()
-
- def open_event(self, account, jid, event):
- '''If an event was handled, return True, else return False'''
- data = event.parameters
- ft = gajim.interface.instances['file_transfers']
- if event.type_ == 'normal':
- dialogs.SingleMessageWindow(account, jid,
- action='receive', from_whom=jid, subject=data[1], message=data[0],
- resource=data[5], session=data[8], form_node=data[9])
- gajim.interface.remove_first_event(account, jid, event.type_)
- return True
- elif event.type_ == 'file-request':
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
- gajim.interface.remove_first_event(account, jid, event.type_)
- ft.show_file_request(account, contact, data)
- return True
- elif event.type_ in ('file-request-error', 'file-send-error'):
- gajim.interface.remove_first_event(account, jid, event.type_)
- ft.show_send_error(data)
- return True
- elif event.type_ in ('file-error', 'file-stopped'):
- gajim.interface.remove_first_event(account, jid, event.type_)
- ft.show_stopped(jid, data)
- return True
- elif event.type_ == 'file-completed':
- gajim.interface.remove_first_event(account, jid, event.type_)
- ft.show_completed(jid, data)
- return True
- elif event.type_ == 'gc-invitation':
- dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
- data[1])
- gajim.interface.remove_first_event(account, jid, event.type_)
- return True
- return False
-
- 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
- able to make this a callback.'''
- jid = contact.jid
- if resource is not None:
- jid = jid + u'/' + resource
- adhoc_commands.CommandWindow(account, jid)
-
- 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, session = session)
- 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)
- if gajim.connections[account].is_zeroconf and \
- gajim.connections[account].status in ('offline', 'invisible'):
- win.get_control(fjid, account).got_disconnected()
-
- win.window.present()
-
- def on_row_activated(self, widget, path):
- '''When an iter is activated (dubblick or single click if gnome is set
- this way'''
- model = self.tree.get_model()
- account = model[path][C_ACCOUNT].decode('utf-8')
- type_ = model[path][C_TYPE]
- jid = model[path][C_JID].decode('utf-8')
- resource = None
- contact = None
- iter = model.get_iter(path)
- if type_ in ('group', 'account'):
- if self.tree.row_expanded(path):
- self.tree.collapse_row(path)
- else:
- self.tree.expand_row(path, False)
- elif jid in gajim.interface.minimized_controls[account]:
- self.on_groupchat_maximized(None, jid, account)
- else:
- first_ev = gajim.events.get_first_event(account, jid)
- if not first_ev:
- # look in other resources
- for c in gajim.contacts.get_contacts(account, jid):
- fjid = c.get_full_jid()
- first_ev = gajim.events.get_first_event(account, fjid)
- if first_ev:
- resource = c.resource
- break
- if not first_ev and model.iter_has_child(iter):
- child_iter = model.iter_children(iter)
- while not first_ev and child_iter:
- child_jid = model[child_iter][C_JID].decode('utf-8')
- first_ev = gajim.events.get_first_event(account, child_jid)
- if first_ev:
- jid = child_jid
- else:
- child_iter = model.iter_next(child_iter)
- session = None
- if first_ev:
- if first_ev.type_ in ('chat', 'normal'):
- session = first_ev.parameters[8]
- fjid = jid
- if resource:
- fjid += '/' + resource
- if self.open_event(account, fjid, first_ev):
- return
- # else
- contact = gajim.contacts.get_contact(account, jid, resource)
- if not contact or isinstance(contact, list):
- contact = \
- gajim.contacts.get_contact_with_highest_priority(account, jid)
- if jid == gajim.get_jid_from_account(account):
- resource = contact.resource
- self.on_open_chat_window(widget, contact, 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'''
- if not gajim.single_click:
- self.on_row_activated(widget, path)
-
- def on_roster_treeview_row_expanded(self, widget, iter, path):
- '''When a row is expanded change the icon of the arrow'''
- model = self.tree.get_model()
- if self.regroup: # merged accounts
- accounts = gajim.connections.keys()
- else:
- accounts = [model[iter][C_ACCOUNT].decode('utf-8')]
- type_ = model[iter][C_TYPE]
- if type_ == 'group':
- model.set_value(iter, 0, gajim.interface.jabber_state_images['16'][
- 'opened'])
- jid = model[iter][C_JID].decode('utf-8')
- for account in accounts:
- if gajim.groups[account].has_key(jid): # This account has this group
- gajim.groups[account][jid]['expand'] = True
- if account + jid in self.collapsed_rows:
- self.collapsed_rows.remove(account + jid)
- elif type_ == 'account':
- account = accounts[0] # There is only one cause we don't use merge
- if account in self.collapsed_rows:
- self.collapsed_rows.remove(account)
- for g in gajim.groups[account]:
- groupIter = self.get_group_iter(g, account)
- if groupIter and gajim.groups[account][g]['expand']:
- pathG = model.get_path(groupIter)
- self.tree.expand_row(pathG, False)
- self.draw_account(account)
- elif type_ == 'contact':
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- self.draw_contact(jid, account)
-
- def on_roster_treeview_row_collapsed(self, widget, iter, path):
- '''When a row is collapsed :
- change the icon of the arrow'''
- model = self.tree.get_model()
- if self.regroup: # merged accounts
- accounts = gajim.connections.keys()
- else:
- accounts = [model[iter][C_ACCOUNT].decode('utf-8')]
- type_ = model[iter][C_TYPE]
- if type_ == 'group':
- model.set_value(iter, 0, gajim.interface.jabber_state_images['16'][
- 'closed'])
- jid = model[iter][C_JID].decode('utf-8')
- for account in accounts:
- if gajim.groups[account].has_key(jid): # This account has this group
- gajim.groups[account][jid]['expand'] = False
- if not account + jid in self.collapsed_rows:
- self.collapsed_rows.append(account + jid)
- elif type_ == 'account':
- account = accounts[0] # There is only one cause we don't use merge
- if not account in self.collapsed_rows:
- self.collapsed_rows.append(account)
- self.draw_account(account)
- elif type_ == 'contact':
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- self.draw_contact(jid, account)
-
- def on_service_disco_menuitem_activate(self, widget, account):
- server_jid = gajim.config.get_per('accounts', account, 'hostname')
- if gajim.interface.instances[account]['disco'].has_key(server_jid):
- gajim.interface.instances[account]['disco'][server_jid].\
- window.present()
- else:
- try:
- # Object will add itself to the window dict
- disco.ServiceDiscoveryWindow(account, address_entry = True)
- except GajimGeneralException:
- pass
-
- def make_transport_state_images(self, transport):
- '''initialise opened and closed 'transport' iconset dict'''
- if gajim.config.get('use_transports_iconsets'):
- folder = os.path.join(helpers.get_transport_path(transport),
- '16x16')
- pixo, pixc = gtkgui_helpers.load_icons_meta()
- self.transports_state_images['opened'][transport] = \
- gtkgui_helpers.load_iconset(folder, pixo, transport = True)
- self.transports_state_images['closed'][transport] = \
- gtkgui_helpers.load_iconset(folder, pixc, transport = True)
- folder = os.path.join(helpers.get_transport_path(transport), '32x32')
- self.transports_state_images['32'][transport] = \
- gtkgui_helpers.load_iconset(folder, transport = True)
- folder = os.path.join(helpers.get_transport_path(transport), '16x16')
- self.transports_state_images['16'][transport] = \
- gtkgui_helpers.load_iconset(folder, transport = True)
-
- def update_jabber_state_images(self):
- # Update the roster
- self.draw_roster()
- # Update the status combobox
- model = self.status_combobox.get_model()
- iter = model.get_iter_root()
- while iter:
- if model[iter][2] != '':
- # If it's not change status message iter
- # eg. if it has show parameter not ''
- model[iter][1] = gajim.interface.jabber_state_images['16'][model[
- iter][2]]
- iter = model.iter_next(iter)
- # Update the systray
- if gajim.interface.systray_enabled:
- gajim.interface.systray.set_img()
-
- for win in gajim.interface.msg_win_mgr.windows():
- for ctrl in win.controls():
- ctrl.update_ui()
- win.redraw_tab(ctrl)
-
- self.update_status_combobox()
-
- def repaint_themed_widgets(self):
- '''Notify windows that contain themed widgets to repaint them'''
- for win in gajim.interface.msg_win_mgr.windows():
- win.repaint_themed_widgets()
- for account in gajim.connections:
- for addr in gajim.interface.instances[account]['disco']:
- gajim.interface.instances[account]['disco'][addr].paint_banner()
- for ctrl in gajim.interface.minimized_controls[account].values():
- ctrl.repaint_themed_widgets()
-
- def on_show_offline_contacts_menuitem_activate(self, widget):
- '''when show offline option is changed:
- redraw the treeview'''
- gajim.config.set('showoffline', not gajim.config.get('showoffline'))
- self.draw_roster()
-
- def on_view_menu_activate(self, widget):
- # Hide the show roster menu if we are not in the right windowing mode.
- if self.hpaned.get_child2() is not None:
- self.xml.get_widget('show_roster_menuitem').show()
- else:
- self.xml.get_widget('show_roster_menuitem').hide()
-
- def on_show_roster_menuitem_toggled(self, widget):
- # when num controls is 0 this menuitem is hidden, but still need to
- # disable keybinding
- if self.hpaned.get_child2() is not None:
- self.show_roster_vbox(widget.get_active())
-
- def show_roster_vbox(self, active):
- if active:
- self.xml.get_widget('roster_vbox2').show()
- else:
- self.xml.get_widget('roster_vbox2').hide()
-
- def set_renderer_color(self, renderer, style, set_background = True):
- '''set style for treeview cell, using PRELIGHT system color'''
- if set_background:
- bgcolor = self.tree.style.bg[style]
- renderer.set_property('cell-background-gdk', bgcolor)
- else:
- fgcolor = self.tree.style.fg[style]
- renderer.set_property('foreground-gdk', fgcolor)
-
- def iconCellDataFunc(self, column, renderer, model, iter, data = None):
- '''When a row is added, set properties for icon renderer'''
- theme = gajim.config.get('roster_theme')
- type_ = model[iter][C_TYPE]
- if type_ == 'account':
- color = gajim.config.get_per('themes', theme, 'accountbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
- renderer.set_property('xalign', 0)
- elif type_ == 'group':
- color = gajim.config.get_per('themes', theme, 'groupbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_PRELIGHT)
- renderer.set_property('xalign', 0.2)
- elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
- if not model[iter][C_JID] or not model[iter][C_ACCOUNT]:
- # This can append when at the moment we add the row
- return
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- if jid in gajim.newly_added[account]:
- renderer.set_property('cell-background', gajim.config.get(
- 'just_connected_bg_color'))
- elif jid in gajim.to_be_removed[account]:
- renderer.set_property('cell-background', gajim.config.get(
- 'just_disconnected_bg_color'))
- else:
- color = gajim.config.get_per('themes', theme, 'contactbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- renderer.set_property('cell-background', None)
- parent_iter = model.iter_parent(iter)
- if model[parent_iter][C_TYPE] == 'contact':
- renderer.set_property('xalign', 1)
- else:
- renderer.set_property('xalign', 0.4)
- renderer.set_property('width', 26)
-
- def nameCellDataFunc(self, column, renderer, model, iter, data = None):
- '''When a row is added, set properties for name renderer'''
- theme = gajim.config.get('roster_theme')
- type_ = model[iter][C_TYPE]
- if type_ == 'account':
- color = gajim.config.get_per('themes', theme, 'accounttextcolor')
- if color:
- renderer.set_property('foreground', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_ACTIVE, False)
- color = gajim.config.get_per('themes', theme, 'accountbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
- renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'accountfont'))
- renderer.set_property('xpad', 0)
- renderer.set_property('width', 3)
- elif type_ == 'group':
- color = gajim.config.get_per('themes', theme, 'grouptextcolor')
- if color:
- renderer.set_property('foreground', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_PRELIGHT, False)
- color = gajim.config.get_per('themes', theme, 'groupbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_PRELIGHT)
- renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
- renderer.set_property('xpad', 4)
- elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
- if not model[iter][C_JID] or not model[iter][C_ACCOUNT]:
- # This can append when at the moment we add the row
- return
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- color = gajim.config.get_per('themes', theme, 'contacttextcolor')
- if color:
- renderer.set_property('foreground', color)
- else:
- renderer.set_property('foreground', None)
- if jid in gajim.newly_added[account]:
- renderer.set_property('cell-background', gajim.config.get(
- 'just_connected_bg_color'))
- elif jid in gajim.to_be_removed[account]:
- renderer.set_property('cell-background', gajim.config.get(
- 'just_disconnected_bg_color'))
- else:
- color = gajim.config.get_per('themes', theme, 'contactbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- renderer.set_property('cell-background', None)
- renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
- parent_iter = model.iter_parent(iter)
- if model[parent_iter][C_TYPE] == 'contact':
- renderer.set_property('xpad', 16)
- else:
- renderer.set_property('xpad', 8)
-
- def fill_avatar_pixbuf_rederer(self, column, renderer, model, iter,
- data = None):
- '''When a row is added, set properties for avatar renderer'''
- theme = gajim.config.get('roster_theme')
- type_ = model[iter][C_TYPE]
- if type_ in ('group', 'account'):
- renderer.set_property('visible', False)
- return
-
- # allocate space for the icon only if needed
- if model[iter][C_AVATAR_PIXBUF] or \
- gajim.config.get('avatar_position_in_roster') == 'left':
- renderer.set_property('visible', True)
- else:
- renderer.set_property('visible', False)
- if type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
- if not model[iter][C_JID] or not model[iter][C_ACCOUNT]:
- # This can append at the moment we add the row
- return
- jid = model[iter][C_JID].decode('utf-8')
- account = model[iter][C_ACCOUNT].decode('utf-8')
- if jid in gajim.newly_added[account]:
- renderer.set_property('cell-background', gajim.config.get(
- 'just_connected_bg_color'))
- elif jid in gajim.to_be_removed[account]:
- renderer.set_property('cell-background', gajim.config.get(
- 'just_disconnected_bg_color'))
- else:
- color = gajim.config.get_per('themes', theme, 'contactbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- renderer.set_property('cell-background', None)
- if gajim.config.get('avatar_position_in_roster') == 'left':
- renderer.set_property('width', gajim.config.get('roster_avatar_width'))
- renderer.set_property('xalign', 0.5)
- else:
- renderer.set_property('xalign', 1) # align pixbuf to the right
-
- def fill_padlock_pixbuf_rederer(self, column, renderer, model, iter,
- data = None):
- '''When a row is added, set properties for padlock renderer'''
- theme = gajim.config.get('roster_theme')
- type_ = model[iter][C_TYPE]
- # allocate space for the icon only if needed
- if type_ == 'account' and model[iter][C_PADLOCK_PIXBUF]:
- renderer.set_property('visible', True)
- color = gajim.config.get_per('themes', theme, 'accountbgcolor')
- if color:
- renderer.set_property('cell-background', color)
- else:
- self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
- renderer.set_property('xalign', 1) # align pixbuf to the right
- else:
- renderer.set_property('visible', False)
-
- def get_show(self, lcontact):
- prio = lcontact[0].priority
- show = lcontact[0].show
- for u in lcontact:
- if u.priority > prio:
- prio = u.priority
- show = u.show
- return show
-
- def compareIters(self, model, iter1, iter2, data = None):
- '''Compare two iters to sort them'''
- name1 = model[iter1][C_NAME]
- name2 = model[iter2][C_NAME]
- if not name1 or not name2:
- return 0
- name1 = name1.decode('utf-8')
- name2 = name2.decode('utf-8')
- type1 = model[iter1][C_TYPE]
- type2 = model[iter2][C_TYPE]
- if type1 == 'self_contact':
- return -1
- if type2 == 'self_contact':
- return 1
- if type1 == 'group':
- name1 = model[iter1][C_JID]
- name2 = model[iter2][C_JID]
- if name1 == _('Transports'):
- return 1
- if name2 == _('Transports'):
- return -1
- if name1 == _('Not in Roster'):
- return 1
- if name2 == _('Not in Roster'):
- return -1
- if name1 == _('Groupchats'):
- return 1
- if name2 == _('Groupchats'):
- return -1
- account1 = model[iter1][C_ACCOUNT]
- account2 = model[iter2][C_ACCOUNT]
- if not account1 or not account2:
- return 0
- account1 = account1.decode('utf-8')
- account2 = account2.decode('utf-8')
- if type1 == 'account':
- if account1 < account2:
- return -1
- return 1
- jid1 = model[iter1][C_JID].decode('utf-8')
- jid2 = model[iter2][C_JID].decode('utf-8')
- if type1 == 'contact':
- lcontact1 = gajim.contacts.get_contacts(account1, jid1)
- contact1 = gajim.contacts.get_first_contact_from_jid(account1, jid1)
- if not contact1:
- return 0
- name1 = contact1.get_shown_name()
- if type2 == 'contact':
- lcontact2 = gajim.contacts.get_contacts(account2, jid2)
- contact2 = gajim.contacts.get_first_contact_from_jid(account2, jid2)
- if not contact2:
- return 0
- name2 = contact2.get_shown_name()
- # We first compare by show if sort_by_show is True or if it's a child
- # contact
- if type1 == 'contact' and type2 == 'contact' and \
- gajim.config.get('sort_by_show'):
- cshow = {'online':0, 'chat': 1, 'away': 2, 'xa': 3, 'dnd': 4,
- 'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
- s = self.get_show(lcontact1)
- if s in cshow:
- show1 = cshow[s]
- else:
- show1 = 9
- s = self.get_show(lcontact2)
- if s in cshow:
- show2 = cshow[s]
- else:
- show2 = 9
- removing1 = False
- removing2 = False
- if show1 == 6 and jid1 in gajim.to_be_removed[account1]:
- removing1 = True
- if show2 == 6 and jid2 in gajim.to_be_removed[account2]:
- removing2 = True
- if removing1 and not removing2:
- return -1
- if removing2 and not removing1:
- return 1
- if show1 < show2:
- return -1
- elif show1 > show2:
- return 1
- # We compare names
- if name1.lower() < name2.lower():
- return -1
- if name2.lower() < name1.lower():
- return 1
- if type1 == 'contact' and type2 == 'contact':
- # We compare account names
- if account1.lower() < account2.lower():
- return -1
- if account2.lower() < account1.lower():
- return 1
- # We compare jids
- if jid1.lower() < jid2.lower():
- return -1
- if jid2.lower() < jid1.lower():
- return 1
- return 0
-
- def drag_data_get_data(self, treeview, context, selection, target_id, etime):
- model, list_of_paths = self.tree.get_selection().get_selected_rows()
- if len(list_of_paths) != 1:
- return
- path = list_of_paths[0]
- data = ''
- if len(path) >= 3:
- data = model[path][C_JID]
- selection.set(selection.target, 8, data)
-
- def drag_begin(self, treeview, context):
- self.dragging = True
-
- def drag_end(self, treeview, context):
- self.dragging = False
-
- def on_drop_in_contact(self, widget, account_source, c_source, account_dest,
- c_dest, was_big_brother, context, etime):
- if not gajim.connections[account_source].private_storage_supported or not\
- gajim.connections[account_dest].private_storage_supported:
- dialogs.WarningDialog(_('Metacontacts storage not supported by your '
- 'server'),
- _('Your server does not support storing metacontacts information. '
- 'So those information will not be saved on next reconnection.'))
- def merge_contacts(is_checked=None):
- if is_checked != None: # dialog has been shown
- if is_checked: # user does not want to be asked again
- gajim.config.set('confirm_metacontacts', 'no')
- else:
- gajim.config.set('confirm_metacontacts', 'yes')
- # children must take the new tag too, so remember old tag
- old_tag = gajim.contacts.get_metacontacts_tag(account_source,
- c_source.jid)
- # remove the source row
- self.remove_contact(c_source, account_source)
- # brother inherite big brother groups
- old_groups = c_source.groups
- c_source.groups = []
- for g in c_dest.groups:
- c_source.groups.append(g)
- gajim.connections[account_source].update_contact(c_source.jid,
- c_source.name, c_source.groups)
- gajim.contacts.add_metacontact(account_dest, c_dest.jid,
- account_source, c_source.jid)
- if was_big_brother:
- # add brothers too
- all_jid = gajim.contacts.get_metacontacts_jids(old_tag)
- for _account in all_jid:
- for _jid in all_jid[_account]:
- gajim.contacts.add_metacontact(account_dest, c_dest.jid,
- _account, _jid)
- _c = gajim.contacts.get_first_contact_from_jid(_account, _jid)
- self.remove_contact(_c, _account)
- self.add_contact_to_roster(_jid, _account)
- self.draw_contact(_jid, _account)
- self.add_contact_to_roster(c_source.jid, account_source)
- self.draw_contact(c_dest.jid, account_dest)
- # FIXME: Why do groups have to be redrawn by hand?
- for g in old_groups:
- self.draw_group(g, account_source)
- self.draw_account(account_source)
- context.finish(True, True, etime)
-
- confirm_metacontacts = gajim.config.get('confirm_metacontacts')
- if confirm_metacontacts == 'no':
- merge_contacts()
- return
- pritext = _('You are about to create a metacontact. Are you sure you want'
- ' to continue?')
- sectext = _('Metacontacts are a way to regroup several contacts in one '
- 'line. Generally it is used when the same person has several Jabber '
- 'accounts or transport accounts.')
- dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
- _('Do _not ask me again'), on_response_ok = merge_contacts)
- if not confirm_metacontacts: # First time we see this window
- dlg.checkbutton.set_active(True)
-
- def on_drop_in_group(self, widget, account, c_source, grp_dest, is_big_brother,
- context, etime, grp_source = None):
- if grp_source:
- self.remove_contact_from_group(account, c_source, grp_source)
- if not is_big_brother:
- # remove tag before readding
- gajim.contacts.remove_metacontact(account, c_source.jid)
- self.add_contact_to_group(account, c_source, grp_dest)
- if is_big_brother:
- # add whole metacontact to new group
- tag = gajim.contacts.get_metacontacts_tag(account, c_source.jid)
- all_jid = gajim.contacts.get_metacontacts_jids(tag)
- for _account in all_jid:
- for _jid in all_jid[_account]:
- _c = gajim.contacts.get_first_contact_from_jid(_account, _jid)
- if grp_source:
- self.remove_contact_from_group(_account, _c, grp_source)
- self.add_contact_to_group(_account, _c, grp_dest)
- if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY):
- context.finish(True, True, etime)
-
- def add_contact_to_group(self, account, contact, group):
- model = self.tree.get_model()
- if not group in contact.groups:
- contact.groups.append(group)
- # Remove all rows because add_contact_to_roster doesn't add it if one
- # is already in roster
- for i in self.get_contact_iter(contact.jid, account):
- model.remove(i)
- self.add_contact_to_roster(contact.jid, account)
- gajim.connections[account].update_contact(contact.jid, contact.name,
- contact.groups)
-
- def remove_contact_from_group(self, account, contact, group):
- # Make sure contact was in the group
- if group in contact.groups:
- contact.groups.remove(group)
- self.remove_contact(contact, account)
-
- def drag_data_received_data(self, treeview, context, x, y, selection, info,
- etime):
- drop_info = treeview.get_dest_row_at_pos(x, y)
- if not drop_info:
- return
- if not selection.data:
- return # prevents tb when several entrys are dragged
- model = treeview.get_model()
- data = selection.data
- path_dest, position = drop_info
-
- if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2 \
- and path_dest[1] == 0: # dropped before the first group
- return
- if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
- # dropped before a group: we drop it in the previous group every time
- path_dest = (path_dest[0], path_dest[1]-1)
- # destination: the row something got dropped on
- iter_dest = model.get_iter(path_dest)
- type_dest = model[iter_dest][C_TYPE].decode('utf-8')
- jid_dest = model[iter_dest][C_JID].decode('utf-8')
- account_dest = model[iter_dest][C_ACCOUNT].decode('utf-8')
-
- # drop on account row in merged mode, we cannot know the desired account
- if account_dest == 'all':
- return
- # nothing can be done, if destination account is offline
- if gajim.connections[account_dest].connected < 2:
- return
-
- # A file got dropped on the roster
- if info == self.TARGET_TYPE_URI_LIST:
- if len(path_dest) < 3:
- return
- if type_dest != 'contact':
- return
- c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
- jid_dest)
- uri = data.strip()
- uri_splitted = uri.split() # we may have more than one file dropped
- nb_uri = len(uri_splitted)
- # Check the URIs
- bad_uris = []
- for a_uri in uri_splitted:
- path = helpers.get_file_path_from_dnd_dropped_uri(a_uri)
- if not os.path.isfile(path):
- bad_uris.append(a_uri)
- if len(bad_uris):
- dialogs.ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris))
- return
- def _on_send_files(account, jid, uris):
- c = gajim.contacts.get_contact_with_highest_priority(account, jid)
- for uri in uris:
- path = helpers.get_file_path_from_dnd_dropped_uri(uri)
- if os.path.isfile(path): # is it file?
- gajim.interface.instances['file_transfers'].send_file(
- account, c, path)
- # Popup dialog to confirm sending
- prim_text = 'Send file?'
- sec_text = i18n.ngettext('Do you want to send this file to %s:',
- 'Do you want to send those files to %s:', nb_uri) %\
- c_dest.get_shown_name()
- for uri in uri_splitted:
- path = helpers.get_file_path_from_dnd_dropped_uri(uri)
- sec_text += '\n' + os.path.basename(path)
- dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
- on_response_ok = (_on_send_files, account_dest, jid_dest,
- uri_splitted))
- dialog.popup()
- return
-
- # a roster entry was dragged and dropped somewhere in the roster
-
- # source: the row that was dragged
- path_source = treeview.get_selection().get_selected_rows()[1][0]
- iter_source = model.get_iter(path_source)
- type_source = model[iter_source][C_TYPE]
- account_source = model[iter_source][C_ACCOUNT].decode('utf-8')
-
- # Only normal contacts can be dragged
- if type_source != 'contact':
- return
- if gajim.config.get_per('accounts', account_source, 'is_zeroconf'):
- return
-
- # A contact was dropped
- if gajim.config.get_per('accounts', account_dest, 'is_zeroconf'):
- # drop on zeroconf account, adding not possible
- return
- if type_dest == 'self_contact':
- # drop on self contact row
- return
- if type_dest == 'account' and account_source == account_dest:
- # drop on the account it was dragged from
- return
- if type_dest == 'groupchat':
- # drop on a minimized groupchat
- # TODO: Invite to groupchat
- return
-
- # Get valid source group, jid and contact
- it = iter_source
- while model[it][C_TYPE] == 'contact':
- it = model.iter_parent(it)
- grp_source = model[it][C_JID].decode('utf-8')
- if grp_source in helpers.special_groups and \
- grp_source not in ('Not in Roster', 'Observers'):
- # a transport or a minimized groupchat was dragged
- # we can add it to other accounts but not move it to another group, see below
- return
- jid_source = data.decode('utf-8')
- c_source = gajim.contacts.get_contact_with_highest_priority(
- account_source, jid_source)
-
- # Get destination group
- grp_dest = None
- if type_dest == 'group':
- grp_dest = model[iter_dest][C_JID].decode('utf-8')
- elif type_dest in ('contact', 'agent'):
- it = iter_dest
- while model[it][C_TYPE] != 'group':
- it = model.iter_parent(it)
- grp_dest = model[it][C_JID].decode('utf-8')
- if grp_dest in helpers.special_groups:
- return
-
- if jid_source == jid_dest:
- if grp_source == grp_dest and account_source == account_dest:
- # Drop on self
- return
-
- # contact drop somewhere in or on a foreign account
- if (type_dest == 'account' or not self.regroup) and \
- account_source != account_dest:
- # add to account in specified group
- dialogs.AddNewContactWindow(account = account_dest, jid = jid_source,
- user_nick = c_source.name, group = grp_dest)
- return
-
- # we may not add contacts from special_groups
- if grp_source in helpers.special_groups :
- return
-
- # Is the contact we drag a meta contact?
- is_big_brother = gajim.contacts.is_big_brother(account_source, jid_source)
-
- # Contact drop on group row or between two contacts
- if type_dest == 'group' or position == gtk.TREE_VIEW_DROP_BEFORE or \
- position == gtk.TREE_VIEW_DROP_AFTER:
- self.on_drop_in_group(None, account_source, c_source, grp_dest,
- is_big_brother, context, etime, grp_source)
- return
-
- # Contact drop on another contact, make meta contacts
- if position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or \
- position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE:
- c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
- jid_dest)
- if not c_dest:
- # c_dest is None if jid_dest doesn't belong to account
- return
- self.on_drop_in_contact(treeview, account_source, c_source,
- account_dest, c_dest, is_big_brother, context, etime)
- return
-
- def show_title(self):
- change_title_allowed = gajim.config.get('change_roster_title')
- if not change_title_allowed:
- return
-
- if gajim.config.get('one_message_window') == 'always_with_roster':
- # always_with_roster mode defers to the MessageWindow
- if not gajim.interface.msg_win_mgr.one_window_opened():
- # No MessageWindow to defer to
- self.window.set_title('Gajim')
- return
-
- nb_unread = 0
- start = ''
- for account in gajim.connections:
- # Count events in roster title only if we don't auto open them
- if not helpers.allow_popup_window(account):
- nb_unread += gajim.events.get_nb_events(['chat', 'normal',
- 'file-request', 'file-error', 'file-completed',
- 'file-request-error', 'file-send-error', 'file-stopped',
- 'printed_chat'], account)
- if nb_unread > 1:
- 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)
-
- def iter_is_separator(self, model, iter):
- if model[iter][0] == 'SEPARATOR':
- return True
- return False
-
- def iter_contact_rows(self):
- '''iterate over all contact rows in the tree model'''
- model = self.tree.get_model()
- account_iter = model.get_iter_root()
- while account_iter:
- group_iter = model.iter_children(account_iter)
- while group_iter:
- contact_iter = model.iter_children(group_iter)
- while contact_iter:
- yield model[contact_iter]
- contact_iter = model.iter_next(contact_iter)
- group_iter = model.iter_next(group_iter)
- account_iter = model.iter_next(account_iter)
-
- def on_roster_treeview_style_set(self, treeview, style):
- '''When style (theme) changes, redraw all contacts'''
- for contact in self.iter_contact_rows():
- self.draw_contact(contact[C_JID].decode('utf-8'),
- contact[C_ACCOUNT].decode('utf-8'))
-
- def _on_treeview_selection_changed(self, selection):
- model, list_of_paths = selection.get_selected_rows()
- if len(self._last_selected_contact):
- # update unselected rows
- for (jid, account) in self._last_selected_contact:
- try:
- self.draw_contact(jid, account)
- except:
- # This can fail when last selected row was on an account we just
- # removed. So we don't care if that fail
- pass
- self._last_selected_contact = []
- if len(list_of_paths) == 0:
- return
- for path in list_of_paths:
- row = model[path]
- if row[C_TYPE] != 'contact':
- self._last_selected_contact = []
- return
- jid = row[C_JID].decode('utf-8')
- account = row[C_ACCOUNT].decode('utf-8')
- self._last_selected_contact.append((jid, account))
- self.draw_contact(jid, account, selected = True)
-
- def search_roster_func(self, model, column, key, iter):
- if model[iter][C_NAME].decode('utf-8').lower().startswith(
- gobject.markup_escape_text(key.lower())):
- return False
- return True
-
+
def setup_for_osx(self):
- # Massage the GTK menu so it will match up to the OS/X nib style menu
- # when passed to sync-menu and merged
+ '''Massage the GTK menu so it will match up to the OS/X nib style menu
+ when passed to sync-menu and merged'''
main_menu = self.xml.get_widget('menubar')
app_item = gtk.MenuItem('Gajim')
main_menu.insert(app_item, 0)
@@ -5261,29 +5759,28 @@ class RosterWindow:
# Hide the GTK menubar itself and let the OS/X menubar do its thing
#self.xml.get_widget('menubar').hide()
return
-
- def _on_message_window_delete(self, win_mgr, msg_win):
- if gajim.config.get('one_message_window') == 'always_with_roster':
- self.show_roster_vbox(True)
- gtkgui_helpers.resize_window(self.window,
- gajim.config.get('roster_width'),
- gajim.config.get('roster_height'))
-
+
+################################################################################
+###
+################################################################################
+
def __init__(self):
+ self.filtering = False
self.xml = gtkgui_helpers.get_glade('roster_window.glade')
self.window = self.xml.get_widget('roster_window')
self.hpaned = self.xml.get_widget('roster_hpaned')
self._music_track_changed_signal = None
gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
- gajim.interface.msg_win_mgr.connect('window-delete', self._on_message_window_delete)
+ gajim.interface.msg_win_mgr.connect('window-delete', self.on_message_window_delete)
self.advanced_menus = [] # We keep them to destroy them
if gajim.config.get('roster_window_skip_taskbar'):
self.window.set_property('skip-taskbar-hint', True)
self.tree = self.xml.get_widget('roster_treeview')
sel = self.tree.get_selection()
sel.set_mode(gtk.SELECTION_MULTIPLE)
- sel.connect('changed',
- self._on_treeview_selection_changed)
+ #FIXME: talk to asterix, why is this needed?
+ #sel.connect('changed',
+ # self.on_treeview_selection_changed)
self._last_selected_contact = [] # holds a list of (jid, account) tupples
self.transports_state_images = {'16': {}, '32': {}, 'opened': {},
@@ -5317,12 +5814,20 @@ class RosterWindow:
self.popup_notification_windows = []
#(icon, name, type, jid, account, editable, avatar_pixbuf, padlock_pixbuf)
- model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf,
+ self.model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf,
gtk.gdk.Pixbuf)
- model.set_sort_func(1, self.compareIters)
- model.set_sort_column_id(1, gtk.SORT_ASCENDING)
- self.tree.set_model(model)
+ self.model.set_sort_func(1, self._compareIters)
+ self.model.set_sort_column_id(1, gtk.SORT_ASCENDING)
+ self.modelfilter = self.model.filter_new()
+ self.modelfilter.set_visible_func(self._visible_func)
+
+
+ self.modelfilter.connect('row-has-child-toggled', self.on_model_row_has_child_toggled)
+ # Workaroung: For strange reasons signal is behaving like row-changed
+ self._toggeling_row = False
+
+ self.tree.set_model(self.modelfilter)
# when this value become 0 we quit main application. If it's more than 0
# it means we are waiting for this number of accounts to disconnect before
@@ -5351,13 +5856,13 @@ class RosterWindow:
# if it will be sensitive or not it is in the fourth column
self.status_combobox.add_attribute(cell, 'sensitive', 3)
- self.status_combobox.set_row_separator_func(self.iter_is_separator)
+ self.status_combobox.set_row_separator_func(self._iter_is_separator)
for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
uf_show = helpers.get_uf_show(show)
- liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
- show], show, True])
- # Add a Separator (self.iter_is_separator() checks on string SEPARATOR)
+ liststore.append([uf_show, gajim.interface.jabber_state_images['16'][show], show,
+ True])
+ # Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
liststore.append(['SEPARATOR', None, '', True])
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
@@ -5366,12 +5871,12 @@ class RosterWindow:
# sensitivity to False because by default we're offline
self.status_message_menuitem_iter = liststore.append(
[_('Change Status Message...'), img, '', False])
- # Add a Separator (self.iter_is_separator() checks on string SEPARATOR)
+ # Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
liststore.append(['SEPARATOR', None, '', True])
uf_show = helpers.get_uf_show('offline')
- liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
- 'offline'], 'offline', True])
+ liststore.append([uf_show, gajim.interface.jabber_state_images['16']['offline'],
+ 'offline', True])
status_combobox_items = ['online', 'chat', 'away', 'xa', 'dnd',
'invisible', 'separator1', 'change_status_msg', 'separator2',
@@ -5408,7 +5913,7 @@ class RosterWindow:
col.pack_start(render_pixbuf, expand = False)
col.add_attribute(render_pixbuf, 'pixbuf', C_AVATAR_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self.fill_avatar_pixbuf_rederer, None)
+ self._fill_avatar_pixbuf_rederer, None)
if gajim.config.get('avatar_position_in_roster') == 'left':
add_avatar_renderer()
@@ -5417,13 +5922,13 @@ class RosterWindow:
# show img or +-
col.pack_start(render_image, expand = False)
col.add_attribute(render_image, 'image', C_IMG)
- col.set_cell_data_func(render_image, self.iconCellDataFunc, None)
+ col.set_cell_data_func(render_image, self._iconCellDataFunc, None)
render_text = gtk.CellRendererText() # contact or group or account name
render_text.set_property('ellipsize', pango.ELLIPSIZE_END)
col.pack_start(render_text, expand = True)
col.add_attribute(render_text, 'markup', C_NAME) # where we hold the name
- col.set_cell_data_func(render_text, self.nameCellDataFunc, None)
+ col.set_cell_data_func(render_text, self._nameCellDataFunc, None)
if gajim.config.get('avatar_position_in_roster') == 'right':
add_avatar_renderer()
@@ -5432,7 +5937,7 @@ class RosterWindow:
col.pack_start(render_pixbuf, expand = False)
col.add_attribute(render_pixbuf, 'pixbuf', C_PADLOCK_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self.fill_padlock_pixbuf_rederer, None)
+ self._fill_padlock_pixbuf_rederer, None)
self.tree.append_column(col)
# do not show gtk arrows workaround
@@ -5444,7 +5949,7 @@ class RosterWindow:
self.tree.set_expander_column(col)
# set search function
- self.tree.set_search_equal_func(self.search_roster_func)
+ self.tree.set_search_equal_func(self._search_roster_func)
# signals
self.TARGET_TYPE_URI_LIST = 80
@@ -5457,6 +5962,7 @@ class RosterWindow:
self.tree.enable_model_drag_dest(TARGETS2, gtk.gdk.ACTION_DEFAULT)
self.tree.connect('drag_begin', self.drag_begin)
self.tree.connect('drag_end', self.drag_end)
+ self.tree.connect('drag_drop', self.drag_drop)
self.tree.connect('drag_data_get', self.drag_data_get_data)
self.tree.connect('drag_data_received', self.drag_data_received_data)
self.dragging = False