diff -u gtkhtml.c gtkhtml.c
--- gtkhtml.c	17 Sep 2004 20:52:47 -0000
+++ gtkhtml.c	21 Sep 2004 18:03:53 -0000
@@ -2993,132 +2993,165 @@
 	}
 }
 
+/* Removes the current preedit string if any; note that it doesn't reset im_pre_pos/im_pre_len,
+ * so it must be followed by a call to update_preedit
+ */
 static void
-gtk_html_im_commit_cb (GtkIMContext *context, const gchar *str, GtkHTML *html)
+remove_preedit (GtkHTML *html)
 {
-	gboolean state = html->priv->im_block_reset;
-	gint pos;
-
-        if (html->priv->im_pre_len > 0) {
-                D_IM (printf ("IM delete last preedit %d + %d\n", html->priv->im_pre_pos, html->priv->im_pre_len);)
-                                                                                
-                html_undo_freeze (html->engine->undo);
-                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
-                html_engine_set_mark (html->engine);
-                html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
-                html_engine_delete (html->engine);
-                html->priv->im_pre_len = 0;
-                html_undo_thaw (html->engine->undo);
-        }
-
-	pos = html->engine->cursor->position;
-	if (html->engine->mark && html->engine->mark->position > pos)
-		pos = html->engine->mark->position;
-
-	html->priv->im_block_reset = TRUE;
-	D_IM (printf ("IM commit %s\n", str);)
-	
-	/* Move cusor before preedit pos and commit the string */
-	html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
-	html_engine_paste_text (html->engine, str, -1);
-	/* Add preedit pos with number of str committed and move back */
-	html->priv->im_pre_pos += g_utf8_strlen (str, -1);
-	html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
-
-	html->priv->im_block_reset = state;
-
-	D_IM (printf ("IM commit pos: %d pre_pos: %d\n", pos, html->priv->im_pre_pos);)
+	if (html->priv->im_pre_len > 0) {
+		gboolean state = html->priv->im_block_reset;
+		html->priv->im_block_reset = TRUE;
+		
+		html_undo_freeze (html->engine->undo);
+		html_engine_selection_push (html->engine);
 
-	if (html->priv->im_pre_pos >= pos)
-		html->priv->im_pre_pos += html->engine->cursor->position - pos;
+		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
+		html_engine_set_mark (html->engine);
+		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
+		html_engine_delete (html->engine);
+		
+		html_engine_selection_pop (html->engine);
+		html_undo_thaw (html->engine->undo);
+		
+		html->priv->im_block_reset = state;
+	} else {
+		/* The current style can get confused while we are have a preedit string
+		 * in place, so we save the current style before we start preediting, and
+		 * restore it when the preedit string vanishes. (See bug #66204)
+		 */
+		html->priv->im_orig_style = html_engine_get_font_style (html->engine);
+	}
 }
 
+/* Gets the preedit string, converting from byte indices to character offsets */
 static void
-gtk_html_im_preedit_start_cb (GtkIMContext *context, GtkHTML *html)
+get_preedit_string (GtkHTML        *html,
+		    gchar         **str,
+		    PangoAttrList **attrs,
+		    gint           *len,
+		    gint           *cursor_offset)
 {
-	html->priv->im_pre_len = 0;
+	gint cursor_pos;
+	
+	gtk_im_context_get_preedit_string (html->priv->im_context, str, attrs, &cursor_pos);
+	
+	*len = g_utf8_strlen (*str, -1);
+	cursor_pos = CLAMP (cursor_pos, 0, strlen (*str)); /* Paranoia */
+	*cursor_offset = g_utf8_pointer_to_offset (*str, *str + cursor_pos);
 }
 
 static void
-gtk_html_im_preedit_changed_cb (GtkIMContext *context, GtkHTML *html)
+update_preedit (GtkHTML *html)
 {
 	PangoAttrList *attrs;
 	gchar *preedit_string;
-	gint cursor_pos, cursor_offset, initial_position;
+	gint cursor_offset, preedit_position;
 	gboolean state = html->priv->im_block_reset;
-	gboolean pop_selection = FALSE;
-	gint deleted = 0;
+	gboolean had_selection = FALSE;
+	gboolean had_preedit = html->priv->im_pre_len > 0;
+	gboolean hide_cursor;
 
-	D_IM (printf ("IM preedit changed cb [begin] cursor %d(%p) mark %d(%p) active: %d\n",
-		      html->engine->cursor ? html->engine->cursor->position : 0, html->engine->cursor,
-		      html->engine->mark ? html->engine->mark->position : 0, html->engine->mark,
-		      html_engine_is_selection_active (html->engine));)
 	html->priv->im_block_reset = TRUE;
 
+	html_undo_freeze (html->engine->undo);
+
+	/* The handling of the combination of a selection and a preedit string
+	 * is imperfect because the selection runs from the 'mark' to the cursor,
+	 * so we can't display the selection plus a separate cursor.
+	 *
+	 * What we do is display the preedit text after the selection (even if
+	 * the cursor is at the beginning of the selection), and hide the cursor.
+	 * It is generally going to be less confusing not to display a cursor
+	 * at all then to display a cursor in the wrong location. Putting the
+	 * preedit text after the selection has the handy side effect that the
+	 * preedit string doesn't affect the selection bounds.
+	 */
 	if (html_engine_is_selection_active (html->engine)) {
-		D_IM (printf ("IM push selection\n");)
 		html_engine_selection_push (html->engine);
+
+		preedit_position = MAX (html_cursor_get_position (html->engine->mark),
+					html_cursor_get_position (html->engine->cursor));
+		
+		/* We need to temporarily remove the selection, or when we paste in the preedit text,
+		 * it will replace the selection. 
+		 */
 		html_engine_disable_selection (html->engine);
+		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, preedit_position);
 		html_engine_edit_selection_updater_update_now (html->engine->selection_updater);
-		pop_selection = TRUE;
+		had_selection = TRUE;
 	}
