root/branches/gajim_0.3/plugins/gtkgui/gtkgui.py

Revision 346, 84.9 kB (checked in by asterix, 4 years ago)

if no image, do not print errors

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
Line 
1##      plugins/gtkgui.py
2##
3## Gajim Team:
4##      - Yann Le Boulanger <asterix@crans.org>
5##      - Vincent Hanquez <tab@snarc.org>
6##
7##      Copyright (C) 2003 Gajim Team
8##
9## This program is free software; you can redistribute it and/or modify
10## it under the terms of the GNU General Public License as published
11## by the Free Software Foundation; version 2 only.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18
19def usage():
20        #TODO: use i18n
21        print "usage :", sys.argv[0], ' [OPTION]'
22        print "  -p\tport on whitch the sock plugin listen"
23        print "  -h, --help\tdisplay this help and exit"
24
25if __name__ == "__main__":
26        import getopt, pickle, sys, socket
27        try:
28                opts, args = getopt.getopt(sys.argv[1:], "p:h", ["help"])
29        except getopt.GetoptError:
30                # print help information and exit:
31                usage()
32                sys.exit(2)
33        port = 8255
34        for o, a in opts:
35                if o == '-p':
36                        port = a
37                if o in ("-h", "--help"):
38                        usage()
39                        sys.exit()
40        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41        try:
42                sock.connect(('', 8255))
43        except:
44                #TODO: use i18n
45                print "unable to connect to localhost on port "+str(port)
46        else:
47                evp = pickle.dumps(('EXEC_PLUGIN', '', 'gtkgui'))
48                sock.send('<'+evp+'>')
49                sock.close()
50        sys.exit()
51
52import pygtk
53pygtk.require('2.0')
54import gtk
55from gtk import TRUE, FALSE
56import gtk.glade,gobject
57import os,string,time,Queue
58import common.optparser,common.sleepy
59from common import i18n
60_ = i18n._
61APP = i18n.APP
62gtk.glade.bindtextdomain (APP, i18n.DIR)
63gtk.glade.textdomain (APP)
64
65from dialogs import *
66from config import *
67
68GTKGUI_GLADE='plugins/gtkgui/gtkgui.glade'
69
70
71class ImageCellRenderer(gtk.GenericCellRenderer):
72
73        __gproperties__ = {
74                "image": (gobject.TYPE_OBJECT, "Image", 
75                "Image", gobject.PARAM_READWRITE),
76        }
77                     
78        def __init__(self):
79                self.__gobject_init__()
80                self.image = None
81
82        def do_set_property(self, pspec, value):
83                setattr(self, pspec.name, value)
84
85        def do_get_property(self, pspec):
86                return getattr(self, pspec.name)
87
88        def func(self, model, path, iter, (image, tree)):
89                if model.get_value(iter, 0) == image:
90                        self.redraw = 1
91                        cell_area = tree.get_cell_area(path, tree.get_column(0))
92                        tree.queue_draw_area(cell_area.x, cell_area.y, cell_area.width, cell_area.height)
93
94        def animation_timeout(self, tree, image):
95                if image.get_storage_type() == gtk.IMAGE_ANIMATION:
96                        self.redraw = 0
97                        image.get_data('iter').advance()
98                        model = tree.get_model()
99                        model.foreach(self.func, (image, tree))
100                        if self.redraw:
101                                gobject.timeout_add(image.get_data('iter').get_delay_time(), self.animation_timeout, tree, image)
102                        else:
103                                image.set_data('iter', None)
104                               
105        def on_render(self, window, widget, background_area,cell_area, \
106                expose_area, flags):
107                pix_rect = gtk.gdk.Rectangle()
108                pix_rect.x, pix_rect.y, pix_rect.width, pix_rect.height = self.on_get_size(widget, cell_area)
109
110                pix_rect.x += cell_area.x
111                pix_rect.y += cell_area.y
112                pix_rect.width  -= 2 * self.get_property("xpad")
113                pix_rect.height -= 2 * self.get_property("ypad")
114
115                draw_rect = cell_area.intersect(pix_rect)
116                draw_rect = expose_area.intersect(draw_rect)
117
118                if self.image.get_storage_type() == gtk.IMAGE_ANIMATION:
119                        if not self.image.get_data('iter'):
120                                animation = self.image.get_animation()
121                                self.image.set_data('iter', animation.get_iter())
122                                gobject.timeout_add(self.image.get_data('iter').get_delay_time(), self.animation_timeout, widget, self.image)
123
124                        pix = self.image.get_data('iter').get_pixbuf()
125                elif self.image.get_storage_type() == gtk.IMAGE_PIXBUF:
126                        pix = self.image.get_pixbuf()
127                else:
128                        return
129                window.draw_pixbuf(widget.style.black_gc, pix, \
130                        draw_rect.x-pix_rect.x, draw_rect.y-pix_rect.y, draw_rect.x, \
131                        draw_rect.y+2, draw_rect.width, draw_rect.height, \
132                        gtk.gdk.RGB_DITHER_NONE, 0, 0)
133
134        def on_get_size(self, widget, cell_area):
135                if self.image.get_storage_type() == gtk.IMAGE_ANIMATION:
136                        animation = self.image.get_animation()
137                        pix = animation.get_iter().get_pixbuf()
138                elif self.image.get_storage_type() == gtk.IMAGE_PIXBUF:
139                        pix = self.image.get_pixbuf()
140                else:
141                        return 0, 0, 0, 0
142                pixbuf_width  = pix.get_width()
143                pixbuf_height = pix.get_height()
144                calc_width  = self.get_property("xpad") * 2 + pixbuf_width
145                calc_height = self.get_property("ypad") * 2 + pixbuf_height
146                x_offset = 0
147                y_offset = 0
148                if cell_area and pixbuf_width > 0 and pixbuf_height > 0:
149                        x_offset = self.get_property("xalign") * (cell_area.width - calc_width -  self.get_property("xpad"))
150                        y_offset = self.get_property("yalign") * (cell_area.height - calc_height -  self.get_property("ypad"))
151                return x_offset, y_offset, calc_width, calc_height
152
153gobject.type_register(ImageCellRenderer)
154
155
156class user:
157        """Informations concerning each users"""
158        def __init__(self, *args):
159                if len(args) == 0:
160                        self.jid = ''
161                        self.name = ''
162                        self.groups = []
163                        self.show = ''
164                        self.status = ''
165                        self.sub = ''
166                        self.ask = ''
167                        self.resource = ''
168                        self.priority = 1
169                        self.keyID = ''
170                elif len(args) == 10:
171                        self.jid = args[0]
172                        self.name = args[1]
173                        self.groups = args[2]
174                        self.show = args[3]
175                        self.status = args[4]
176                        self.sub = args[5]
177                        self.ask = args[6]
178                        self.resource = args[7]
179                        self.priority = args[8]
180                        self.keyID = args[9]
181                else: raise TypeError, _('bad arguments')
182
183
184class message_Window:
185        """Class for chat window"""
186        def delete_event(self, widget):
187                """close window"""
188                del self.plugin.windows[self.account]['chats'][self.user.jid]
189       
190        def print_conversation(self, txt, contact = None, tim = None):
191                """Print a line in the conversation :
192                if contact is set to status : it's a status message
193                if contact is set to another value : it's an outgoing message
194                if contact is not set : it's an incomming message"""
195                conversation = self.xml.get_widget('conversation')
196                buffer = conversation.get_buffer()
197                if not txt:
198                        txt = ""
199                end_iter = buffer.get_end_iter()
200                if not tim:
201                        tim = time.localtime()
202                tims = time.strftime("[%H:%M:%S]", tim)
203                buffer.insert(end_iter, tims + ' ')
204               
205                otxt = ''
206                ttxt = ''
207                if contact and contact == 'status':
208                        tag = 'status'
209                        ttxt = txt + '\n'
210                else:
211                        if contact:
212                                tag = 'outgoing'
213                                name = self.plugin.nicks[self.account] 
214                        else:
215                                tag = 'incoming'
216                                name = self.user.name
217                               
218                        if string.find(txt, '/me ') == 0:
219                                ttxt = name + ' ' + txt[4:] + '\n'
220                        else:
221                                ttxt = '<' + name + '> '
222                                otxt = txt + '\n'
223
224                buffer.insert_with_tags_by_name(end_iter, ttxt, tag)
225                if len(otxt) > 0:
226                        buffer.insert(end_iter, otxt)
227               
228                #scroll to the end of the textview
229                conversation.scroll_to_mark(buffer.get_mark('end'), 0.1, 0, 0, 0)
230                if not self.window.is_active() and contact != 'status':
231                        self.nb_unread += 1
232                        self.show_title()
233
234        def show_title(self):
235                start = ""
236                if self.nb_unread > 1:
237                        start = "[" + str(self.nb_unread) + "] "
238                elif self.nb_unread == 1:
239                        start = "* "
240                self.window.set_title(start + self.user.name + " (" + self.account + ")")
241
242        def read_queue(self, q):
243                """read queue and print messages containted in it"""
244                while not q.empty():
245                        evt = q.get()
246                        self.print_conversation(evt[0], tim = evt[1])
247                        self.plugin.roster.nb_unread -= 1
248                self.plugin.roster.show_title()
249                del self.plugin.queues[self.account][self.user.jid]
250                self.plugin.roster.redraw_jid(self.user.jid, self.account)
251                self.plugin.systray.remove_jid(self.user.jid, self.account)
252                showOffline = self.plugin.config['showoffline']
253                if (self.user.show == 'offline' or self.user.show == 'error') and \
254                        not showOffline:
255                        if len(self.plugin.roster.contacts[self.account][self.user.jid]) == 1:
256                                self.plugin.roster.remove_user(self.user, self.account)
257
258        def on_msg_key_press_event(self, widget, event):
259                """When a key is pressed :
260                if enter is pressed without the shit key, message (if not empty) is sent
261                and printed in the conversation"""
262                if event.keyval == gtk.keysyms.Return:
263                        if (event.state & gtk.gdk.SHIFT_MASK):
264                                return 0
265                        txt_buffer = widget.get_buffer()
266                        start_iter = txt_buffer.get_start_iter()
267                        end_iter = txt_buffer.get_end_iter()
268                        txt = txt_buffer.get_text(start_iter, end_iter, 0)
269                        if txt != '':
270                                keyID = ''
271                                if self.xml.get_widget('toggle_gpg').get_active():
272                                        keyID = self.keyID
273                                self.plugin.send('MSG', self.account, (self.user.jid, txt, keyID))
274                                txt_buffer.set_text('', -1)
275                                self.print_conversation(txt, self.user.jid)
276                                widget.grab_focus()
277                        return 1
278                return 0
279
280        def on_clear(self, widget):
281                """When clear button is pressed :
282                clear the conversation"""
283                buffer = self.xml.get_widget('conversation').get_buffer()
284                deb, end = buffer.get_bounds()
285                buffer.delete(deb, end)
286
287        def on_history(self, widget):
288                """When history button is pressed : call log window"""
289                if not self.plugin.windows['logs'].has_key(self.user.jid):
290                        self.plugin.windows['logs'][self.user.jid] = log_Window(self.plugin, self.user.jid)
291
292        def on_focus(self, widget, event):
293                """When window get focus"""
294                self.plugin.systray.remove_jid(self.user.jid, self.account)
295                if self.nb_unread > 0:
296                        self.nb_unread = 0
297                        self.show_title()
298       
299        def __init__(self, user, plugin, account):
300                self.user = user
301                self.plugin = plugin
302                self.account = account
303                self.keyID = self.user.keyID
304                self.nb_unread = 0
305                self.xml = gtk.glade.XML(GTKGUI_GLADE, 'Chat', APP)
306                self.window = self.xml.get_widget('Chat')
307                self.show_title()
308                self.img = self.xml.get_widget('image')
309                image = self.plugin.roster.pixbufs[user.show]
310                if image.get_storage_type() == gtk.IMAGE_ANIMATION:
311                        self.img.set_from_animation(image.get_animation())
312                elif image.get_storage_type() == gtk.IMAGE_PIXBUF:
313                        self.img.set_from_pixbuf(image.get_pixbuf())
314                self.xml.get_widget('button_contact').set_label(user.name + ' <'\
315                        + user.jid + '>')
316                self.xml.get_widget('button_contact').set_resize_mode(gtk.RESIZE_QUEUE)
317                if not self.keyID:
318                        self.xml.get_widget('toggle_gpg').set_sensitive(False)
319                message = self.xml.get_widget('message')
320                message.grab_focus()
321                conversation = self.xml.get_widget('conversation')
322                buffer = conversation.get_buffer()
323                end_iter = buffer.get_end_iter()
324                buffer.create_mark('end', end_iter, 0)
325                self.xml.signal_connect('gtk_widget_destroy', self.delete_event)
326                self.xml.signal_connect('on_clear_clicked', self.on_clear)
327                self.xml.signal_connect('on_focus', self.on_focus)
328                self.xml.signal_connect('on_history_clicked', self.on_history)
329                self.xml.signal_connect('on_msg_key_press_event', \
330                        self.on_msg_key_press_event)
331                self.tagIn = buffer.create_tag("incoming")
332                color = self.plugin.config['inmsgcolor']
333                self.tagIn.set_property("foreground", color)
334                self.tagOut = buffer.create_tag("outgoing")
335                color = self.plugin.config['outmsgcolor']
336                self.tagOut.set_property("foreground", color)
337                self.tagStatus = buffer.create_tag("status")
338                color = self.plugin.config['statusmsgcolor']
339                self.tagStatus.set_property("foreground", color)
340                #print queued messages
341                if plugin.queues[account].has_key(user.jid):
342                        self.read_queue(plugin.queues[account][user.jid])
343                if self.user.show != 'online':
344                        self.print_conversation(_("%s is now %s (%s)") % (user.name, \
345                                user.show, user.status), 'status')
346
347class gc:
348        def delete_event(self, widget):
349                """close window"""
350                self.plugin.send('GC_STATUS', self.account, (self.nick, self.jid,\
351                        'offline', 'offline'))
352                del self.plugin.windows[self.account]['gc'][self.jid]
353
354        def on_close(self, widget):
355                """When Cancel button is clicked"""
356                widget.get_toplevel().destroy()
357
358        def get_role_iter(self, name):
359                model = self.tree.get_model()
360                fin = False
361                iter = model.get_iter_root()
362                if not iter:
363                        return None
364                while not fin:
365                        account_name = model.get_value(iter, 1)
366                        if name == account_name:
367                                return iter
368                        iter = model.iter_next(iter)
369                        if not iter:
370                                fin = True
371                return None
372
373        def get_user_iter(self, jid):
374                model = self.tree.get_model()
375                fin = False
376                role = model.get_iter_root()
377                if not role:
378                        return None
379                while not fin:
380                        fin2 = False
381                        user = model.iter_children(role)
382                        if not user:
383                                fin2=True
384                        while not fin2:
385                                if jid == model.get_value(user, 1):
386                                        return user
387                                user = model.iter_next(user)
388                                if not user:
389                                        fin2 = True
390                        role = model.iter_next(role)
391                        if not role:
392                                fin = True
393                return None
394
395        def remove_user(self, nick):
396                """Remove a user from the roster"""
397                model = self.tree.get_model()
398                iter = self.get_user_iter(nick)
399                parent_iter = model.iter_parent(iter)
400                model.remove(iter)
401                if model.iter_n_children(parent_iter) == 0:
402                        model.remove(parent_iter)
403       
404        def add_user_to_roster(self, nick, show, role):
405                model = self.tree.get_model()
406                img = self.plugin.roster.pixbufs[show]
407                role_iter = self.get_role_iter(role)
408                if not role_iter:
409                        role_iter = model.append(None, (self.plugin.roster.pixbufs['closed']\
410                                , role))
411                iter = model.append(role_iter, (img, nick))
412                self.tree.expand_row((model.get_path(role_iter)), False)
413                return iter
414       
415        def get_role(self, jid_iter):
416                model = self.tree.get_model()
417                path = model.get_path(jid_iter)[0]
418                iter = model.get_iter(path)
419                return model.get_value(iter, 1)
420
421        def chg_user_status(self, nick, show, status, role, affiliation, jid, \
422                reason, actor, statusCode, account):
423                """When a user change his status"""
424                model = self.tree.get_model()
425                if show == 'offline' or show == 'error':
426                        if statusCode == '307':
427                                self.print_conversation(_("%s has been kicked by %s: %s") % (nick, \
428                                        actor, reason))
429                        self.remove_user(nick)
430                else:
431                        iter = self.get_user_iter(nick)
432                        if not iter:
433                                iter = self.add_user_to_roster(nick, show, role)
434                        else:
435                                actual_role = self.get_role(iter)
436                                if role != actual_role:
437                                        self.remove_user(nick)
438                                        self.add_user_to_roster(nick, show, role)
439                                else:
440                                        img = self.plugin.roster.pixbufs[show]
441                                        model.set_value(iter, 0, img)
442
443        def on_msg_key_press_event(self, widget, event):
444                """When a key is pressed :
445                if enter is pressed without the shit key, message (if not empty) is sent
446                and printed in the conversation"""
447                if event.keyval == gtk.keysyms.Return:
448                        if (event.state & gtk.gdk.SHIFT_MASK):
449                                return 0
450                        txt_buffer = widget.get_buffer()
451                        start_iter = txt_buffer.get_start_iter()
452                        end_iter = txt_buffer.get_end_iter()
453                        txt = txt_buffer.get_text(start_iter, end_iter, 0)
454                        if txt != '':
455                                self.plugin.send('GC_MSG', self.account, (self.jid, txt))
456                                txt_buffer.set_text('', -1)
457                                widget.grab_focus()
458                        return 1
459                return 0
460
461        def print_conversation(self, txt, contact = None, tim = None):
462                """Print a line in the conversation :
463                if contact is set : it's a message from someone
464                if contact is not set : it's a message from the server"""
465                conversation = self.xml.get_widget('conversation')
466                buffer = conversation.get_buffer()
467                if not txt:
468                        txt = ""
469                end_iter = buffer.get_end_iter()
470                if not tim:
471                        tim = time.localtime()
472                tims = time.strftime("[%H:%M:%S]", tim)
473                buffer.insert(end_iter, tims)
474                if contact:
475                        if contact == self.nick:
476                                buffer.insert_with_tags_by_name(end_iter, '<'+contact+'> ', \
477                                        'outgoing')
478                        else:
479                                buffer.insert_with_tags_by_name(end_iter, '<' + contact + '> ', \
480                                        'incoming')
481                        buffer.insert(end_iter, txt+'\n')
482                else:
483                        buffer.insert_with_tags_by_name(end_iter, txt+'\n', \
484                                'status')
485                #scroll to the end of the textview
486                conversation.scroll_to_mark(buffer.get_mark('end'), 0.1, 0, 0, 0)
487
488        def kick(self, widget, room_jid, nick):
489                """kick a user"""
490                self.plugin.send('SET_ROLE', self.account, (room_jid, nick, 'none'))
491
492        def grant_voice(self, widget, room_jid, nick):
493                """grant voice privilege to a user"""
494                self.plugin.send('SET_ROLE', self.account, (room_jid, nick, \
495                        'participant'))
496
497        def revoke_voice(self, widget, room_jid, nick):
498                """revoke voice privilege to a user"""
499                self.plugin.send('SET_ROLE', self.account, (room_jid, nick, 'visitor'))
500
501        def grant_moderator(self, widget, room_jid, nick):
502                """grant moderator privilege to a user"""
503                self.plugin.send('SET_ROLE', self.account, (room_jid, nick, 'moderator'))
504
505        def revoke_moderator(self, widget, room_jid, nick):
506                """revoke moderator privilege to a user"""
507                self.plugin.send('SET_ROLE', self.account, (room_jid, nick, \
508                        'participant'))
509
510        def ban(self, widget, room_jid, nick):
511                """ban a user"""
512                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
513                        'outcast'))
514
515        def grant_membership(self, widget, room_jid, nick):
516                """grant membership privilege to a user"""
517                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
518                        'member'))
519
520        def revoke_membership(self, widget, room_jid, nick):
521                """revoke membership privilege to a user"""
522                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
523                        'none'))
524
525        def grant_admin(self, widget, room_jid, nick):
526                """grant administrative privilege to a user"""
527                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
528                        'admin'))
529
530        def revoke_admin(self, widget, room_jid, nick):
531                """revoke administrative privilege to a user"""
532                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
533                        'member'))
534
535        def grant_owner(self, widget, room_jid, nick):
536                """grant owner privilege to a user"""
537                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
538                        'owner'))
539
540        def revoke_owner(self, widget, room_jid, nick):
541                """revoke owner privilege to a user"""
542                self.plugin.send('SET_AFFILIATION', self.account, (room_jid, nick, \
543                        'admin'))
544
545        def mk_menu(self, event, iter):
546                """Make user's popup menu"""
547                model = self.tree.get_model()
548                nick = model.get_value(iter,