Index: src/conversation_textview.py
===================================================================
--- src/conversation_textview.py	(révision 8490)
+++ src/conversation_textview.py	(copie de travail)
@@ -10,13 +10,14 @@
 ##
 ## This program is distributed in the hope that it will be useful,
 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 ## GNU General Public License for more details.
 ##
 
 import random
 from tempfile import gettempdir
 from subprocess import Popen
+from threading import Timer # for smooth scrolling
 
 import gtk
 import pango
@@ -44,6 +45,10 @@
 	path_to_file = os.path.join(gajim.DATA_DIR, 'pixmaps', 'muc_separator.png')
 	FOCUS_OUT_LINE_PIXBUF = gtk.gdk.pixbuf_new_from_file(path_to_file)
 
+	# smooth scroll constants
+	MAX_SCROLL_TIME = 0.4 # seconds
+	SCROLL_DELAY = 33 # milliseconds
+
 	def __init__(self, account, used_in_history_window = False):
 		'''if used_in_history_window is True, then we do not show
 		Clear menuitem in context menu'''
@@ -154,6 +159,7 @@
 		self.line_tooltip = tooltips.BaseTooltip()
 		# use it for hr too
 		self.tv.focus_out_line_pixbuf = ConversationTextview.FOCUS_OUT_LINE_PIXBUF
+		self.smooth_id = None
 
 	def del_handlers(self):
 		for i in self.handlers.keys():
@@ -181,6 +187,44 @@
 			return True
 		return False
 
+	# Smooth scrolling inspired by Pidgin code
+	def smooth_scroll(self):
+		parent = self.tv.get_parent()
+		if not parent:
+			return False
+		vadj = parent.get_vadjustment()
+		max_val = vadj.upper - vadj.page_size + 1
+		cur_val = vadj.get_value()
+		# scroll by 1/3rd of remaining distance
+		onethird = cur_val + ((max_val - cur_val) / 3.0)
+		vadj.set_value(onethird)
+		if max_val - onethird < 0.01:
+			print "sroll finished"
+			self.smooth_id = None
+			self.smooth_scroll_timer.cancel()
+			return False
+		return True		   
+
+	def smooth_scroll_timeout(self):
+		print "-->smooth_scroll_timeout"
+		gobject.source_remove(self.smooth_id)
+		self.smooth_id = None
+		parent = self.tv.get_parent()
+		if parent:
+			vadj = parent.get_vadjustment()
+			vadj.set_value(vadj.upper - vadj.page_size + 1)
+
+	def smooth_scroll_to_end(self):
+		if None != self.smooth_id: # already scrolling
+			return False
+		print "-->add timeout"
+		self.smooth_id = gobject.timeout_add(self.SCROLL_DELAY,
+											 self.smooth_scroll)
+		self.smooth_scroll_timer = Timer(self.MAX_SCROLL_TIME,
+										 self.smooth_scroll_timeout)
+		self.smooth_scroll_timer.start()
+		return False
+
 	def scroll_to_end(self):
 		parent = self.tv.get_parent()
 		buffer = self.tv.get_buffer()
@@ -192,7 +236,9 @@
 		adjustment.set_value(0)
 		return False # when called in an idle_add, just do it once
 
-	def bring_scroll_to_end(self, diff_y = 0):
+	def bring_scroll_to_end(self, diff_y = 0,\
+							use_smooth =\
+							gajim.config.get('use_smooth_scrolling')):
 		''' scrolls to the end of textview if end is not visible '''
 		buffer = self.tv.get_buffer()
 		end_iter = buffer.get_end_iter()
@@ -200,7 +246,10 @@
 		visible_rect = self.tv.get_visible_rect()
 		# scroll only if expected end is not visible
 		if end_rect.y >= (visible_rect.y + visible_rect.height + diff_y):
-			gobject.idle_add(self.scroll_to_end_iter)
+			if use_smooth:
+				gobject.idle_add(self.smooth_scroll_to_end)
+			else:
+				gobject.idle_add(self.scroll_to_end_iter)
 
 	def scroll_to_end_iter(self):
 		buffer = self.tv.get_buffer()
@@ -857,7 +906,10 @@
 		if at_the_end or kind == 'outgoing':
 			# we are at the end or we are sending something
 			# scroll to the end (via idle in case the scrollbar has appeared)
-			gobject.idle_add(self.scroll_to_end)
+			if gajim.config.get('use_smooth_scrolling'):
+				gobject.idle_add(self.smooth_scroll_to_end)
+			else:
+				gobject.idle_add(self.scroll_to_end)
 
 		buffer.end_user_action()
 
Index: src/common/config.py
===================================================================
--- src/common/config.py	(révision 8490)
+++ src/common/config.py	(copie de travail)
@@ -218,6 +218,7 @@
 		'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the group chat occupants list in group chat window.')],
 		'chat_merge_consecutive_nickname': [opt_bool, False, _('In a chat, show the nickname at the beginning of a line only when it\'s not the same person talking than in previous message.')],
 		'chat_merge_consecutive_nickname_indent': [opt_str, '  ', _('Indentation when using merge consecutive nickname.')],
+        'use_smooth_scrolling': [opt_bool, True, _('Smooth scroll message in conversation window')],
 		'gc_nicknames_colors': [ opt_str, '#a34526:#c000ff:#0012ff:#388a99:#045723:#7c7c7c:#ff8a00:#94452d:#244b5a:#32645a', _('List of colors that will be used to color nicknames in group chats.'), True ],
 		'ctrl_tab_go_to_next_composing': [opt_bool, True, _('Ctrl-Tab go to next composing tab when none is unread.')],
 		'confirm_metacontacts': [ opt_str, '', _('Should we show the confirm metacontacts creation dialog or not? Empty string means we never show the dialog.')],
Index: src/chat_control.py
===================================================================
--- src/chat_control.py	(révision 8490)
+++ src/chat_control.py	(copie de travail)
@@ -812,8 +812,9 @@
 				self.msg_scrolledwindow.set_property('vscrollbar-policy', 
 					gtk.POLICY_NEVER)
 				self.msg_scrolledwindow.set_property('height-request', -1)
-
-		self.conv_textview.bring_scroll_to_end(diff_y - 18)
+			self.conv_textview.bring_scroll_to_end(diff_y - 18, False)
+		else:
+			self.conv_textview.bring_scroll_to_end(diff_y - 18)
 		
 		# enable scrollbar automatic policy for horizontal scrollbar
 		# if message we have in message_textview is too big
