From 5a31ba2ef0a71459466a327c09669ada91374431 Mon Sep 17 00:00:00 2001
From: Julien Pivotto <roidelapluie@esquimaux.be>
Date: Fri, 14 Nov 2008 11:13:15 +0000
Subject: [PATCH] Very basic integration of xHtml GUI

---
 data/glade/message_window.glade |  80 +++++++++---
 src/chat_control.py             |  84 +++++++++++-
 src/gajim.py                    |   3 +
 src/groupchat_control.py        |   8 +-
 src/message_control.py          |   4 +-
 src/message_textview.py         | 221 +++++++++++++++++++++++++++++++-
 6 files changed, 367 insertions(+), 33 deletions(-)

diff --git a/data/glade/message_window.glade b/data/glade/message_window.glade
index 6bc5daa8cc..b3e2c3cc32 100644
--- a/data/glade/message_window.glade
+++ b/data/glade/message_window.glade
@@ -231,15 +231,36 @@
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkVSeparator" id="vseparator1">
+                      <widget class="GtkButton" id="formattings_button">
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="tooltip" translatable="yes">Show a list of formattings</property>
+                        <property name="relief">GTK_RELIEF_NONE</property>
+                        <property name="focus_on_click">False</property>
+                        <property name="response_id">0</property>
+                        <child>
+                          <widget class="GtkImage" id="image10">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-bold</property>
+                            <property name="icon_size">1</property>
+                          </widget>
+                        </child>
                       </widget>
                       <packing>
                         <property name="expand">False</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
+                    <child>
+                      <widget class="GtkVSeparator" id="vseparator1">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
                     <child>
                       <widget class="GtkButton" id="add_to_roster_button">
                         <property name="can_focus">True</property>
@@ -261,7 +282,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">2</property>
+                        <property name="position">3</property>
                       </packing>
                     </child>
                     <child>
@@ -282,7 +303,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">3</property>
+                        <property name="position">4</property>
                       </packing>
                     </child>
                     <child>
@@ -304,7 +325,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">4</property>
+                        <property name="position">5</property>
                       </packing>
                     </child>
                     <child>
@@ -326,7 +347,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">5</property>
+                        <property name="position">6</property>
                       </packing>
                     </child>
                     <child>
@@ -348,7 +369,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">6</property>
+                        <property name="position">7</property>
                       </packing>
                     </child>
                     <child>
@@ -358,7 +379,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">7</property>
+                        <property name="position">8</property>
                       </packing>
                     </child>
                     <child>
@@ -380,7 +401,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">8</property>
+                        <property name="position">9</property>
                       </packing>
                     </child>
                     <child>
@@ -392,7 +413,7 @@
                         </child>
                       </widget>
                       <packing>
-                        <property name="position">9</property>
+                        <property name="position">10</property>
                       </packing>
                     </child>
                     <child>
@@ -438,7 +459,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">10</property>
+                        <property name="position">11</property>
                       </packing>
                     </child>
                   </widget>
@@ -679,15 +700,36 @@
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkVSeparator" id="vseparator2">
+                      <widget class="GtkButton" id="formattings_button">
                         <property name="visible">True</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="tooltip" translatable="yes">Show a list of formattings</property>
+                        <property name="relief">GTK_RELIEF_NONE</property>
+                        <property name="focus_on_click">False</property>
+                        <property name="response_id">0</property>
+                        <child>
+                          <widget class="GtkImage" id="image11">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-bold</property>
+                            <property name="icon_size">1</property>
+                          </widget>
+                        </child>
                       </widget>
                       <packing>
                         <property name="expand">False</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
+                    <child>
+                      <widget class="GtkVSeparator" id="vseparator2">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                      </widget>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
                     <child>
                       <widget class="GtkButton" id="change_nick_button">
                         <property name="visible">True</property>
@@ -707,7 +749,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">2</property>
+                        <property name="position">3</property>
                       </packing>
                     </child>
                     <child>
