| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | import gtk |
|---|
| 19 | import gobject |
|---|
| 20 | import os |
|---|
| 21 | import time |
|---|
| 22 | import urllib |
|---|
| 23 | |
|---|
| 24 | import common.sleepy |
|---|
| 25 | import history_window |
|---|
| 26 | import dialogs |
|---|
| 27 | import vcard |
|---|
| 28 | import config |
|---|
| 29 | import disco |
|---|
| 30 | import gtkgui_helpers |
|---|
| 31 | import cell_renderer_image |
|---|
| 32 | import tooltips |
|---|
| 33 | import message_control |
|---|
| 34 | import adhoc_commands |
|---|
| 35 | import notify |
|---|
| 36 | |
|---|
| 37 | from common import gajim |
|---|
| 38 | from common import helpers |
|---|
| 39 | from common import passwords |
|---|
| 40 | from common.exceptions import GajimGeneralException |
|---|
| 41 | |
|---|
| 42 | from message_window import MessageWindowMgr |
|---|
| 43 | from chat_control import ChatControl |
|---|
| 44 | from groupchat_control import GroupchatControl |
|---|
| 45 | from groupchat_control import PrivateChatControl |
|---|
| 46 | |
|---|
| 47 | from common import dbus_support |
|---|
| 48 | if dbus_support.supported: |
|---|
| 49 | from music_track_listener import MusicTrackListener |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | ( |
|---|
| 53 | C_IMG, |
|---|
| 54 | C_NAME, |
|---|
| 55 | C_TYPE, |
|---|
| 56 | C_JID, |
|---|
| 57 | C_ACCOUNT, |
|---|
| 58 | C_SECPIXBUF, |
|---|
| 59 | ) = range(6) |
|---|
| 60 | |
|---|
| 61 | class RosterWindow: |
|---|
| 62 | '''Class for main window of the GTK+ interface''' |
|---|
| 63 | |
|---|
| 64 | def get_account_iter(self, name): |
|---|
| 65 | model = self.tree.get_model() |
|---|
| 66 | if model is None: |
|---|
| 67 | return |
|---|
| 68 | account_iter = model.get_iter_root() |
|---|
| 69 | if self.regroup: |
|---|
| 70 | return account_iter |
|---|
| 71 | while account_iter: |
|---|
| 72 | account_name = model[account_iter][C_ACCOUNT].decode('utf-8') |
|---|
| 73 | if name == account_name: |
|---|
| 74 | break |
|---|
| 75 | account_iter = model.iter_next(account_iter) |
|---|
| 76 | return account_iter |
|---|
| 77 | |
|---|
| 78 | def get_group_iter(self, name, account): |
|---|
| 79 | model = self.tree.get_model() |
|---|
| 80 | root = self.get_account_iter(account) |
|---|
| 81 | group_iter = model.iter_children(root) |
|---|
| 82 | |
|---|
| 83 | while group_iter: |
|---|
| 84 | group_name = model[group_iter][C_JID].decode('utf-8') |
|---|
| 85 | if name == group_name: |
|---|
| 86 | break |
|---|
| 87 | group_iter = model.iter_next(group_iter) |
|---|
| 88 | return group_iter |
|---|
| 89 | |
|---|
| 90 | def get_contact_iter(self, jid, account): |
|---|
| 91 | if jid == gajim.get_jid_from_account(account): |
|---|
| 92 | iter = self.get_self_contact_iter(account) |
|---|
| 93 | if iter: |
|---|
| 94 | return [iter] |
|---|
| 95 | else: |
|---|
| 96 | return [] |
|---|
| 97 | model = self.tree.get_model() |
|---|
| 98 | acct = self.get_account_iter(account) |
|---|
| 99 | found = [] |
|---|
| 100 | if model is None: |
|---|
| 101 | return found |
|---|
| 102 | group_iter = model.iter_children(acct) |
|---|
| 103 | while group_iter: |
|---|
| 104 | contact_iter = model.iter_children(group_iter) |
|---|
| 105 | while contact_iter: |
|---|
| 106 | if jid == model[contact_iter][C_JID].decode('utf-8') and \ |
|---|
| 107 | account == model[contact_iter][C_ACCOUNT].decode('utf-8'): |
|---|
| 108 | found.append(contact_iter) |
|---|
| 109 | |
|---|
| 110 | if model.iter_has_child(contact_iter): |
|---|
| 111 | |
|---|
| 112 | contact_iter = model.iter_children(contact_iter) |
|---|
| 113 | else: |
|---|
| 114 | next_contact_iter = model.iter_next(contact_iter) |
|---|
| 115 | if not next_contact_iter: |
|---|
| 116 | |
|---|
| 117 | parent_iter = model.iter_parent(contact_iter) |
|---|
| 118 | parent_type = model[parent_iter][C_TYPE] |
|---|
| 119 | while parent_type != 'group': |
|---|
| 120 | contact_iter = model.iter_next(parent_iter) |
|---|
| 121 | if contact_iter: |
|---|
| 122 | break |
|---|
| 123 | else: |
|---|
| 124 | parent_iter = model.iter_parent(parent_iter) |
|---|
| 125 | parent_type = model[parent_iter][C_TYPE] |
|---|
| 126 | else: |
|---|
| 127 | |
|---|
| 128 | contact_iter = None |
|---|
| 129 | else: |
|---|
| 130 | |
|---|
| 131 | contact_iter = next_contact_iter |
|---|
| 132 | group_iter = model.iter_next(group_iter) |
|---|
| 133 | return found |
|---|
| 134 | |
|---|
| 135 | def get_path(self, jid, account): |
|---|
| 136 | ''' Try to get line of contact in roster ''' |
|---|
| 137 | iters = self.get_contact_iter(jid, account) |
|---|
| 138 | if iters: |
|---|
| 139 | path = self.tree.get_model().get_path(iters[0]) |
|---|
| 140 | else: |
|---|
| 141 | path = None |
|---|
| 142 | return path |
|---|
| 143 | |
|---|
| 144 | def show_and_select_path(self, path, jid, account): |
|---|
| 145 | '''Show contact in roster (if he is invisible for example) |
|---|
| 146 | and select line''' |
|---|
| 147 | if not path: |
|---|
| 148 | |
|---|
| 149 | |
|---|
| 150 | self.add_contact_to_roster(jid, account) |
|---|
| 151 | iters = self.get_contact_iter(jid, account) |
|---|
| 152 | path = self.tree.get_model().get_path(iters[0]) |
|---|
| 153 | if self.dragging: |
|---|
| 154 | |
|---|
| 155 | return |
|---|
| 156 | |
|---|
| 157 | |
|---|
| 158 | self.tree.expand_row(path[0:1], False) |
|---|
| 159 | self.tree.expand_row(path[0:2], False) |
|---|
| 160 | self.tree.scroll_to_cell(path) |
|---|
| 161 | self.tree.set_cursor(path) |
|---|
| 162 | |
|---|
| 163 | def add_account_to_roster(self, account): |
|---|
| 164 | model = self.tree.get_model() |
|---|
| 165 | if self.get_account_iter(account): |
|---|
| 166 | return |
|---|
| 167 | |
|---|
| 168 | if self.regroup: |
|---|
| 169 | show = helpers.get_global_show() |
|---|
| 170 | model.append(None, [self.jabber_state_images['16'][show], |
|---|
| 171 | _('Merged accounts'), 'account', '', 'all', None]) |
|---|
| 172 | self.draw_account(account) |
|---|
| 173 | return |
|---|
| 174 | |
|---|
| 175 | show = gajim.SHOW_LIST[gajim.connections[account].connected] |
|---|
| 176 | |
|---|
| 177 | tls_pixbuf = None |
|---|
| 178 | if gajim.account_is_securely_connected(account): |
|---|
| 179 | tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION, |
|---|
| 180 | gtk.ICON_SIZE_MENU) |
|---|
| 181 | |
|---|
| 182 | our_jid = gajim.get_jid_from_account(account) |
|---|
| 183 | |
|---|
| 184 | model.append(None, [self.jabber_state_images['16'][show], |
|---|
| 185 | gtkgui_helpers.escape_for_pango_markup(account), |
|---|
| 186 | 'account', our_jid, account, tls_pixbuf]) |
|---|
| 187 | |
|---|
| 188 | def draw_account(self, account): |
|---|
| 189 | model = self.tree.get_model() |
|---|
| 190 | iter = self.get_account_iter(account) |
|---|
| 191 | if self.regroup: |
|---|
| 192 | accounts = gajim.connections.keys() |
|---|
| 193 | else: |
|---|
| 194 | accounts = [account] |
|---|
| 195 | num_of_accounts = len(accounts) |
|---|
| 196 | num_of_secured = gajim.get_number_of_securely_connected_accounts() |
|---|
| 197 | if num_of_secured and gajim.con_types.has_key(account) and \ |
|---|
| 198 | gajim.con_types[account] in ('tls', 'ssl'): |
|---|
| 199 | tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION, |
|---|
| 200 | gtk.ICON_SIZE_MENU) |
|---|
| 201 | if num_of_secured < num_of_accounts: |
|---|
| 202 | |
|---|
| 203 | colorspace = tls_pixbuf.get_colorspace() |
|---|
| 204 | bps = tls_pixbuf.get_bits_per_sample() |
|---|
| 205 | rowstride = tls_pixbuf.get_rowstride() |
|---|
| 206 | pixels = tls_pixbuf.get_pixels() |
|---|
| 207 | new_pixels = '' |
|---|
| 208 | width = tls_pixbuf.get_width() |
|---|
| 209 | height = tls_pixbuf.get_height() |
|---|
| 210 | for i in range(0, width*height): |
|---|
| 211 | rgb = pixels[4*i:4*i+3] |
|---|
| 212 | new_pixels += rgb |
|---|
| 213 | if rgb == chr(0)*3: |
|---|
| 214 | new_pixels += chr(0) |
|---|
| 215 | else: |
|---|
| 216 | new_pixels += chr(128) |
|---|
| 217 | tls_pixbuf = gtk.gdk.pixbuf_new_from_data(new_pixels, colorspace, |
|---|
| 218 | True, bps, width, height, rowstride) |
|---|
| 219 | model[iter][C_SECPIXBUF] = tls_pixbuf |
|---|
| 220 | else: |
|---|
| 221 | model[iter][C_SECPIXBUF] = None |
|---|
| 222 | path = model.get_path(iter) |
|---|
| 223 | account_name = account |
|---|
| 224 | accounts = [account] |
|---|
| 225 | if self.regroup: |
|---|
| 226 | account_name = _('Merged accounts') |
|---|
| 227 | accounts = [] |
|---|
| 228 | if not self.tree.row_expanded(path) and model.iter_has_child(iter): |
|---|
| 229 | |
|---|
| 230 | account_name = '[%s]' % account_name |
|---|
| 231 | if (gajim.account_is_connected(account) or (self.regroup and \ |
|---|
| 232 | gajim.get_number_of_connected_accounts())) and gajim.config.get( |
|---|
| 233 | 'show_contacts_number'): |
|---|
| 234 | nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts( |
|---|
| 235 | accounts = accounts) |
|---|
| 236 | account_name += ' (%s/%s)' % (repr(nbr_on),repr(nbr_total)) |
|---|
| 237 | model[iter][C_NAME] = account_name |
|---|
| 238 | |
|---|
| 239 | def remove_newly_added(self, jid, account): |
|---|
| 240 | if jid in gajim.newly_added[account]: |
|---|
| 241 | gajim.newly_added[account].remove(jid) |
|---|
| 242 | self.draw_contact(jid, account) |
|---|
| 243 | |
|---|
| 244 | def add_contact_to_roster(self, jid, account): |
|---|
| 245 | '''Add a contact to the roster and add groups if they aren't in roster |
|---|
| 246 | force is about force to add it, even if it is offline and show offline |
|---|
| 247 | is False, because it has online children, so we need to show it. |
|---|
| 248 | If add_children is True, we also add all children, even if they were not |
|---|
| 249 | already drawn''' |
|---|
| 250 | showOffline = gajim.config.get('showoffline') |
|---|
| 251 | model = self.tree.get_model() |
|---|
| 252 | contact = gajim.contacts.get_first_contact_from_jid(account, jid) |
|---|
| 253 | nb_events = gajim.events.get_nb_roster_events(account, contact.jid) |
|---|
| 254 | |
|---|
| 255 | for contact_ in gajim.contacts.get_contact(account, jid): |
|---|
| 256 | if contact_.resource: |
|---|
| 257 | nb_events += gajim.events.get_nb_roster_events(account, |
|---|
| 258 | contact_.get_full_jid()) |
|---|
| 259 | if not contact: |
|---|
| 260 | return |
|---|
| 261 | |
|---|
| 262 | if len(self.get_contact_iter(jid, account)): |
|---|
| 263 | return |
|---|
| 264 | if jid == gajim.get_jid_from_account(account): |
|---|
| 265 | self.add_self_contact(account) |
|---|
| 266 | return |
|---|
| 267 | if gajim.jid_is_transport(contact.jid): |
|---|
| 268 | |
|---|
| 269 | if not gajim.config.get('show_transports_group') and not nb_events: |
|---|
| 270 | return |
|---|
| 271 | contact.groups = [_('Transports')] |
|---|
| 272 | elif not showOffline and not gajim.account_is_connected(account) and \ |
|---|
| 273 | nb_events == 0: |
|---|
| 274 | return |
|---|
| 275 | |
|---|
| 276 | |
|---|
| 277 | hide = contact.is_hidden_from_roster() |
|---|
| 278 | if hide and contact.sub != 'from': |
|---|
| 279 | return |
|---|
| 280 | observer = contact.is_observer() |
|---|
| 281 | |
|---|
| 282 | if observer: |
|---|
| 283 | |
|---|
| 284 | tag = gajim.contacts.get_metacontacts_tag(account, jid) |
|---|
| 285 | if tag: |
|---|
| 286 | gajim.contacts.remove_metacontact(account, jid) |
|---|
| 287 | |
|---|
| 288 | |
|---|
| 289 | |
|---|
| 290 | family = gajim.contacts.get_metacontacts_family(account, jid) |
|---|
| 291 | |
|---|
| 292 | |
|---|
| 293 | shown_family = [] |
|---|
| 294 | if family: |
|---|
| 295 | for data in family: |
|---|
| 296 | _account = data['account'] |
|---|
| 297 | |
|---|
| 298 | if _account != account and not self.regroup: |
|---|
| 299 | continue |
|---|
| 300 | _jid = data['jid'] |
|---|
| 301 | |
|---|
| 302 | if self.get_contact_iter(_jid, _account): |
|---|
| 303 | shown_family.append(data) |
|---|
| 304 | if _jid == jid and _account == account: |
|---|
| 305 | our_data = data |
|---|
| 306 | shown_family.append(our_data) |
|---|
| 307 | big_brother_data = gajim.contacts.get_metacontacts_big_brother( |
|---|
| 308 | shown_family) |
|---|
| 309 | big_brother_jid = big_brother_data['jid'] |
|---|
| 310 | big_brother_account = big_brother_data['account'] |
|---|
| 311 | if big_brother_jid != jid or big_brother_account != account: |
|---|
| 312 | |
|---|
| 313 | if contact.show in ('offline', 'error') and \ |
|---|
| 314 | not showOffline and len(gajim.events.get_events(account, jid)) == 0: |
|---|
| 315 | return |
|---|
| 316 | parent_iters = self.get_contact_iter(big_brother_jid, |
|---|
| 317 | big_brother_account) |
|---|
| 318 | name = contact.get_shown_name() |
|---|
| 319 | for i in parent_iters: |
|---|
| 320 | |
|---|
| 321 | model.append(i, (None, name, 'contact', jid, account, None)) |
|---|
| 322 | self.draw_contact(jid, account) |
|---|
| 323 | self.draw_avatar(jid, account) |
|---|
| 324 | |
|---|
| 325 | self.draw_contact(big_brother_jid, big_brother_account) |
|---|
| 326 | return |
|---|
| 327 | |
|---|
| 328 | if (contact.show in ('offline', 'error') or hide) and \ |
|---|
| 329 | not showOffline and (not _('Transports') in contact.groups or \ |
|---|
| 330 | gajim.connections[account].connected < 2) and \ |
|---|
| 331 | len(gajim.contacts.get_contact(account, jid)) == 1 and nb_events == 0 and\ |
|---|
| 332 | not _('Not in Roster') in contact.groups: |
|---|
| 333 | return |
|---|
| 334 | |
|---|
| 335 | |
|---|
| 336 | |
|---|
| 337 | for data in shown_family: |
|---|
| 338 | contacts = gajim.contacts.get_contact(data['account'], |
|---|
| 339 | data['jid']) |
|---|
| 340 | for c in contacts: |
|---|
| 341 | self.remove_contact(c, data['account']) |
|---|
| 342 | groups = contact.groups |
|---|
| 343 | if observer: |
|---|
| 344 | groups = [_('Observers')] |
|---|
| 345 | elif not groups: |
|---|
| 346 | groups = [_('General')] |
|---|
| 347 | for group in groups: |
|---|
| 348 | iterG = self.get_group_iter(group, account) |
|---|
| 349 | if not iterG: |
|---|
| 350 | IterAcct = self.get_account_iter(account) |
|---|
| 351 | iterG = model.append(IterAcct, [ |
|---|
| 352 | self.jabber_state_images['16']['closed'], |
|---|
| 353 | gtkgui_helpers.escape_for_pango_markup(group), 'group', |
|---|
| 354 | group, account, None]) |
|---|
| 355 | self.draw_group(group, account) |
|---|
| 356 | if model.iter_n_children(IterAcct) == 1: |
|---|
| 357 | self.draw_account(account) |
|---|
| 358 | if group not in gajim.groups[account]: |
|---|
| 359 | if account + group in self.collapsed_rows: |
|---|
| 360 | ishidden = False |
|---|
| 361 | else: |
|---|
| 362 | ishidden = True |
|---|
| 363 | gajim.groups[account][group] = {'expand': ishidden} |
|---|
| 364 | if not account in self.collapsed_rows: |
|---|
| 365 | self.tree.expand_row((model.get_path(iterG)[0]), False) |
|---|
| 366 | |
|---|
| 367 | typestr = 'contact' |
|---|
| 368 | if group == _('Transports'): |
|---|
| 369 | typestr = 'agent' |
|---|
| 370 | |
|---|
| 371 | name = contact.get_shown_name() |
|---|
| 372 | |
|---|
| 373 | model.append(iterG, (None, name, typestr, contact.jid, account, None)) |
|---|
| 374 | |
|---|
| 375 | if gajim.groups[account][group]['expand']: |
|---|
| 376 | self.tree.expand_row(model.get_path(iterG), False) |
|---|
| 377 | self.draw_contact(jid, account) |
|---|
| 378 | self.draw_avatar(jid, account) |
|---|
| 379 | |
|---|
| 380 | for data in shown_family: |
|---|
| 381 | contacts = gajim.contacts.get_contact(data['account'], |
|---|
| 382 | data['jid']) |
|---|
| 383 | self.add_contact_to_roster(data['jid'], data['account']) |
|---|
| 384 | |
|---|
| 385 | def draw_group(self, group, account): |
|---|
| 386 | iter = self.get_group_iter(group, account) |
|---|
| 387 | if not iter: |
|---|
| 388 | return |
|---|
| 389 | if self.regroup: |
|---|
| 390 | accounts = [] |
|---|
| 391 | else: |
|---|
| 392 | accounts = [account] |
|---|
| 393 | text = group |
|---|
| 394 | if gajim.config.get('show_contacts_number'): |
|---|
| 395 | nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts( |
|---|
| 396 | accounts = accounts, groups = [group]) |
|---|
| 397 | text += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total)) |
|---|
| 398 | model = self.tree.get_model() |
|---|
| 399 | model.set_value(iter, 1 , gtkgui_helpers.escape_for_pango_markup(text)) |
|---|
| 400 | |
|---|
| 401 | def add_to_not_in_the_roster(self, account, jid, nick = '', resource = ''): |
|---|
| 402 | ''' add jid to group "not in the roster", he MUST not be in roster yet, |
|---|
| 403 | return contact ''' |
|---|
| 404 | keyID = '' |
|---|
| 405 | attached_keys = gajim.config.get_per('accounts', account, |
|---|
| 406 | 'attached_gpg_keys').split() |
|---|
| 407 | if jid in attached_keys: |
|---|
| 408 | keyID = attached_keys[attached_keys.index(jid) + 1] |
|---|
| 409 | contact = gajim.contacts.create_contact(jid = jid, name = nick, |
|---|
| 410 | groups = [_('Not in Roster')], show = 'not in roster', status = '', |
|---|
| 411 | sub = 'none', resource = resource, keyID = keyID) |
|---|
| 412 | gajim.contacts.add_contact(account, contact) |
|---|
| 413 | self.add_contact_to_roster(contact.jid, account) |
|---|
| 414 | return contact |
|---|
| 415 | |
|---|
| 416 | def get_self_contact_iter(self, account): |
|---|
| 417 | model = self.tree.get_model() |
|---|
| 418 | iterAcct = self.get_account_iter(account) |
|---|
| 419 | iter = model.iter_children(iterAcct) |
|---|
| 420 | if not iter: |
|---|
| 421 | return None |
|---|
| 422 | if model[iter][C_TYPE] == 'self_contact': |
|---|
| 423 | return iter |
|---|
| 424 | return None |
|---|
| 425 | |
|---|
| 426 | def add_self_contact(self, account): |
|---|
| 427 | jid = gajim.get_jid_from_account(account) |
|---|
| 428 | if self.get_self_contact_iter(account): |
|---|
| 429 | self.draw_contact(jid, account) |
|---|
| 430 | self.draw_avatar(jid, account) |
|---|
| 431 | return |
|---|
| 432 | |
|---|
| 433 | contact = gajim.contacts.get_first_contact_from_jid(account, jid) |
|---|
| 434 | if not contact: |
|---|
| 435 | return |
|---|
| 436 | showOffline = gajim.config.get('showoffline') |
|---|
| 437 | if (contact.show in ('offline', 'error')) and not showOffline and \ |
|---|
| 438 | len(gajim.events.get_events(account, jid)) == 0: |
|---|
| 439 | return |
|---|
| 440 | |
|---|
| 441 | model = self.tree.get_model() |
|---|
| 442 | iterAcct = self.get_account_iter(account) |
|---|
| 443 | model.append(iterAcct, (None, gajim.nicks[account], 'self_contact', jid, |
|---|
| 444 | account, None)) |
|---|
| 445 | self.draw_contact(jid, account) |
|---|
| 446 | self.draw_avatar(jid, account) |
|---|
| 447 | |
|---|
| 448 | def add_transport_to_roster(self, account, transport): |
|---|
| 449 | c = gajim.contacts.create_contact(jid = transport, name = transport, |
|---|
| 450 | groups = [_('Transports')], show = 'offline', status = 'offline', |
|---|
| 451 | sub = 'from') |
|---|
| 452 | gajim.contacts.add_contact(account, c) |
|---|
| 453 | self.add_contact_to_roster(transport, account) |
|---|
| 454 | |
|---|
| 455 | def really_remove_contact(self, contact, account): |
|---|
| 456 | if not gajim.interface.instances.has_key(account): |
|---|
| 457 | |
|---|
| 458 | return |
|---|
| 459 | if contact.jid in gajim.newly_added[account]: |
|---|
| 460 | return |
|---|
| 461 | if gajim.jid_is_transport(contact.jid) and gajim.account_is_connected( |
|---|
| 462 | account) and gajim.config.get('show_transports_group'): |
|---|
| 463 | |
|---|
| 464 | return |
|---|
| 465 | if contact.jid in gajim.to_be_removed[account]: |
|---|
| 466 | gajim.to_be_removed[account].remove(contact.jid) |
|---|
| 467 | |
|---|
| 468 | |
|---|
| 469 | hide = contact.is_hidden_from_roster() |
|---|
| 470 | |
|---|
| 471 | show_offline = gajim.config.get('showoffline') |
|---|
| 472 | show_transports = gajim.config.get('show_transports_group') |
|---|
| 473 | |
|---|
| 474 | nb_events = 0 |
|---|
| 475 | jid_list = [contact.jid] |
|---|
| 476 | if contact.get_full_jid() != contact.jid: |
|---|
| 477 | jid_list.append(contact.get_full_jid()) |
|---|
| 478 | for jid in jid_list: |
|---|
| 479 | |
|---|
| 480 | nb_events += gajim.events.get_nb_roster_events(account, jid, ['chat']) |
|---|
| 481 | |
|---|
| 482 | if (_('Transports') in contact.groups and not show_transports) or \ |
|---|
| 483 | ((contact.show in ('offline', 'error') or hide) and not show_offline and \ |
|---|
| 484 | (not _('Transports') in contact.groups or \ |
|---|
| 485 | gajim.account_is_disconnected(account))) and nb_events == 0: |
|---|
| 486 | self.remove_contact(contact, account) |
|---|
| 487 | else: |
|---|
| 488 | self.draw_contact(contact.jid, account) |
|---|
| 489 | |
|---|
| 490 | def remove_contact(self, contact, account): |
|---|
| 491 | '''Remove a contact from the roster''' |
|---|
| 492 | if contact.jid in gajim.to_be_removed[account]: |
|---|
| 493 | return |
|---|
| 494 | model = self.tree.get_model() |
|---|
| 495 | iters = self.get_contact_iter(contact.jid, account) |
|---|
| 496 | if not iters: |
|---|
| 497 | return |
|---|
| 498 | parent_iter = model.iter_parent(iters[0]) |
|---|
| 499 | parent_type = model[parent_iter][C_TYPE] |
|---|
| 500 | |
|---|
| 501 | children = [] |
|---|
| 502 | child_iter = model.iter_children(iters[0]) |
|---|
| 503 | while child_iter: |
|---|
| 504 | c_jid = model[child_iter][C_JID].decode('utf-8') |
|---|
| 505 | c_account = model[child_iter][C_ACCOUNT].decode('utf-8') |
|---|
| 506 | children.append((c_jid, c_account)) |
|---|
| 507 | child_iter = model.iter_next(child_iter) |
|---|
| 508 | |
|---|
| 509 | |
|---|
| 510 | for i in iters: |
|---|
| 511 | parent_i = model.iter_parent(i) |
|---|
| 512 | model.remove(i) |
|---|
| 513 | if parent_type == 'group': |
|---|
| 514 | group = model[parent_i][C_JID].decode('utf-8') |
|---|
| 515 | if model.iter_n_children(parent_i) == 0: |
|---|
| 516 | model.remove(parent_i) |
|---|
| 517 | |
|---|
| 518 | for jid in gajim.contacts.get_jid_list(account): |
|---|
| 519 | if group in gajim.contacts.get_contact_with_highest_priority( |
|---|
| 520 | account, jid).groups: |
|---|
| 521 | break |
|---|
| 522 | else: |
|---|
| 523 | if gajim.groups[account].has_key(group): |
|---|
| 524 | del gajim.groups[account][group] |
|---|
| 525 | |
|---|
| 526 | |
|---|
| 527 | for child in children: |
|---|
| 528 | self.add_contact_to_roster(child[0], child[1]) |
|---|
| 529 | |
|---|
| 530 | if parent_type == 'contact': |
|---|
| 531 | parent_jid = model[parent_iter][C_JID].decode('utf-8') |
|---|
| 532 | parent_account = model[parent_iter][C_ACCOUNT].decode('utf-8') |
|---|
| 533 | self.draw_contact(parent_jid, parent_account) |
|---|
| 534 | |
|---|
| 535 | def get_appropriate_state_images(self, jid, size = '16', |
|---|
| 536 | icon_name = 'online'): |
|---|
| 537 | '''check jid and return the appropriate state images dict for |
|---|
| 538 | the demanded size. icon_name is taken into account when jid is from |
|---|
| 539 | transport: transport iconset doesn't contain all icons, so we fall back |
|---|
| 540 | to jabber one''' |
|---|
| 541 | transport = gajim.get_transport_name_from_jid(jid) |
|---|
| 542 | if transport and self.transports_state_images.has_key(size) and \ |
|---|
| 543 | self.transports_state_images[size].has_key(transport) and icon_name in \ |
|---|
| 544 | self.transports_state_images[size][transport]: |
|---|
| 545 | return self.transports_state_images[size][transport] |
|---|
| 546 | return self.jabber_state_images[size] |
|---|
| 547 | |
|---|
| 548 | def draw_contact(self, jid, account, selected = False, focus = False): |
|---|
| 549 | '''draw the correct state image, name BUT not avatar''' |
|---|
| 550 | |
|---|
| 551 | model = self.tree.get_model() |
|---|
| 552 | iters = self.get_contact_iter(jid, account) |
|---|
| 553 | if len(iters) == 0: |
|---|
| 554 | return |
|---|
| 555 | contact_instances = gajim.contacts.get_contact(account, jid) |
|---|
| 556 | contact = gajim.contacts.get_highest_prio_contact_from_contacts( |
|---|
| 557 | contact_instances) |
|---|
| 558 | if not contact: |
|---|
| 559 | return |
|---|
| 560 | name = gtkgui_helpers.escape_for_pango_markup(contact.get_shown_name()) |
|---|
| 561 | |
|---|
| 562 | nb_connected_contact = 0 |
|---|
| 563 | for c in contact_instances: |
|---|
| 564 | if c.show not in ('error', 'offline'): |
|---|
| 565 | nb_connected_contact += 1 |
|---|
| 566 | if nb_connected_contact > 1: |
|---|
| 567 | name += ' (' + unicode(nb_connected_contact) + ')' |
|---|
| 568 | |
|---|
| 569 | |
|---|
| 570 | if self.regroup: |
|---|
| 571 | add_acct = False |
|---|
| 572 | |
|---|
| 573 | for account_iter in gajim.connections: |
|---|
| 574 | if account_iter == account: |
|---|
| 575 | continue |
|---|
| 576 | for jid_iter in gajim.contacts.get_jid_list(account_iter): |
|---|
| 577 | |
|---|
| 578 | contact_iter = gajim.contacts.\ |
|---|
| 579 | get_first_contact_from_jid(account_iter, jid_iter) |
|---|
| 580 | if contact_iter.get_shown_name() == \ |
|---|
| 581 | contact.get_shown_name() and\ |
|---|
| 582 | (jid_iter, account_iter) != (jid, account): |
|---|
| 583 | add_acct = True |
|---|
| 584 | break |
|---|
| 585 | if add_acct: |
|---|
| 586 | |
|---|
| 587 | break |
|---|
| 588 | if add_acct: |
|---|
| 589 | name += ' (' + account + ')' |
|---|
| 590 | |
|---|
| 591 | |
|---|
| 592 | if contact.status and gajim.config.get('show_status_msgs_in_roster'): |
|---|
| 593 | status = contact.status.strip() |
|---|
| 594 | if status != '': |
|---|
| 595 | status = helpers.reduce_chars_newlines(status, max_lines = 1) |
|---|
| 596 | |
|---|
| 597 | color = gtkgui_helpers._get_fade_color(self.tree, selected, focus) |
|---|
| 598 | colorstring = "#%04x%04x%04x" % (color.red, color.green, color.blue) |
|---|
| 599 | name += \ |
|---|
| 600 | '\n<span size="small" style="italic" foreground="%s">%s</span>' \ |
|---|
| 601 | % (colorstring, gtkgui_helpers.escape_for_pango_markup(status)) |
|---|
| 602 | |
|---|
| 603 | iter = iters[0] |
|---|
| 604 | icon_name = helpers.get_icon_name_to_show(contact, account) |
|---|
| 605 | |
|---|
| 606 | for c in contact_instances: |
|---|
| 607 | c_icon_name = helpers.get_icon_name_to_show(c, account) |
|---|
| 608 | if c_icon_name == 'message': |
|---|
| 609 | icon_name = c_icon_name |
|---|
| 610 | break |
|---|
| 611 | path = model.get_path(iter) |
|---|
| 612 | if model.iter_has_child(iter): |
|---|
| 613 | if not self.tree.row_expanded(path) and icon_name != 'message': |
|---|
| 614 | child_iter = model.iter_children(iter) |
|---|
| 615 | if icon_name in ('error', 'offline'): |
|---|
| 616 | |
|---|
| 617 | child_jid = model[child_iter][C_JID].decode('utf-8') |
|---|
| 618 | child_account = model[child_iter][C_ACCOUNT].decode('utf-8') |
|---|
| 619 | child_contact = gajim.contacts.get_contact_with_highest_priority( |
|---|
| 620 | child_account, child_jid) |
|---|
| 621 | child_icon_name = helpers.get_icon_name_to_show(child_contact, |
|---|
| 622 | child_account) |
|---|
| 623 | if child_icon_name not in ('error', 'not in roster'): |
|---|
| 624 | icon_name = child_icon_name |
|---|
| 625 | while child_iter: |
|---|
| 626 | |
|---|
| 627 | child_jid = model[child_iter][C_JID].decode('utf-8') |
|---|
| 628 | child_account = model[child_iter][C_ACCOUNT].decode('utf-8') |
|---|
| 629 | if len(gajim.events.get_events(child_account, child_jid)): |
|---|
| 630 | icon_name = 'message' |
|---|
| 631 | break |
|---|
| 632 | child_iter = model.iter_next(child_iter) |
|---|
| 633 | if self.tree.row_expanded(path): |
|---|
| 634 | state_images = self.get_appropriate_state_images(jid, |
|---|
| 635 | size = 'opened', icon_name = icon_name) |
|---|
| 636 | else: |
|---|
| 637 | state_images = self.get_appropriate_state_images(jid, |
|---|
| 638 | size = 'closed', icon_name = icon_name) |
|---|
| 639 | else: |
|---|
| 640 | |
|---|
| 641 | self.draw_parent_contact(jid, account) |
|---|
| 642 | state_images = self.get_appropriate_state_images(jid, |
|---|
| 643 | icon_name = icon_name) |
|---|
| 644 | |
|---|
| 645 | img = state_images[icon_name] |
|---|
| 646 | |
|---|
| 647 | for iter in iters: |
|---|
| 648 | model[iter][C_IMG] = img |
|---|
| 649 | model[iter][C_NAME] = name |
|---|
| 650 | |
|---|
| 651 | def draw_parent_contact(self, jid, account): |
|---|
| 652 | model = self.tree.get_model() |
|---|
| 653 | iters = self.get_contact_iter(jid, account) |
|---|
| 654 | if not len(iters): |
|---|
| 655 | return |
|---|
| 656 | parent_iter = model.iter_parent(iters[0]) |
|---|
| 657 | if model[parent_iter][C_TYPE] != 'contact': |
|---|
| 658 | |
|---|
| 659 | return |
|---|
| 660 | parent_jid = model[parent_iter][C_JID].decode('utf-8') |
|---|
| 661 | parent_account = model[parent_iter][C_ACCOUNT].decode('utf-8') |
|---|
| 662 | self.draw_contact(parent_jid, parent_account) |
|---|
| 663 | |
|---|
| 664 | def draw_avatar(self, jid, account): |
|---|
| 665 | '''draw the avatar''' |
|---|
| 666 | model = self.tree.get_model() |
|---|
| 667 | iters = self.get_contact_iter(jid, account) |
|---|
| 668 | if gajim.config.get('show_avatars_in_roster'): |
|---|
| 669 | pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid) |
|---|
| 670 | if pixbuf in ('ask', None): |
|---|
| 671 | scaled_pixbuf = None |
|---|
| 672 | else: |
|---|
| 673 | scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'roster') |
|---|
| 674 | else: |
|---|
| 675 | scaled_pixbuf = None |
|---|
| 676 | for iter in iters: |
|---|
| 677 | model[iter][C_SECPIXBUF] = scaled_pixbuf |
|---|
| 678 | |
|---|
| 679 | def join_gc_room(self, account, room_jid, nick, password): |
|---|
| 680 | '''joins the room immediatelly''' |
|---|
| 681 | if gajim.interface.msg_win_mgr.has_window(room_jid, account) and \ |
|---|
| 682 | gajim.gc_connected[account][room_jid]: |
|---|
| 683 | win = gajim.interface.msg_win_mgr.get_window(room_jid, account) |
|---|
| 684 | win.window.present() |
|---|
| 685 | win.set_active_tab(room_jid, account) |
|---|
| 686 | dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid) |
|---|
| 687 | return |
|---|
| 688 | invisible_show = gajim.SHOW_LIST.index('invisible') |
|---|
| 689 | if gajim.connections[account].connected == invisible_show: |
|---|
| 690 | dialogs.ErrorDialog( |
|---|
| 691 | _('You cannot join a group chat while you are invisible')) |
|---|
| 692 | return |
|---|
| 693 | if not gajim.interface.msg_win_mgr.has_window(room_jid, account): |
|---|
| 694 | self.new_room(room_jid, nick, account) |
|---|
| 695 | gc_win = gajim.interface.msg_win_mgr.get_window(room_jid, account) |
|---|
| 696 | gc_win.set_active_tab(room_jid, account) |
|---|
| 697 | gc_win.window.present() |
|---|
| 698 | gajim.connections[account].join_gc(nick, room_jid, password) |
|---|
| 699 | if password: |
|---|
| 700 | gajim.gc_passwords[room_jid] = password |
|---|
| 701 | |
|---|
| 702 | def on_actions_menuitem_activate(self, widget): |
|---|
| 703 | self.make_menu() |
|---|
| 704 | |
|---|
| 705 | def on_edit_menuitem_activate(self, widget): |
|---|
| 706 | '''need to call make_menu to build profile, avatar item''' |
|---|
| 707 | self.make_menu() |
|---|
| 708 | |
|---|
| 709 | def on_bookmark_menuitem_activate(self, widget, account, bookmark): |
|---|
| 710 | self.join_gc_room(account, bookmark['jid'], bookmark['nick'], |
|---|
| 711 | bookmark['password']) |
|---|
| 712 | |
|---|
| 713 | def on_send_server_message_menuitem_activate(self, widget, account): |
|---|
| 714 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 715 | server += '/announce/online' |
|---|
| 716 | dialogs.SingleMessageWindow(account, server, 'send') |
|---|
| 717 | |
|---|
| 718 | def on_xml_console_menuitem_activate(self, widget, account): |
|---|
| 719 | if gajim.interface.instances[account].has_key('xml_console'): |
|---|
| 720 | gajim.interface.instances[account]['xml_console'].window.present() |
|---|
| 721 | else: |
|---|
| 722 | gajim.interface.instances[account]['xml_console'] = \ |
|---|
| 723 | dialogs.XMLConsoleWindow(account) |
|---|
| 724 | |
|---|
| 725 | def on_privacy_lists_menuitem_activate(self, widget, account): |
|---|
| 726 | if gajim.interface.instances[account].has_key('privacy_lists'): |
|---|
| 727 | gajim.interface.instances[account]['privacy_lists'].window.present() |
|---|
| 728 | else: |
|---|
| 729 | gajim.interface.instances[account]['privacy_lists'] = \ |
|---|
| 730 | dialogs.PrivacyListsWindow(account) |
|---|
| 731 | |
|---|
| 732 | def on_set_motd_menuitem_activate(self, widget, account): |
|---|
| 733 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 734 | server += '/announce/motd' |
|---|
| 735 | dialogs.SingleMessageWindow(account, server, 'send') |
|---|
| 736 | |
|---|
| 737 | def on_update_motd_menuitem_activate(self, widget, account): |
|---|
| 738 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 739 | server += '/announce/motd/update' |
|---|
| 740 | dialogs.SingleMessageWindow(account, server, 'send') |
|---|
| 741 | |
|---|
| 742 | def on_delete_motd_menuitem_activate(self, widget, account): |
|---|
| 743 | server = gajim.config.get_per('accounts', account, 'hostname') |
|---|
| 744 | server += '/announce/motd/delete' |
|---|
| 745 | gajim.connections[account].send_motd(server) |
|---|
| 746 | |
|---|
| 747 | def on_history_manager_menuitem_activate(self, widget): |
|---|
| 748 | if os.name == 'nt': |
|---|
| 749 | if os.path.exists('history_manager.exe'): |
|---|
| 750 | helpers.exec_command('history_manager.exe') |
|---|
| 751 | else: |
|---|
| 752 | helpers.exec_command('python history_manager.py') |
|---|
| 753 | else: |
|---|
| 754 | helpers.exec_command('python history_manager.py &') |
<