| 1 | ## roster_window.py |
|---|
| 2 | ## |
|---|
| 3 | ## Contributors for this file: |
|---|
| 4 | ## - Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 5 | ## - Nikos Kouremenos <kourem@gmail.com> |
|---|
| 6 | ## - Dimitur Kirov <dkirov@gmail.com> |
|---|
| 7 | ## |
|---|
| 8 | ## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 9 | ## Vincent Hanquez <tab@snarc.org> |
|---|
| 10 | ## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 11 | ## Vincent Hanquez <tab@snarc.org> |
|---|
| 12 | ## Nikos Kouremenos <nkour@jabber.org> |
|---|
| 13 | ## Dimitur Kirov <dkirov@gmail.com> |
|---|
| 14 | ## Travis Shirk <travis@pobox.com> |
|---|
| 15 | ## Norman Rasmussen <norman@rasmussen.co.za> |
|---|
| 16 | ## |
|---|
| 17 | ## This program is free software; you can redistribute it and/or modify |
|---|
| 18 | ## it under the terms of the GNU General Public License as published |
|---|
| 19 | ## by the Free Software Foundation; version 2 only. |
|---|
| 20 | ## |
|---|
| 21 | ## This program is distributed in the hope that it will be useful, |
|---|
| 22 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 23 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 24 | ## GNU General Public License for more details. |
|---|
| 25 | ## |
|---|
| 26 | |
|---|
| 27 | import gtk |
|---|
| 28 | import gtk.glade |
|---|
| 29 | import gobject |
|---|
| 30 | import os |
|---|
| 31 | import time |
|---|
| 32 | |
|---|
| 33 | import common.sleepy |
|---|
| 34 | import tabbed_chat_window |
|---|
| 35 | import groupchat_window |
|---|
| 36 | import history_window |
|---|
| 37 | import dialogs |
|---|
| 38 | import vcard |
|---|
| 39 | import config |
|---|
| 40 | import disco |
|---|
| 41 | import gtkgui_helpers |
|---|
| 42 | import cell_renderer_image |
|---|
| 43 | import tooltips |
|---|
| 44 | |
|---|
| 45 | from gajim import Contact |
|---|
| 46 | from common import gajim |
|---|
| 47 | from common import helpers |
|---|
| 48 | from common import i18n |
|---|
| 49 | |
|---|
| 50 | _ = i18n._ |
|---|
| 51 | APP = i18n.APP |
|---|
| 52 | gtk.glade.bindtextdomain(APP, i18n.DIR) |
|---|
| 53 | gtk.glade.textdomain(APP) |
|---|
| 54 | |
|---|
| 55 | #(icon, name, type, jid, account, editable, second pixbuf) |
|---|
| 56 | ( |
|---|
| 57 | C_IMG, # image to show state (online, new message etc) |
|---|
| 58 | C_NAME, # cellrenderer text that holds contact nickame |
|---|
| 59 | C_TYPE, # account, group or contact? |
|---|
| 60 | C_JID, # the jid of the row |
|---|
| 61 | C_ACCOUNT, # cellrenderer text that holds account name |
|---|
| 62 | C_EDITABLE, # cellrenderer text that holds name editable or not? |
|---|
| 63 | C_SECPIXBUF, # secondary_pixbuf (holds avatar or padlock) |
|---|
| 64 | ) = range(7) |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | GTKGUI_GLADE = 'gtkgui.glade' |
|---|
| 68 | |
|---|
| 69 | class RosterWindow: |
|---|
| 70 | '''Class for main window of gtkgui interface''' |
|---|
| 71 | |
|---|
| 72 | def get_account_iter(self, name): |
|---|
| 73 | model = self.tree.get_model() |
|---|
| 74 | if model is None: |
|---|
| 75 | return |
|---|
| 76 | account_iter = model.get_iter_root() |
|---|
| 77 | if self.regroup: |
|---|
| 78 | return account_iter |
|---|
| 79 | while account_iter: |
|---|
| 80 | account_name = model[account_iter][C_NAME].decode('utf-8') |
|---|
| 81 | if name == account_name: |
|---|
| 82 | break |
|---|
| 83 | account_iter = model.iter_next(account_iter) |
|---|
| 84 | return account_iter |
|---|
| 85 | |
|---|
| 86 | def get_group_iter(self, name, account): |
|---|
| 87 | model = self.tree.get_model() |
|---|
| 88 | root = self.get_account_iter(account) |
|---|
| 89 | group_iter = model.iter_children(root) |
|---|
| 90 | while group_iter: |
|---|
| 91 | group_name = model[group_iter][C_NAME].decode('utf-8') |
|---|
| 92 | if name == group_name: |
|---|
| 93 | break |
|---|
| 94 | group_iter = model.iter_next(group_iter) |
|---|
| 95 | return group_iter |
|---|
| 96 | |
|---|
| 97 | def get_contact_iter(self, jid, account): |
|---|
| 98 | model = self.tree.get_model() |
|---|
| 99 | acct = self.get_account_iter(account) |
|---|
| 100 | found = [] |
|---|
| 101 | if model is None: # when closing Gajim model can be none (async pbs?) |
|---|
| 102 | return found |
|---|
| 103 | group_iter = model.iter_children(acct) |
|---|
| 104 | while group_iter: |
|---|
| 105 | user_iter = model.iter_children(group_iter) |
|---|
| 106 | while user_iter: |
|---|
| 107 | if jid == model[user_iter][C_JID].decode('utf-8') and \ |
|---|
| 108 | account == model[user_iter][C_ACCOUNT].decode('utf-8'): |
|---|
| 109 | found.append(user_iter) |
|---|
| 110 | user_iter = model.iter_next(user_iter) |
|---|
| 111 | group_iter = model.iter_next(group_iter) |
|---|
| 112 | return found |
|---|
| 113 | |
|---|
| 114 | def add_account_to_roster(self, account): |
|---|
| 115 | model = self.tree.get_model() |
|---|
| 116 | if self.get_account_iter(account): |
|---|
| 117 | return |
|---|
| 118 | |
|---|
| 119 | if self.regroup: |
|---|
| 120 | show = helpers.get_global_show() |
|---|
| 121 | model.append(None, [self.jabber_state_images['16'][show], |
|---|
| 122 | _('Merged accounts'), 'account', '', 'all', False, None]) |
|---|
| 123 | return |
|---|
| 124 | |
|---|
| 125 | show = gajim.SHOW_LIST[gajim.connections[account].connected] |
|---|
| 126 | |
|---|
| 127 | tls_pixbuf = None |
|---|
| 128 | if gajim.con_types.has_key(account) and \ |
|---|
| 129 | gajim.con_types[account] in ('tls', 'ssl'): |
|---|
| 130 | tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION, |
|---|
| 131 | gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock |
|---|
| 132 | |
|---|
| 133 | our_jid = gajim.get_jid_from_account(account) |
|---|
| 134 | |
|---|
| 135 | model.append(None, [self.jabber_state_images['16'][show], |
|---|
| 136 | gtkgui_helpers.escape_for_pango_markup(account), |
|---|
| 137 | 'account', our_jid, account, False, tls_pixbuf]) |
|---|
| 138 | |
|---|
| 139 | def remove_newly_added(self, jid, account): |
|---|
| 140 | if jid in gajim.newly_added[account]: |
|---|
| 141 | gajim.newly_added[account].remove(jid) |
|---|
| 142 | self.draw_contact(jid, account) |
|---|
| 143 | |
|---|
| 144 | def add_contact_to_roster(self, jid, account): |
|---|
| 145 | '''Add a contact to the roster and add groups if they aren't in roster''' |
|---|
| 146 | showOffline = gajim.config.get('showoffline') |
|---|
| 147 | if not gajim.contacts[account].has_key(jid): |
|---|
| 148 | return |
|---|
| 149 | users = gajim.contacts[account][jid] |
|---|
| 150 | user = users[0] |
|---|
| 151 | if user.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent |
|---|
| 152 | user.groups = [_('Transports')] |
|---|
| 153 | elif user.groups == []: |
|---|
| 154 | user.groups.append(_('General')) |
|---|
| 155 | |
|---|
| 156 | hide = True |
|---|
| 157 | if user.sub in ('both', 'to'): |
|---|
| 158 | hide = False |
|---|
| 159 | elif user.ask == 'subscribe': |
|---|
| 160 | hide = False |
|---|
| 161 | # FIXME: uncomment when we support contacts in no group |
|---|
| 162 | # elif user.name or len(user.groups): |
|---|
| 163 | elif user.name: |
|---|
| 164 | hide = False |
|---|
| 165 | |
|---|
| 166 | # JEP-0162 |
|---|
| 167 | if hide: |
|---|
| 168 | return |
|---|
| 169 | if user.show in ('offline', 'error') and \ |
|---|
| 170 | not showOffline and (not _('Transports') in user.groups or \ |
|---|
| 171 | gajim.connections[account].connected < 2) and \ |
|---|
| 172 | not gajim.awaiting_events[account].has_key(user.jid): |
|---|
| 173 | return |
|---|
| 174 | |
|---|
| 175 | model = self.tree.get_model() |
|---|
| 176 | for g in user.groups: |
|---|
| 177 | iterG = self.get_group_iter(g, account) |
|---|
| 178 | if not iterG: |
|---|
| 179 | IterAcct = self.get_account_iter(account) |
|---|
| 180 | iterG = model.append(IterAcct, [ |
|---|
| 181 | self.jabber_state_images['16']['closed'], |
|---|
| 182 | gtkgui_helpers.escape_for_pango_markup(g), 'group', g, account, |
|---|
| 183 | False, None]) |
|---|
| 184 | if not gajim.groups[account].has_key(g): #It can probably never append |
|---|
| 185 | if account + g in self.collapsed_rows: |
|---|
| 186 | ishidden = False |
|---|
| 187 | else: |
|---|
| 188 | ishidden = True |
|---|
| 189 | gajim.groups[account][g] = { 'expand': ishidden } |
|---|
| 190 | if not account in self.collapsed_rows: |
|---|
| 191 | self.tree.expand_row((model.get_path(iterG)[0]), False) |
|---|
| 192 | |
|---|
| 193 | typestr = 'contact' |
|---|
| 194 | if g == _('Transports'): |
|---|
| 195 | typestr = 'agent' |
|---|
| 196 | |
|---|
| 197 | # we add some values here. see draw_contact for more |
|---|
| 198 | model.append(iterG, (None, user.name, |
|---|
| 199 | typestr, user.jid, account, False, None)) |
|---|
| 200 | |
|---|
| 201 | if gajim.groups[account][g]['expand']: |
|---|
| 202 | self.tree.expand_row(model.get_path(iterG), False) |
|---|
| 203 | self.draw_contact(jid, account) |
|---|
| 204 | self.draw_avatar(jid, account) |
|---|
| 205 | |
|---|
| 206 | def really_remove_contact(self, user, account): |
|---|
| 207 | if user.jid in gajim.newly_added[account]: |
|---|
| 208 | return |
|---|
| 209 | if user.jid.find('@') < 1 and gajim.connections[account].connected > 1: # It's an agent |
|---|
| 210 | return |
|---|
| 211 | if user.jid in gajim.to_be_removed[account]: |
|---|
| 212 | gajim.to_be_removed[account].remove(user.jid) |
|---|
| 213 | if gajim.config.get('showoffline'): |
|---|
| 214 | self.draw_contact(user.jid, account) |
|---|
| 215 | return |
|---|
| 216 | self.remove_contact(user, account) |
|---|
| 217 | |
|---|
| 218 | def remove_contact(self, user, account): |
|---|
| 219 | '''Remove a user from the roster''' |
|---|
| 220 | if user.jid in gajim.to_be_removed[account]: |
|---|
| 221 | return |
|---|
| 222 | model = self.tree.get_model() |
|---|
| 223 | for i in self.get_contact_iter(user.jid, account): |
|---|
| 224 | parent_i = model.iter_parent(i) |
|---|
| 225 | group = model.get_value(parent_i, 3).decode('utf-8') |
|---|
| 226 | model.remove(i) |
|---|
| 227 | if model.iter_n_children(parent_i) == 0: |
|---|
| 228 | model.remove(parent_i) |
|---|
| 229 | # We need to check all contacts, even offline contacts |
|---|
| 230 | for jid in gajim.contacts[account]: |
|---|
| 231 | if group in gajim.get_contact_instance_with_highest_priority(account, jid).groups: |
|---|
| 232 | break |
|---|
| 233 | else: |
|---|
| 234 | if gajim.groups[account].has_key(group): |
|---|
| 235 | del gajim.groups[account][group] |
|---|
| 236 | |
|---|
| 237 | def get_appropriate_state_images(self, jid, size = '16'): |
|---|
| 238 | '''check jid and return the appropriate state images dict for |
|---|
| 239 | the demanded size''' |
|---|
| 240 | transport = gajim.get_transport_name_from_jid(jid) |
|---|
| 241 | if transport: |
|---|
| 242 | return self.transports_state_images[size][transport] |
|---|
| 243 | return self.jabber_state_images[size] |
|---|
| 244 | |
|---|
| 245 | def draw_contact(self, jid, account, selected = False, focus = False): |
|---|
| 246 | '''draw the correct state image, name BUT not avatar''' |
|---|
| 247 | # focus is about if the roster window has toplevel-focus or not |
|---|
| 248 | model = self.tree.get_model() |
|---|
| 249 | iters = self.get_contact_iter(jid, account) |
|---|
| 250 | if len(iters) == 0: |
|---|
| 251 | return |
|---|
| 252 | contact_instances = gajim.get_contact_instances_from_jid(account, jid) |
|---|
| 253 | contact = gajim.get_highest_prio_contact_from_contacts(contact_instances) |
|---|
| 254 | name = gtkgui_helpers.escape_for_pango_markup(contact.name) |
|---|
| 255 | |
|---|
| 256 | if len(contact_instances) > 1: |
|---|
| 257 | name += ' (' + unicode(len(contact_instances)) + ')' |
|---|
| 258 | |
|---|
| 259 | # FIXME: remove when we use metacontacts |
|---|
| 260 | # shoz (account_name) if there are 2 contact with same jid in merged mode |
|---|
| 261 | if self.regroup: |
|---|
| 262 | add_acct = False |
|---|
| 263 | # look through all contacts of all accounts |
|---|
| 264 | for a in gajim.connections: |
|---|
| 265 | for j in gajim.contacts[a]: |
|---|
| 266 | # [0] cause it'fster than highest_prio |
|---|
| 267 | c = gajim.contacts[a][j][0] |
|---|
| 268 | if c.name == contact.name and (j, a) != (jid, account): |
|---|
| 269 | add_acct = True |
|---|
| 270 | break |
|---|
| 271 | if add_acct: |
|---|
| 272 | # No need to continue in other account if we already found one |
|---|
| 273 | break |
|---|
| 274 | if add_acct: |
|---|
| 275 | name += ' (' + account + ')' |
|---|
| 276 | |
|---|
| 277 | # add status msg, if not empty, under contact name in the treeview |
|---|
| 278 | if contact.status and gajim.config.get('show_status_msgs_in_roster'): |
|---|
| 279 | status = contact.status.strip() |
|---|
| 280 | if status != '': |
|---|
| 281 | status = gtkgui_helpers.reduce_chars_newlines(status, max_lines = 1) |
|---|
| 282 | # escape markup entities and make them small italic and fg color |
|---|
| 283 | color = gtkgui_helpers._get_fade_color(self.tree, selected, focus) |
|---|
| 284 | colorstring = "#%04x%04x%04x" % (color.red, color.green, color.blue) |
|---|
| 285 | name += '\n<span size="small" style="italic" foreground="%s">%s</span>'\ |
|---|
| 286 | % (colorstring, gtkgui_helpers.escape_for_pango_markup(status)) |
|---|
| 287 | |
|---|
| 288 | |
|---|
| 289 | icon_name = helpers.get_icon_name_to_show(contact, account) |
|---|
| 290 | state_images = self.get_appropriate_state_images(jid, size = '16') |
|---|
| 291 | |
|---|
| 292 | img = state_images[icon_name] |
|---|
| 293 | |
|---|
| 294 | for iter in iters: |
|---|
| 295 | model[iter][C_IMG] = img |
|---|
| 296 | model[iter][C_NAME] = name |
|---|
| 297 | |
|---|
| 298 | def draw_avatar(self, jid, account): |
|---|
| 299 | '''draw the avatar''' |
|---|
| 300 | model = self.tree.get_model() |
|---|
| 301 | iters = self.get_contact_iter(jid, account) |
|---|
| 302 | if gajim.config.get('show_avatars_in_roster'): |
|---|
| 303 | pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid) |
|---|
| 304 | if pixbuf in ('ask', None): |
|---|
| 305 | scaled_pixbuf = None |
|---|
| 306 | else: |
|---|
| 307 | scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'roster') |
|---|
| 308 | else: |
|---|
| 309 | scaled_pixbuf = None |
|---|
| 310 | for iter in iters: |
|---|
| 311 | model[iter][C_SECPIXBUF] = scaled_pixbuf |
|---|
| 312 | |
|---|
| 313 | def join_gc_room(self, account, room_jid, nick, password): |
|---|
| 314 | '''joins the room immediatelly''' |
|---|
| 315 | if room_jid in gajim.interface.instances[account]['gc'] and \ |
|---|
| 316 | gajim.gc_connected[account][room_jid]: |
|---|
| 317 | dialogs.ErrorDialog(_('You are already in room %s') % room_jid |
|---|
| 318 | ).get_response() |
|---|
| 319 | return |
|---|
| 320 | invisible_show = gajim.SHOW_LIST.index('invisible') |
|---|
| 321 | if gajim.connections[account].connected == invisible_show: |
|---|
| 322 | dialogs.ErrorDialog(_('You cannot join a room while you are invisible') |
|---|
| 323 | ).get_response() |
|---|
| 324 | return |
|---|
| 325 | room, server = room_jid.split('@') |
|---|
| 326 | if not room_jid in gajim.interface.instances[account]['gc']: |
|---|
| 327 | self.new_room(room_jid, nick, account) |
|---|
| 328 | gajim.interface.instances[account]['gc'][room_jid].set_active_tab(room_jid) |
|---|
| 329 | gajim.interface.instances[account]['gc'][room_jid].window.present() |
|---|
| 330 | gajim.connections[account].join_gc(nick, room, server, password) |
|---|
| 331 | if password: |
|---|
| 332 | gajim.gc_passwords[room_jid] = password |
|---|
| 333 | |
|---|
| 334 | def on_bookmark_menuitem_activate(self, widget, account, bookmark): |
|---|
| 335 | self.join_gc_room(account, bookmark['jid'], bookmark['nick'], |
|---|
| 336 | bookmark['password']) |
|---|
| 337 | |
|---|
| 338 | def on_bm_header_changed_state(self, widget, event): |
|---|
| 339 | widget.set_state(gtk.STATE_NORMAL) #do not allow selected_state |
|---|
| 340 | |
|---|
| 341 | def on_send_server_message_menuitem_activate(self, widget, account): |
|---|
| 342 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 343 | server += '/announce/online' |
|---|
| 344 | dialogs.SingleMessageWindow(account, server, 'send') |
|---|
| 345 | |
|---|
| 346 | def on_xml_console_menuitem_activate(self, widget, account): |
|---|
| 347 | if gajim.interface.instances[account].has_key('xml_console'): |
|---|
| 348 | gajim.interface.instances[account]['xml_console'].window.present() |
|---|
| 349 | else: |
|---|
| 350 | gajim.interface.instances[account]['xml_console'].window.show_all() |
|---|
| 351 | |
|---|
| 352 | def on_set_motd_menuitem_activate(self, widget, account): |
|---|
| 353 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 354 | server += '/announce/motd' |
|---|
| 355 | dialogs.SingleMessageWindow(account, server, 'send') |
|---|
| 356 | |
|---|
| 357 | def on_update_motd_menuitem_activate(self, widget, account): |
|---|
| 358 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 359 | server += '/announce/motd/update' |
|---|
| 360 | dialogs.SingleMessageWindow(account, server, 'send') |
|---|
| 361 | |
|---|
| 362 | def on_delete_motd_menuitem_activate(self, widget, account): |
|---|
| 363 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 364 | server += '/announce/motd/delete' |
|---|
| 365 | gajim.connections[account].send_motd(server) |
|---|
| 366 | |
|---|
| 367 | def on_online_users_menuitem_activate(self, widget, account): |
|---|
| 368 | pass #FIXME: impement disco in users for 0.9 |
|---|
| 369 | |
|---|
| 370 | def get_and_connect_advanced_menuitem_menu(self, account): |
|---|
| 371 | xml = gtk.glade.XML(GTKGUI_GLADE, 'advanced_menuitem_menu', APP) |
|---|
| 372 | advanced_menuitem_menu = xml.get_widget('advanced_menuitem_menu') |
|---|
| 373 | |
|---|
| 374 | send_single_message_menuitem = xml.get_widget( |
|---|
| 375 | 'send_single_message_menuitem') |
|---|
| 376 | xml_console_menuitem = xml.get_widget('xml_console_menuitem') |
|---|
| 377 | administrator_menuitem = xml.get_widget('administrator_menuitem') |
|---|
| 378 | online_users_menuitem = xml.get_widget('online_users_menuitem') |
|---|
| 379 | send_server_message_menuitem = xml.get_widget( |
|---|
| 380 | 'send_server_message_menuitem') |
|---|
| 381 | set_motd_menuitem = xml.get_widget('set_motd_menuitem') |
|---|
| 382 | update_motd_menuitem = xml.get_widget('update_motd_menuitem') |
|---|
| 383 | delete_motd_menuitem = xml.get_widget('delete_motd_menuitem') |
|---|
| 384 | |
|---|
| 385 | send_single_message_menuitem.connect('activate', |
|---|
| 386 | self.on_send_single_message_menuitem_activate, account) |
|---|
| 387 | |
|---|
| 388 | xml_console_menuitem.connect('activate', |
|---|
| 389 | self.on_xml_console_menuitem_activate, account) |
|---|
| 390 | |
|---|
| 391 | #FIXME: 0.9 should have this: it does disco in the place where users are |
|---|
| 392 | online_users_menuitem.set_no_show_all(True) |
|---|
| 393 | online_users_menuitem.hide() |
|---|
| 394 | online_users_menuitem.connect('activate', |
|---|
| 395 | self.on_online_users_menuitem_activate, account) |
|---|
| 396 | |
|---|
| 397 | send_server_message_menuitem.connect('activate', |
|---|
| 398 | self.on_send_server_message_menuitem_activate, account) |
|---|
| 399 | |
|---|
| 400 | set_motd_menuitem.connect('activate', |
|---|
| 401 | self.on_set_motd_menuitem_activate, account) |
|---|
| 402 | |
|---|
| 403 | update_motd_menuitem.connect('activate', |
|---|
| 404 | self.on_update_motd_menuitem_activate, account) |
|---|
| 405 | |
|---|
| 406 | delete_motd_menuitem.connect('activate', |
|---|
| 407 | self.on_delete_motd_menuitem_activate, account) |
|---|
| 408 | |
|---|
| 409 | advanced_menuitem_menu.show_all() |
|---|
| 410 | |
|---|
| 411 | return advanced_menuitem_menu |
|---|
| 412 | |
|---|
| 413 | def make_menu(self): |
|---|
| 414 | '''create the main window's menus''' |
|---|
| 415 | new_message_menuitem = self.xml.get_widget('new_message_menuitem') |
|---|
| 416 | join_gc_menuitem = self.xml.get_widget('join_gc_menuitem') |
|---|
| 417 | add_new_contact_menuitem = self.xml.get_widget('add_new_contact_menuitem') |
|---|
| 418 | service_disco_menuitem = self.xml.get_widget('service_disco_menuitem') |
|---|
| 419 | advanced_menuitem = self.xml.get_widget('advanced_menuitem') |
|---|
| 420 | show_offline_contacts_menuitem = self.xml.get_widget( |
|---|
| 421 | 'show_offline_contacts_menuitem') |
|---|
| 422 | profile_avatar_menuitem = self.xml.get_widget('profile_avatar_menuitem') |
|---|
| 423 | |
|---|
| 424 | # make it sensitive. it is insensitive only if no accounts are *available* |
|---|
| 425 | advanced_menuitem.set_sensitive(True) |
|---|
| 426 | |
|---|
| 427 | |
|---|
| 428 | if self.add_new_contact_handler_id: |
|---|
| 429 | add_new_contact_menuitem.handler_disconnect( |
|---|
| 430 | self.add_new_contact_handler_id) |
|---|
| 431 | self.add_new_contact_handler_id = None |
|---|
| 432 | |
|---|
| 433 | if self.service_disco_handler_id: |
|---|
| 434 | service_disco_menuitem.handler_disconnect( |
|---|
| 435 | self.service_disco_handler_id) |
|---|
| 436 | self.service_disco_handler_id = None |
|---|
| 437 | |
|---|
| 438 | if self.new_message_menuitem_handler_id: |
|---|
| 439 | new_message_menuitem.handler_disconnect( |
|---|
| 440 | self.new_message_menuitem_handler_id) |
|---|
| 441 | self.new_message_menuitem_handler_id = None |
|---|
| 442 | |
|---|
| 443 | #remove the existing submenus |
|---|
| 444 | add_new_contact_menuitem.remove_submenu() |
|---|
| 445 | service_disco_menuitem.remove_submenu() |
|---|
| 446 | join_gc_menuitem.remove_submenu() |
|---|
| 447 | new_message_menuitem.remove_submenu() |
|---|
| 448 | advanced_menuitem.remove_submenu() |
|---|
| 449 | |
|---|
| 450 | #remove the existing accelerator |
|---|
| 451 | if self.have_new_message_accel: |
|---|
| 452 | ag = gtk.accel_groups_from_object(self.window)[0] |
|---|
| 453 | new_message_menuitem.remove_accelerator(ag, gtk.keysyms.n, |
|---|
| 454 | gtk.gdk.CONTROL_MASK) |
|---|
| 455 | self.have_new_message_accel = False |
|---|
| 456 | |
|---|
| 457 | #join gc |
|---|
| 458 | sub_menu = gtk.Menu() |
|---|
| 459 | join_gc_menuitem.set_submenu(sub_menu) |
|---|
| 460 | at_least_one_account_connected = False |
|---|
| 461 | multiple_accounts = len(gajim.connections) >= 2 #FIXME: stop using bool var here |
|---|
| 462 | for account in gajim.connections: |
|---|
| 463 | if gajim.connections[account].connected <= 1: #if offline or connecting |
|---|
| 464 | continue |
|---|
| 465 | if not at_least_one_account_connected: |
|---|
| 466 | at_least_one_account_connected = True |
|---|
| 467 | if multiple_accounts: |
|---|
| 468 | label = gtk.Label() |
|---|
| 469 | label.set_markup('<u>' + account.upper() +'</u>') |
|---|
| 470 | item = gtk.MenuItem() |
|---|
| 471 | item.add(label) |
|---|
| 472 | item.connect('state-changed', self.on_bm_header_changed_state) |
|---|
| 473 | sub_menu.append(item) |
|---|
| 474 | |
|---|
| 475 | item = gtk.MenuItem(_('_Join New Room')) |
|---|
| 476 | item.connect('activate', self.on_join_gc_activate, account) |
|---|
| 477 | sub_menu.append(item) |
|---|
| 478 | |
|---|
| 479 | for bookmark in gajim.connections[account].bookmarks: |
|---|
| 480 | item = gtk.MenuItem(bookmark['name']) |
|---|
| 481 | item.connect('activate', self.on_bookmark_menuitem_activate, |
|---|
| 482 | account, bookmark) |
|---|
| 483 | sub_menu.append(item) |
|---|
| 484 | |
|---|
| 485 | if at_least_one_account_connected: #FIXME: move this below where we do this check |
|---|
| 486 | #and make sure it works |
|---|
| 487 | newitem = gtk.SeparatorMenuItem() # seperator |
|---|
| 488 | sub_menu.append(newitem) |
|---|
| 489 | |
|---|
| 490 | newitem = gtk.ImageMenuItem(_('Manage Bookmarks...')) |
|---|
| 491 | img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, |
|---|
| 492 | gtk.ICON_SIZE_MENU) |
|---|
| 493 | newitem.set_image(img) |
|---|
| 494 | newitem.connect('activate', self.on_manage_bookmarks_menuitem_activate) |
|---|
| 495 | sub_menu.append(newitem) |
|---|
| 496 | sub_menu.show_all() |
|---|
| 497 | |
|---|
| 498 | if multiple_accounts: # 2 or more accounts? make submenus |
|---|
| 499 | #add |
|---|
| 500 | sub_menu = gtk.Menu() |
|---|
| 501 | for account in gajim.connections: |
|---|
| 502 | if gajim.connections[account].connected <= 1: |
|---|
| 503 | #if offline or connecting |
|---|
| 504 | continue |
|---|
| 505 | item = gtk.MenuItem(_('to %s account') % account) |
|---|
| 506 | sub_menu.append(item) |
|---|
| 507 | item.connect('activate', self.on_add_new_contact, account) |
|---|
| 508 | add_new_contact_menuitem.set_submenu(sub_menu) |
|---|
| 509 | sub_menu.show_all() |
|---|
| 510 | |
|---|
| 511 | #disco |
|---|
| 512 | sub_menu = gtk.Menu() |
|---|
| 513 | for account in gajim.connections: |
|---|
| 514 | if gajim.connections[account].connected <= 1: |
|---|
| 515 | #if offline or connecting |
|---|
| 516 | continue |
|---|
| 517 | item = gtk.MenuItem(_('using %s account') % account) |
|---|
| 518 | sub_menu.append(item) |
|---|
| 519 | item.connect('activate', self.on_service_disco_menuitem_activate, |
|---|
| 520 | account) |
|---|
| 521 | |
|---|
| 522 | service_disco_menuitem.set_submenu(sub_menu) |
|---|
| 523 | sub_menu.show_all() |
|---|
| 524 | |
|---|
| 525 | #new message |
|---|
| 526 | sub_menu = gtk.Menu() |
|---|
| 527 | for account in gajim.connections: |
|---|
| 528 | if gajim.connections[account].connected <= 1: |
|---|
| 529 | #if offline or connecting |
|---|
| 530 | continue |
|---|
| 531 | item = gtk.MenuItem(_('using account %s') % account) |
|---|
| 532 | sub_menu.append(item) |
|---|
| 533 | item.connect('activate', self.on_new_message_menuitem_activate, |
|---|
| 534 | account) |
|---|
| 535 | |
|---|
| 536 | new_message_menuitem.set_submenu(sub_menu) |
|---|
| 537 | sub_menu.show_all() |
|---|
| 538 | |
|---|
| 539 | #Advanced Actions |
|---|
| 540 | sub_menu = gtk.Menu() |
|---|
| 541 | for account in gajim.connections: |
|---|
| 542 | item = gtk.MenuItem(_('for account %s') % account) |
|---|
| 543 | sub_menu.append(item) |
|---|
| 544 | advanced_menuitem_menu = self.get_and_connect_advanced_menuitem_menu( |
|---|
| 545 | account) |
|---|
| 546 | item.set_submenu(advanced_menuitem_menu) |
|---|
| 547 | |
|---|
| 548 | advanced_menuitem.set_submenu(sub_menu) |
|---|
| 549 | sub_menu.show_all() |
|---|
| 550 | |
|---|
| 551 | else: |
|---|
| 552 | if len(gajim.connections) == 1: # user has only one account |
|---|
| 553 | #add |
|---|
| 554 | if not self.add_new_contact_handler_id: |
|---|
| 555 | self.add_new_contact_handler_id = add_new_contact_menuitem.connect( |
|---|
| 556 | 'activate', self.on_add_new_contact, gajim.connections.keys()[0]) |
|---|
| 557 | #disco |
|---|
| 558 | if not self.service_disco_handler_id: |
|---|
| 559 | self.service_disco_handler_id = service_disco_menuitem.connect( |
|---|
| 560 | 'activate', self.on_service_disco_menuitem_activate, |
|---|
| 561 | gajim.connections.keys()[0]) |
|---|
| 562 | #new msg |
|---|
| 563 | if not self.new_message_menuitem_handler_id: |
|---|
| 564 | self.new_message_menuitem_handler_id = new_message_menuitem.\ |
|---|
| 565 | connect('activate', self.on_new_message_menuitem_activate, |
|---|
| 566 | gajim.connections.keys()[0]) |
|---|
| 567 | #new msg accel |
|---|
| 568 | if not self.have_new_message_accel: |
|---|
| 569 | ag = gtk.accel_groups_from_object(self.window)[0] |
|---|
| 570 | new_message_menuitem.add_accelerator('activate', ag, |
|---|
| 571 | gtk.keysyms.n, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) |
|---|
| 572 | self.have_new_message_accel = True |
|---|
| 573 | |
|---|
| 574 | account = gajim.connections.keys()[0] |
|---|
| 575 | advanced_menuitem_menu = self.get_and_connect_advanced_menuitem_menu( |
|---|
| 576 | account) |
|---|
| 577 | advanced_menuitem.set_submenu(advanced_menuitem_menu) |
|---|
| 578 | elif len(gajim.connections) == 0: # user has no accounts |
|---|
| 579 | advanced_menuitem.set_sensitive(False) |
|---|
| 580 | |
|---|
| 581 | #FIXME: Gajim 0.9 should have this visible |
|---|
| 582 | profile_avatar_menuitem.set_no_show_all(True) |
|---|
| 583 | profile_avatar_menuitem.hide() |
|---|
| 584 | |
|---|
| 585 | if at_least_one_account_connected: |
|---|
| 586 | new_message_menuitem.set_sensitive(True) |
|---|
| 587 | join_gc_menuitem.set_sensitive(True) |
|---|
| 588 | add_new_contact_menuitem.set_sensitive(True) |
|---|
| 589 | service_disco_menuitem.set_sensitive(True) |
|---|
| 590 | show_offline_contacts_menuitem.set_sensitive(True) |
|---|
| 591 | else: |
|---|
| 592 | # make the menuitems insensitive |
|---|
| 593 | new_message_menuitem.set_sensitive(False) |
|---|
| 594 | join_gc_menuitem.set_sensitive(False) |
|---|
| 595 | add_new_contact_menuitem.set_sensitive(False) |
|---|
| 596 | service_disco_menuitem.set_sensitive(False) |
|---|
| 597 | show_offline_contacts_menuitem.set_sensitive(False) |
|---|
| 598 | profile_avatar_menuitem.set_sensitive(False) |
|---|
| 599 | |
|---|
| 600 | def _change_style(self, model, path, iter, option): |
|---|
| 601 | if option is None: |
|---|
| 602 | model[iter< |
|---|