@@ -729,7 +771,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">3</property>
+                        <property name="position">4</property>
                       </packing>
                     </child>
                     <child>
@@ -752,7 +794,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">4</property>
+                        <property name="position">5</property>
                       </packing>
                     </child>
                     <child>
@@ -774,7 +816,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">5</property>
+                        <property name="position">6</property>
                       </packing>
                     </child>
                     <child>
@@ -784,7 +826,7 @@
                       </widget>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="position">6</property>
+                        <property name="position">7</property>
                       </packing>
                     </child>
                     <child>
@@ -813,7 +855,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">7</property>
+                        <property name="position">8</property>
                       </packing>
                     </child>
                     <child>
@@ -825,7 +867,7 @@
                         </child>
                       </widget>
                       <packing>
-                        <property name="position">8</property>
+                        <property name="position">9</property>
                       </packing>
                     </child>
                     <child>
@@ -872,7 +914,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">False</property>
-                        <property name="position">9</property>
+                        <property name="position">10</property>
                       </packing>
                     </child>
                   </widget>
diff --git a/src/chat_control.py b/src/chat_control.py
index d8a1d6e32f..8cf50b0bf4 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -258,6 +258,10 @@ def __init__(self, type_id, parent_win, widget_name, contact, acct,
 		id = widget.connect('clicked', self._on_send_button_clicked)
 		self.handlers[id] = widget
 
+		widget = self.xml.get_widget('formattings_button')
+		id = widget.connect('clicked', self.on_formattings_button_clicked)
+		self.handlers[id] = widget
+
 		# the following vars are used to keep history of user's messages
 		self.sent_history = []
 		self.sent_history_pos = 0
@@ -359,9 +363,10 @@ def _on_send_button_clicked(self, widget):
 		start_iter = message_buffer.get_start_iter()
 		end_iter = message_buffer.get_end_iter()
 		message = message_buffer.get_text(start_iter, end_iter, 0).decode('utf-8')
+		xhtml = self.msg_textview.get_xhtml()
 
 		# send the message
-		self.send_message(message)
+		self.send_message(message, xhtml=xhtml)
 
 	def _paint_banner(self):
 		'''Repaint banner with theme color'''
@@ -508,6 +513,7 @@ def _on_message_textview_mykeypress_event(self, widget, event_keyval,
 		start_iter, end_iter = message_buffer.get_bounds()
 		message = message_buffer.get_text(start_iter, end_iter, False).decode(
 			'utf-8')
+		xhtml = self.msg_textview.get_xhtml() 
 
 		# construct event instance from binding
 		event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) # it's always a key-press here
@@ -551,7 +557,7 @@ def _on_message_textview_mykeypress_event(self, widget, event_keyval,
 				send_message = False
 
 			if send_message:
-				self.send_message(message) # send the message
+				self.send_message(message, xhtml=xhtml) # send the message
 		else:
 			# Give the control itself a chance to process
 			self.handle_message_textview_mykey_press(widget, event_keyval,
@@ -595,7 +601,7 @@ def _process_command(self, message):
 
 	def send_message(self, message, keyID = '', type_ = 'chat', chatstate = None,
 	msg_id = None, composing_xep = None, resource = None,
-	process_command = True):
+	process_command = True, xhtml = None):
 		'''Send the given message to the active tab. Doesn't return None if error
 		'''
 		if not message or message == '\n':
@@ -607,7 +613,7 @@ def send_message(self, message, keyID = '', type_ = 'chat', chatstate = None,
 			ret = MessageControl.send_message(self, message, keyID, type_ = type_,
 				chatstate = chatstate, msg_id = msg_id,
 				composing_xep = composing_xep, resource = resource,
-				user_nick = self.user_nick)
+				user_nick = self.user_nick, xhtml = xhtml)
 
 			# Record message history
 			self.save_sent_message(message)
@@ -739,6 +745,68 @@ def on_emoticons_button_clicked(self, widget):
 		gajim.interface.emoticon_menuitem_clicked = self.append_emoticon
 		gajim.interface.popup_emoticons_under_button(widget, self.parent_win)
 
+	def on_formattings_button_clicked(self, widget):
+		'''popup formattings menu'''
+ 		menu = gtk.Menu()
+ 
+ 		menuitems = ((_('Bold'), 'bold'),
+ 		(_('Italic'), 'italic'),
+ 		(_('Underline'), 'underline'),
+ 		(_('Strike'), 'strike'))
+
+		active_tags = self.msg_textview.get_active_tags()
+
+		for menuitem in menuitems:
+			item = gtk.CheckMenuItem(menuitem[0])
+			if menuitem[1] in active_tags:
+				item.set_active(True)
+			else:
+				item.set_active(False)
+			item.connect('activate', self.msg_textview.set_tag,
+				menuitem[1])
+			menu.append(item)
+
+		item = gtk.SeparatorMenuItem() # separator
+		menu.append(item)
+
+		item = gtk.ImageMenuItem(_('Color'))
+		icon = gtk.image_new_from_stock(gtk.STOCK_SELECT_COLOR, gtk.ICON_SIZE_MENU)
+		item.set_image(icon)
+		item.connect('activate', self.on_color_menuitem_activale)
+		menu.append(item)
+
+		item = gtk.ImageMenuItem(_('Font'))
+		icon = gtk.image_new_from_stock(gtk.STOCK_SELECT_FONT, gtk.ICON_SIZE_MENU)
+		item.set_image(icon)
+		item.connect('activate', self.on_font_menuitem_activale)
+		menu.append(item)
+
+		item = gtk.SeparatorMenuItem() # separator
+		menu.append(item)
+
+		item = gtk.ImageMenuItem(_('Clear formating'))
+		icon = gtk.image_new_from_stock(gtk.STOCK_CLEAR, gtk.ICON_SIZE_MENU)
+		item.set_image(icon)
+		item.connect('activate', self.msg_textview.clear_tags)
+		menu.append(item)
+
+		menu.show_all()
+		gtkgui_helpers.popup_emoticons_under_button(menu, widget,
+			self.parent_win)
+
+	def on_color_menuitem_activale(self, widget):
+		color_dialog = gtk.ColorSelectionDialog('Select a color')
+		color_dialog.connect('response', self.msg_textview.color_set, 
+			color_dialog.colorsel)
+		color_dialog.show_all()
+
+	def on_font_menuitem_activale(self, widget):
+		font_dialog = gtk.FontSelectionDialog('Select a font')
+		font_dialog.connect('response', self.msg_textview.font_set, 
+			font_dialog.fontsel)
+		font_dialog.show_all()
+		
+
 	def on_actions_button_clicked(self, widget):
 		'''popup action menu'''
 		menu = self.prepare_context_menu(True)
@@ -1668,7 +1736,8 @@ def get_command_help(self, command):
 		else:
 			self.print_conversation(_('No help info for /%s') % command, 'info')
 
-	def send_message(self, message, keyID = '', chatstate = None):
+	def send_message(self, message, keyID = '', chatstate = None,
+	xhtml = None):
 		'''Send a message to contact'''
 		if message in ('', None, '\n') or self._process_command(message):
 			return None
@@ -1725,7 +1794,7 @@ def send_message(self, message, keyID = '', chatstate = None):
 		id = ChatControlBase.send_message(self, message, keyID,
 			type_ = 'chat', chatstate = chatstate_to_send,
 			composing_xep = composing_xep,
-			process_command = process_command)
+			process_command = process_command, xhtml = xhtml)
 		if id:
 			# XXX: Once we have fallback to disco, remove
 			#      notexistant check
@@ -1738,7 +1807,8 @@ def send_message(self, message, keyID = '', chatstate = None):
 				xep0184_id = None
 
 			self.print_conversation(message, self.contact.jid,
-				encrypted = encrypted, xep0184_id = xep0184_id)
+				encrypted = encrypted, xep0184_id = xep0184_id,
+				xhtml = xhtml)
 
 	def check_for_possible_paused_chatstate(self, arg):
 		''' did we move mouse of that window or write something in message
diff --git a/src/gajim.py b/src/gajim.py
index acc6038377..a48898580f 100755
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -2453,6 +2453,9 @@ def make_regexps(self):
 		latex = r'|\$\$[^$\\]*?([\]\[0-9A-Za-z()|+*/-]|[\\][\]\[0-9A-Za-z()|{}$])(.*?[^\\])?\$\$'
 
 		basic_pattern = links + '|' + mail + '|' + legacy_prefixes
+		
+		link_pattern = basic_pattern
+		self.link_pattern_re = re.compile(link_pattern, re.IGNORECASE)
 
 		if gajim.config.get('use_latex'):
 			basic_pattern += latex
diff --git a/src/groupchat_control.py b/src/groupchat_control.py
index 31421d657d..91facdd30a 100644
--- a/src/groupchat_control.py
+++ b/src/groupchat_control.py
@@ -127,7 +127,7 @@ def __init__(self, parent_win, gc_contact, contact, account, session):
 		ChatControl.__init__(self, parent_win, contact, account, session)
 		self.TYPE_ID = 'pm'
 
-	def send_message(self, message):
+	def send_message(self, message, xhtml=None):
 		'''call this function to send our message'''
 		if not message:
 			return
@@ -153,7 +153,7 @@ def send_message(self, message):
 					'left.') % {'room': room, 'nick': nick})
 				return
 
-		ChatControl.send_message(self, message)
+		ChatControl.send_message(self, message, xhtml=xhtml)
 
 	def update_ui(self):
 		if self.contact.show == 'offline':
@@ -1628,7 +1628,7 @@ def _process_command(self, message):
 
 		return False
 
-	def send_message(self, message):
+	def send_message(self, message, xhtml=None):
 		'''call this function to send our message'''
 		if not message:
 			return
@@ -1644,7 +1644,7 @@ def send_message(self, message):
 			if not self._process_command(message):
 				# Send the message
 				gajim.connections[self.account].send_gc_message(self.room_jid,
-					message)
+					message, xhtml=xhtml)
 				self.msg_textview.get_buffer().set_text('')
 				self.msg_textview.grab_focus()
 
diff --git a/src/message_control.py b/src/message_control.py
index 7172b7e65d..26b6e84e12 100644
--- a/src/message_control.py
+++ b/src/message_control.py
@@ -162,7 +162,7 @@ def set_session(self, session):
 
 	def send_message(self, message, keyID = '', type_ = 'chat',
 	chatstate = None, msg_id = None, composing_xep = None, resource = None,
-	user_nick = None):
+	user_nick = None, xhtml = None):
 		# Send the given message to the active tab.
 		# Doesn't return None if error
 		jid = self.contact.jid
@@ -189,6 +189,6 @@ def send_message(self, message, keyID = '', type_ = 'chat',
 			composing_xep = composing_xep,
 			resource = self.resource, user_nick = user_nick,
 			session = self.session,
-			original_message = original_message)
+			original_message = original_message, xhtml = xhtml)
 
 # vim: se ts=3:
diff --git a/src/message_textview.py b/src/message_textview.py
index 334bdac933..1f37e3f624 100644
--- a/src/message_textview.py
+++ b/src/message_textview.py
@@ -22,6 +22,9 @@
 
 import gtk
 import gobject
+import pango
+import gtkgui_helpers
+from common import gajim
 
 class MessageTextView(gtk.TextView):
 	'''Class for the message textview (where user writes new messages)
