Index: gtk/gtktextlayout.c
===================================================================
--- gtk/gtktextlayout.c	(revision 17783)
+++ gtk/gtktextlayout.c	(working copy)
@@ -140,6 +140,7 @@ static void gtk_text_layout_buffer_delet
 						 gpointer           data);
 
 static void gtk_text_layout_update_cursor_line (GtkTextLayout *layout);
+static void gtk_text_layout_update_line_display_cursors (GtkTextLayout *layout);
 
 enum {
   INVALIDATED,
@@ -498,7 +499,7 @@ gtk_text_layout_set_cursor_visible (GtkT
       gtk_text_layout_get_line_yrange (layout, &iter, &y, &height);
       gtk_text_layout_emit_changed (layout, y, height, height);
 
-      gtk_text_layout_invalidate_cache (layout, _gtk_text_iter_get_text_line (&iter));
+      gtk_text_layout_update_line_display_cursors (layout);
     }
 }
 
@@ -1787,6 +1789,202 @@ add_preedit_attrs (GtkTextLayout     *la
   pango_attr_iterator_destroy (iter);
 }
 
+static void
+gtk_text_layout_update_line_display_cursors (GtkTextLayout *layout)
+{
+  GtkTextLineDisplay *display;
+  GtkTextLine *line;
+  GtkTextLineSegment *seg;
+  GtkTextIter iter;
+  GtkTextAttributes *style;
+  gint layout_byte_offset, buffer_byte_offset;
+  GSList *cursor_byte_offsets = NULL;
+  GSList *cursor_segs = NULL;
+  GSList *tmp_list1, *tmp_list2;
+  
+  display = layout->one_display_cache;
+  if (!display)
+    return;
+
+  if (display->cursors)
+    {
+      g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
+      g_slist_free (display->cursors);
+      display->cursors = NULL;
+    }
+
+  line = display->line;
+
+  _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
+                                    &iter, line, 0);
+
+  /* Special-case optimization for completely
+   * invisible lines; makes it faster to deal
+   * with sequences of invisible lines.
+   */
+  if (totally_invisible_line (layout, line, &iter))
+    return;
+
+  layout_byte_offset = 0; /* current length of layout text (includes preedit, does not include invisible text) */
+  buffer_byte_offset = 0; /* position in the buffer line */
+  seg = _gtk_text_iter_get_any_segment (&iter);
+  while (seg != NULL)
+    {
+      /* Displayable segments */
+      if (seg->type == &gtk_text_char_type ||
+          seg->type == &gtk_text_pixbuf_type ||
+          seg->type == &gtk_text_child_type)
+        {
+          _gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
+                                            &iter, line,
+                                            buffer_byte_offset);
+          style = get_style (layout, &iter);
+
+          /* First see if the chunk is invisible, and ignore it if so. Tk
+           * looked at tabs, wrap mode, etc. before doing this, but
+           * that made no sense to me, so I am just skipping the
+           * invisible chunks
+           */
+          if (!style->invisible)
+            {
+              if (seg->type == &gtk_text_char_type)
+                {
+                  /* We don't want to split segments because of marks,
+                   * so we scan forward for more segments only
+                   * separated from us by marks. In theory, we should
+                   * also merge segments with identical styles, even
+                   * if there are toggles in-between
+                   */
+
+                  gint bytes = 0;
+ 		  GtkTextLineSegment *prev_seg = NULL;
+  
+ 		  while (seg)
+                    {
+                      if (seg->type == &gtk_text_char_type)
+                        {
+                          layout_byte_offset += seg->byte_count;
+                          buffer_byte_offset += seg->byte_count;
+                          bytes += seg->byte_count;
+                        }
+ 		      else if (seg->type == &gtk_text_right_mark_type ||
+ 			       seg->type == &gtk_text_left_mark_type)
+                        {
+ 			  /* If we have preedit string, break out of this loop - we'll almost
+ 			   * certainly have different attributes on the preedit string
+ 			   */
+
+ 			  if (layout->preedit_len > 0 &&
+ 			      _gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
+ 							     seg->body.mark.obj))
+			    break;
+
+ 			  if (seg->body.mark.visible)
+ 			    {
+			      cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (layout_byte_offset));
+			      cursor_segs = g_slist_prepend (cursor_segs, seg);
+			    }
+                        }
+		      else
+			break;
+
+ 		      prev_seg = seg;
+                      seg = seg->next;
+                    }
+
+ 		  seg = prev_seg; /* Back up one */
+                }
+              else if (seg->type == &gtk_text_pixbuf_type)
+                {
+                  layout_byte_offset += seg->byte_count;
+                  buffer_byte_offset += seg->byte_count;
+                }
+              else if (seg->type == &gtk_text_child_type)
+                {
+                  layout_byte_offset += seg->byte_count;
+                  buffer_byte_offset += seg->byte_count;
+                }
+              else
+                {
+                  /* We don't know this segment type */
+                  g_assert_not_reached ();
+                }
+              
+            } /* if (segment was visible) */
+          else
+            {
+              /* Invisible segment */
+              buffer_byte_offset += seg->byte_count;
+            }
+
+          release_style (layout, style);
+        }
+
+      /* Toggles */
+      else if (seg->type == &gtk_text_toggle_on_type ||
+               seg->type == &gtk_text_toggle_off_type)
+        {
+          /* Style may have changed, drop our
+             current cached style */
+          invalidate_cached_style (layout);
+        }
+
+      /* Marks */
+      else if (seg->type == &gtk_text_right_mark_type ||
+               seg->type == &gtk_text_left_mark_type)
+        {
+	  gint cursor_offset = 0;
+ 	  
+	  /* At the insertion point, add the preedit string, if any */
+	  
+	  if (_gtk_text_btree_mark_is_insert (_gtk_text_buffer_get_btree (layout->buffer),
+					     seg->body.mark.obj))
+	    {
+	      display->insert_index = layout_byte_offset;
+	      
+	      if (layout->preedit_len > 0)
+		{
+		  layout_byte_offset += layout->preedit_len;
+                  /* DO NOT increment the buffer byte offset for preedit */
+                  
+		  cursor_offset = layout->preedit_cursor - layout->preedit_len;
+		}
+	    }
+	  
+
+          /* Display visible marks */
+
+          if (seg->body.mark.visible)
+            {
+              cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets,
+                                                     GINT_TO_POINTER (layout_byte_offset + cursor_offset));
+              cursor_segs = g_slist_prepend (cursor_segs, seg);
+            }
+        }
+
+      else
+        g_error ("Unknown segment type: %s", seg->type->name);
+
+      seg = seg->next;
+    }
+
+  tmp_list1 = cursor_byte_offsets;
+  tmp_list2 = cursor_segs;
+  while (tmp_list1)
+    {
+      add_cursor (layout, display, tmp_list2->data,
+                  GPOINTER_TO_INT (tmp_list1->data));
+      tmp_list1 = tmp_list1->next;
+      tmp_list2 = tmp_list2->next;
+    }
+  g_slist_free (cursor_byte_offsets);
+  g_slist_free (cursor_segs);
+
+  /* Free this if we aren't in a loop */
+  if (layout->wrap_loop_count == 0)
+    invalidate_cached_style (layout);
+}
+
 GtkTextLineDisplay *
 gtk_text_layout_get_line_display (GtkTextLayout *layout,
                                   GtkTextLine   *line,

