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

Revision 1607, 52.3 kB (checked in by asterix, 4 years ago)

when we DND to a group in which the user is already, don't add it twice

Line 
1##      roster_window.py
2##
3## Gajim Team:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Vincent Hanquez <tab@snarc.org>
6##      - Nikos Kouremenos <kourem@gmail.com>
7##
8##      Copyright (C) 2003-2005 Gajim Team
9##
10## This program is free software; you can redistribute it and/or modify
11## it under the terms of the GNU General Public License as published
12## by the Free Software Foundation; version 2 only.
13##
14## This program is distributed in the hope that it will be useful,
15## but WITHOUT ANY WARRANTY; without even the implied warranty of
16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17## GNU General Public License for more details.
18##
19
20import gtk
21import gtk.glade
22import pango
23import gobject
24import os
25import time
26import Queue
27import common.sleepy
28
29import tabbed_chat_window
30import groupchat_window
31import history_window
32import dialogs
33import config
34import cell_renderer_image
35
36from gajim import User
37from common import gajim
38from common import helpers
39from common import i18n
40
41_ = i18n._
42APP = i18n.APP
43gtk.glade.bindtextdomain(APP, i18n.DIR)
44gtk.glade.textdomain(APP)
45
46GTKGUI_GLADE = 'gtkgui.glade'
47
48class Roster_window:
49        '''Class for main window of gtkgui plugin'''
50
51        def get_account_iter(self, name):
52                if self.regroup:
53                        return None
54                model = self.tree.get_model()
55                account = model.get_iter_root()
56                while account:
57                        account_name = model.get_value(account, 3)
58                        if name == account_name:
59                                break
60                        account = model.iter_next(account)
61                return account
62
63        def get_group_iter(self, name, account):
64                model = self.tree.get_model()
65                root = self.get_account_iter(account)
66                group = model.iter_children(root)
67                while group:
68                        group_name = model.get_value(group, 3)
69                        if name == group_name:
70                                break
71                        group = model.iter_next(group)
72                return group
73
74        def get_user_iter(self, jid, account):
75                model = self.tree.get_model()
76                acct = self.get_account_iter(account)
77                found = []
78                fin = False
79                group = model.iter_children(acct)
80                while group:
81                        user = model.iter_children(group)
82                        while user:
83                                if jid == model.get_value(user, 3):
84                                        found.append(user)
85                                user = model.iter_next(user)
86                        group = model.iter_next(group)
87                return found
88
89        def add_account_to_roster(self, account):
90                if self.regroup:
91                        return
92                model = self.tree.get_model()
93                if self.get_account_iter(account):
94                        return
95                statuss = ['offline', 'connecting', 'online', 'chat',
96                        'away', 'xa', 'dnd', 'invisible']
97                status = statuss[gajim.connections[account].connected]
98                model.append(None, (self.jabber_state_images[status], account,
99                                'account', account, account, False))
100
101        def remove_newly_added(self, jid, account):
102                if jid in self.newly_added[account]:
103                        self.newly_added[account].remove(jid)
104                        self.draw_contact(jid, account)
105
106        def add_user_to_roster(self, jid, account):
107                '''Add a user to the roster and add groups if they aren't in roster'''
108                showOffline = gajim.config.get('showoffline')
109                if not self.contacts[account].has_key(jid):
110                        return
111                users = self.contacts[account][jid]
112                user = users[0]
113                if user.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
114                        user.groups = ['Transports']
115                elif user.groups == []:
116                        user.groups.append('General')
117
118                if (user.show == 'offline' or user.show == 'error') and \
119                   not showOffline and not 'Transports' in user.groups and \
120                   not self.plugin.queues[account].has_key(user.jid):
121                        return
122
123                model = self.tree.get_model()
124                for g in user.groups:
125                        iterG = self.get_group_iter(g, account)
126                        if not iterG:
127                                IterAcct = self.get_account_iter(account)
128                                iterG = model.append(IterAcct, 
129                                        (self.jabber_state_images['closed'], g, 'group', g, account, False))
130                        if not self.groups[account].has_key(g): #It can probably never append
131                                if account + g in self.collapsed_rows:
132                                        ishidden = False
133                                else:
134                                        ishidden = True
135                                self.groups[account][g] = { 'expand': ishidden }
136                        if not account in self.collapsed_rows and \
137                           not gajim.config.get('mergeaccounts'):
138                                self.tree.expand_row((model.get_path(iterG)[0]), False)
139
140                        typestr = 'user'
141                        if g == 'Transports':
142                                typestr = 'agent'
143
144                        model.append(iterG, (self.jabber_state_images[user.show], user.name,
145                                        typestr, user.jid, account, False))
146                       
147                        if self.groups[account][g]['expand']:
148                                self.tree.expand_row(model.get_path(iterG),
149                                                        False)
150                self.draw_contact(jid, account)
151       
152        def really_remove_user(self, user, account):
153                if user.jid in self.newly_added[account]:
154                        return
155                if user.jid in self.to_be_removed[account]:
156                        self.to_be_removed[account].remove(user.jid)
157                if gajim.config.get('showoffline'):
158                        self.draw_contact(user.jid, account)
159                        return
160                self.remove_user(user, account)
161       
162        def remove_user(self, user, account):
163                '''Remove a user from the roster'''
164                if user.jid in self.to_be_removed[account]:
165                        return
166                model = self.tree.get_model()
167                for i in self.get_user_iter(user.jid, account):
168                        parent_i = model.iter_parent(i)
169                        group = model.get_value(parent_i, 3)
170                        model.remove(i)
171                        if model.iter_n_children(parent_i) == 0:
172                                model.remove(parent_i)
173                                # We need to check all contacts, even offline contacts
174                                group_empty = True
175                                for jid in self.contacts[account]:
176                                        if group in self.contacts[account][jid][0].groups:
177                                                group_empty = False
178                                                break
179                                if group_empty:
180                                        del self.groups[account][group]
181
182        def get_appropriate_state_images(self, jid):
183                '''check jid and return the appropriate state images dict'''
184                if jid:
185                        if jid.find('@aim.') != -1:
186                                state_images = self.transports_state_images['aim']
187                        elif jid.find('@gadugadu.') != -1:
188                                state_images = self.transports_state_images['gadugadu']
189                        elif jid.find('@icq.') != -1:
190                                state_images = self.transports_state_images['icq']
191                        elif jid.find('@msn.') != -1:
192                                state_images = self.transports_state_images['msn']
193                        elif jid.find('@yahoo.') != -1:
194                                state_images = self.transports_state_images['yahoo']
195                        else: #jabber
196                                state_images = self.jabber_state_images
197                else: # in GC we don't have a jid
198                        state_images = self.jabber_state_images
199
200                return state_images
201
202        def draw_contact(self, jid, account):
203                '''draw the correct state image and name'''
204                model = self.tree.get_model()
205                iters = self.get_user_iter(jid, account)
206                if len(iters) == 0:
207                        return
208                users = self.contacts[account][jid]
209                name = users[0].name
210                if len(users) > 1:
211                        name += ' (' + str(len(users)) + ')'
212                prio = 0
213                user = users[0]
214                for u in users:
215                        if u.priority > prio:
216                                prio = u.priority
217                                user = u
218
219                for iter in iters:
220                        state_images = self.get_appropriate_state_images(jid)
221
222                        if jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
223                                if jid.find('aim.') != -1:
224                                        state_images = self.transports_state_images['aim']
225                                elif jid.find('gadugadu.') != -1:
226                                        state_images = self.transports_state_images['gadugadu']
227                                elif jid.find('icq.') != -1:
228                                        state_images = self.transports_state_images['icq']
229                                elif jid.find('msn.') != -1:
230                                        state_images = self.transports_state_images['msn']
231                                elif jid.find('yahoo.') != -1:
232                                        state_images = self.transports_state_images['yahoo']
233                               
234                                img = state_images[user.show]                                   
235                        elif self.plugin.queues[account].has_key(jid):
236                                img = state_images['message']
237                        else:
238                                if user.sub != 'both':
239                                        if user.ask == 'subscribe':
240                                                img = state_images['requested']
241                                        else:
242                                                img = state_images['not in the roster']
243                                else:
244                                        img = state_images[user.show]
245                        model.set_value(iter, 0, img)
246                        model.set_value(iter, 1, name)
247       
248        def make_menu(self):
249                '''create the main_window's menus'''
250                new_message_menuitem = self.xml.get_widget('new_message_menuitem')
251                join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
252                add_new_contact_menuitem  = self.xml.get_widget('add_new_contact_menuitem')
253                service_disco_menuitem  = self.xml.get_widget('service_disco_menuitem')
254                if self.add_new_contact_handler_id:
255                        add_new_contact_menuitem.handler_disconnect(self.add_new_contact_handler_id)
256                        self.add_new_contact_handler_id = None
257                if self.service_disco_handler_id:
258                        service_disco_menuitem.handler_disconnect(
259                                        self.service_disco_handler_id)
260                        self.service_disco_handler_id = None
261                if self.join_gc_handler_id:
262                        join_gc_menuitem.handler_disconnect(self.join_gc_handler_id)
263                        self.join_gc_handler_id = None
264                if self.new_message_menuitem_handler_id:
265                        new_message_menuitem.handler_disconnect(
266                                        self.new_message_menuitem_handler_id)
267                        self.new_message_menuitem_handler_id = None
268                #remove the existing submenus
269                if add_new_contact_menuitem.get_submenu():
270                        add_new_contact_menuitem.remove_submenu()
271                if service_disco_menuitem.get_submenu():
272                        service_disco_menuitem.remove_submenu()
273                if join_gc_menuitem.get_submenu():
274                        join_gc_menuitem.remove_submenu()
275                if new_message_menuitem.get_submenu():
276                        new_message_menuitem.remove_submenu()
277                if len(gajim.connections) > 0:
278                        new_message_menuitem.set_sensitive(True)
279                        join_gc_menuitem.set_sensitive(True)
280                        add_new_contact_menuitem.set_sensitive(True)
281                        service_disco_menuitem.set_sensitive(True)
282                else:
283                        new_message_menuitem.set_sensitive(False)
284                        join_gc_menuitem.set_sensitive(False)
285                        add_new_contact_menuitem.set_sensitive(False)
286                        service_disco_menuitem.set_sensitive(False)
287                if len(gajim.connections) >= 2: # 2 or more accounts? make submenus
288                        #add
289                        sub_menu = gtk.Menu()
290                        add_new_contact_menuitem.set_submenu(sub_menu)
291                        for account in gajim.connections:
292                                item = gtk.MenuItem(_('to ') + account + _(' account'))
293                                sub_menu.append(item)
294                                item.connect('activate', self.on_add_new_contact, account)
295                        sub_menu.show_all()
296                        #disco
297                        sub_menu = gtk.Menu()
298                        service_disco_menuitem.set_submenu(sub_menu)
299                        for account in gajim.connections:
300                                our_jid = gajim.config.get_per('accounts', account, 'name') + '@' +\
301                                        gajim.config.get_per('accounts', account, 'hostname')
302                                item = gtk.MenuItem(_('using ') + account + _(' account'))
303                                sub_menu.append(item)
304                                item.connect('activate', self.on_service_disco_menuitem_activate, account)
305                        sub_menu.show_all()
306                        #join gc
307                        sub_menu = gtk.Menu()
308                        join_gc_menuitem.set_submenu(sub_menu)
309                        for account in gajim.connections:
310                                our_jid = gajim.config.get_per('accounts', account, 'name') + '@' +\
311                                        gajim.config.get_per('accounts', account, 'hostname')
312                                item = gtk.MenuItem(_('as ') + our_jid)
313                                sub_menu.append(item)
314                                item.connect('activate', self.on_join_gc_activate, account)
315                        sub_menu.show_all()
316                        #new message
317                        sub_menu = gtk.Menu()
318                        new_message_menuitem.set_submenu(sub_menu)
319                        for account in gajim.connections:
320                                our_jid = gajim.config.get_per('accounts', account, 'name') + '@' +\
321                                        gajim.config.get_per('accounts', account, 'hostname')
322                                item = gtk.MenuItem(_('as ') + our_jid)
323                                sub_menu.append(item)
324                                item.connect('activate', self.on_new_message_menuitem_activate, 
325                                                                        account)
326                        sub_menu.show_all()
327                elif len(gajim.connections) == 1: # one account
328                        #add
329                        if not self.add_new_contact_handler_id:
330                                self.add_new_contact_handler_id = add_new_contact_menuitem.connect(
331                                        'activate', self.on_add_new_contact, gajim.connections.keys()[0])
332                        #disco
333                        if not self.service_disco_handler_id:
334                                self.service_disco_handler_id = service_disco_menuitem.connect( 
335                                        'activate', self.on_service_disco_menuitem_activate, 
336                                        gajim.connections.keys()[0])
337                        #join_gc
338                        if not self.join_gc_handler_id:
339                                self.join_gc_handler_id = join_gc_menuitem.connect( 
340                                        'activate', self.on_join_gc_activate, 
341                                        gajim.connections.keys()[0])
342                        if not self.new_message_menuitem_handler_id:
343                                self.new_message_menuitem_handler_id = new_message_menuitem.\
344                                        connect('activate', self.on_new_message_menuitem_activate, 
345                                        gajim.connections.keys()[0])
346
347        def draw_roster(self):
348                '''Clear and draw roster'''
349                self.make_menu()
350                self.tree.get_model().clear()
351                for acct in gajim.connections:
352                        self.add_account_to_roster(acct)
353                        for jid in self.contacts[acct].keys():
354                                self.add_user_to_roster(jid, acct)
355       
356        def mklists(self, array, account):
357                '''fill self.contacts and self.groups'''
358                if not self.contacts.has_key(account):
359                        self.contacts[account] = {}
360                if not self.groups.has_key(account):
361                        self.groups[account] = {}
362                for jid in array.keys():
363                        jids = jid.split('/')
364                        #get jid
365                        ji = jids[0]
366                        #get resource
367                        resource = ''
368                        if len(jids) > 1:
369                                resource = '/'.join(jids[1:])
370                        #get name
371                        name = array[jid]['name']
372                        if not name:
373                                if ji.find('@') <= 0:
374                                        name = ji
375                                else:
376                                        name = jid.split('@')[0]
377                        show = 'offline' # show is offline by default
378                        status = '' #no status message by default
379
380                        user1 = User(ji, name, array[jid]['groups'], show, status,\
381                                        array[jid]['subscription'], array[jid]['ask'], resource, 0, '')
382
383                        # when we draw the roster, we can't have twice the same
384                        # user with 2 resources
385                        self.contacts[account][ji] = [user1]
386                        for g in array[jid]['groups'] :
387                                if g in self.groups[account].keys():
388                                        continue
389
390                                if account + g in self.collapsed_rows:
391                                        ishidden = False
392                                else:
393                                        ishidden = True
394                                self.groups[account][g] = { 'expand': ishidden }
395
396        def chg_user_status(self, user, show, status, account):
397                '''When a user change his status'''
398                showOffline = gajim.config.get('showoffline')
399                model = self.tree.get_model()
400                luser = self.contacts[account][user.jid]
401                user.show = show
402                user.status = status
403                if (show == 'offline' or show == 'error') and \
404                   not self.plugin.queues[account].has_key(user.jid):
405                        if len(luser) > 1:
406                                luser.remove(user)
407                                self.draw_contact(user.jid, account)
408                        elif not showOffline:
409                                self.remove_user(user, account)
410                        else:
411                                self.draw_contact(user.jid, account)
412                else:
413                        if not self.get_user_iter(user.jid, account):
414                                self.add_user_to_roster(user.jid, account)
415                        self.draw_contact(user.jid, account)
416                #print status in chat window and update status image
417                if self.plugin.windows[account]['chats'].has_key(user.jid):
418                        jid = user.jid
419                        self.plugin.windows[account]['chats'][jid].set_state_image(jid)
420                        name = user.name
421                        if user.resource != '':
422                                name += '/' + user.resource
423                        uf_show = helpers.get_uf_show(show)
424                        self.plugin.windows[account]['chats'][jid].print_conversation(
425                                _('%s is now %s (%s)') % (name, uf_show, status), jid, 'status')
426
427        def on_info(self, widget, user, account):
428                '''Call vcard_information_window class to display user's information'''
429                info = self.plugin.windows[account]['infos']
430                if info.has_key(user.jid):
431                        info[user.jid].window.present()
432                else:
433                        info[user.jid] = dialogs.Vcard_window(user, self.plugin,
434                                                                account)
435
436        def on_agent_logging(self, widget, jid, state, account):
437                '''When an agent is requested to log in or off'''
438                gajim.connections[account].send_agent_status(jid, state)
439
440        def on_remove_agent(self, widget, user, account):
441                '''When an agent is requested to log in or off'''
442                window = dialogs.Confirmation_dialog(_('Are you sure you want to remove %s transport from your roster?') % user.jid)
443                if window.get_response() == gtk.RESPONSE_YES:
444                        gajim.connections[account].unsubscribe_agent(user.jid + '/' \
445                                                                                                                                                + user.resource)
446                        self.remove_user(user, account)
447                        del self.contacts[account][user.jid]
448
449        def on_rename(self, widget, iter, path):
450                model = self.tree.get_model()
451                model.set_value(iter, 5, True)
452                self.tree.set_cursor(path, self.tree.get_column(0), True)
453               
454        def on_edit_groups(self, widget, user, account):
455                dlg = dialogs.Edit_groups_dialog(user, account, self.plugin)
456                dlg.run()
457               
458        def on_history(self, widget, user):
459                '''When history menuitem is activated: call log window'''
460                if self.plugin.windows['logs'].has_key(user.jid):
461                        self.plugin.windows['logs'][user.jid].window.present()
462                else:
463                        self.plugin.windows['logs'][user.jid] = history_window.\
464                                History_window(self.plugin, user.jid)
465       
466        def mk_menu_user(self, event, iter):
467                '''Make user's popup menu'''
468                model = self.tree.get_model()
469                jid = model.get_value(iter, 3)
470                path = model.get_path(iter)
471                account = model.get_value(iter, 4)
472                user = self.contacts[account][jid][0]
473               
474                menu = gtk.Menu()
475                item = gtk.MenuItem(_('Start chat'))
476                menu.append(item)
477                item.connect('activate', self.on_roster_treeview_row_activated, path)
478                item = gtk.MenuItem(_('Rename'))
479                menu.append(item)
480                item.connect('activate', self.on_rename, iter, path)
481                if not 'not in the roster' in user.groups:
482                        item = gtk.MenuItem(_('Edit Groups'))
483                        menu.append(item)
484                        item.connect('activate', self.on_edit_groups, user, account)
485                        item = gtk.MenuItem()
486                        menu.append(item)
487                        item = gtk.MenuItem(_('Subscription'))
488                        menu.append(item)
489
490                        sub_menu = gtk.Menu()
491                        item.set_submenu(sub_menu)
492                        item = gtk.MenuItem(_('Resend Authorization to'))
493                        sub_menu.append(item)
494                        item.connect('activate', self.authorize, jid, account)
495                        item = gtk.MenuItem(_('Rerequest Authorization from'))
496                        sub_menu.append(item)
497                        item.connect('activate', self.req_sub, jid, 
498                                _('I would like to add you to my roster'), account)
499                else:
500                        item = gtk.MenuItem()
501                        menu.append(item)
502                        item = gtk.MenuItem(_('Add to Roster'))
503                        menu.append(item)
504                        item.connect('activate', self.on_add_to_roster, user, account)
505
506                item = gtk.MenuItem(_('Remove'))
507                menu.append(item)
508                item.connect('activate', self.on_req_usub, user, account)
509
510                item = gtk.MenuItem()
511                menu.append(item)
512                item = gtk.MenuItem(_('Information'))
513                menu.append(item)
514                item.connect('activate', self.on_info, user, account)
515                item = gtk.MenuItem(_('History'))
516                menu.append(item)
517                item.connect('activate', self.on_history, user)
518
519                menu.popup(None, None, None, event.button, event.time)
520                menu.show_all()
521                menu.reposition()
522
523        def mk_menu_g(self, event, iter):
524                '''Make group's popup menu'''
525                model = self.tree.get_model()
526                path = model.get_path(iter)
527
528                menu = gtk.Menu()
529                item = gtk.MenuItem(_('Rename'))
530                menu.append(item)
531                item.connect('activate', self.on_rename, iter, path)
532                menu.popup(None, None, None, event.button, event.time)
533                menu.show_all()
534                menu.reposition()
535       
536        def mk_menu_agent(self, event, iter):
537                '''Make agent's popup menu'''
538                model = self.tree.get_model()
539                jid = model.get_value(iter, 3)
540                path = model.get_path(iter)
541                account = model.get_value(iter, 4)
542                user = self.contacts[account][jid][0]
543                menu = gtk.Menu()
544                item = gtk.MenuItem(_('Log on'))
545                #FIXME: what about error?
546                if self.contacts[account][jid][0].show != 'offline':
547                        item.set_sensitive(False)
548                menu.append(item)
549                item.connect('activate', self.on_agent_logging, jid, 'available', account)
550
551                item = gtk.MenuItem(_('Log off'))
552                if self.<