@@ -35,7 +38,7 @@ class MessageTextView(gtk.TextView):
 		
 	def __init__(self):
 		gtk.TextView.__init__(self)
-		
+
 		# set properties
 		self.set_border_width(1)
 		self.set_accepts_tab(True)
@@ -48,6 +51,222 @@ def __init__(self):
 		self.set_pixels_below_lines(2)
 
 		self.lang = None # Lang used for spell checking
+		buffer = self.get_buffer()
+		self.begin_tags = {}
+		self.end_tags = {}
+		self.color_tags = []
+		self.fonts_tags = []
+		self.other_tags = {}		
+		self.other_tags['bold'] = buffer.create_tag('bold')
+		self.other_tags['bold'].set_property('weight', pango.WEIGHT_BOLD)
+		self.begin_tags['bold'] = '<strong>'
+		self.end_tags['bold'] = '</strong>'
+		self.other_tags['italic'] = buffer.create_tag('italic')
+		self.other_tags['italic'].set_property('style', pango.STYLE_ITALIC)
+		self.begin_tags['italic'] = '<em>'
+		self.end_tags['italic'] = '</em>'
+		self.other_tags['underline'] = buffer.create_tag('underline')
+		self.other_tags['underline'].set_property('underline', pango.UNDERLINE_SINGLE)
+		self.begin_tags['underline'] = '<span style="text-decoration: underline;">'
+		self.end_tags['underline'] = '</span>'
+		self.other_tags['strike'] = buffer.create_tag('strike')
+		self.other_tags['strike'].set_property('strikethrough', True)
+		self.begin_tags['strike'] = '<span style="text-decoration: line-through;">'
+		self.end_tags['strike'] = '</span>'
+
+	def make_clickable_urls(self, text):
+		buffer = self.get_buffer()
+
+		start = 0
+		end = 0
+		index = 0
+		
+		new_text = ''
+		iterator = gajim.interface.link_pattern_re.finditer(text)
+		for match in iterator:
+			start, end = match.span()
+			url = text[start:end]
+			if start != 0:
+				text_before_special_text = text[index:start]
+			else:
+				text_before_special_text = ''
+			end_iter = buffer.get_end_iter()
+			# we insert normal text
+			new_text += text_before_special_text + \
+			'<a href="'+ url +'">' + url + '</a>'
+				
+			index = end # update index
+
+		if end < len(text):
+			new_text += text[end:]
+
+		return new_text # the position after *last* special text
+
+	def get_active_tags(self):
+		buffer = self.get_buffer()
+		return_val = buffer.get_selection_bounds()
+		if return_val: # if sth was selected
+			start, finish = return_val[0], return_val[1]
+		else:
+			start, finish = buffer.get_bounds()
+		active_tags = []
+		for tag in start.get_tags():
+			active_tags.append(tag.get_property('name'))
+		return 	active_tags
+
+	def set_tag(self, widget, tag):
+		buffer = self.get_buffer()
+		return_val = buffer.get_selection_bounds()
+		if return_val: # if sth was selected
+			start, finish = return_val[0], return_val[1]
+		else:
+			start, finish = buffer.get_bounds()
+		if start.has_tag(self.other_tags[tag]):
+			buffer.remove_tag_by_name(tag, start, finish)
+		else:
+			if tag == 'underline':
+				buffer.remove_tag_by_name('strike', start, finish)
+			elif tag == 'strike':
+				buffer.remove_tag_by_name('underline', start, finish)
+			buffer.apply_tag_by_name(tag, start, finish)		
+
+	def clear_tags(self, widget):
+		buffer = self.get_buffer()
+		return_val = buffer.get_selection_bounds()
+		if return_val: # if sth was selected
+			start, finish = return_val[0], return_val[1]
+		else:
+			start, finish = buffer.get_bounds()
+		buffer.remove_all_tags(start, finish)
+
+	def color_set(self, widget, response, color):
+		if response == -6:
+			widget.destroy()
+			return
+		buffer = self.get_buffer()
+		color = color.get_current_color()
+		widget.destroy()
+		color_string = gtkgui_helpers.make_color_string(color)
+		tag_name = 'color' + color_string
+		if not tag_name in self.color_tags:
+			tagColor = buffer.create_tag(tag_name)
+			tagColor.set_property('foreground', color_string)
+			self.begin_tags[tag_name] = '<span style="color: ' + color_string + ';">'
+			self.end_tags[tag_name] = '</span>'
+			self.color_tags.append(tag_name)
+
+		return_val = buffer.get_selection_bounds()
+		if return_val: # if sth was selected
+			start, finish = return_val[0], return_val[1]
+		else:
+			start, finish = buffer.get_bounds()
+
+		for tag in self.color_tags:
+			buffer.remove_tag_by_name(tag, start, finish)
+
+		buffer.apply_tag_by_name(tag_name, start, finish)
+
+	def font_set(self, widget, response, font):
+		if response == -6:
+			widget.destroy()
+			return
+
+		buffer = self.get_buffer()
+
+		font = font.get_font_name()
+		font_desc = pango.FontDescription(font)
+		family = font_desc.get_family()
+		size = font_desc.get_size()
+		size = size / pango.SCALE
+		weight = font_desc.get_weight()
+		style = font_desc.get_style()
+
+		widget.destroy()
+
+		tag_name = 'font' + font
+		if not tag_name in self.fonts_tags:
+			tagFont = buffer.create_tag(tag_name)
+			tagFont.set_property('font', family + ' ' + str(size))
+			self.begin_tags[tag_name] = \
+				'<span style="font-family: ' + family + '; ' + \
+				'font-size: ' + str(size) + 'px">'
+			self.end_tags[tag_name] = '</span>'	
+			self.fonts_tags.append(tag_name)
+
+		return_val = buffer.get_selection_bounds()
+		if return_val: # if sth was selected
+			start, finish = return_val[0], return_val[1]
+		else:
+			start, finish = buffer.get_bounds()
+		
+		for tag in self.fonts_tags:
+			buffer.remove_tag_by_name(tag, start, finish)
+
+		buffer.apply_tag_by_name(tag_name, start, finish)
+
+		if weight == pango.WEIGHT_BOLD:
+			buffer.apply_tag_by_name('bold', start, finish)
+		else:
+			buffer.remove_tag_by_name('bold', start, finish)
+
+		if style == pango.STYLE_ITALIC:
+			buffer.apply_tag_by_name('italic', start, finish)
+		else:
+			buffer.remove_tag_by_name('italic', start, finish)
+
+	def get_xhtml(self):
+		buffer = self.get_buffer()
+		old = buffer.get_start_iter()
+		tags = {}
+		tags['bold'] = False
+		iter = buffer.get_start_iter()
+		old = buffer.get_start_iter()
+		texte = ''
+		modified = False
+		def xhtml_special(text):
+			text = text.replace('<', '&lt;')
+			text = text.replace('>', '&gt;')
+			text = text.replace('\n', '<br />')
+			return text
+
+		for tag in iter.get_toggled_tags(True):
+			texte += self.begin_tags[tag.get_property('name')]
+			modified = True
+		while (iter.forward_to_tag_toggle(None) and not iter.is_end()):
+			modified = True
+			texte += xhtml_special(buffer.get_text(old, iter))
+			old.forward_to_tag_toggle(None)
+			new_tags = []
+			old_tags = []
+			end_tags = []
+			for tag in iter.get_toggled_tags(True):
+				new_tags.append(tag.get_property('name'))
+			
+			for tag in iter.get_tags():
+				if tag.get_property('name') not in new_tags:
+					old_tags.append(tag.get_property('name'))
+			
+			for tag in iter.get_toggled_tags(False):
+				end_tags.append(tag.get_property('name'))
+
+			for tag in old_tags:
+				texte += self.end_tags[tag]
+			for tag in end_tags:
+				texte += self.end_tags[tag]
+			for tag in new_tags:
+				texte += self.begin_tags[tag]
+			for tag in old_tags:
+				texte += self.begin_tags[tag]		
+
+		texte += xhtml_special(buffer.get_text(old, buffer.get_end_iter()))
+		for tag in iter.get_toggled_tags(False):
+			texte += self.end_tags[tag.get_property('name')]
+
+		if modified:
+			return '<p>' + self.make_clickable_urls(texte) + '</p>'
+		else:
+			return None
+	
 
 	def destroy(self):
 		import gc
-- 
GitLab