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

Revision 7984, 164.1 kB (checked in by asterix, 19 months ago)

mrege diff from trunk

  • 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 gobject
20import os
21import time
22import urllib
23
24import common.sleepy
25import history_window
26import dialogs
27import vcard
28import config
29import disco
30import gtkgui_helpers
31import cell_renderer_image
32import tooltips
33import message_control
34import adhoc_commands
35import notify
36
37from common import gajim
38from common import helpers
39from common import passwords
40from common.exceptions import GajimGeneralException
41
42from message_window import MessageWindowMgr
43from chat_control import ChatControl
44from groupchat_control import GroupchatControl
45from groupchat_control import PrivateChatControl
46
47from common import dbus_support
48if dbus_support.supported:
49        from music_track_listener import MusicTrackListener
50
51#(icon, name, type, jid, account, editable, second pixbuf)
52(
53C_IMG, # image to show state (online, new message etc)
54C_NAME, # cellrenderer text that holds contact nickame
55C_TYPE, # account, group or contact?
56C_JID, # the jid of the row
57C_ACCOUNT, # cellrenderer text that holds account name
58C_SECPIXBUF, # secondary_pixbuf (holds avatar or padlock)
59) = range(6)
60
61class 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                # C_NAME column contacts the pango escaped group name
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: # when closing Gajim model can be none (async pbs?)
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                                # find next contact iter
110                                if model.iter_has_child(contact_iter):
111                                        # his first child if it has some
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                                                # now we need to go up
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                                                        # we tested all contacts in this group
128                                                        contact_iter = None
129                                        else:
130                                                # his brother if he has
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                        # contact is in roster but we curently don't see him online
149                        # show him
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                        # do not change selection while DND'ing
155                        return
156                # popup == False so we show awaiting event in roster
157                # show and select contact line in roster (even if he is not in roster)
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) # the only way to create a pixbuf from stock
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) # the only way to create a pixbuf from stock
201                        if num_of_secured < num_of_accounts:
202                                # Make it transparent
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                        # account row not expanded
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                # count events from all resources
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                # If contact already in roster, do not add it
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                        # if jid is transport, check if we wanna show it in roster
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                # XEP-0162
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                        # if he has a tag, remove it
284                        tag = gajim.contacts.get_metacontacts_tag(account, jid)
285                        if tag:
286                                gajim.contacts.remove_metacontact(account, jid)
287
288                # family is [{'account': acct, 'jid': jid, 'priority': prio}, ]
289                # 'priority' is optional
290                family = gajim.contacts.get_metacontacts_family(account, jid)
291
292                # family members that are in roster and belong to the same account.
293                shown_family = []
294                if family:
295                        for data in family:
296                                _account = data['account']
297                                # Metacontacts over different accounts only in merged mode
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                                # We are adding a child contact
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                                        # we add some values here. see draw_contact for more
321                                        model.append(i, (None, name, 'contact', jid, account, None))
322                                self.draw_contact(jid, account)
323                                self.draw_avatar(jid, account)
324                                # Redraw parent to change icon
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                # Remove brother contacts that are already in roster to add them
336                # under this iter
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: # We added the first one
357                                        self.draw_account(account)
358                        if group not in gajim.groups[account]: # It can probably never append
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                        # we add some values here. see draw_contact for more
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                # put the children under this iter
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                        # Account has been deleted during the timeout that called us
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                        # It's an agent and we show them
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                        # dont't count printed_chat messages
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                # remember children to re-add them
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                # Remove iters and group iter if they are empty
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                                        # We need to check all contacts, even offline contacts
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                # re-add children
527                for child in children:
528                        self.add_contact_to_roster(child[0], child[1])
529                # redraw parent
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                # focus is about if the roster window has toplevel-focus or not
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                # show (account_name) if there are 2 contact with same jid in merged mode
570                if self.regroup:
571                        add_acct = False
572                        # look through all contacts of all accounts
573                        for account_iter in gajim.connections:
574                                if account_iter == account: # useless to add accout name
575                                        continue
576                                for jid_iter in gajim.contacts.get_jid_list(account_iter):
577                                        # [0] cause it'fster than highest_prio
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                                        # No need to continue in other account if we already found one
587                                        break
588                        if add_acct:
589                                name += ' (' + account + ')'
590
591                # add status msg, if not empty, under contact name in the treeview
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                                # escape markup entities and make them small italic and fg color
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] # choose the icon with the first iter
604                icon_name = helpers.get_icon_name_to_show(contact, account)
605                # look if another resource has awaiting events
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                                        # get the icon from the first child as they are sorted by show
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                                        # a child has awaiting messages ?
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                        # redraw parent
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                        # parent is not a contact
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'): # user is running stable
750                                helpers.exec_command('history_manager.exe')
751                        else: # user is running svn
752                                helpers.exec_command('python history_manager.py')
753                else: # Unix user
754                        helpers.exec_command('python history_manager.py &')