-	initial_position = html->engine->cursor->position;
-	D_IM (printf ("IM initial position %d\n", initial_position);)
 
-	html_undo_freeze (html->engine->undo);
-
-	if (html->priv->im_pre_len > 0) {
-		D_IM (printf ("IM delete last preedit %d + %d\n", html->priv->im_pre_pos, html->priv->im_pre_len);)
-		
-		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos);
-		html_engine_set_mark (html->engine);
-		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + html->priv->im_pre_len);
-		html_engine_delete (html->engine);
-		deleted = html->priv->im_pre_len;
-	} else
-		html->priv->im_orig_style = html_engine_get_font_style (html->engine);
-
-	gtk_im_context_get_preedit_string (html->priv->im_context, &preedit_string, &attrs, &cursor_pos);
-	cursor_pos = CLAMP (cursor_pos, 0, strlen (preedit_string)); /* Paranoia */
-	cursor_offset = g_utf8_pointer_to_offset (preedit_string, preedit_string + cursor_pos);
-
-	D_IM (printf ("IM preedit changed to %s\n", preedit_string);)
-	html->priv->im_pre_len = g_utf8_strlen (preedit_string, -1);
+	/* Insert the new preedit string
+	 */
+	get_preedit_string (html, &preedit_string, &attrs, &html->priv->im_pre_len, &cursor_offset);
 
 	if (html->priv->im_pre_len > 0) {
 		html->priv->im_pre_pos = html->engine->cursor->position;
 		html_engine_paste_text_with_extra_attributes (html->engine, preedit_string, html->priv->im_pre_len, attrs);
-		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + cursor_offset);
-	} else
+	} else if (had_preedit)
 		html_engine_set_font_style (html->engine, 0, html->priv->im_orig_style);
+
 	g_free (preedit_string);
+	pango_attr_list_unref (attrs);
 
-	if (pop_selection) {
-		gint position= html->engine->cursor->position, cpos, mpos;
-		D_IM (printf ("IM pop selection\n");)
-		g_assert (html_engine_selection_stack_top (html->engine, &cpos, &mpos));
-		if (position < MAX (cpos, mpos) + html->priv->im_pre_len - deleted)
-			g_assert (html_engine_selection_stack_top_modify (html->engine, html->priv->im_pre_len - deleted));
-		html_engine_selection_pop (html->engine);
+	/* Update the cursor hiding
+	 */
+	hide_cursor = had_selection && html->priv->im_pre_len > 0;
+	if (hide_cursor != html->priv->im_hide_cursor) {
+		if (hide_cursor)
+			html_engine_hide_cursor (html->engine);
+		else
+			html_engine_show_cursor (html->engine);
+
+		html->priv->im_hide_cursor = hide_cursor;
 	}
-	/* that works for now, but idealy we should be able to have cursor positioned outside selection, so that preedit
-	   cursor is in the right place */
-	if (html->priv->im_pre_len == 0)
-		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine,
-						       initial_position >= html->priv->im_pre_pos + deleted ? initial_position - deleted : initial_position);
+
+	/* Get the correct cursor and mark for the new preedit string
+	 */
+	if (had_selection)
+		html_engine_selection_pop (html->engine);
+	else if (html->priv->im_pre_len > 0)
+		html_cursor_jump_to_position_no_spell (html->engine->cursor, html->engine, html->priv->im_pre_pos + cursor_offset);
 
 	if (html->engine->freeze_count == 1)
 		html_engine_thaw_idle_flush (html->engine);
-	/* FIXME gtk_im_context_set_cursor_location (im_context, &area); */
+	
 	html->priv->im_block_reset = state;
 
 	html_undo_thaw (html->engine->undo);
+}
+
+static void
+gtk_html_im_commit_cb (GtkIMContext *context, const gchar *str, GtkHTML *html)
+{
+	gboolean state = html->priv->im_block_reset;
 
-	D_IM (printf ("IM preedit changed cb [end] cursor %d(%p) mark %d(%p) active: %d\n",
-		      html->engine->cursor ? html->engine->cursor->position : 0, html->engine->cursor,
-		      html->engine->mark ? html->engine->mark->position : 0, html->engine->mark,
-		      html_engine_is_selection_active (html->engine));)
+	html->priv->im_block_reset = TRUE;
+	
+	D_IM (printf ("IM commit %s\n", str);)
+	
+	remove_preedit (html);
+
+	html_engine_paste_text (html->engine, str, -1);
+	
+	update_preedit (html);
+
+	html->priv->im_block_reset = state;
+}
+
+static void
+gtk_html_im_preedit_start_cb (GtkIMContext *context, GtkHTML *html)
+{
+	html->priv->im_pre_len = 0;
+}
+
+static void
+gtk_html_im_preedit_changed_cb (GtkIMContext *context, GtkHTML *html)
+{
+	remove_preedit (html);
+	update_preedit (html);
 }
 
 static gchar *
--- gtkhtml-private.h	14 May 2004 11:24:15 -0000	1.44
+++ gtkhtml-private.h	21 Sep 2004 18:03:53 -0000
@@ -57,6 +57,7 @@ struct _GtkHTMLPrivate {
 	gint im_pre_len;
 	gint im_pre_pos;
 	GtkHTMLFontStyle im_orig_style;
+	gboolean im_hide_cursor;
 	gboolean im_block_reset;
 
 	HTMLObject *dnd_object;
