2 ## src/profile_window.py
4 ## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
5 ## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
6 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
8 ## This file is part of Gajim.
10 ## Gajim 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 3 only.
14 ## Gajim 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.
19 ## You should have received a copy of the GNU General Public License
20 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
23 # THIS FILE IS FOR **OUR** PROFILE (when we edit our INFO)
36 from common
import gajim
37 from common
import ged
42 Class for our information window
45 def __init__(self
, account
):
46 self
.xml
= gtkgui_helpers
.get_gtk_builder('profile_window.ui')
47 self
.window
= self
.xml
.get_object('profile_window')
48 self
.window
.set_transient_for(gajim
.interface
.roster
.window
)
49 self
.progressbar
= self
.xml
.get_object('progressbar')
50 self
.statusbar
= self
.xml
.get_object('statusbar')
51 self
.context_id
= self
.statusbar
.get_context_id('profile')
53 self
.account
= account
54 self
.jid
= gajim
.get_jid_from_account(account
)
57 self
.avatar_mime_type
= None
58 self
.avatar_encoded
= None
59 self
.message_id
= self
.statusbar
.push(self
.context_id
,
60 _('Retrieving profile...'))
61 self
.update_progressbar_timeout_id
= gobject
.timeout_add(100,
62 self
.update_progressbar
)
63 self
.remove_statusbar_timeout_id
= None
65 # Create Image for avatar button
67 self
.xml
.get_object('PHOTO_button').set_image(image
)
68 self
.xml
.connect_signals(self
)
69 gajim
.ged
.register_event_handler('vcard-published', ged
.GUI1
,
70 self
._nec
_vcard
_published
)
71 gajim
.ged
.register_event_handler('vcard-not-published', ged
.GUI1
,
72 self
._nec
_vcard
_not
_published
)
73 gajim
.ged
.register_event_handler('vcard-received', ged
.GUI1
,
74 self
._nec
_vcard
_received
)
75 self
.window
.show_all()
77 def update_progressbar(self
):
78 self
.progressbar
.pulse()
79 return True # loop forever
81 def remove_statusbar(self
, message_id
):
82 self
.statusbar
.remove_message(self
.context_id
, message_id
)
83 self
.remove_statusbar_timeout_id
= None
85 def on_profile_window_destroy(self
, widget
):
86 if self
.update_progressbar_timeout_id
is not None:
87 gobject
.source_remove(self
.update_progressbar_timeout_id
)
88 if self
.remove_statusbar_timeout_id
is not None:
89 gobject
.source_remove(self
.remove_statusbar_timeout_id
)
90 gajim
.ged
.remove_event_handler('vcard-published', ged
.GUI1
,
91 self
._nec
_vcard
_published
)
92 gajim
.ged
.remove_event_handler('vcard-not-published', ged
.GUI1
,
93 self
._nec
_vcard
_not
_published
)
94 gajim
.ged
.remove_event_handler('vcard-received', ged
.GUI1
,
95 self
._nec
_vcard
_received
)
96 del gajim
.interface
.instances
[self
.account
]['profile']
97 if self
.dialog
: # Image chooser dialog
100 def on_profile_window_key_press_event(self
, widget
, event
):
101 if event
.keyval
== gtk
.keysyms
.Escape
:
102 self
.window
.destroy()
104 def on_clear_button_clicked(self
, widget
):
106 button
= self
.xml
.get_object('PHOTO_button')
107 image
= button
.get_image()
108 image
.set_from_pixbuf(None)
110 text_button
= self
.xml
.get_object('NOPHOTO_button')
112 self
.avatar_encoded
= None
113 self
.avatar_mime_type
= None
115 def on_set_avatar_button_clicked(self
, widget
):
116 def on_ok(widget
, path_to_file
):
118 filesize
= os
.path
.getsize(path_to_file
) # in bytes
121 if os
.path
.isfile(path_to_file
):
122 stat
= os
.stat(path_to_file
)
125 msg
= _('File is empty')
128 msg
= _('File does not exist')
129 if not invalid_file
and filesize
> 16384: # 16 kb
131 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(path_to_file
)
132 # get the image at 'notification size'
133 # and hope that user did not specify in ACE crazy size
134 scaled_pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
,
136 except gobject
.GError
, msg
: # unknown format
137 # msg should be string, not object instance
141 if True: # keep identation
142 dialogs
.ErrorDialog(_('Could not load image'), msg
)
146 path_to_file
= os
.path
.join(gajim
.TMP
,
148 scaled_pixbuf
.save(path_to_file
, 'png')
151 fd
= open(path_to_file
, 'rb')
153 pixbuf
= gtkgui_helpers
.get_pixbuf_from_data(data
)
156 pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
, 'vcard')
157 except AttributeError: # unknown format
158 dialogs
.ErrorDialog(_('Could not load image'))
160 self
.dialog
.destroy()
162 button
= self
.xml
.get_object('PHOTO_button')
163 image
= button
.get_image()
164 image
.set_from_pixbuf(pixbuf
)
166 text_button
= self
.xml
.get_object('NOPHOTO_button')
168 self
.avatar_encoded
= base64
.encodestring(data
)
169 # returns None if unknown type
170 self
.avatar_mime_type
= mimetypes
.guess_type(path_to_file
)[0]
173 os
.remove(path_to_file
)
175 gajim
.log
.debug('Cannot remove %s' % path_to_file
)
177 def on_clear(widget
):
178 self
.dialog
.destroy()
180 self
.on_clear_button_clicked(widget
)
182 def on_cancel(widget
):
183 self
.dialog
.destroy()
187 self
.dialog
.present()
189 self
.dialog
= dialogs
.AvatarChooserDialog(on_response_ok
= on_ok
,
190 on_response_cancel
= on_cancel
, on_response_clear
= on_clear
)
192 def on_PHOTO_button_press_event(self
, widget
, event
):
194 If right-clicked, show popup
196 if event
.button
== 3 and self
.avatar_encoded
: # right click
200 pixbuf
= gtkgui_helpers
.get_avatar_pixbuf_from_cache(self
.jid
,
203 if pixbuf
not in (None, 'ask'):
204 nick
= gajim
.config
.get_per('accounts', self
.account
, 'name')
205 menuitem
= gtk
.ImageMenuItem(gtk
.STOCK_SAVE_AS
)
206 menuitem
.connect('activate',
207 gtkgui_helpers
.on_avatar_save_as_menuitem_activate
,
209 menu
.append(menuitem
)
211 menuitem
= gtk
.ImageMenuItem(gtk
.STOCK_CLEAR
)
212 menuitem
.connect('activate', self
.on_clear_button_clicked
)
213 menu
.append(menuitem
)
214 menu
.connect('selection-done', lambda w
:w
.destroy())
217 menu
.popup(None, None, None, event
.button
, event
.time
)
218 elif event
.button
== 1: # left click
219 self
.on_set_avatar_button_clicked(widget
)
221 def on_BDAY_entry_focus_out_event(self
, widget
, event
):
222 txt
= widget
.get_text()
226 time
.strptime(txt
, '%Y-%m-%d')
228 if not widget
.is_focus():
229 pritext
= _('Wrong date format')
230 dialogs
.ErrorDialog(pritext
, _('Format of the date must be '
232 gobject
.idle_add(lambda: widget
.grab_focus())
235 def set_value(self
, entry_name
, value
):
237 self
.xml
.get_object(entry_name
).set_text(value
)
238 except AttributeError:
241 def set_values(self
, vcard_
):
242 button
= self
.xml
.get_object('PHOTO_button')
243 image
= button
.get_image()
244 text_button
= self
.xml
.get_object('NOPHOTO_button')
245 if not 'PHOTO' in vcard_
:
247 image
.set_from_pixbuf(None)
250 for i
in vcard_
.keys():
252 pixbuf
, self
.avatar_encoded
, self
.avatar_mime_type
= \
253 vcard
.get_avatar_pixbuf_encoded_mime(vcard_
[i
])
255 image
.set_from_pixbuf(None)
259 pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
, 'vcard')
260 image
.set_from_pixbuf(pixbuf
)
264 if i
== 'ADR' or i
== 'TEL' or i
== 'EMAIL':
265 for entry
in vcard_
[i
]:
269 for j
in entry
.keys():
270 self
.set_value(i
+ add_on
+ '_' + j
+ '_entry', entry
[j
])
271 if isinstance(vcard_
[i
], dict):
272 for j
in vcard_
[i
].keys():
273 self
.set_value(i
+ '_' + j
+ '_entry', vcard_
[i
][j
])
276 self
.xml
.get_object('DESC_textview').get_buffer().set_text(
279 self
.set_value(i
+ '_entry', vcard_
[i
])
280 if self
.update_progressbar_timeout_id
is not None:
282 self
.statusbar
.remove_message(self
.context_id
, self
.message_id
)
283 self
.message_id
= self
.statusbar
.push(self
.context_id
,
284 _('Information received'))
285 self
.remove_statusbar_timeout_id
= gobject
.timeout_add_seconds(3,
286 self
.remove_statusbar
, self
.message_id
)
287 gobject
.source_remove(self
.update_progressbar_timeout_id
)
288 self
.progressbar
.hide()
289 self
.progressbar
.set_fraction(0)
290 self
.update_progressbar_timeout_id
= None
292 def _nec_vcard_received(self
, obj
):
293 if obj
.conn
.name
!= self
.account
:
295 if obj
.jid
!= self
.jid
:
297 self
.set_values(obj
.vcard_dict
)
299 def add_to_vcard(self
, vcard_
, entry
, txt
):
301 Add an information to the vCard dictionary
303 entries
= entry
.split('_')
305 if len(entries
) == 3: # We need to use lists
306 if entries
[0] not in loc
:
309 for e
in loc
[entries
[0]]:
314 loc
[entries
[0]].append({entries
[1]: '', entries
[2]: txt
})
316 while len(entries
) > 1:
317 if entries
[0] not in loc
:
319 loc
= loc
[entries
[0]]
321 loc
[entries
[0]] = txt
324 def make_vcard(self
):
326 Make the vCard dictionary
328 entries
= ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
329 'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
330 'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
331 'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
332 'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
333 'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
334 'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
337 txt
= self
.xml
.get_object(e
+ '_entry').get_text().decode('utf-8')
339 vcard_
= self
.add_to_vcard(vcard_
, e
, txt
)
342 buff
= self
.xml
.get_object('DESC_textview').get_buffer()
343 start_iter
= buff
.get_start_iter()
344 end_iter
= buff
.get_end_iter()
345 txt
= buff
.get_text(start_iter
, end_iter
, 0)
347 vcard_
['DESC'] = txt
.decode('utf-8')
350 if self
.avatar_encoded
:
351 vcard_
['PHOTO'] = {'BINVAL': self
.avatar_encoded
}
352 if self
.avatar_mime_type
:
353 vcard_
['PHOTO']['TYPE'] = self
.avatar_mime_type
356 def on_ok_button_clicked(self
, widget
):
357 if self
.update_progressbar_timeout_id
:
358 # Operation in progress
360 if gajim
.connections
[self
.account
].connected
< 2:
361 dialogs
.ErrorDialog(_('You are not connected to the server'),
362 _('Without a connection you can not publish your contact '
365 vcard_
= self
.make_vcard()
367 if 'NICKNAME' in vcard_
:
368 nick
= vcard_
['NICKNAME']
369 gajim
.connections
[self
.account
].send_nickname(nick
)
371 nick
= gajim
.config
.get_per('accounts', self
.account
, 'name')
372 gajim
.nicks
[self
.account
] = nick
373 gajim
.connections
[self
.account
].send_vcard(vcard_
)
374 self
.message_id
= self
.statusbar
.push(self
.context_id
,
375 _('Sending profile...'))
376 self
.progressbar
.show()
377 self
.update_progressbar_timeout_id
= gobject
.timeout_add(100,
378 self
.update_progressbar
)
380 def _nec_vcard_published(self
, obj
):
381 if obj
.conn
.name
!= self
.account
:
383 if self
.update_progressbar_timeout_id
is not None:
384 gobject
.source_remove(self
.update_progressbar_timeout_id
)
385 self
.update_progressbar_timeout_id
= None
386 self
.window
.destroy()
388 def _nec_vcard_not_published(self
, obj
):
389 if obj
.conn
.name
!= self
.account
:
392 self
.statusbar
.remove_message(self
.context_id
, self
.message_id
)
393 self
.message_id
= self
.statusbar
.push(self
.context_id
,
394 _('Information NOT published'))
395 self
.remove_statusbar_timeout_id
= gobject
.timeout_add_seconds(3,
396 self
.remove_statusbar
, self
.message_id
)
397 if self
.update_progressbar_timeout_id
is not None:
398 gobject
.source_remove(self
.update_progressbar_timeout_id
)
399 self
.progressbar
.set_fraction(0)
400 self
.update_progressbar_timeout_id
= None
401 dialogs
.InformationDialog(_('vCard publication failed'),
402 _('There was an error while publishing your personal information, '
405 def on_cancel_button_clicked(self
, widget
):
406 self
.window
.destroy()