add ability to set an avatar for a contact. fixes #2967
This commit is contained in:
parent
42bd8d64b3
commit
2ef20efac5
|
@ -392,7 +392,57 @@
|
||||||
<widget class="GtkVBox" id="vbox2">
|
<widget class="GtkVBox" id="vbox2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="homogeneous">False</property>
|
<property name="homogeneous">False</property>
|
||||||
<property name="spacing">0</property>
|
<property name="spacing">6</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label59">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">User avatar:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="no_user_avatar_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">None</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<widget class="GtkEventBox" id="PHOTO_eventbox">
|
<widget class="GtkEventBox" id="PHOTO_eventbox">
|
||||||
|
@ -417,6 +467,65 @@
|
||||||
<property name="fill">False</property>
|
<property name="fill">False</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label60">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Configured avatar:</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
|
||||||
|
<property name="width_chars">-1</property>
|
||||||
|
<property name="single_line_mode">False</property>
|
||||||
|
<property name="angle">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="PHOTO_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes"></property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="button_press_event" handler="on_PHOTO_button_press_event" last_modification_time="Sat, 21 Apr 2007 23:35:12 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="NOPHOTO_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">Click to force avatar</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
<property name="focus_on_click">True</property>
|
||||||
|
<signal name="clicked" handler="on_NOPHOTO_button_clicked" last_modification_time="Sat, 21 Apr 2007 23:25:59 GMT"/>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</widget>
|
</widget>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="padding">0</property>
|
<property name="padding">0</property>
|
||||||
|
|
|
@ -528,7 +528,7 @@ def get_scaled_pixbuf(pixbuf, kind):
|
||||||
scaled_buf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_HYPER)
|
scaled_buf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_HYPER)
|
||||||
return scaled_buf
|
return scaled_buf
|
||||||
|
|
||||||
def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False):
|
def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False, use_local = True):
|
||||||
'''checks if jid has cached avatar and if that avatar is valid image
|
'''checks if jid has cached avatar and if that avatar is valid image
|
||||||
(can be shown)
|
(can be shown)
|
||||||
returns None if there is no image in vcard
|
returns None if there is no image in vcard
|
||||||
|
@ -545,8 +545,21 @@ def get_avatar_pixbuf_from_cache(fjid, is_fake_jid = False):
|
||||||
if is_fake_jid:
|
if is_fake_jid:
|
||||||
puny_nick = helpers.sanitize_filename(nick)
|
puny_nick = helpers.sanitize_filename(nick)
|
||||||
path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
|
path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
|
||||||
|
local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid,
|
||||||
|
puny_nick) + '_local'
|
||||||
else:
|
else:
|
||||||
path = os.path.join(gajim.VCARD_PATH, puny_jid)
|
path = os.path.join(gajim.VCARD_PATH, puny_jid)
|
||||||
|
local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid) + \
|
||||||
|
'_local'
|
||||||
|
if use_local:
|
||||||
|
for extension in ('.png', '.jpeg'):
|
||||||
|
local_avatar_path = local_avatar_basepath + extension
|
||||||
|
if os.path.isfile(local_avatar_path):
|
||||||
|
avatar_file = open(local_avatar_path)
|
||||||
|
avatar_data = avatar_file.read()
|
||||||
|
avatar_file.close()
|
||||||
|
return get_pixbuf_from_data(avatar_data)
|
||||||
|
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
return 'ask'
|
return 'ask'
|
||||||
|
|
||||||
|
@ -591,6 +604,10 @@ def get_path_to_generic_or_avatar(generic, jid = None, suffix = None):
|
||||||
if jid:
|
if jid:
|
||||||
puny_jid = helpers.sanitize_filename(jid)
|
puny_jid = helpers.sanitize_filename(jid)
|
||||||
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + suffix
|
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + suffix
|
||||||
|
filepath, extension = os.path.splitext(path_to_file)
|
||||||
|
path_to_local_file = filepath + '_local' + extension
|
||||||
|
if os.path.exists(path_to_local_file):
|
||||||
|
return path_to_local_file
|
||||||
if os.path.exists(path_to_file):
|
if os.path.exists(path_to_file):
|
||||||
return path_to_file
|
return path_to_file
|
||||||
return os.path.abspath(generic)
|
return os.path.abspath(generic)
|
||||||
|
@ -773,7 +790,7 @@ default_name = ''):
|
||||||
is_fake = False
|
is_fake = False
|
||||||
if account and gajim.contacts.is_pm_from_jid(account, jid):
|
if account and gajim.contacts.is_pm_from_jid(account, jid):
|
||||||
is_fake = True
|
is_fake = True
|
||||||
pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake)
|
pixbuf = get_avatar_pixbuf_from_cache(jid, is_fake, False)
|
||||||
ext = file_path.split('.')[-1]
|
ext = file_path.split('.')[-1]
|
||||||
type_ = ''
|
type_ = ''
|
||||||
if not ext:
|
if not ext:
|
||||||
|
|
|
@ -118,15 +118,16 @@ class ProfileWindow:
|
||||||
def on_ok(widget, path_to_file):
|
def on_ok(widget, path_to_file):
|
||||||
must_delete = False
|
must_delete = False
|
||||||
filesize = os.path.getsize(path_to_file) # in bytes
|
filesize = os.path.getsize(path_to_file) # in bytes
|
||||||
#FIXME: use messages for invalid file for 0.11
|
|
||||||
invalid_file = False
|
invalid_file = False
|
||||||
msg = ''
|
msg = ''
|
||||||
if os.path.isfile(path_to_file):
|
if os.path.isfile(path_to_file):
|
||||||
stat = os.stat(path_to_file)
|
stat = os.stat(path_to_file)
|
||||||
if stat[6] == 0:
|
if stat[6] == 0:
|
||||||
invalid_file = True
|
invalid_file = True
|
||||||
|
msg = _('File is emty')
|
||||||
else:
|
else:
|
||||||
invalid_file = True
|
invalid_file = True
|
||||||
|
msg = _('File does not exist')
|
||||||
if not invalid_file and filesize > 16384: # 16 kb
|
if not invalid_file and filesize > 16384: # 16 kb
|
||||||
try:
|
try:
|
||||||
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
||||||
|
@ -183,7 +184,8 @@ class ProfileWindow:
|
||||||
menu = gtk.Menu()
|
menu = gtk.Menu()
|
||||||
|
|
||||||
# Try to get pixbuf
|
# Try to get pixbuf
|
||||||
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid)
|
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid,
|
||||||
|
use_local = False)
|
||||||
|
|
||||||
if pixbuf:
|
if pixbuf:
|
||||||
nick = gajim.config.get_per('accounts', self.account, 'name')
|
nick = gajim.config.get_per('accounts', self.account, 'name')
|
||||||
|
|
126
src/vcard.py
126
src/vcard.py
|
@ -21,8 +21,11 @@ import gobject
|
||||||
import base64
|
import base64
|
||||||
import time
|
import time
|
||||||
import locale
|
import locale
|
||||||
|
import os
|
||||||
|
|
||||||
import gtkgui_helpers
|
import gtkgui_helpers
|
||||||
|
import dialogs
|
||||||
|
import message_control
|
||||||
|
|
||||||
from common import helpers
|
from common import helpers
|
||||||
from common import gajim
|
from common import gajim
|
||||||
|
@ -67,6 +70,27 @@ class VcardWindow:
|
||||||
self.account = account
|
self.account = account
|
||||||
self.gc_contact = gc_contact
|
self.gc_contact = gc_contact
|
||||||
|
|
||||||
|
self.xml.get_widget('no_user_avatar_label').set_no_show_all(True)
|
||||||
|
self.xml.get_widget('no_user_avatar_label').hide()
|
||||||
|
self.xml.get_widget('PHOTO_image').set_no_show_all(True)
|
||||||
|
self.xml.get_widget('PHOTO_image').hide()
|
||||||
|
image = gtk.Image()
|
||||||
|
self.photo_button = self.xml.get_widget('PHOTO_button')
|
||||||
|
self.photo_button.set_image(image)
|
||||||
|
self.nophoto_button = self.xml.get_widget('NOPHOTO_button')
|
||||||
|
puny_jid = helpers.sanitize_filename(contact.jid)
|
||||||
|
local_avatar_basepath = os.path.join(gajim.AVATAR_PATH, puny_jid) + \
|
||||||
|
'_local'
|
||||||
|
for extension in ('.png', '.jpeg'):
|
||||||
|
local_avatar_path = local_avatar_basepath + extension
|
||||||
|
if os.path.isfile(local_avatar_path):
|
||||||
|
image.set_from_file(local_avatar_path)
|
||||||
|
self.nophoto_button.set_no_show_all(True)
|
||||||
|
self.nophoto_button.hide()
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.photo_button.set_no_show_all(True)
|
||||||
|
self.photo_button.hide()
|
||||||
self.avatar_mime_type = None
|
self.avatar_mime_type = None
|
||||||
self.avatar_encoded = None
|
self.avatar_encoded = None
|
||||||
self.vcard_arrived = False
|
self.vcard_arrived = False
|
||||||
|
@ -88,6 +112,104 @@ class VcardWindow:
|
||||||
self.progressbar.pulse()
|
self.progressbar.pulse()
|
||||||
return True # loop forever
|
return True # loop forever
|
||||||
|
|
||||||
|
def update_avatar_in_gui(self):
|
||||||
|
jid = self.contact.jid
|
||||||
|
# Update roster
|
||||||
|
gajim.interface.roster.draw_avatar(jid, self.account)
|
||||||
|
# Update chat window
|
||||||
|
if gajim.interface.msg_win_mgr.has_window(jid, self.account):
|
||||||
|
win = gajim.interface.msg_win_mgr.get_window(jid, self.account)
|
||||||
|
ctrl = win.get_control(jid, self.account)
|
||||||
|
if win and ctrl.type_id != message_control.TYPE_GC:
|
||||||
|
ctrl.show_avatar()
|
||||||
|
|
||||||
|
def on_NOPHOTO_button_clicked(self, button):
|
||||||
|
def on_ok(widget, path_to_file):
|
||||||
|
filesize = os.path.getsize(path_to_file) # in bytes
|
||||||
|
invalid_file = False
|
||||||
|
msg = ''
|
||||||
|
if os.path.isfile(path_to_file):
|
||||||
|
stat = os.stat(path_to_file)
|
||||||
|
if stat[6] == 0:
|
||||||
|
invalid_file = True
|
||||||
|
msg = _('File is emty')
|
||||||
|
else:
|
||||||
|
invalid_file = True
|
||||||
|
msg = _('File does not exist')
|
||||||
|
if invalid_file:
|
||||||
|
dialogs.ErrorDialog(_('Could not load image'), msg)
|
||||||
|
return
|
||||||
|
pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
|
||||||
|
if filesize > 16384: # 16 kb
|
||||||
|
try:
|
||||||
|
# get the image at 'notification size'
|
||||||
|
# and use that user did not specify in ACE crazy size
|
||||||
|
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip')
|
||||||
|
except gobject.GError, msg: # unknown format
|
||||||
|
# msg should be string, not object instance
|
||||||
|
msg = str(msg)
|
||||||
|
invalid_file = True
|
||||||
|
puny_jid = helpers.sanitize_filename(self.contact.jid)
|
||||||
|
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png'
|
||||||
|
pixbuf.save(path_to_file, 'png')
|
||||||
|
self.dialog.destroy()
|
||||||
|
self.update_avatar_in_gui()
|
||||||
|
|
||||||
|
# rescale it
|
||||||
|
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'vcard')
|
||||||
|
image = self.photo_button.get_image()
|
||||||
|
image.set_from_pixbuf(pixbuf)
|
||||||
|
self.photo_button.show()
|
||||||
|
self.nophoto_button.hide()
|
||||||
|
|
||||||
|
def on_clear(widget):
|
||||||
|
self.dialog.destroy()
|
||||||
|
self.on_clear_button_clicked(widget)
|
||||||
|
|
||||||
|
self.dialog = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
|
||||||
|
on_response_clear = on_clear)
|
||||||
|
|
||||||
|
def on_clear_button_clicked(self, widget):
|
||||||
|
# empty the image
|
||||||
|
image = self.photo_button.get_image()
|
||||||
|
image.set_from_pixbuf(None)
|
||||||
|
self.photo_button.hide()
|
||||||
|
self.nophoto_button.show()
|
||||||
|
# Delete file:
|
||||||
|
puny_jid = helpers.sanitize_filename(self.contact.jid)
|
||||||
|
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid) + '_local.png'
|
||||||
|
try:
|
||||||
|
os.remove(path_to_file)
|
||||||
|
except OSError:
|
||||||
|
gajim.log.debug('Cannot remove %s' % path_to_file)
|
||||||
|
self.update_avatar_in_gui()
|
||||||
|
|
||||||
|
def on_PHOTO_button_press_event(self, widget, event):
|
||||||
|
'''If right-clicked, show popup'''
|
||||||
|
if event.button == 3 and self.avatar_encoded: # right click
|
||||||
|
menu = gtk.Menu()
|
||||||
|
|
||||||
|
# Try to get pixbuf
|
||||||
|
# pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(self.jid)
|
||||||
|
|
||||||
|
# if pixbuf:
|
||||||
|
# nick = self.contact.get_shown_name()
|
||||||
|
# menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
|
||||||
|
# menuitem.connect('activate',
|
||||||
|
# gtkgui_helpers.on_avatar_save_as_menuitem_activate, self.jid,
|
||||||
|
# None, nick + '.jpeg')
|
||||||
|
# menu.append(menuitem)
|
||||||
|
# show clear
|
||||||
|
menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
|
||||||
|
menuitem.connect('activate', self.on_clear_button_clicked)
|
||||||
|
menu.append(menuitem)
|
||||||
|
menu.connect('selection-done', lambda w:w.destroy())
|
||||||
|
# show the menu
|
||||||
|
menu.show_all()
|
||||||
|
menu.popup(None, None, None, event.button, event.time)
|
||||||
|
elif event.button == 1: # left click
|
||||||
|
self.on_set_avatar_button_clicked(widget)
|
||||||
|
|
||||||
def on_vcard_information_window_destroy(self, widget):
|
def on_vcard_information_window_destroy(self, widget):
|
||||||
if self.update_progressbar_timeout_id is not None:
|
if self.update_progressbar_timeout_id is not None:
|
||||||
gobject.source_remove(self.update_progressbar_timeout_id)
|
gobject.source_remove(self.update_progressbar_timeout_id)
|
||||||
|
@ -100,7 +222,6 @@ class VcardWindow:
|
||||||
connection.annotations[self.contact.jid] = annotation
|
connection.annotations[self.contact.jid] = annotation
|
||||||
connection.store_annotations()
|
connection.store_annotations()
|
||||||
|
|
||||||
|
|
||||||
def on_vcard_information_window_key_press_event(self, widget, event):
|
def on_vcard_information_window_key_press_event(self, widget, event):
|
||||||
if event.keyval == gtk.keysyms.Escape:
|
if event.keyval == gtk.keysyms.Escape:
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
@ -137,12 +258,15 @@ class VcardWindow:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_values(self, vcard):
|
def set_values(self, vcard):
|
||||||
|
if not 'PHOTO' in vcard:
|
||||||
|
self.xml.get_widget('no_user_avatar_label').show()
|
||||||
for i in vcard.keys():
|
for i in vcard.keys():
|
||||||
if i == 'PHOTO' and self.xml.get_widget('information_notebook').\
|
if i == 'PHOTO' and self.xml.get_widget('information_notebook').\
|
||||||
get_n_pages() > 4:
|
get_n_pages() > 4:
|
||||||
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
|
pixbuf, self.avatar_encoded, self.avatar_mime_type = \
|
||||||
get_avatar_pixbuf_encoded_mime(vcard[i])
|
get_avatar_pixbuf_encoded_mime(vcard[i])
|
||||||
image = self.xml.get_widget('PHOTO_image')
|
image = self.xml.get_widget('PHOTO_image')
|
||||||
|
image.show()
|
||||||
if not pixbuf:
|
if not pixbuf:
|
||||||
image.set_from_icon_name('stock_person',
|
image.set_from_icon_name('stock_person',
|
||||||
gtk.ICON_SIZE_DIALOG)
|
gtk.ICON_SIZE_DIALOG)
|
||||||
|
|
Loading…
Reference in New Issue