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