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
.progressbar
= self
.xml
.get_object('progressbar')
49 self
.statusbar
= self
.xml
.get_object('statusbar')
50 self
.context_id
= self
.statusbar
.get_context_id('profile')
52 self
.account
= account
53 self
.jid
= gajim
.get_jid_from_account(account
)
56 self
.avatar_mime_type
= None
57 self
.avatar_encoded
= None
58 self
.message_id
= self
.statusbar
.push(self
.context_id
,
59 _('Retrieving profile...'))
60 self
.update_progressbar_timeout_id
= gobject
.timeout_add(100,
61 self
.update_progressbar
)
62 self
.remove_statusbar_timeout_id
= None
64 # Create Image for avatar button
66 self
.xml
.get_object('PHOTO_button').set_image(image
)
67 self
.xml
.connect_signals(self
)
68 gajim
.ged
.register_event_handler('vcard-published', ged
.GUI1
,
69 self
._nec
_vcard
_published
)
70 gajim
.ged
.register_event_handler('vcard-not-published', ged
.GUI1
,
71 self
._nec
_vcard
_not
_published
)
72 gajim
.ged
.register_event_handler('vcard-received', ged
.GUI1
,
73 self
._nec
_vcard
_received
)
74 self
.window
.show_all()
76 def update_progressbar(self
):
77 self
.progressbar
.pulse()
78 return True # loop forever
80 def remove_statusbar(self
, message_id
):
81 self
.statusbar
.remove_message(self
.context_id
, message_id
)
82 self
.remove_statusbar_timeout_id
= None
84 def on_profile_window_destroy(self
, widget
):
85 if self
.update_progressbar_timeout_id
is not None:
86 gobject
.source_remove(self
.update_progressbar_timeout_id
)
87 if self
.remove_statusbar_timeout_id
is not None:
88 gobject
.source_remove(self
.remove_statusbar_timeout_id
)
89 gajim
.ged
.remove_event_handler('vcard-published', ged
.GUI1
,
90 self
._nec
_vcard
_published
)
91 gajim
.ged
.remove_event_handler('vcard-not-published', ged
.GUI1
,
92 self
._nec
_vcard
_not
_published
)
93 gajim
.ged
.remove_event_handler('vcard-received', ged
.GUI1
,
94 self
._nec
_vcard
_received
)
95 del gajim
.interface
.instances
[self
.account
]['profile']
96 if self
.dialog
: # Image chooser dialog
99 def on_profile_window_key_press_event(self
, widget
, event
):
100 if event
.keyval
== gtk
.keysyms
.Escape
:
101 self
.window
.destroy()
103 def on_clear_button_clicked(self
, widget
):
105 button
= self
.xml
.get_object('PHOTO_button')
106 image
= button
.get_image()
107 image
.set_from_pixbuf(None)
109 text_button
= self
.xml
.get_object('NOPHOTO_button')
111 self
.avatar_encoded
= None
112 self
.avatar_mime_type
= None
114 def on_set_avatar_button_clicked(self
, widget
):
115 def on_ok(widget
, path_to_file
):
117 filesize
= os
.path
.getsize(path_to_file
) # in bytes
120 if os
.path
.isfile(path_to_file
):
121 stat
= os
.stat(path_to_file
)
124 msg
= _('File is empty')
127 msg
= _('File does not exist')
128 if not invalid_file
and filesize
> 16384: # 16 kb
130 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(path_to_file
)
131 # get the image at 'notification size'
132 # and hope that user did not specify in ACE crazy size
133 scaled_pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
,
135 except gobject
.GError
, msg
: # unknown format
136 # msg should be string, not object instance
140 if True: # keep identation
141 dialogs
.ErrorDialog(_('Could not load image'), msg
)
145 path_to_file
= os
.path
.join(gajim
.TMP
,
147 scaled_pixbuf
.save(path_to_file
, 'png')
150 fd
= open(path_to_file
, 'rb')
152 pixbuf
= gtkgui_helpers
.get_pixbuf_from_data(data
)
155 pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
, 'vcard')
156 except AttributeError: # unknown format
157 dialogs
.ErrorDialog(_('Could not load image'))
159 self
.dialog
.destroy()
161 button
= self
.xml
.get_object('PHOTO_button')
162 image
= button
.get_image()
163 image
.set_from_pixbuf(pixbuf
)
165 text_button
= self
.xml
.get_object('NOPHOTO_button')
167 self
.avatar_encoded
= base64
.encodestring(data
)
168 # returns None if unknown type
169 self
.avatar_mime_type
= mimetypes
.guess_type(path_to_file
)[0]
172 os
.remove(path_to_file
)
174 gajim
.log
.debug('Cannot remove %s' % path_to_file
)
176 def on_clear(widget
):
177 self
.dialog
.destroy()
179 self
.on_clear_button_clicked(widget
)
181 def on_cancel(widget
):
182 self
.dialog
.destroy()
186 self
.dialog
.present()
188 self
.dialog
= dialogs
.AvatarChooserDialog(on_response_ok
= on_ok
,
189 on_response_cancel
= on_cancel
, on_response_clear
= on_clear
)
191 def on_PHOTO_button_press_event(self
, widget
, event
):
193 If right-clicked, show popup
195 if event
.button
== 3 and self
.avatar_encoded
: # right click
199 pixbuf
= gtkgui_helpers
.get_avatar_pixbuf_from_cache(self
.jid
,
202 if pixbuf
not in (None, 'ask'):
203 nick
= gajim
.config
.get_per('accounts', self
.account
, 'name')
204 menuitem
= gtk
.ImageMenuItem(gtk
.STOCK_SAVE_AS
)
205 menuitem
.connect('activate',
206 gtkgui_helpers
.on_avatar_save_as_menuitem_activate
,
208 menu
.append(menuitem
)
210 menuitem
= gtk
.ImageMenuItem(gtk
.STOCK_CLEAR
)
211 menuitem
.connect('activate', self
.on_clear_button_clicked
)
212 menu
.append(menuitem
)
213 menu
.connect('selection-done', lambda w
:w
.destroy())
216 menu
.popup(None, None, None, event
.button
, event
.time
)
217 elif event
.button
== 1: # left click
218 self
.on_set_avatar_button_clicked(widget
)
220 def on_BDAY_entry_focus_out_event(self
, widget
, event
):
221 txt
= widget
.get_text()
225 time
.strptime(txt
, '%Y-%m-%d')
227 if not widget
.is_focus():
228 pritext
= _('Wrong date format')
229 dialogs
.ErrorDialog(pritext
, _('Format of the date must be '
231 gobject
.idle_add(lambda: widget
.grab_focus())
234 def set_value(self
, entry_name
, value
):
236 self
.xml
.get_object(entry_name
).set_text(value
)
237 except AttributeError:
240 def set_values(self
, vcard_
):
241 button
= self
.xml
.get_object('PHOTO_button')
242 image
= button
.get_image()
243 text_button
= self
.xml
.get_object('NOPHOTO_button')
244 if not 'PHOTO' in vcard_
:
246 image
.set_from_pixbuf(None)
249 for i
in vcard_
.keys():
251 pixbuf
, self
.avatar_encoded
, self
.avatar_mime_type
= \
252 vcard
.get_avatar_pixbuf_encoded_mime(vcard_
[i
])
254 image
.set_from_pixbuf(None)
258 pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
, 'vcard')
259 image
.set_from_pixbuf(pixbuf
)
263 if i
== 'ADR' or i
== 'TEL' or i
== 'EMAIL':
264 for entry
in vcard_
[i
]:
268 for j
in entry
.keys():
269 self
.set_value(i
+ add_on
+ '_' + j
+ '_entry', entry
[j
])
270 if isinstance(vcard_
[i
], dict):
271 for j
in vcard_
[i
].keys():
272 self
.set_value(i
+ '_' + j
+ '_entry', vcard_
[i
][j
])
275 self
.xml
.get_object('DESC_textview').get_buffer().set_text(
278 self
.set_value(i
+ '_entry', vcard_
[i
])
279 if self
.update_progressbar_timeout_id
is not None:
281 self
.statusbar
.remove_message(self
.context_id
, self
.message_id
)
282 self
.message_id
= self
.statusbar
.push(self
.context_id
,
283 _('Information received'))
284 self
.remove_statusbar_timeout_id
= gobject
.timeout_add_seconds(3,
285 self
.remove_statusbar
, self
.message_id
)
286 gobject
.source_remove(self
.update_progressbar_timeout_id
)
287 self
.progressbar
.hide()
288 self
.progressbar
.set_fraction(0)
289 self
.update_progressbar_timeout_id
= None
291 def _nec_vcard_received(self
, obj
):
292 if obj
.conn
.name
!= self
.account
:
294 if obj
.jid
!= self
.jid
:
296 self
.set_values(obj
.vcard_dict
)
298 def add_to_vcard(self
, vcard_
, entry
, txt
):
300 Add an information to the vCard dictionary
302 entries
= entry
.split('_')
304 if len(entries
) == 3: # We need to use lists
305 if entries
[0] not in loc
:
308 for e
in loc
[entries
[0]]:
313 loc
[entries
[0]].append({entries
[1]: '', entries
[2]: txt
})
315 while len(entries
) > 1:
316 if entries
[0] not in loc
:
318 loc
= loc
[entries
[0]]
320 loc
[entries
[0]] = txt
323 def make_vcard(self
):
325 Make the vCard dictionary
327 entries
= ['FN', 'NICKNAME', 'BDAY', 'EMAIL_HOME_USERID', 'URL',
328 'TEL_HOME_NUMBER', 'N_FAMILY', 'N_GIVEN', 'N_MIDDLE', 'N_PREFIX',
329 'N_SUFFIX', 'ADR_HOME_STREET', 'ADR_HOME_EXTADR', 'ADR_HOME_LOCALITY',
330 'ADR_HOME_REGION', 'ADR_HOME_PCODE', 'ADR_HOME_CTRY', 'ORG_ORGNAME',
331 'ORG_ORGUNIT', 'TITLE', 'ROLE', 'TEL_WORK_NUMBER', 'EMAIL_WORK_USERID',
332 'ADR_WORK_STREET', 'ADR_WORK_EXTADR', 'ADR_WORK_LOCALITY',
333 'ADR_WORK_REGION', 'ADR_WORK_PCODE', 'ADR_WORK_CTRY']
336 txt
= self
.xml
.get_object(e
+ '_entry').get_text().decode('utf-8')
338 vcard_
= self
.add_to_vcard(vcard_
, e
, txt
)
341 buff
= self
.xml
.get_object('DESC_textview').get_buffer()
342 start_iter
= buff
.get_start_iter()
343 end_iter
= buff
.get_end_iter()
344 txt
= buff
.get_text(start_iter
, end_iter
, 0)
346 vcard_
['DESC'] = txt
.decode('utf-8')
349 if self
.avatar_encoded
:
350 vcard_
['PHOTO'] = {'BINVAL': self
.avatar_encoded
}
351 if self
.avatar_mime_type
:
352 vcard_
['PHOTO']['TYPE'] = self
.avatar_mime_type
355 def on_ok_button_clicked(self
, widget
):
356 if self
.update_progressbar_timeout_id
:
357 # Operation in progress
359 if gajim
.connections
[self
.account
].connected
< 2:
360 dialogs
.ErrorDialog(_('You are not connected to the server'),
361 _('Without a connection you can not publish your contact '
364 vcard_
= self
.make_vcard()
366 if 'NICKNAME' in vcard_
:
367 nick
= vcard_
['NICKNAME']
368 gajim
.connections
[self
.account
].send_nickname(nick
)
370 nick
= gajim
.config
.get_per('accounts', self
.account
, 'name')
371 gajim
.nicks
[self
.account
] = nick
372 gajim
.connections
[self
.account
].send_vcard(vcard_
)
373 self
.message_id
= self
.statusbar
.push(self
.context_id
,
374 _('Sending profile...'))
375 self
.progressbar
.show()
376 self
.update_progressbar_timeout_id
= gobject
.timeout_add(100,
377 self
.update_progressbar
)
379 def _nec_vcard_published(self
, obj
):
380 if obj
.conn
.name
!= self
.account
:
382 if self
.update_progressbar_timeout_id
is not None:
383 gobject
.source_remove(self
.update_progressbar_timeout_id
)
384 self
.update_progressbar_timeout_id
= None
385 self
.window
.destroy()
387 def _nec_vcard_not_published(self
, obj
):
388 if obj
.conn
.name
!= self
.account
:
391 self
.statusbar
.remove_message(self
.context_id
, self
.message_id
)
392 self
.message_id
= self
.statusbar
.push(self
.context_id
,
393 _('Information NOT published'))
394 self
.remove_statusbar_timeout_id
= gobject
.timeout_add_seconds(3,
395 self
.remove_statusbar
, self
.message_id
)
396 if self
.update_progressbar_timeout_id
is not None:
397 gobject
.source_remove(self
.update_progressbar_timeout_id
)
398 self
.progressbar
.set_fraction(0)
399 self
.update_progressbar_timeout_id
= None
400 dialogs
.InformationDialog(_('vCard publication failed'),
401 _('There was an error while publishing your personal information, '
404 def on_cancel_button_clicked(self
, widget
):
405 self
.window
.destroy()