root/branches/gajim_0.11.1/src/roster_window.py

Revision 8842, 166.0 kB (checked in by asterix, 11 months ago)

allow to execute commands even if there is no resource. XEP don't forbid that. fixes #3453

  • Property svn:eol-style set to LF
Line 
1# -*- coding: utf-8 -*-
2##      roster_window.py
3##
4## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
5## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
6## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
7##
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published
10## by the Free Software Foundation; version 2 only.
11##
12## This program is distributed in the hope that it will be useful,
13## but WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15## GNU General Public License for more details.
16##
17
18import gtk
19import pango
20import gobject
21import os
22import time
23import urllib
24
25import common.sleepy
26import history_window
27import dialogs
28import vcard
29import config
30import disco
31import gtkgui_helpers
32import cell_renderer_image
33import tooltips
34import message_control
35import adhoc_commands
36import notify
37
38from common import gajim
39from common import helpers
40from common import passwords
41from common.exceptions import GajimGeneralException
42
43from message_window import MessageWindowMgr
44from chat_control import ChatControl
45from groupchat_control import GroupchatControl
46from groupchat_control import PrivateChatControl
47
48from common import dbus_support
49if dbus_support.supported:
50        from music_track_listener import MusicTrackListener
51
52#(icon, name, type, jid, account, editable, second pixbuf)
53(
54C_IMG, # image to show state (online, new message etc)
55C_NAME, # cellrenderer text that holds contact nickame
56C_TYPE, # account, group or contact?
57C_JID, # the jid of the row
58C_ACCOUNT, # cellrenderer text that holds account name
59C_SECPIXBUF, # secondary_pixbuf (holds avatar or padlock)
60) = range(6)
61
62class 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                # C_NAME column contacts the pango escaped group name
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: # when closing Gajim model can be none (async pbs?)
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                                # find next contact iter
111                                if model.iter_has_child(contact_iter):
112                                        # his first child if it has some
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                                                # now we need to go up
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                                                        # we tested all contacts in this group
129                                                        contact_iter = None
130                                        else:
131                                                # his brother if he has
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                        # contact is in roster but we curently don't see him online
150                        # show him
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                        # do not change selection while DND'ing
156                        return
157                # popup == False so we show awaiting event in roster
158                # show and select contact line in roster (even if he is not in roster)
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) # the only way to create a pixbuf from stock
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) # the only way to create a pixbuf from stock
202                        if num_of_secured < num_of_accounts:
203                                # Make it transparent
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                        # account row not expanded
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                # count events from all resources
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                # If contact already in roster, do not add it
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                        # if jid is transport, check if we wanna show it in roster
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                # XEP-0162
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                        # if he has a tag, remove it
285                        tag = gajim.contacts.get_metacontacts_tag(account, jid)
286                        if tag:
287                                gajim.contacts.remove_metacontact(account, jid)
288
289                # family is [{'account': acct, 'jid': jid, 'priority': prio}, ]
290                # 'priority' is optional
291                family = gajim.contacts.get_metacontacts_family(account, jid)
292
293                # family members that are in roster and belong to the same account.
294                shown_family = []
295                if family:
296                        for data in family:
297                                _account = data['account']
298                                # Metacontacts over different accounts only in merged mode
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                                # We are adding a child contact
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                                        # we add some values here. see draw_contact for more
322                                        model.append(i, (None, name, 'contact', jid, account, None))
323                                self.draw_contact(jid, account)
324                                self.draw_avatar(jid, account)
325                                # Redraw parent to change icon
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                # Remove brother contacts that are already in roster to add them
337                # under this iter
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: # We added the first one
359                                        self.draw_account(account)
360                        if group not in gajim.groups[account]: # It can probably never append
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                        # we add some values here. see draw_contact for more
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                # put the children under this iter
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                        # Account has been deleted during the timeout that called us
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                        # It's an agent and we show them
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                        # dont't count printed_chat messages
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                # remember children to re-add them
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                # Remove iters and group iter if they are empty
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                                        # We need to check all contacts, even offline contacts
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                # re-add children
531                for child in children:
532                        self.add_contact_to_roster(child[0], child[1])
533                # redraw parent
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                # focus is about if the roster window has toplevel-focus or not
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                # show (account_name) if there are 2 contact with same jid in merged mode
574                if self.regroup:
575                        add_acct = False
576                        # look through all contacts of all accounts
577                        for account_iter in gajim.connections:
578                                if account_iter == account: # useless to add accout name
579                                        continue
580                                for jid_iter in gajim.contacts.get_jid_list(account_iter):
581                                        # [0] cause it'fster than highest_prio
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                                        # No need to continue in other account if we already found one
591                                        break
592                        if add_acct:
593                                name += ' (' + account + ')'
594
595                # add status msg, if not empty, under contact name in the treeview
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                                # escape markup entities and make them small italic and fg color
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] # choose the icon with the first iter
608                icon_name = helpers.get_icon_name_to_show(contact, account)
609                # look if another resource has awaiting events
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                                        # get the icon from the first child as they are sorted by show
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                                        # a child has awaiting messages ?
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                        # redraw parent
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                        # parent is not a contact
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'): # user is running stable
754                                helpers.exec_command('history_manager.exe')
755                        else: # user is running svn<