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

Revision 7940, 11.7 kB (checked in by asterix, 20 months ago)

merge diff from trunk

Line 
1##      profile_window.py
2##
3## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
4## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
5##
6## This program is free software; you can redistribute it and/or modify
7## it under the terms of the GNU General Public License as published
8## by the Free Software Foundation; version 2 only.
9##
10## This program is distributed in the hope that it will be useful,
11## but WITHOUT ANY WARRANTY; without even the implied warranty of
12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13## GNU General Public License for more details.
14##
15
16# THIS FILE IS FOR **OUR** PROFILE (when we edit our INFO)
17
18import gtk
19import gobject
20import base64
21import mimetypes
22import os
23
24import gtkgui_helpers
25import dialogs
26
27from common import gajim
28from common.i18n import Q_
29
30def get_avatar_pixbuf_encoded_mime(photo):
31        '''return the pixbuf of the image
32        photo is a dictionary containing PHOTO information'''
33        if not isinstance(photo, dict):
34                return None, None, None
35        img_decoded = None
36        avatar_encoded = None
37        avatar_mime_type = None
38        if photo.has_key('BINVAL'):
39                img_encoded = photo['BINVAL']
40                avatar_encoded = img_encoded
41                try:
42                        img_decoded = base64.decodestring(img_encoded)
43                except:
44                        pass
45        if img_decoded:
46                if photo.has_key('TYPE'):
47                        avatar_mime_type = photo['TYPE']
48                        pixbuf = gtkgui_helpers.get_pixbuf_from_data(img_decoded)
49                else:
50                        pixbuf, avatar_mime_type = gtkgui_helpers.get_pixbuf_from_data(
51                                                        img_decoded, want_type=True)
52        else:
53                pixbuf = None
54        return pixbuf, avatar_encoded, avatar_mime_type
55
56class ProfileWindow:
57        '''Class for our information window'''
58
59        def __init__(self, account):
60                self.xml = gtkgui_helpers.get_glade('profile_window.glade')
61                self.window = self.xml.get_widget('profile_window')
62                self.progressbar = self.xml.get_widget('progressbar')
63                self.statusbar = self.xml.get_widget('statusbar')
64                self.context_id = self.statusbar.get_context_id('profile')
65
66                self.account = account
67                self.jid = gajim.get_jid_from_account(account)
68
69                self.avatar_mime_type = None
70                self.avatar_encoded = None
71                self.message_id = self.statusbar.push(self.context_id,
72                        _('Retrieving profile...'))
73                self.update_progressbar_timeout_id = gobject.timeout_add(100,
74                        self.update_progressbar)
75                self.remove_statusbar_timeout_id = None
76
77                # Create Image for avatar button
78                image = gtk.Image()
79                self.xml.get_widget('PHOTO_button').set_image(image)
80                text_button = self.xml.get_widget('NOPHOTO_button')
81                # We use 2 buttons because some GTK theme don't show images in buttons
82                text_button.set_no_show_all(True)
83                text_button.hide()
84                self.xml.signal_autoconnect(self)
85                self.window.show_all()
86
87        def update_progressbar(self):
88                self.progressbar.pulse()
89                return True # loop forever
90
91        def remove_statusbar(self, message_id):
92                self.statusbar.remove(self.context_id, message_id)
93                self.remove_statusbar_timeout_id = None
94
95        def on_profile_window_destroy(self, widget):
96                if self.update_progressbar_timeout_id is not None:
97                        gobject.source_remove(self.update_progressbar_timeout_id)
98                if self.remove_statusbar_timeout_id is not None:
99                        gobject.source_remove(self.remove_statusbar_timeout_id)
100                del gajim.interface.instances[self.account]['profile']
101
102        def on_profile_window_key_press_event(self, widget, event):
103                if event.keyval == gtk.keysyms.Escape:
104                        self.window.destroy()
105
106        def on_clear_button_clicked(self, widget):
107                # empty the image
108                button = self.xml.get_widget('PHOTO_button')
109                image = button.get_image()
110                image.set_from_pixbuf(None)
111                button.hide()
112                text_button = self.xml.get_widget('NOPHOTO_button')
113                text_button.show()
114                self.avatar_encoded = None
115                self.avatar_mime_type = None
116
117        def on_set_avatar_button_clicked(self, widget):
118                def on_ok(widget, path_to_file):
119                        must_delete = False
120                        filesize = os.path.getsize(path_to_file) # in bytes
121                        #FIXME: use messages for invalid file for 0.11
122                        invalid_file = False
123                        msg = ''
124                        if os.path.isfile(path_to_file):
125                                stat = os.stat(path_to_file)
126                                if stat[6] == 0:
127                                        invalid_file = True
128                        else:
129                                invalid_file = True
130                        if not invalid_file and filesize > 16384: # 16 kb
131                                try:
132                                        pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
133                                        # get the image at 'notification size'
134                                        # and use that user did not specify in ACE crazy size
135                                        scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf,
136                                                'tooltip')
137                                except gobject.GError, msg: # unknown format
138                                        # msg should be string, not object instance
139                                        msg = str(msg)
140                                        invalid_file = True
141                        if invalid_file:
142                                if True: # keep identation
143                                        dialogs.ErrorDialog(_('Could not load image'), msg)
144                                        return
145                        if filesize > 16384:
146                                        if scaled_pixbuf:
147                                                path_to_file = os.path.join(gajim.TMP,
148                                                        'avatar_scaled.png')
149                                                scaled_pixbuf.save(path_to_file, 'png')
150                                                must_delete = True
151                        self.dialog.destroy()
152
153                        fd = open(path_to_file, 'rb')
154                        data = fd.read()
155                        pixbuf = gtkgui_helpers.get_pixbuf_from_data(data)
156                        # rescale it
157                        pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
158                        button = self.xml.get_widget('PHOTO_button')
159                        image = button.get_image()
160                        image.set_from_pixbuf(pixbuf)
161                        button.show()
162                        text_button = self.xml.get_widget('NOPHOTO_button')
163                        text_button.hide()
164                        self.avatar_encoded = base64.encodestring(data)
165                        # returns None if unknown type
166                        self.avatar_mime_type = mimetypes.guess_type(path_to_file)[0]
167                        if must_delete:
168                                try:
169                                        os.remove(path_to_file)
170                                except OSError:
171                                        gajim.log.debug('Cannot remove %s' % path_to_file)
172
173                def on_clear(widget):
174                        self.dialog.destroy()
175                        self.on_clear_button_clicked(widget)
176
177                self.dialog = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
178                        on_response_clear = on_clear)
179
180        def on_PHOTO_button_press_event(self, widget, event):
181                '''If right-clicked, show popup'''
182                if event.button == 3 and self.avatar_encoded: # right click
183                        menu = gtk.Menu()
184                       
185                        # Try to get pixbuf
186                        pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid)
187
188                        if pixbuf:
189                                nick = gajim.config.get_per('accounts', self.account, 'name')
190                                menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
191                                menuitem.connect('activate',
192                                        gtkgui_helpers.on_avatar_save_as_menuitem_activate,
193                                        self.jid, None, nick + '.jpeg')
194                                menu.append(menuitem)
195                        # show clear
196                        menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
197                        menuitem.connect('activate', self.on_clear_button_clicked)
198                        menu.append(menuitem)
199                        menu.connect('selection-done', lambda w:w.destroy())   
200                        # show the menu
201                        menu.show_all()
202                        menu.popup(None, None, None, event.button, event.time)
203                elif event.button == 1: # left click
204                        self.on_set_avatar_button_clicked(widget)
205
206        def set_value(self, entry_name, value):
207                try:
208                        self.xml.get_widget(entry_name).set_text(value)
209                except AttributeError:
210                        pass
211
212        def set_values(self, vcard):
213                button = self.xml.get_widget('PHOTO_button')
214                image = button.get_image()
215                text_button = self.xml.get_widget('NOPHOTO_button')
216                if not 'PHOTO' in vcard:
217                        # set default image
218                        image.set_from_pixbuf(None)
219                        button.hide()
220                        text_button.show()
221                for i in vcard.keys():
222                        if i == 'PHOTO':
223                                pixbuf, self.avatar_encoded, self.avatar_mime_type = \
224                                        get_avatar_pixbuf_encoded_mime(vcard[i])
225                                if not pixbuf:
226                                        image.set_from_pixbuf(None)
227                                        button.hide()
228                                        text_button.show()
229                                        continue
230                                pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
231                                image.set_from_pixbuf(pixbuf)
232                                button.show()
233                                text_button.hide()
234                                continue
235                        if i == 'ADR' or i == 'TEL' or i == 'EMAIL':
236                                for entry in vcard[i]:
237                                        add_on = '_HOME'
238                                        if 'WORK' in entry:
239                                                add_on = '_WORK'
240                                        for j in entry.keys():
241                                                self.set_value(i + add_on + '_' + j + '_entry', entry[j])
242                        if isinstance(vcard[i], dict):
243                                for j in vcard[i].keys():
244                                        self.set_value(i + '_' + j + '_entry', vcard[i][j])
245                        else:
246                                if i == 'DESC':
247                                        self.xml.get_widget('DESC_textview').get_buffer().set_text(
248                                                vcard[i], 0)
249                                else:
250                                        self.set_value(i + '_entry', vcard[i])
251                if self.update_progressbar_timeout_id is not None:
252                        if self.message_id:
253                                self.statusbar.remove(self.context_id, self.message_id)
254                        self.message_id = self.statusbar.push(self.context_id,
255                                _('Information received'))
256                        self.remove_statusbar_timeout_id = gobject.timeout_add(3000,
257                                self.remove_statusbar, self.message_id)
258                        gobject.source_remove(self.update_progressbar_timeout_id)
259                        self.progressbar.hide()
260                        self.progressbar.set_fraction(0)
261                        self.update_progressbar_timeout_id = None
262
263        def add_to_vcard(self, vcard, entry, txt):
264                '''Add an information to the vCard dictionary'''
265                entries = entry.split('_')
266                loc = vcard
267                if len(entries) == 3: # We need to use lists
268                        if not loc.has_key(entries[0]):
269                                loc[entries[0]] = []
270                        found = False
271                        for e in loc[entries[0]]:
272                                if entries[1] in e:
273                                        found = True
274                                        break
275                        if found:
276                                e[entries[2]] = txt
277                        else:
278                                loc[entries[0]].append({entries[1]: '', entries[2]: txt})
279                        return vcard
280                while len(entries) > 1:
281                        if not loc.has_key(entries[0]):
282                                loc[entries[0]] = {}
283                        loc = loc[entries[0]]
284                        del entries[0]
285                loc[entries[0]] = txt
286                return vcard
287
288        def make_vcard(self):
289                '''make the vCard dictionary'''
290                entries = ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
291                        'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
292                        'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
293                        'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
294                        'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
295                        'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
296                        'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
297                vcard = {}
298                for e in entries:
299                        txt = self.xml.get_widget(e + '_entry').get_text().decode('utf-8')
300                        if txt != '':
301                                vcard = self.add_to_vcard(vcard, e, txt)
302
303                # DESC textview
304                buff = self.xml.get_widget('DESC_textview').get_buffer()
305                start_iter = buff.get_start_iter()
306                end_iter = buff.get_end_iter()
307                txt = buff.get_text(start_iter, end_iter, 0)
308                if txt != '':
309                        vcard['DESC'] = txt.decode('utf-8')
310
311                # Avatar
312                if self.avatar_encoded:
313                        vcard['PHOTO'] = {'BINVAL': self.avatar_encoded}
314                        if self.avatar_mime_type:
315                                vcard['PHOTO']['TYPE'] = self.avatar_mime_type
316                return vcard
317
318        def on_ok_button_clicked(self, widget):
319                if self.update_progressbar_timeout_id:
320                        # Operation in progress
321                        return
322                if gajim.connections[self.account].connected < 2:
323                        dialogs.ErrorDialog(_('You are not connected to the server'),
324                                _('Without a connection you can not publish your contact '
325                                'information.'))
326                        return
327                vcard = self.make_vcard()
328                nick = ''
329                if vcard.has_key('NICKNAME'):
330                        nick = vcard['NICKNAME']
331                if nick == '':
332                        nick = gajim.config.get_per('accounts', self.account, 'name')
333                gajim.nicks[self.account] = nick
334                gajim.connections[self.account].send_vcard(vcard)
335                self.message_id = self.statusbar.push(self.context_id,
336                        _('Sending profile...'))
337                self.progressbar.show()
338                self.update_progressbar_timeout_id = gobject.timeout_add(100,
339                        self.update_progressbar)
340
341        def vcard_published(self):
342                if self.update_progressbar_timeout_id is not None:
343                        gobject.source_remove(self.update_progressbar_timeout_id)
344                        self.update_progressbar_timeout_id = None
345                self.window.destroy()
346
347        def vcard_not_published(self):
348                if self.message_id:
349                        self.statusbar.remove(self.context_id, self.message_id)
350                self.message_id = self.statusbar.push(self.context_id,
351                        _('Information NOT published'))
352                self.remove_statusbar_timeout_id = gobject.timeout_add(3000,
353                        self.remove_statusbar, self.message_id)
354                if self.update_progressbar_timeout_id is not None:
355                        gobject.source_remove(self.update_progressbar_timeout_id)
356                        self.progressbar.set_fraction(0)
357                        self.update_progressbar_timeout_id = None
358                dialogs.InformationDialog(_('vCard publication failed'),
359                        _('There was an error while publishing your personal information, '
360                        'try again later.'))
361
362        def on_cancel_button_clicked(self, widget):
363                self.window.destroy()
Note: See TracBrowser for help on using the browser.