From 2ef20efac533e2b72db99b95ee7b80a559a7cf2c Mon Sep 17 00:00:00 2001
From: Yann Leboulanger <asterix@lagaule.org>
Date: Sun, 22 Apr 2007 10:23:23 +0000
Subject: [PATCH] add ability to set an avatar for a contact. fixes #2967

---
 data/glade/vcard_information_window.glade | 111 ++++++++++++++++++-
 src/gtkgui_helpers.py                     |  21 +++-
 src/profile_window.py                     |   6 +-
 src/vcard.py                              | 126 +++++++++++++++++++++-
 4 files changed, 258 insertions(+), 6 deletions(-)

diff --git a/data/glade/vcard_information_window.glade b/data/glade/vcard_information_window.glade
index 334ab9bc5a..21db09890c 100644
--- a/data/glade/vcard_information_window.glade
+++ b/data/glade/vcard_information_window.glade
@@ -392,7 +392,57 @@
 		    <widget class="GtkVBox" id="vbox2">
 		      <property name="visible">True</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>
 			<widget class="GtkEventBox" id="PHOTO_eventbox">
@@ -417,6 +467,65 @@
 			  <property name="fill">False</property>
 			</packing>
 		      </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>
 		    <packing>
 		      <property name="padding">0</property>
diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py
index e19a90e0a2..7b50812765 100644
--- a/src/gtkgui_helpers.py
+++ b/src/gtkgui_helpers.py
@@ -528,7 +528,7 @@ def get_scaled_pixbuf(pixbuf, kind):
 	scaled_buf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_HYPER)
 	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
 	(can be shown)
 	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:
 		puny_nick = helpers.sanitize_filename(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:
 		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):
 		return 'ask'
 
@@ -591,6 +604,10 @@ def get_path_to_generic_or_avatar(generic, jid = None, suffix = None):
 	if jid:
 		puny_jid = helpers.sanitize_filename(jid)
 		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):
 			return path_to_file
 	return os.path.abspath(generic)
@@ -773,7 +790,7 @@ default_name = ''):
 		is_fake = False
 		if account and gajim.contacts.is_pm_from_jid(account, jid):
 			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]
 		type_ = ''
 		if not ext:
diff --git a/src/profile_window.py b/src/profile_window.py
index 2015d5df9a..c2aea284a3 100644
--- a/src/profile_window.py
+++ b/src/profile_window.py
@@ -118,15 +118,16 @@ class ProfileWindow:
 		def on_ok(widget, path_to_file):
 			must_delete = False
 			filesize = os.path.getsize(path_to_file) # in bytes
-			#FIXME: use messages for invalid file for 0.11
 			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 not invalid_file and filesize > 16384: # 16 kb
 				try:
 					pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
@@ -183,7 +184,8 @@ class ProfileWindow:
 			menu = gtk.Menu()
 			
 			# 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:
 				nick = gajim.config.get_per('accounts', self.account, 'name')
diff --git a/src/vcard.py b/src/vcard.py
index ccb22c5d45..c12e395081 100644
--- a/src/vcard.py
+++ b/src/vcard.py
@@ -21,8 +21,11 @@ import gobject
 import base64
 import time
 import locale
+import os
 
 import gtkgui_helpers
+import dialogs
+import message_control
 
 from common import helpers
 from common import gajim
@@ -67,6 +70,27 @@ class VcardWindow:
 		self.account = account
 		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_encoded = None
 		self.vcard_arrived = False
@@ -88,6 +112,104 @@ class VcardWindow:
 		self.progressbar.pulse()
 		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):
 		if self.update_progressbar_timeout_id is not None:
 			gobject.source_remove(self.update_progressbar_timeout_id)
@@ -100,7 +222,6 @@ class VcardWindow:
 			connection.annotations[self.contact.jid] = annotation
 			connection.store_annotations()
 
-
 	def on_vcard_information_window_key_press_event(self, widget, event):
 		if event.keyval == gtk.keysyms.Escape:
 			self.window.destroy()
@@ -137,12 +258,15 @@ class VcardWindow:
 			pass
 
 	def set_values(self, vcard):
+		if not 'PHOTO' in vcard:
+			self.xml.get_widget('no_user_avatar_label').show()
 		for i in vcard.keys():
 			if i == 'PHOTO' and self.xml.get_widget('information_notebook').\
 			get_n_pages() > 4:
 				pixbuf, self.avatar_encoded, self.avatar_mime_type = \
 					get_avatar_pixbuf_encoded_mime(vcard[i])
 				image = self.xml.get_widget('PHOTO_image')
+				image.show()
 				if not pixbuf:
 					image.set_from_icon_name('stock_person',
 						gtk.ICON_SIZE_DIALOG)
-- 
GitLab