]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbuffer.c
3a11c9dfa8dc3f44e5810fa73b53496a8e716f9f
[~andy/gtk] / gtk / gtktextbuffer.c
1 /* GTK - The GIMP Toolkit
2  * gtktextbuffer.c Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27
28 #include <string.h>
29 #include <stdarg.h>
30
31 #include "gtkclipboard.h"
32 #include "gtkinvisible.h"
33 #include "gtksignal.h"
34 #include "gtktextbuffer.h"
35 #include "gtktextbtree.h"
36 #include "gtktextiterprivate.h"
37 #include <string.h>
38
39 typedef struct _ClipboardRequest ClipboardRequest;
40
41 struct _ClipboardRequest
42 {
43   GtkTextBuffer *buffer;
44   gboolean interactive;
45   gboolean default_editable;
46   gboolean is_clipboard;
47   gboolean replace_selection;
48 };
49
50 enum {
51   INSERT_TEXT,
52   INSERT_PIXBUF,
53   INSERT_CHILD_ANCHOR,
54   DELETE_RANGE,
55   CHANGED,
56   MODIFIED_CHANGED,
57   MARK_SET,
58   MARK_DELETED,
59   APPLY_TAG,
60   REMOVE_TAG,
61   BEGIN_USER_ACTION,
62   END_USER_ACTION,
63   LAST_SIGNAL
64 };
65
66 enum {
67   ARG_0,
68   LAST_ARG
69 };
70
71 enum {
72   TARGET_STRING,
73   TARGET_TEXT,
74   TARGET_COMPOUND_TEXT,
75   TARGET_UTF8_STRING,
76   TARGET_TEXT_BUFFER_CONTENTS
77 };
78
79 static void gtk_text_buffer_init       (GtkTextBuffer      *tkxt_buffer);
80 static void gtk_text_buffer_class_init (GtkTextBufferClass *klass);
81 static void gtk_text_buffer_finalize   (GObject            *object);
82
83
84 static void gtk_text_buffer_real_insert_text           (GtkTextBuffer     *buffer,
85                                                         GtkTextIter       *iter,
86                                                         const gchar       *text,
87                                                         gint               len);
88 static void gtk_text_buffer_real_insert_pixbuf         (GtkTextBuffer     *buffer,
89                                                         GtkTextIter       *iter,
90                                                         GdkPixbuf         *pixbuf);
91 static void gtk_text_buffer_real_insert_anchor         (GtkTextBuffer     *buffer,
92                                                         GtkTextIter       *iter,
93                                                         GtkTextChildAnchor *anchor);
94 static void gtk_text_buffer_real_delete_range          (GtkTextBuffer     *buffer,
95                                                         GtkTextIter       *start,
96                                                         GtkTextIter       *end);
97 static void gtk_text_buffer_real_apply_tag             (GtkTextBuffer     *buffer,
98                                                         GtkTextTag        *tag,
99                                                         const GtkTextIter *start_char,
100                                                         const GtkTextIter *end_char);
101 static void gtk_text_buffer_real_remove_tag            (GtkTextBuffer     *buffer,
102                                                         GtkTextTag        *tag,
103                                                         const GtkTextIter *start_char,
104                                                         const GtkTextIter *end_char);
105 static void gtk_text_buffer_real_changed               (GtkTextBuffer     *buffer);
106
107 static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
108 static void          free_log_attr_cache (GtkTextLogAttrCache *cache);
109
110 static void remove_all_clipboard_contents_buffers (GtkTextBuffer *buffer);
111 static void remove_all_selection_clipboards       (GtkTextBuffer *buffer);
112 static void update_selection_clipboards           (GtkTextBuffer *buffer);
113
114 static GtkObjectClass *parent_class = NULL;
115 static guint signals[LAST_SIGNAL] = { 0 };
116
117 GType
118 gtk_text_buffer_get_type (void)
119 {
120   static GType our_type = 0;
121
122   if (our_type == 0)
123     {
124       static const GTypeInfo our_info =
125       {
126         sizeof (GtkTextBufferClass),
127         (GBaseInitFunc) NULL,
128         (GBaseFinalizeFunc) NULL,
129         (GClassInitFunc) gtk_text_buffer_class_init,
130         NULL,           /* class_finalize */
131         NULL,           /* class_data */
132         sizeof (GtkTextBuffer),
133         0,              /* n_preallocs */
134         (GInstanceInitFunc) gtk_text_buffer_init
135       };
136
137       our_type = g_type_register_static (G_TYPE_OBJECT,
138                                          "GtkTextBuffer",
139                                          &our_info,
140                                          0);
141     }
142
143   return our_type;
144 }
145
146 static void
147 gtk_text_buffer_class_init (GtkTextBufferClass *klass)
148 {
149   GObjectClass *object_class = G_OBJECT_CLASS (klass);
150
151   parent_class = g_type_class_peek_parent (klass);
152
153   object_class->finalize = gtk_text_buffer_finalize;
154
155   klass->insert_text = gtk_text_buffer_real_insert_text;
156   klass->insert_pixbuf = gtk_text_buffer_real_insert_pixbuf;
157   klass->insert_child_anchor = gtk_text_buffer_real_insert_anchor;
158   klass->delete_range = gtk_text_buffer_real_delete_range;
159   klass->apply_tag = gtk_text_buffer_real_apply_tag;
160   klass->remove_tag = gtk_text_buffer_real_remove_tag;
161   klass->changed = gtk_text_buffer_real_changed;
162
163   signals[INSERT_TEXT] =
164     g_signal_new ("insert_text",
165                   G_OBJECT_CLASS_TYPE (object_class),
166                   G_SIGNAL_RUN_LAST,
167                   G_STRUCT_OFFSET (GtkTextBufferClass, insert_text),
168                   NULL, NULL,
169                   gtk_marshal_VOID__BOXED_STRING_INT,
170                   GTK_TYPE_NONE,
171                   3,
172                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
173                   GTK_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
174                   GTK_TYPE_INT);
175
176   signals[INSERT_PIXBUF] =
177     g_signal_new ("insert_pixbuf",
178                   G_OBJECT_CLASS_TYPE (object_class),
179                   G_SIGNAL_RUN_LAST,
180                   G_STRUCT_OFFSET (GtkTextBufferClass, insert_pixbuf),
181                   NULL, NULL,
182                   gtk_marshal_VOID__BOXED_OBJECT,
183                   GTK_TYPE_NONE,
184                   2,
185                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
186                   GDK_TYPE_PIXBUF);
187
188   signals[INSERT_CHILD_ANCHOR] =
189     g_signal_new ("insert_child_anchor",
190                   G_OBJECT_CLASS_TYPE (object_class),
191                   G_SIGNAL_RUN_LAST,
192                   G_STRUCT_OFFSET (GtkTextBufferClass, insert_child_anchor),
193                   NULL, NULL,
194                   gtk_marshal_VOID__BOXED_OBJECT,
195                   GTK_TYPE_NONE,
196                   2,
197                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
198                   GTK_TYPE_TEXT_CHILD_ANCHOR);
199   
200   signals[DELETE_RANGE] =
201     g_signal_new ("delete_range",
202                   G_OBJECT_CLASS_TYPE (object_class),
203                   G_SIGNAL_RUN_LAST,
204                   G_STRUCT_OFFSET (GtkTextBufferClass, delete_range),
205                   NULL, NULL,
206                   gtk_marshal_VOID__BOXED_BOXED,
207                   GTK_TYPE_NONE,
208                   2,
209                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
210                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
211
212   signals[CHANGED] =
213     g_signal_new ("changed",
214                   G_OBJECT_CLASS_TYPE (object_class),
215                   G_SIGNAL_RUN_LAST,                   
216                   G_STRUCT_OFFSET (GtkTextBufferClass, changed),
217                   NULL, NULL,
218                   gtk_marshal_VOID__VOID,
219                   GTK_TYPE_NONE,
220                   0);
221
222   signals[MODIFIED_CHANGED] =
223     g_signal_new ("modified_changed",
224                   G_OBJECT_CLASS_TYPE (object_class),
225                   G_SIGNAL_RUN_LAST,
226                   G_STRUCT_OFFSET (GtkTextBufferClass, modified_changed),
227                   NULL, NULL,
228                   gtk_marshal_VOID__VOID,
229                   GTK_TYPE_NONE,
230                   0);
231
232   signals[MARK_SET] =
233     g_signal_new ("mark_set",
234                   G_OBJECT_CLASS_TYPE (object_class),
235                   G_SIGNAL_RUN_LAST,                   
236                   G_STRUCT_OFFSET (GtkTextBufferClass, mark_set),
237                   NULL, NULL,
238                   gtk_marshal_VOID__BOXED_OBJECT,
239                   GTK_TYPE_NONE,
240                   2,
241                   GTK_TYPE_TEXT_ITER,
242                   GTK_TYPE_TEXT_MARK);
243
244   signals[MARK_DELETED] =
245     g_signal_new ("mark_deleted",
246                   G_OBJECT_CLASS_TYPE (object_class),
247                   G_SIGNAL_RUN_LAST,                   
248                   G_STRUCT_OFFSET (GtkTextBufferClass, mark_deleted),
249                   NULL, NULL,
250                   gtk_marshal_VOID__OBJECT,
251                   GTK_TYPE_NONE,
252                   1,
253                   GTK_TYPE_TEXT_MARK);
254   
255   signals[APPLY_TAG] =
256     g_signal_new ("apply_tag",
257                   G_OBJECT_CLASS_TYPE (object_class),
258                   G_SIGNAL_RUN_LAST,
259                   G_STRUCT_OFFSET (GtkTextBufferClass, apply_tag),
260                   NULL, NULL,
261                   gtk_marshal_VOID__OBJECT_BOXED_BOXED,
262                   GTK_TYPE_NONE,
263                   3,
264                   GTK_TYPE_TEXT_TAG,
265                   GTK_TYPE_TEXT_ITER,
266                   GTK_TYPE_TEXT_ITER);
267
268   signals[REMOVE_TAG] =
269     g_signal_new ("remove_tag",
270                   G_OBJECT_CLASS_TYPE (object_class),
271                   G_SIGNAL_RUN_LAST,
272                   G_STRUCT_OFFSET (GtkTextBufferClass, remove_tag),
273                   NULL, NULL,
274                   gtk_marshal_VOID__OBJECT_BOXED_BOXED,
275                   GTK_TYPE_NONE,
276                   3,
277                   GTK_TYPE_TEXT_TAG,
278                   GTK_TYPE_TEXT_ITER,
279                   GTK_TYPE_TEXT_ITER);
280
281   signals[BEGIN_USER_ACTION] =
282     g_signal_new ("begin_user_action",
283                   G_OBJECT_CLASS_TYPE (object_class),
284                   G_SIGNAL_RUN_LAST,                   
285                   G_STRUCT_OFFSET (GtkTextBufferClass, begin_user_action),
286                   NULL, NULL,
287                   gtk_marshal_VOID__VOID,
288                   GTK_TYPE_NONE,
289                   0);
290
291   signals[END_USER_ACTION] =
292     g_signal_new ("end_user_action",
293                   G_OBJECT_CLASS_TYPE (object_class),
294                   G_SIGNAL_RUN_LAST,                   
295                   G_STRUCT_OFFSET (GtkTextBufferClass, end_user_action),
296                   NULL, NULL,
297                   gtk_marshal_VOID__VOID,
298                   GTK_TYPE_NONE,
299                   0);  
300 }
301
302 void
303 gtk_text_buffer_init (GtkTextBuffer *buffer)
304 {
305   buffer->clipboard_contents_buffers = NULL;
306 }
307
308 /**
309  * gtk_text_buffer_new:
310  * @table: a tag table, or NULL to create a new one
311  *
312  * Creates a new text buffer.
313  *
314  * Return value: a new text buffer
315  **/
316 GtkTextBuffer*
317 gtk_text_buffer_new (GtkTextTagTable *table)
318 {
319   GtkTextBuffer *text_buffer;
320
321   text_buffer = GTK_TEXT_BUFFER (g_object_new (gtk_text_buffer_get_type (), NULL));
322
323   if (table)
324     {
325       text_buffer->tag_table = table;
326
327       g_object_ref (G_OBJECT (text_buffer->tag_table));
328     }
329   
330   return text_buffer;
331 }
332
333 static void
334 gtk_text_buffer_finalize (GObject *object)
335 {
336   GtkTextBuffer *buffer;
337
338   buffer = GTK_TEXT_BUFFER (object);
339
340   remove_all_clipboard_contents_buffers (buffer);
341   remove_all_selection_clipboards (buffer);
342
343   if (buffer->tag_table)
344     {
345       g_object_unref (G_OBJECT (buffer->tag_table));
346       buffer->tag_table = NULL;
347     }
348
349   if (buffer->btree)
350     {
351       _gtk_text_btree_unref (buffer->btree);
352       buffer->btree = NULL;
353     }
354
355   if (buffer->log_attr_cache)
356     free_log_attr_cache (buffer->log_attr_cache);
357
358   buffer->log_attr_cache = NULL;
359   
360   G_OBJECT_CLASS (parent_class)->finalize (object);
361 }
362
363 static GtkTextTagTable*
364 get_table (GtkTextBuffer *buffer)
365 {
366   if (buffer->tag_table == NULL)
367     buffer->tag_table = gtk_text_tag_table_new ();
368
369   return buffer->tag_table;
370 }
371
372 static GtkTextBTree*
373 get_btree (GtkTextBuffer *buffer)
374 {
375   if (buffer->btree == NULL)
376     buffer->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer),
377                                         buffer);
378
379   return buffer->btree;
380 }
381
382 GtkTextBTree*
383 _gtk_text_buffer_get_btree (GtkTextBuffer *buffer)
384 {
385   return get_btree (buffer);
386 }
387
388 /**
389  * gtk_text_buffer_get_tag_table:
390  * @buffer: a #GtkTextBuffer
391  *
392  * Get the #GtkTextTagTable associated with this buffer.
393  *
394  * Return value: the buffer's tag table
395  **/
396 GtkTextTagTable*
397 gtk_text_buffer_get_tag_table (GtkTextBuffer  *buffer)
398 {
399   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
400
401   return get_table (buffer);
402 }
403
404 /**
405  * gtk_text_buffer_set_text:
406  * @buffer: a #GtkTextBuffer
407  * @text: UTF-8 text to insert
408  * @len: length of @text in bytes
409  *
410  * Deletes current contents of @buffer, and inserts @text instead. If
411  * @len is -1, @text must be nul-terminated. @text must be valid UTF-8.
412  **/
413 void
414 gtk_text_buffer_set_text (GtkTextBuffer *buffer,
415                           const gchar   *text,
416                           gint           len)
417 {
418   GtkTextIter start, end;
419
420   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
421   g_return_if_fail (text != NULL);
422
423   if (len < 0)
424     len = strlen (text);
425
426   gtk_text_buffer_get_bounds (buffer, &start, &end);
427
428   gtk_text_buffer_delete (buffer, &start, &end);
429
430   if (len > 0)
431     {
432       gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
433       gtk_text_buffer_insert (buffer, &start, text, len);
434     }
435 }
436
437 /*
438  * Insertion
439  */
440
441 static void
442
443 gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
444                                   GtkTextIter *iter,
445                                   const gchar *text,
446                                   gint len)
447 {
448   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
449   g_return_if_fail (iter != NULL);
450   
451   _gtk_text_btree_insert (iter, text, len);
452
453   g_signal_emit (G_OBJECT (buffer), signals[CHANGED], 0);
454 }
455
456 static void
457 gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
458                              GtkTextIter   *iter,
459                              const gchar   *text,
460                              gint           len)
461 {
462   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
463   g_return_if_fail (iter != NULL);
464   g_return_if_fail (text != NULL);
465
466   if (len < 0)
467     len = strlen (text);
468
469   g_return_if_fail (g_utf8_validate (text, len, NULL));
470   
471   if (len > 0)
472     {
473       g_signal_emit (G_OBJECT (buffer), signals[INSERT_TEXT], 0,
474                      iter, text, len);
475     }
476 }
477
478 /**
479  * gtk_text_buffer_insert:
480  * @buffer: a #GtkTextBuffer
481  * @iter: a position in the buffer
482  * @text: UTF-8 format text to insert
483  * @len: length of text in bytes, or -1
484  *
485  * Inserts @len bytes of @text at position @iter.  If @len is -1,
486  * @text must be nul-terminated and will be inserted in its
487  * entirety. Emits the "insert_text" signal; insertion actually occurs
488  * in the default handler for the signal. @iter is invalidated when
489  * insertion occurs (because the buffer contents change), but the
490  * default signal handler revalidates it to point to the end of the
491  * inserted text.
492  *
493  **/
494 void
495 gtk_text_buffer_insert (GtkTextBuffer *buffer,
496                         GtkTextIter *iter,
497                         const gchar *text,
498                         gint len)
499 {
500   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
501   g_return_if_fail (iter != NULL);
502   g_return_if_fail (text != NULL);
503   g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
504   
505   gtk_text_buffer_emit_insert (buffer, iter, text, len);
506 }
507
508 /**
509  * gtk_text_buffer_insert_at_cursor:
510  * @buffer: a #GtkTextBuffer
511  * @text: some text in UTF-8 format
512  * @len: length of text, in bytes
513  *
514  * Simply calls gtk_text_buffer_insert (), using the current
515  * cursor position as the insertion point.
516  **/
517 void
518 gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
519                                   const gchar *text,
520                                   gint len)
521 {
522   GtkTextIter iter;
523
524   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
525   g_return_if_fail (text != NULL);
526
527   gtk_text_buffer_get_iter_at_mark (buffer, &iter,
528                                     gtk_text_buffer_get_mark (buffer,
529                                                               "insert"));
530
531   gtk_text_buffer_insert (buffer, &iter, text, len);
532 }
533
534 /**
535  * gtk_text_buffer_insert_interactive:
536  * @buffer: a #GtkTextBuffer
537  * @iter: a position in @buffer
538  * @text: some UTF-8 text
539  * @len: length of text in bytes, or -1
540  * @default_editable: default editability of buffer
541  *
542  * Like gtk_text_buffer_insert (), but the insertion will not occur if
543  * @iter is at a non-editable location in the buffer.  Usually you
544  * want to prevent insertions at ineditable locations if the insertion
545  * results from a user action (is interactive).
546  *
547  * @default_editable indicates the editability of text that doesn't
548  * have a tag affecting editability applied to it. Typically the
549  * result of gtk_text_view_get_editable() is appropriate here.
550  *
551  * Return value: whether text was actually inserted
552  **/
553 gboolean
554 gtk_text_buffer_insert_interactive (GtkTextBuffer *buffer,
555                                     GtkTextIter   *iter,
556                                     const gchar   *text,
557                                     gint           len,
558                                     gboolean       default_editable)
559 {
560   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
561   g_return_val_if_fail (text != NULL, FALSE);
562   g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, FALSE);
563
564   if (gtk_text_iter_editable (iter, default_editable))
565     {
566       gtk_text_buffer_begin_user_action (buffer);
567       gtk_text_buffer_emit_insert (buffer, iter, text, len);
568       gtk_text_buffer_end_user_action (buffer);
569       return TRUE;
570     }
571   else
572     return FALSE;
573 }
574
575 /**
576  * gtk_text_buffer_insert_interactive_at_cursor:
577  * @buffer: a #GtkTextBuffer
578  * @text: text in UTF-8 format
579  * @len: length of text in bytes, or -1
580  * @default_editable: default editability of buffer
581  *
582  * Calls gtk_text_buffer_insert_interactive () at the cursor
583  * position.
584  *
585  * @default_editable indicates the editability of text that doesn't
586  * have a tag affecting editability applied to it. Typically the
587  * result of gtk_text_view_get_editable() is appropriate here.
588  * 
589  * Return value: whether text was actually inserted
590  **/
591 gboolean
592 gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer,
593                                               const gchar   *text,
594                                               gint           len,
595                                               gboolean       default_editable)
596 {
597   GtkTextIter iter;
598
599   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
600   g_return_val_if_fail (text != NULL, FALSE);
601
602   gtk_text_buffer_get_iter_at_mark (buffer, &iter,
603                                     gtk_text_buffer_get_mark (buffer,
604                                                               "insert"));
605
606   return gtk_text_buffer_insert_interactive (buffer, &iter, text, len,
607                                              default_editable);
608 }
609
610 static gboolean
611 possibly_not_text (gunichar ch,
612                    gpointer user_data)
613 {
614   return ch == GTK_TEXT_UNKNOWN_CHAR;
615 }
616
617 static void
618 insert_text_range (GtkTextBuffer     *buffer,
619                    GtkTextIter       *iter,
620                    const GtkTextIter *orig_start,
621                    const GtkTextIter *orig_end,
622                    gboolean           interactive)
623 {
624   gchar *text;
625
626   text = gtk_text_iter_get_text (orig_start, orig_end);
627
628   gtk_text_buffer_emit_insert (buffer, iter, text, -1);
629
630   g_free (text);
631 }
632
633 typedef struct _Range Range;
634 struct _Range
635 {
636   GtkTextBuffer *buffer;
637   GtkTextMark *start_mark;
638   GtkTextMark *end_mark;
639   GtkTextMark *whole_end_mark;
640   GtkTextIter *range_start;
641   GtkTextIter *range_end;
642   GtkTextIter *whole_end;
643 };
644
645 static Range*
646 save_range (GtkTextIter *range_start,
647             GtkTextIter *range_end,
648             GtkTextIter *whole_end)
649 {
650   Range *r;
651
652   r = g_new (Range, 1);
653
654   r->buffer = gtk_text_iter_get_buffer (range_start);
655   g_object_ref (G_OBJECT (r->buffer));
656   
657   r->start_mark = 
658     gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
659                                  NULL,
660                                  range_start,
661                                  TRUE);
662   r->end_mark = 
663     gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
664                                  NULL,
665                                  range_end,
666                                  FALSE);
667
668   r->whole_end_mark = 
669     gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
670                                  NULL,
671                                  whole_end,
672                                  FALSE);
673
674   r->range_start = range_start;
675   r->range_end = range_end;
676   r->whole_end = whole_end;
677
678   return r;
679 }
680
681 static void
682 restore_range (Range *r)
683 {
684   gtk_text_buffer_get_iter_at_mark (r->buffer,
685                                     r->range_start,
686                                     r->start_mark);
687       
688   gtk_text_buffer_get_iter_at_mark (r->buffer,
689                                     r->range_end,
690                                     r->end_mark);
691       
692   gtk_text_buffer_get_iter_at_mark (r->buffer,
693                                     r->whole_end,
694                                     r->whole_end_mark);
695       
696   gtk_text_buffer_delete_mark (r->buffer, r->start_mark);
697   gtk_text_buffer_delete_mark (r->buffer, r->end_mark);
698   gtk_text_buffer_delete_mark (r->buffer, r->whole_end_mark);
699
700   g_object_unref (G_OBJECT (r->buffer));
701   g_free (r); 
702 }
703
704 static void
705 insert_range_untagged (GtkTextBuffer     *buffer,
706                        GtkTextIter       *iter,
707                        const GtkTextIter *orig_start,
708                        const GtkTextIter *orig_end,
709                        gboolean           interactive)
710 {
711   GtkTextIter range_start;
712   GtkTextIter range_end;
713   GtkTextIter start, end;
714   GtkTextBuffer *src_buffer;
715   Range *r;
716   
717   if (gtk_text_iter_equal (orig_start, orig_end))
718     return;
719
720   start = *orig_start;
721   end = *orig_end;
722   
723   src_buffer = gtk_text_iter_get_buffer (&start);
724   
725   range_start = start;
726   range_end = start;
727   
728   while (TRUE)
729     {
730       if (gtk_text_iter_equal (&range_start, &range_end))
731         {
732           /* Figure out how to move forward */
733
734           g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
735           
736           if (gtk_text_iter_equal (&range_end, &end))
737             {
738               /* nothing left to do */
739               break;
740             }
741           else if (gtk_text_iter_get_char (&range_end) == GTK_TEXT_UNKNOWN_CHAR)
742             {
743               GdkPixbuf *pixbuf = NULL;
744               GtkTextChildAnchor *anchor = NULL;
745               pixbuf = gtk_text_iter_get_pixbuf (&range_end);
746               anchor = gtk_text_iter_get_child_anchor (&range_end);
747
748               if (pixbuf)
749                 {
750                   r = save_range (&range_start,
751                                   &range_end,
752                                   &end);
753
754                   gtk_text_buffer_insert_pixbuf (buffer,
755                                                  iter,
756                                                  pixbuf);
757
758                   restore_range (r);
759                   r = NULL;
760                   
761                   gtk_text_iter_forward_char (&range_end);
762                   
763                   range_start = range_end;
764                 }
765               else if (anchor)
766                 {
767                   /* Just skip anchors */
768
769                   gtk_text_iter_forward_char (&range_end);
770                   range_start = range_end;
771                 }
772               else
773                 {
774                   /* The GTK_TEXT_UNKNOWN_CHAR was in a text segment, so
775                    * keep going. 
776                    */
777                   gtk_text_iter_forward_find_char (&range_end,
778                                                    possibly_not_text, NULL,
779                                                    &end);
780                   
781                   g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
782                 }
783             }
784           else
785             {
786               /* Text segment starts here, so forward search to
787                * find its possible endpoint
788                */
789               gtk_text_iter_forward_find_char (&range_end,
790                                                possibly_not_text, NULL,
791                                                &end);
792               
793               g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
794             }
795         }
796       else
797         {
798           r = save_range (&range_start,
799                           &range_end,
800                           &end);
801           
802           insert_text_range (buffer,
803                              iter,
804                              &range_start,
805                              &range_end,
806                              interactive);
807
808           restore_range (r);
809           r = NULL;
810           
811           range_start = range_end;
812         }
813     }
814 }
815
816 static void
817 gtk_text_buffer_real_insert_range (GtkTextBuffer     *buffer,
818                                    GtkTextIter       *iter,
819                                    const GtkTextIter *orig_start,
820                                    const GtkTextIter *orig_end,
821                                    gboolean           interactive)
822 {
823   /* Find each range of uniformly-tagged text, insert it,
824    * then apply the tags.
825    */
826   GtkTextIter start = *orig_start;
827   GtkTextIter end = *orig_end;
828   GtkTextIter range_start;
829   GtkTextIter range_end;
830   GtkTextBuffer *src_buffer;
831   Range *r;
832   
833   if (gtk_text_iter_equal (orig_start, orig_end))
834     return;
835
836   if (interactive)
837     gtk_text_buffer_begin_user_action (buffer);
838   
839   src_buffer = gtk_text_iter_get_buffer (orig_start);
840   
841   gtk_text_iter_order (&start, &end);
842
843   range_start = start;
844   range_end = start;
845
846   while (TRUE)
847     {
848       gint start_offset;
849       GtkTextIter start_iter;
850       GSList *tags;
851       GSList *tmp_list;
852       
853       if (gtk_text_iter_equal (&range_start, &end))
854         break; /* All done */
855
856       g_assert (gtk_text_iter_compare (&range_start, &end) < 0);
857       
858       gtk_text_iter_forward_to_tag_toggle (&range_end, NULL);
859
860       g_assert (!gtk_text_iter_equal (&range_start, &range_end));
861
862       /* Clamp to the end iterator */
863       if (gtk_text_iter_compare (&range_end, &end) > 0)
864         range_end = end;
865       
866       /* We have a range with unique tags; insert it, and
867        * apply all tags.
868        */
869       start_offset = gtk_text_iter_get_offset (iter);
870
871       r = save_range (&range_start, &range_end, &end);
872       
873       insert_range_untagged (buffer, iter, &range_start, &range_end, interactive);
874
875       restore_range (r);
876       r = NULL;
877       
878       gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
879       
880       tags = gtk_text_iter_get_tags (&range_start);
881       tmp_list = tags;
882       while (tmp_list != NULL)
883         {
884           gtk_text_buffer_apply_tag (buffer,
885                                      tmp_list->data,
886                                      &start_iter,
887                                      iter);
888           
889           tmp_list = g_slist_next (tmp_list);
890         }
891       g_slist_free (tags);
892
893       range_start = range_end;
894     }
895
896   if (interactive)
897     gtk_text_buffer_end_user_action (buffer);
898 }
899
900 /**
901  * gtk_text_buffer_insert_range:
902  * @buffer: a #GtkTextBuffer
903  * @iter: a position in @buffer
904  * @start: a position in a #GtkTextBuffer
905  * @end: another position in the same buffer as @start
906  *
907  * Copies text, tags, and pixbufs between @start and @end (the order
908  * of @start and @end doesn't matter) and inserts the copy at @iter.
909  * Used instead of simply getting/inserting text because it preserves
910  * images and tags. If @start and @end are in a different buffer from
911  * @buffer, the two buffers must share the same tag table.
912  *
913  * Implemented via emissions of the insert_text and apply_tag signals,
914  * so expect those.
915  **/
916 void
917 gtk_text_buffer_insert_range (GtkTextBuffer     *buffer,
918                               GtkTextIter       *iter,
919                               const GtkTextIter *start,
920                               const GtkTextIter *end)
921 {
922   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
923   g_return_if_fail (iter != NULL);
924   g_return_if_fail (start != NULL);
925   g_return_if_fail (end != NULL);
926   g_return_if_fail (gtk_text_iter_get_buffer (start) ==
927                     gtk_text_iter_get_buffer (end));
928   g_return_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
929                     buffer->tag_table);  
930   g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
931   
932   gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE);
933 }
934
935 /**
936  * gtk_text_buffer_insert_range_interactive:
937  * @buffer: a #GtkTextBuffer
938  * @iter: a position in @buffer
939  * @start: a position in a #GtkTextBuffer
940  * @end: another position in the same buffer as @start
941  * @default_editable: default editability of the buffer
942  *
943  * Same as gtk_text_buffer_insert_range(), but does nothing if the
944  * insertion point isn't editable. The @default_editable parameter
945  * indicates whether the text is editable at @iter if no tags
946  * enclosing @iter affect editability. Typically the result of
947  * gtk_text_view_get_editable() is appropriate here.
948  *
949  * Returns: whether an insertion was possible at @iter
950  **/
951 gboolean
952 gtk_text_buffer_insert_range_interactive (GtkTextBuffer     *buffer,
953                                           GtkTextIter       *iter,
954                                           const GtkTextIter *start,
955                                           const GtkTextIter *end,
956                                           gboolean           default_editable)
957 {
958   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
959   g_return_val_if_fail (iter != NULL, FALSE);
960   g_return_val_if_fail (start != NULL, FALSE);
961   g_return_val_if_fail (end != NULL, FALSE);
962   g_return_val_if_fail (gtk_text_iter_get_buffer (start) ==
963                         gtk_text_iter_get_buffer (end), FALSE);
964   g_return_val_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
965                         buffer->tag_table, FALSE);
966
967
968   if (gtk_text_iter_editable (iter, default_editable))
969     {
970       gtk_text_buffer_real_insert_range (buffer, iter, start, end, TRUE);
971       return TRUE;
972     }
973   else
974     return FALSE;
975 }
976
977 /**
978  * gtk_text_buffer_insert_with_tags:
979  * @buffer: a #GtkTextBuffer
980  * @iter: an iterator in @buffer
981  * @text: UTF-8 text
982  * @len: length of @text, or -1
983  * @first_tag: first tag to apply to @text
984  * @Varargs: NULL-terminated list of tags to apply
985  *
986  * Inserts @text into @buffer at @iter, applying the list of tags to
987  * the newly-inserted text. The last tag specified must be NULL to
988  * terminate the list. Equivalent to calling gtk_text_buffer_insert (),
989  * then gtk_text_buffer_apply_tag () on the inserted text;
990  * gtk_text_buffer_insert_with_tags () is just a convenience function.
991  **/
992 void
993 gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer,
994                                   GtkTextIter   *iter,
995                                   const gchar   *text,
996                                   gint           len,
997                                   GtkTextTag    *first_tag,
998                                   ...)
999 {
1000   gint start_offset;
1001   GtkTextIter start;
1002   va_list args;
1003   GtkTextTag *tag;
1004
1005   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1006   g_return_if_fail (iter != NULL);
1007   g_return_if_fail (text != NULL);
1008   g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1009   
1010   start_offset = gtk_text_iter_get_offset (iter);
1011
1012   gtk_text_buffer_insert (buffer, iter, text, len);
1013
1014   if (first_tag == NULL)
1015     return;
1016
1017   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1018
1019   va_start (args, first_tag);
1020   tag = first_tag;
1021   while (tag)
1022     {
1023       gtk_text_buffer_apply_tag (buffer, tag, &start, iter);
1024
1025       tag = va_arg (args, GtkTextTag*);
1026     }
1027
1028   va_end (args);
1029 }
1030
1031 /**
1032  * gtk_text_buffer_insert_with_tags_by_name:
1033  * @buffer: a #GtkTextBuffer
1034  * @iter: position in @buffer
1035  * @text: UTF-8 text
1036  * @len: length of @text, or -1
1037  * @first_tag_name: name of a tag to apply to @text
1038  * @Varargs: more tag names
1039  *
1040  * Same as gtk_text_buffer_insert_with_tags (), but allows you
1041  * to pass in tag names instead of tag objects.
1042  **/
1043 void
1044 gtk_text_buffer_insert_with_tags_by_name  (GtkTextBuffer *buffer,
1045                                            GtkTextIter   *iter,
1046                                            const gchar   *text,
1047                                            gint           len,
1048                                            const gchar   *first_tag_name,
1049                                            ...)
1050 {
1051   gint start_offset;
1052   GtkTextIter start;
1053   va_list args;
1054   const gchar *tag_name;
1055
1056   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1057   g_return_if_fail (iter != NULL);
1058   g_return_if_fail (text != NULL);
1059   g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1060   
1061   start_offset = gtk_text_iter_get_offset (iter);
1062
1063   gtk_text_buffer_insert (buffer, iter, text, len);
1064
1065   if (first_tag_name == NULL)
1066     return;
1067
1068   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1069
1070   va_start (args, first_tag_name);
1071   tag_name = first_tag_name;
1072   while (tag_name)
1073     {
1074       GtkTextTag *tag;
1075
1076       tag = gtk_text_tag_table_lookup (buffer->tag_table,
1077                                        tag_name);
1078
1079       if (tag == NULL)
1080         {
1081           g_warning ("%s: no tag with name '%s'!", G_STRLOC, tag_name);
1082           return;
1083         }
1084
1085       gtk_text_buffer_apply_tag (buffer, tag, &start, iter);
1086
1087       tag_name = va_arg (args, const gchar*);
1088     }
1089
1090   va_end (args);
1091 }
1092
1093
1094 /*
1095  * Deletion
1096  */
1097
1098 static void
1099 gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer,
1100                                    GtkTextIter   *start,
1101                                    GtkTextIter   *end)
1102 {
1103   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1104   g_return_if_fail (start != NULL);
1105   g_return_if_fail (end != NULL);
1106
1107   _gtk_text_btree_delete (start, end);
1108
1109   /* may have deleted the selection... */
1110   update_selection_clipboards (buffer);
1111
1112   g_signal_emit (G_OBJECT (buffer), signals[CHANGED], 0);
1113 }
1114
1115 static void
1116 gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
1117                              GtkTextIter *start,
1118                              GtkTextIter *end)
1119 {
1120   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1121   g_return_if_fail (start != NULL);
1122   g_return_if_fail (end != NULL);
1123
1124   if (gtk_text_iter_equal (start, end))
1125     return;
1126
1127   gtk_text_iter_order (start, end);
1128
1129   g_signal_emit (G_OBJECT (buffer),
1130                  signals[DELETE_RANGE],
1131                  0,
1132                  start, end);
1133 }
1134
1135 /**
1136  * gtk_text_buffer_delete:
1137  * @buffer: a #GtkTextBuffer
1138  * @start: a position in @buffer
1139  * @end: another position in @buffer
1140  *
1141  * Deletes text between @start and @end. The order of @start and @end
1142  * is not actually relevant; gtk_text_buffer_delete () will reorder
1143  * them. This function actually emits the "delete_range" signal, and
1144  * the default handler of that signal deletes the text. Because the
1145  * buffer is modified, all outstanding iterators become invalid after
1146  * calling this function; however, the @start and @end will be
1147  * re-initialized to point to the location where text was deleted.
1148  *
1149  **/
1150 void
1151 gtk_text_buffer_delete (GtkTextBuffer *buffer,
1152                         GtkTextIter   *start,
1153                         GtkTextIter   *end)
1154 {
1155   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1156   g_return_if_fail (start != NULL);
1157   g_return_if_fail (end != NULL);
1158   g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
1159   g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
1160   
1161   gtk_text_buffer_emit_delete (buffer, start, end);
1162 }
1163
1164 /**
1165  * gtk_text_buffer_delete_interactive:
1166  * @buffer: a #GtkTextBuffer
1167  * @start_iter: start of range to delete
1168  * @end_iter: end of range
1169  * @default_editable: whether the buffer is editable by default
1170  *
1171  * Deletes all <emphasis>editable</emphasis> text in the given range.
1172  * Calls gtk_text_buffer_delete () for each editable sub-range of
1173  * [@start,@end). @start and @end are revalidated to point to
1174  * the location of the last deleted range, or left untouched if
1175  * no text was deleted.
1176  *
1177  * Return value: whether some text was actually deleted
1178  **/
1179 gboolean
1180 gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
1181                                     GtkTextIter   *start_iter,
1182                                     GtkTextIter   *end_iter,
1183                                     gboolean       default_editable)
1184 {
1185   GtkTextMark *end_mark;
1186   GtkTextMark *start_mark;
1187   GtkTextIter iter;
1188   gboolean current_state;
1189   gboolean deleted_stuff = FALSE;
1190
1191   /* Delete all editable text in the range start_iter, end_iter */
1192
1193   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1194   g_return_val_if_fail (start_iter != NULL, FALSE);
1195   g_return_val_if_fail (end_iter != NULL, FALSE);
1196   g_return_val_if_fail (gtk_text_iter_get_buffer (start_iter) == buffer, FALSE);
1197   g_return_val_if_fail (gtk_text_iter_get_buffer (end_iter) == buffer, FALSE);
1198
1199   
1200   gtk_text_buffer_begin_user_action (buffer);
1201   
1202   gtk_text_iter_order (start_iter, end_iter);
1203
1204   start_mark = gtk_text_buffer_create_mark (buffer, NULL,
1205                                             start_iter, TRUE);
1206   end_mark = gtk_text_buffer_create_mark (buffer, NULL,
1207                                           end_iter, FALSE);
1208   iter = *start_iter;
1209
1210   current_state = gtk_text_iter_editable (&iter, default_editable);
1211
1212   while (TRUE)
1213     {
1214       gboolean new_state;
1215       gboolean done = FALSE;
1216       GtkTextIter end;
1217
1218       gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
1219
1220       gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
1221
1222       if (gtk_text_iter_compare (&iter, &end) >= 0)
1223         {
1224           done = TRUE;
1225           iter = end; /* clamp to the last boundary */
1226         }
1227
1228       new_state = gtk_text_iter_editable (&iter, default_editable);
1229
1230       if (current_state == new_state)
1231         {
1232           if (done)
1233             {
1234               if (current_state)
1235                 {
1236                   /* We're ending an editable region. Delete said region. */
1237                   GtkTextIter start;
1238
1239                   gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1240
1241                   gtk_text_buffer_emit_delete (buffer, &start, &iter);
1242
1243                   deleted_stuff = TRUE;
1244
1245                   /* revalidate user's iterators. */
1246                   *start_iter = start;
1247                   *end_iter = iter;
1248                 }
1249
1250               break;
1251             }
1252           else
1253             continue;
1254         }
1255
1256       if (current_state && !new_state)
1257         {
1258           /* End of an editable region. Delete it. */
1259           GtkTextIter start;
1260
1261           gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1262
1263           gtk_text_buffer_emit_delete (buffer, &start, &iter);
1264
1265           current_state = FALSE;
1266           deleted_stuff = TRUE;
1267
1268           /* revalidate user's iterators. */
1269           *start_iter = start;
1270           *end_iter = iter;
1271         }
1272       else
1273         {
1274           /* We are at the start of an editable region. We won't be deleting
1275            * the previous region. Move start mark to start of this region.
1276            */
1277
1278           g_assert (!current_state && new_state);
1279
1280           gtk_text_buffer_move_mark (buffer, start_mark,
1281                                      &iter);
1282
1283
1284           current_state = TRUE;
1285         }
1286
1287       if (done)
1288         break;
1289     }
1290
1291
1292   gtk_text_buffer_delete_mark (buffer, start_mark);
1293   gtk_text_buffer_delete_mark (buffer, end_mark);
1294
1295   gtk_text_buffer_end_user_action (buffer);
1296   
1297   return deleted_stuff;
1298 }
1299
1300 /*
1301  * Extracting textual buffer contents
1302  */
1303
1304 /**
1305  * gtk_text_buffer_get_text:
1306  * @buffer: a #GtkTextBuffer
1307  * @start: start of a range
1308  * @end: end of a range
1309  * @include_hidden_chars: whether to include invisible text
1310  *
1311  * Returns the text in the range [@start,@end). Excludes undisplayed
1312  * text (text marked with tags that set the invisibility attribute) if
1313  * @include_hidden_chars is FALSE. Does not include characters
1314  * representing embedded images, so byte and character indexes into
1315  * the returned string do <emphasis>not</emphasis> correspond to byte
1316  * and character indexes into the buffer. Contrast with
1317  * gtk_text_buffer_get_slice ().
1318  *
1319  * Return value: an allocated UTF-8 string
1320  **/
1321 gchar*
1322 gtk_text_buffer_get_text (GtkTextBuffer      *buffer,
1323                           const GtkTextIter *start,
1324                           const GtkTextIter *end,
1325                           gboolean             include_hidden_chars)
1326 {
1327   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1328   g_return_val_if_fail (start != NULL, NULL);
1329   g_return_val_if_fail (end != NULL, NULL);
1330   g_return_val_if_fail (gtk_text_iter_get_buffer (start) == buffer, NULL);
1331   g_return_val_if_fail (gtk_text_iter_get_buffer (end) == buffer, NULL);
1332   
1333   if (include_hidden_chars)
1334     return gtk_text_iter_get_text (start, end);
1335   else
1336     return gtk_text_iter_get_visible_text (start, end);
1337 }
1338
1339 /**
1340  * gtk_text_buffer_get_slice:
1341  * @buffer: a #GtkTextBuffer
1342  * @start: start of a range
1343  * @end: end of a range
1344  * @include_hidden_chars: whether to include invisible text
1345  *
1346  * Returns the text in the range [@start,@end). Excludes undisplayed
1347  * text (text marked with tags that set the invisibility attribute) if
1348  * @include_hidden_chars is FALSE. The returned string includes a
1349  * 0xFFFC character whenever the buffer contains
1350  * embedded images, so byte and character indexes into
1351  * the returned string <emphasis>do</emphasis> correspond to byte
1352  * and character indexes into the buffer. Contrast with
1353  * gtk_text_buffer_get_text (). Note that 0xFFFC can occur in normal
1354  * text as well, so it is not a reliable indicator that a pixbuf or
1355  * widget is in the buffer.
1356  *
1357  * Return value: an allocated UTF-8 string
1358  **/
1359 gchar*
1360 gtk_text_buffer_get_slice (GtkTextBuffer      *buffer,
1361                            const GtkTextIter *start,
1362                            const GtkTextIter *end,
1363                            gboolean             include_hidden_chars)
1364 {
1365   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1366   g_return_val_if_fail (start != NULL, NULL);
1367   g_return_val_if_fail (end != NULL, NULL);
1368   g_return_val_if_fail (gtk_text_iter_get_buffer (start) == buffer, NULL);
1369   g_return_val_if_fail (gtk_text_iter_get_buffer (end) == buffer, NULL);
1370   
1371   if (include_hidden_chars)
1372     return gtk_text_iter_get_slice (start, end);
1373   else
1374     return gtk_text_iter_get_visible_slice (start, end);
1375 }
1376
1377 /*
1378  * Pixbufs
1379  */
1380
1381 static void
1382 gtk_text_buffer_real_insert_pixbuf (GtkTextBuffer     *buffer,
1383                                     GtkTextIter       *iter,
1384                                     GdkPixbuf         *pixbuf)
1385
1386   _gtk_text_btree_insert_pixbuf (iter, pixbuf);
1387
1388   g_signal_emit (G_OBJECT (buffer), signals[CHANGED], 0);
1389 }
1390
1391 /**
1392  * gtk_text_buffer_insert_pixbuf:
1393  * @buffer: a #GtkTextBuffer
1394  * @iter: location to insert the pixbuf
1395  * @pixbuf: a #GdkPixbuf
1396  *
1397  * Inserts an image into the text buffer at @iter. The image will be
1398  * counted as one character in character counts, and when obtaining
1399  * the buffer contents as a string, will be represented by the Unicode
1400  * "object replacement character" 0xFFFC. Note that the "slice"
1401  * variants for obtaining portions of the buffer as a string include
1402  * this character for pixbufs, but the "text" variants do
1403  * not. e.g. see gtk_text_buffer_get_slice() and
1404  * gtk_text_buffer_get_text().
1405  * 
1406  **/
1407 void
1408 gtk_text_buffer_insert_pixbuf         (GtkTextBuffer      *buffer,
1409                                        GtkTextIter        *iter,
1410                                        GdkPixbuf          *pixbuf)
1411 {
1412   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1413   g_return_if_fail (iter != NULL);
1414   g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1415   g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1416   
1417   g_signal_emit (G_OBJECT (buffer), signals[INSERT_PIXBUF], 0,
1418                  iter, pixbuf);
1419 }
1420
1421 /*
1422  * Child anchor
1423  */
1424
1425
1426 static void
1427 gtk_text_buffer_real_insert_anchor (GtkTextBuffer      *buffer,
1428                                     GtkTextIter        *iter,
1429                                     GtkTextChildAnchor *anchor)
1430 {
1431   _gtk_text_btree_insert_child_anchor (iter, anchor);
1432
1433   g_signal_emit (G_OBJECT (buffer), signals[CHANGED], 0);
1434 }
1435
1436 /**
1437  * gtk_text_buffer_insert_child_anchor:
1438  * @buffer: a #GtkTextBuffer
1439  * @iter: location to insert the anchor
1440  * @anchor: a #GtkTextChildAnchor
1441  *
1442  * Inserts a child widget anchor into the text buffer at @iter. The
1443  * anchor will be counted as one character in character counts, and
1444  * when obtaining the buffer contents as a string, will be represented
1445  * by the Unicode "object replacement character" 0xFFFC. Note that the
1446  * "slice" variants for obtaining portions of the buffer as a string
1447  * include this character for pixbufs, but the "text" variants do
1448  * not. e.g. see gtk_text_buffer_get_slice() and
1449  * gtk_text_buffer_get_text(). Consider
1450  * gtk_text_buffer_create_child_anchor() as a more convenient
1451  * alternative to this function. The buffer will add a reference to
1452  * the anchor, so you can unref it after insertion.
1453  * 
1454  **/
1455 void
1456 gtk_text_buffer_insert_child_anchor (GtkTextBuffer      *buffer,
1457                                      GtkTextIter        *iter,
1458                                      GtkTextChildAnchor *anchor)
1459 {
1460   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1461   g_return_if_fail (iter != NULL);
1462   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
1463   g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1464   
1465   g_signal_emit (G_OBJECT (buffer), signals[INSERT_CHILD_ANCHOR], 0,
1466                  iter, anchor);
1467 }
1468
1469
1470 /**
1471  * gtk_text_buffer_create_child_anchor:
1472  * @buffer: a #GtkTextBuffer
1473  * @iter: location in the buffer
1474  * 
1475  * This is a convenience function which simply creates a child anchor
1476  * with gtk_text_child_anchor_new() and inserts it into the buffer
1477  * with gtk_text_buffer_insert_child_anchor().
1478  * 
1479  * Return value: the created child anchor
1480  **/
1481 GtkTextChildAnchor*
1482 gtk_text_buffer_create_child_anchor (GtkTextBuffer *buffer,
1483                                      GtkTextIter   *iter)
1484 {
1485   GtkTextChildAnchor *anchor;
1486   
1487   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1488   g_return_val_if_fail (iter != NULL, NULL);
1489   g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, NULL);
1490   
1491   anchor = gtk_text_child_anchor_new ();
1492
1493   gtk_text_buffer_insert_child_anchor (buffer, iter, anchor);
1494
1495   g_object_unref (G_OBJECT (anchor));
1496
1497   return anchor;
1498 }
1499
1500 /*
1501  * Mark manipulation
1502  */
1503
1504 static void
1505 gtk_text_buffer_mark_set (GtkTextBuffer     *buffer,
1506                           const GtkTextIter *location,
1507                           GtkTextMark       *mark)
1508 {
1509   /* IMO this should NOT work like insert_text and delete_range,
1510    * where the real action happens in the default handler.
1511    * 
1512    * The reason is that the default handler would be _required_,
1513    * i.e. the whole widget would start breaking and segfaulting if the
1514    * default handler didn't get run. So you can't really override the
1515    * default handler or stop the emission; that is, this signal is
1516    * purely for notification, and not to allow users to modify the
1517    * default behavior.
1518    */
1519
1520   g_object_ref (G_OBJECT (mark));
1521
1522   g_signal_emit (G_OBJECT (buffer),
1523                  signals[MARK_SET],
1524                  0,
1525                  location,
1526                  mark);
1527
1528   g_object_unref (G_OBJECT (mark));
1529 }
1530
1531 /**
1532  * gtk_text_buffer_set_mark:
1533  * @buffer:       a #GtkTextBuffer
1534  * @mark_name:    name of the mark
1535  * @iter:         location for the mark.
1536  * @left_gravity: if the mark is created by this function, gravity for the new
1537  *                mark.
1538  * @should_exist: if %TRUE, warn if the mark does not exist, and return
1539  *                immediately.
1540  *
1541  * Move the mark to the given position, if not @should_exist, create the mark.
1542  *
1543  * Return value: mark
1544  **/
1545 static GtkTextMark*
1546 gtk_text_buffer_set_mark (GtkTextBuffer *buffer,
1547                           GtkTextMark *existing_mark,
1548                           const gchar *mark_name,
1549                           const GtkTextIter *iter,
1550                           gboolean left_gravity,
1551                           gboolean should_exist)
1552 {
1553   GtkTextIter location;
1554   GtkTextMark *mark;
1555
1556   g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, NULL);
1557   
1558   mark = _gtk_text_btree_set_mark (get_btree (buffer),
1559                                    existing_mark,
1560                                    mark_name,
1561                                    left_gravity,
1562                                    iter,
1563                                    should_exist);
1564   
1565   if (_gtk_text_btree_mark_is_insert (get_btree (buffer), mark) ||
1566       _gtk_text_btree_mark_is_selection_bound (get_btree (buffer), mark))
1567     {
1568       update_selection_clipboards (buffer);
1569     }
1570
1571   _gtk_text_btree_get_iter_at_mark (get_btree (buffer),
1572                                    &location,
1573                                    mark);
1574
1575   gtk_text_buffer_mark_set (buffer, &location, mark);
1576
1577   return mark;
1578 }
1579
1580 /**
1581  * gtk_text_buffer_create_mark:
1582  * @buffer: a #GtkTextBuffer
1583  * @mark_name: name for mark, or %NULL
1584  * @where: location to place mark
1585  * @left_gravity: whether the mark has left gravity
1586  *
1587  * Creates a mark at position @where. If @mark_name is %NULL, the mark
1588  * is anonymous; otherwise, the mark can be retrieved by name using
1589  * gtk_text_buffer_get_mark (). If a mark has left gravity, and text is
1590  * inserted at the mark's current location, the mark will be moved to
1591  * the left of the newly-inserted text. If the mark has right gravity
1592  * (@left_gravity = %FALSE), the mark will end up on the right of
1593  * newly-inserted text. The standard left-to-right cursor is a mark
1594  * with right gravity (when you type, the cursor stays on the right
1595  * side of the text you're typing).
1596  *
1597  * The caller of this function does <emphasis>not</emphasis> own a reference
1598  * to the returned #GtkTextMark, so you can ignore the return value
1599  * if you like. Marks are owned by the buffer and go away when the
1600  * buffer does.
1601  *
1602  * Emits the "mark_set" signal as notification of the mark's initial
1603  * placement.
1604  *
1605  * Return value: the new #GtkTextMark object
1606  **/
1607 GtkTextMark*
1608 gtk_text_buffer_create_mark (GtkTextBuffer *buffer,
1609                              const gchar *mark_name,
1610                              const GtkTextIter *where,
1611                              gboolean left_gravity)
1612 {
1613   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1614
1615   return gtk_text_buffer_set_mark (buffer, NULL, mark_name, where,
1616                                    left_gravity, FALSE);
1617 }
1618
1619 /**
1620  * gtk_text_buffer_move_mark:
1621  * @buffer: a #GtkTextBuffer
1622  * @mark: a #GtkTextMark
1623  * @where: new location for @mark in @buffer
1624  *
1625  * Moves @mark to the new location @where. Emits the "mark_set" signal
1626  * as notification of the move.
1627  **/
1628 void
1629 gtk_text_buffer_move_mark (GtkTextBuffer *buffer,
1630                            GtkTextMark *mark,
1631                            const GtkTextIter *where)
1632 {
1633   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1634   g_return_if_fail (!gtk_text_mark_get_deleted (mark));
1635   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1636
1637   gtk_text_buffer_set_mark (buffer, mark, NULL, where, FALSE, TRUE);
1638 }
1639
1640 /**
1641  * gtk_text_buffer_get_iter_at_mark:
1642  * @buffer: a #GtkTextBuffer
1643  * @iter: iterator to initialize
1644  * @mark: a #GtkTextMark in @buffer
1645  *
1646  * Initializes @iter with the current position of @mark.
1647  **/
1648 void
1649 gtk_text_buffer_get_iter_at_mark (GtkTextBuffer *buffer,
1650                                   GtkTextIter *iter,
1651                                   GtkTextMark *mark)
1652 {
1653   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1654   g_return_if_fail (!gtk_text_mark_get_deleted (mark));
1655   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1656
1657   _gtk_text_btree_get_iter_at_mark (get_btree (buffer),
1658                                     iter,
1659                                     mark);
1660 }
1661
1662 /**
1663  * gtk_text_buffer_delete_mark:
1664  * @buffer: a #GtkTextBuffer
1665  * @mark: a #GtkTextMark in @buffer
1666  *
1667  * Deletes @mark, so that it's no longer located anywhere in the
1668  * buffer. Removes the reference the buffer holds to the mark, so if
1669  * you haven't called g_object_ref () on the mark, it will be freed. Even
1670  * if the mark isn't freed, most operations on @mark become
1671  * invalid. There is no way to undelete a
1672  * mark. gtk_text_mark_get_deleted () will return TRUE after this
1673  * function has been called on a mark; gtk_text_mark_get_deleted ()
1674  * indicates that a mark no longer belongs to a buffer. The "mark_deleted"
1675  * signal will be emitted as notification after the mark is deleted.
1676  **/
1677 void
1678 gtk_text_buffer_delete_mark (GtkTextBuffer *buffer,
1679                              GtkTextMark   *mark)
1680 {
1681   g_return_if_fail (GTK_IS_TEXT_MARK (mark));
1682   g_return_if_fail (!gtk_text_mark_get_deleted (mark));
1683   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1684
1685   g_object_ref (G_OBJECT (mark));
1686
1687   _gtk_text_btree_remove_mark (get_btree (buffer), mark);
1688
1689   /* See rationale above for MARK_SET on why we emit this after
1690    * removing the mark, rather than removing the mark in a default
1691    * handler.
1692    */
1693   g_signal_emit (G_OBJECT (buffer), signals[MARK_DELETED],
1694                  0,
1695                  mark);
1696
1697   g_object_unref (G_OBJECT (mark));
1698 }
1699
1700 /**
1701  * gtk_text_buffer_get_mark:
1702  * @buffer: a #GtkTextBuffer
1703  * @name: a mark name
1704  *
1705  * Returns the mark named @name in buffer @buffer, or NULL if no such
1706  * mark exists in the buffer.
1707  *
1708  * Return value: a #GtkTextMark, or NULL
1709  **/
1710 GtkTextMark*
1711 gtk_text_buffer_get_mark (GtkTextBuffer      *buffer,
1712                           const gchar         *name)
1713 {
1714   GtkTextMark *mark;
1715
1716   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1717   g_return_val_if_fail (name != NULL, NULL);
1718
1719   mark = _gtk_text_btree_get_mark_by_name (get_btree (buffer), name);
1720
1721   return mark;
1722 }
1723
1724
1725 /**
1726  * gtk_text_buffer_move_mark_by_name:
1727  * @buffer: a #GtkTextBuffer
1728  * @name: name of a mark
1729  * @where: new location for mark
1730  *
1731  * Moves the mark named @name (which must exist) to location @where.
1732  * See gtk_text_buffer_move_mark () for details.
1733  **/
1734 void
1735 gtk_text_buffer_move_mark_by_name (GtkTextBuffer     *buffer,
1736                                    const gchar       *name,
1737                                    const GtkTextIter *where)
1738 {
1739   GtkTextMark *mark;
1740
1741   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1742   g_return_if_fail (name != NULL);
1743
1744   mark = _gtk_text_btree_get_mark_by_name (get_btree (buffer), name);
1745
1746   if (mark == NULL)
1747     {
1748       g_warning ("%s: no mark named '%s'", G_STRLOC, name);
1749       return;
1750     }
1751
1752   gtk_text_buffer_move_mark (buffer, mark, where);
1753 }
1754
1755 /**
1756  * gtk_text_buffer_delete_mark_by_name:
1757  * @buffer: a #GtkTextBuffer
1758  * @name: name of a mark in @buffer
1759  *
1760  * Deletes the mark named @name; the mark must exist. See
1761  * gtk_text_buffer_delete_mark () for details.
1762  **/
1763 void
1764 gtk_text_buffer_delete_mark_by_name (GtkTextBuffer     *buffer,
1765                                      const gchar       *name)
1766 {
1767   GtkTextMark *mark;
1768
1769   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1770   g_return_if_fail (name != NULL);
1771
1772   mark = _gtk_text_btree_get_mark_by_name (get_btree (buffer), name);
1773
1774   if (mark == NULL)
1775     {
1776       g_warning ("%s: no mark named '%s'", G_STRLOC, name);
1777       return;
1778     }
1779
1780   gtk_text_buffer_delete_mark (buffer, mark);
1781 }
1782
1783 /**
1784  * gtk_text_buffer_get_insert:
1785  * @buffer: a #GtkTextBuffer
1786  *
1787  * Returns the mark that represents the cursor (insertion point).
1788  * Equivalent to calling gtk_text_buffer_get_mark () to get the mark
1789  * name "insert," but very slightly more efficient, and involves less
1790  * typing.
1791  *
1792  * Return value: insertion point mark
1793  **/
1794 GtkTextMark*
1795 gtk_text_buffer_get_insert (GtkTextBuffer *buffer)
1796 {
1797   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1798
1799   /* FIXME use struct member in btree */
1800   return gtk_text_buffer_get_mark (buffer, "insert");
1801 }
1802
1803 /**
1804  * gtk_text_buffer_get_selection_bound:
1805  * @buffer: a #GtkTextBuffer
1806  *
1807  * Returns the mark that represents the selection bound.  Equivalent
1808  * to calling gtk_text_buffer_get_mark () to get the mark name
1809  * "selection_bound," but very slightly more efficient, and involves
1810  * less typing.
1811  *
1812  * The currently-selected text in @buffer is the region between the
1813  * "selection_bound" and "insert" marks. If "selection_bound" and
1814  * "insert" are in the same place, then there is no current selection.
1815  * gtk_text_buffer_get_selection_bounds () is another convenient function
1816  * for handling the selection, if you just want to know whether there's a
1817  * selection and what its bounds are.
1818  *
1819  * Return value: selection bound mark
1820  **/
1821 GtkTextMark*
1822 gtk_text_buffer_get_selection_bound (GtkTextBuffer *buffer)
1823 {
1824   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1825
1826   /* FIXME use struct member in btree */
1827   return gtk_text_buffer_get_mark (buffer, "selection_bound");
1828 }
1829
1830 void
1831 gtk_text_buffer_get_iter_at_child_anchor (GtkTextBuffer      *buffer,
1832                                           GtkTextIter        *iter,
1833                                           GtkTextChildAnchor *anchor)
1834 {
1835   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1836   g_return_if_fail (iter != NULL);
1837   g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
1838   g_return_if_fail (!gtk_text_child_anchor_get_deleted (anchor));
1839   
1840   _gtk_text_btree_get_iter_at_child_anchor (get_btree (buffer),
1841                                            iter,
1842                                            anchor);
1843 }
1844
1845 /**
1846  * gtk_text_buffer_place_cursor:
1847  * @buffer: a #GtkTextBuffer
1848  * @where: where to put the cursor
1849  *
1850  * This function moves the "insert" and "selection_bound" marks
1851  * simultaneously.  If you move them to the same place in two steps
1852  * with gtk_text_buffer_move_mark (), you will temporarily select a
1853  * region in between their old and new locations, which can be pretty
1854  * inefficient since the temporarily-selected region will force stuff
1855  * to be recalculated. This function moves them as a unit, which can
1856  * be optimized.
1857  **/
1858 void
1859 gtk_text_buffer_place_cursor (GtkTextBuffer     *buffer,
1860                               const GtkTextIter *where)
1861 {
1862   GtkTextIter real;
1863
1864   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1865
1866   real = *where;
1867
1868   if (gtk_text_iter_is_end (&real))
1869     gtk_text_iter_backward_char (&real);
1870
1871   _gtk_text_btree_place_cursor (get_btree (buffer), &real);
1872   gtk_text_buffer_mark_set (buffer, &real,
1873                             gtk_text_buffer_get_mark (buffer,
1874                                                       "insert"));
1875   gtk_text_buffer_mark_set (buffer, &real,
1876                             gtk_text_buffer_get_mark (buffer,
1877                                                       "selection_bound"));
1878 }
1879
1880 /*
1881  * Tags
1882  */
1883
1884 /**
1885  * gtk_text_buffer_create_tag:
1886  * @buffer: a #GtkTextBuffer
1887  * @tag_name: name of the new tag, or %NULL
1888  * @first_property_name: name of first property to set, or %NULL
1889  * @Varargs: %NULL-terminated list of property names and values
1890  *
1891  *
1892  * Creates a tag and adds it to the tag table for @buffer.
1893  * Equivalent to calling gtk_text_tag_new () and then adding the
1894  * tag to the buffer's tag table. The returned tag is owned by
1895  * the buffer's tag table, so the ref count will be equal to one.
1896  *
1897  * If @tag_name is %NULL, the tag is anonymous.
1898  *
1899  * If @tag_name is non-%NULL, a tag called @tag_name must not already
1900  * exist in the tag table for this buffer.
1901  *
1902  * The @first_property_name argument and subsequent arguments are a list
1903  * of properties to set on the tag, as with g_object_set().
1904  *
1905  * Return value: a new tag
1906  **/
1907 GtkTextTag*
1908 gtk_text_buffer_create_tag (GtkTextBuffer *buffer,
1909                             const gchar   *tag_name,
1910                             const gchar   *first_property_name,
1911                             ...)
1912 {
1913   GtkTextTag *tag;
1914   va_list list;
1915   
1916   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1917
1918   tag = gtk_text_tag_new (tag_name);
1919
1920   gtk_text_tag_table_add (get_table (buffer), tag);
1921
1922   if (first_property_name)
1923     {
1924       va_start (list, first_property_name);
1925       g_object_set_valist (G_OBJECT (tag), first_property_name, list);
1926       va_end (list);
1927     }
1928   
1929   g_object_unref (tag);
1930
1931   return tag;
1932 }
1933
1934 static void
1935 gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
1936                                 GtkTextTag *tag,
1937                                 const GtkTextIter *start,
1938                                 const GtkTextIter *end)
1939 {
1940   if (tag->table != buffer->tag_table)
1941     {
1942       g_warning ("Can only apply tags that are in the tag table for the buffer");
1943       return;
1944     }
1945   
1946   _gtk_text_btree_tag (start, end, tag, TRUE);
1947 }
1948
1949 static void
1950 gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
1951                                  GtkTextTag *tag,
1952                                  const GtkTextIter *start,
1953                                  const GtkTextIter *end)
1954 {
1955   if (tag->table != buffer->tag_table)
1956     {
1957       g_warning ("Can only remove tags that are in the tag table for the buffer");
1958       return;
1959     }
1960   
1961   _gtk_text_btree_tag (start, end, tag, FALSE);
1962 }
1963
1964 static void
1965 gtk_text_buffer_real_changed (GtkTextBuffer *buffer)
1966 {
1967   gtk_text_buffer_set_modified (buffer, TRUE);
1968 }
1969
1970 static void
1971 gtk_text_buffer_emit_tag (GtkTextBuffer *buffer,
1972                           GtkTextTag *tag,
1973                           gboolean apply,
1974                           const GtkTextIter *start,
1975                           const GtkTextIter *end)
1976 {
1977   GtkTextIter start_tmp = *start;
1978   GtkTextIter end_tmp = *end;
1979
1980   g_return_if_fail (tag != NULL);
1981
1982   gtk_text_iter_order (&start_tmp, &end_tmp);
1983
1984   if (apply)
1985     g_signal_emit (G_OBJECT (buffer), signals[APPLY_TAG],
1986                    0,
1987                    tag, &start_tmp, &end_tmp);
1988   else
1989     g_signal_emit (G_OBJECT (buffer), signals[REMOVE_TAG],
1990                    0,
1991                    tag, &start_tmp, &end_tmp);
1992 }
1993
1994
1995 /**
1996  * gtk_text_buffer_apply_tag:
1997  * @buffer: a #GtkTextBuffer
1998  * @tag: a #GtkTextTag
1999  * @start: one bound of range to be tagged
2000  * @end: other bound of range to be tagged
2001  *
2002  * Emits the "apply_tag" signal on @buffer. The default
2003  * handler for the signal applies @tag to the given range.
2004  * @start and @end do not have to be in order.
2005  * 
2006  **/
2007 void
2008 gtk_text_buffer_apply_tag (GtkTextBuffer *buffer,
2009                            GtkTextTag    *tag,
2010                            const GtkTextIter *start,
2011                            const GtkTextIter *end)
2012 {
2013   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2014   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
2015   g_return_if_fail (start != NULL);
2016   g_return_if_fail (end != NULL);
2017   g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2018   g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2019   g_return_if_fail (tag->table == buffer->tag_table);
2020   
2021   gtk_text_buffer_emit_tag (buffer, tag, TRUE, start, end);
2022 }
2023
2024 /**
2025  * gtk_text_buffer_remove_tag:
2026  * @buffer: a #GtkTextBuffer
2027  * @tag: a #GtkTextTag
2028  * @start: one bound of range to be untagged
2029  * @end: other bound of range to be untagged
2030  *
2031  * Emits the "remove_tag" signal. The default handler for the signal
2032  * removes all occurrences of @tag from the given range. @start and
2033  * @end don't have to be in order.
2034  * 
2035  **/
2036 void
2037 gtk_text_buffer_remove_tag (GtkTextBuffer *buffer,
2038                             GtkTextTag    *tag,
2039                             const GtkTextIter *start,
2040                             const GtkTextIter *end)
2041
2042 {
2043   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2044   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
2045   g_return_if_fail (start != NULL);
2046   g_return_if_fail (end != NULL);
2047   g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2048   g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2049   g_return_if_fail (tag->table == buffer->tag_table);
2050   
2051   gtk_text_buffer_emit_tag (buffer, tag, FALSE, start, end);
2052 }
2053
2054
2055 /**
2056  * gtk_text_buffer_apply_tag_by_name:
2057  * @buffer: a #GtkTextBuffer
2058  * @name: name of a named #GtkTextTag
2059  * @start: one bound of range to be tagged
2060  * @end: other bound of range to be tagged
2061  *
2062  * Calls gtk_text_tag_table_lookup() on the buffer's tag table to
2063  * get a #GtkTextTag, then calls gtk_text_buffer_apply_tag().
2064  * 
2065  **/
2066 void
2067 gtk_text_buffer_apply_tag_by_name (GtkTextBuffer *buffer,
2068                                    const gchar   *name,
2069                                    const GtkTextIter *start,
2070                                    const GtkTextIter *end)
2071 {
2072   GtkTextTag *tag;
2073
2074   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2075   g_return_if_fail (name != NULL);
2076   g_return_if_fail (start != NULL);
2077   g_return_if_fail (end != NULL);
2078   g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2079   g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2080
2081   tag = gtk_text_tag_table_lookup (get_table (buffer),
2082                                    name);
2083
2084   if (tag == NULL)
2085     {
2086       g_warning ("Unknown tag `%s'", name);
2087       return;
2088     }
2089
2090   gtk_text_buffer_emit_tag (buffer, tag, TRUE, start, end);
2091 }
2092
2093 /**
2094  * gtk_text_buffer_remove_tag_by_name:
2095  * @buffer: a #GtkTextBuffer
2096  * @name: name of a #GtkTextTag
2097  * @start: one bound of range to be untagged
2098  * @end: other bound of range to be untagged
2099  *
2100  * Calls gtk_text_tag_table_lookup() on the buffer's tag table to
2101  * get a #GtkTextTag, then calls gtk_text_buffer_remove_tag().
2102  * 
2103  * 
2104  **/
2105 void
2106 gtk_text_buffer_remove_tag_by_name (GtkTextBuffer *buffer,
2107                                     const gchar *name,
2108                                     const GtkTextIter *start,
2109                                     const GtkTextIter *end)
2110 {
2111   GtkTextTag *tag;
2112
2113   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2114   g_return_if_fail (name != NULL);
2115   g_return_if_fail (start != NULL);
2116   g_return_if_fail (end != NULL);
2117   g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2118   g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2119   
2120   tag = gtk_text_tag_table_lookup (get_table (buffer),
2121                                    name);
2122
2123   if (tag == NULL)
2124     {
2125       g_warning ("Unknown tag `%s'", name);
2126       return;
2127     }
2128
2129   gtk_text_buffer_emit_tag (buffer, tag, FALSE, start, end);
2130 }
2131
2132 static gint
2133 pointer_cmp (gconstpointer a,
2134              gconstpointer b)
2135 {
2136   if (a < b)
2137     return -1;
2138   else if (a > b)
2139     return 1;
2140   else
2141     return 0;
2142 }
2143
2144 /**
2145  * gtk_text_buffer_remove_all_tags:
2146  * @buffer: a #GtkTextBuffer
2147  * @start: one bound of range to be untagged
2148  * @end: other bound of range to be untagged
2149  * 
2150  * Removes all tags in the range between @start and @end.  Be careful
2151  * with this function; it could remove tags added in code unrelated to
2152  * the code you're currently writing. That is, using this function is
2153  * probably a bad idea if you have two or more unrelated code sections
2154  * that add tags.
2155  **/
2156 void
2157 gtk_text_buffer_remove_all_tags (GtkTextBuffer     *buffer,
2158                                  const GtkTextIter *start,
2159                                  const GtkTextIter *end)
2160 {
2161   GtkTextIter first, second, tmp;
2162   GSList *tags;
2163   GSList *tmp_list;
2164   GSList *prev;
2165   GtkTextTag *tag;
2166   
2167   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2168   g_return_if_fail (start != NULL);
2169   g_return_if_fail (end != NULL);
2170   g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2171   g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2172   
2173   first = *start;
2174   second = *end;
2175
2176   gtk_text_iter_order (&first, &second);
2177
2178   /* Get all tags turned on at the start */
2179   tags = gtk_text_iter_get_tags (&first);
2180   
2181   /* Find any that are toggled on within the range */
2182   tmp = first;
2183   while (gtk_text_iter_forward_to_tag_toggle (&tmp, NULL))
2184     {
2185       GSList *toggled;
2186       GSList *tmp_list2;
2187
2188       if (gtk_text_iter_compare (&tmp, &second) >= 0)
2189         break; /* past the end of the range */
2190       
2191       toggled = gtk_text_iter_get_toggled_tags (&tmp, TRUE);
2192
2193       /* We could end up with a really big-ass list here.
2194        * Fix it someday.
2195        */
2196       tmp_list2 = toggled;
2197       while (tmp_list2 != NULL)
2198         {
2199           tags = g_slist_prepend (tags, tmp_list2->data);
2200
2201           tmp_list2 = g_slist_next (tmp_list2);
2202         }
2203
2204       g_slist_free (toggled);
2205     }
2206   
2207   /* Sort the list */
2208   tags = g_slist_sort (tags, pointer_cmp);
2209
2210   /* Strip duplicates */
2211   tag = NULL;
2212   prev = NULL;
2213   tmp_list = tags;
2214   while (tmp_list != NULL)
2215     {
2216       if (tag == tmp_list->data)
2217         {
2218           /* duplicate */
2219           if (prev)
2220             prev->next = tmp_list->next;
2221
2222           tmp_list->next = NULL;
2223
2224           g_slist_free (tmp_list);
2225
2226           tmp_list = prev->next;
2227           /* prev is unchanged */
2228         }
2229       else
2230         {
2231           /* not a duplicate */
2232           tag = GTK_TEXT_TAG (tmp_list->data);
2233           prev = tmp_list;
2234           tmp_list = tmp_list->next;
2235         }
2236     }
2237
2238   g_slist_foreach (tags, (GFunc) g_object_ref, NULL);
2239   
2240   tmp_list = tags;
2241   while (tmp_list != NULL)
2242     {
2243       tag = GTK_TEXT_TAG (tmp_list->data);
2244
2245       gtk_text_buffer_remove_tag (buffer, tag, &first, &second);
2246       
2247       tmp_list = tmp_list->next;
2248     }
2249
2250   g_slist_foreach (tags, (GFunc) g_object_unref, NULL);
2251   
2252   g_slist_free (tags);
2253 }
2254
2255
2256 /*
2257  * Obtain various iterators
2258  */
2259
2260 /**
2261  * gtk_text_buffer_get_iter_at_line_offset:
2262  * @buffer: a #GtkTextBuffer
2263  * @iter: iterator to initialize
2264  * @line_number: line number counting from 0
2265  * @char_offset: char offset from start of line
2266  *
2267  * Obtains an iterator pointing to @char_offset within the given
2268  * line. The @char_offset must exist, offsets off the end of the line
2269  * are not allowed. Note <emphasis>characters</emphasis>, not bytes;
2270  * UTF-8 may encode one character as multiple bytes.
2271  * 
2272  **/
2273 void
2274 gtk_text_buffer_get_iter_at_line_offset (GtkTextBuffer      *buffer,
2275                                          GtkTextIter        *iter,
2276                                          gint                line_number,
2277                                          gint                char_offset)
2278 {
2279   g_return_if_fail (iter != NULL);
2280   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2281
2282   _gtk_text_btree_get_iter_at_line_char (get_btree (buffer),
2283                                          iter, line_number, char_offset);
2284 }
2285
2286 /**
2287  * gtk_text_buffer_get_iter_at_line_index:
2288  * @buffer: a #GtkTextBuffer 
2289  * @iter: iterator to initialize 
2290  * @line_number: line number counting from 0
2291  * @byte_index: byte index from start of line
2292  *
2293  * Obtains an iterator pointing to @byte_index within the given line.
2294  * @byte_index must be the start of a UTF-8 character, and must not be
2295  * beyond the end of the line.  Note <emphasis>bytes</emphasis>, not
2296  * characters; UTF-8 may encode one character as multiple bytes.
2297  * 
2298  **/
2299 void
2300 gtk_text_buffer_get_iter_at_line_index  (GtkTextBuffer *buffer,
2301                                          GtkTextIter   *iter,
2302                                          gint           line_number,
2303                                          gint           byte_index)
2304 {
2305   g_return_if_fail (iter != NULL);
2306   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2307
2308   _gtk_text_btree_get_iter_at_line_byte (get_btree (buffer),
2309                                          iter, line_number, byte_index);
2310 }
2311
2312 /**
2313  * gtk_text_buffer_get_iter_at_line:
2314  * @buffer: a #GtkTextBuffer 
2315  * @iter: iterator to initialize
2316  * @line_number: line number counting from 0
2317  * 
2318  * Initializes @iter to the start of the given line.
2319  **/
2320 void
2321 gtk_text_buffer_get_iter_at_line    (GtkTextBuffer      *buffer,
2322                                      GtkTextIter        *iter,
2323                                      gint                line_number)
2324 {
2325   g_return_if_fail (iter != NULL);
2326   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2327
2328   gtk_text_buffer_get_iter_at_line_offset (buffer, iter, line_number, 0);
2329 }
2330
2331 /**
2332  * gtk_text_buffer_get_iter_at_offset:
2333  * @buffer: a #GtkTextBuffer 
2334  * @iter: iterator to initialize
2335  * @char_offset: char offset from start of buffer, counting from 0
2336  *
2337  * Initializes @iter to a position @char_offset chars from the start
2338  * of the entire buffer.
2339  * 
2340  **/
2341 void
2342 gtk_text_buffer_get_iter_at_offset         (GtkTextBuffer      *buffer,
2343                                             GtkTextIter        *iter,
2344                                             gint                char_offset)
2345 {
2346   g_return_if_fail (iter != NULL);
2347   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2348
2349   _gtk_text_btree_get_iter_at_char (get_btree (buffer), iter, char_offset);
2350 }
2351
2352 /**
2353  * gtk_text_buffer_get_start_iter:
2354  * @buffer: a #GtkTextBuffer
2355  * @iter: iterator to initialize
2356  *
2357  * Initialized @iter with the first position in the text buffer. This
2358  * is the same as using gtk_text_buffer_get_iter_at_offset() to get
2359  * the iter at character offset 0.
2360  * 
2361  **/
2362 void
2363 gtk_text_buffer_get_start_iter (GtkTextBuffer *buffer,
2364                                 GtkTextIter   *iter)
2365 {
2366   g_return_if_fail (iter != NULL);
2367   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2368
2369   _gtk_text_btree_get_iter_at_char (get_btree (buffer), iter, 0);
2370 }
2371
2372 /**
2373  * gtk_text_buffer_get_end_iter:
2374  * @buffer: a #GtkTextBuffer 
2375  * @iter: iterator to initialize
2376  *
2377  * Initializes @iter with the "end iterator," one past the last valid
2378  * character in the text buffer. If dereferenced with
2379  * gtk_text_iter_get_char(), the end iterator has a character value of
2380  * 0. The entire buffer lies in the range from the first position in
2381  * the buffer (call gtk_text_buffer_get_start_iter() to get
2382  * character position 0) to the end iterator.
2383  * 
2384  **/
2385 void
2386 gtk_text_buffer_get_end_iter         (GtkTextBuffer      *buffer,
2387                                        GtkTextIter        *iter)
2388 {
2389   g_return_if_fail (iter != NULL);
2390   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2391
2392   _gtk_text_btree_get_end_iter (get_btree (buffer), iter);
2393 }
2394
2395 /**
2396  * gtk_text_buffer_get_bounds:
2397  * @buffer: a #GtkTextBuffer 
2398  * @start: iterator to initialize with first position in the buffer
2399  * @end: iterator to initialize with the end iterator
2400  *
2401  * Retrieves the first and last iterators in the buffer, i.e. the
2402  * entire buffer lies within the range [@start,@end).
2403  * 
2404  **/
2405 void
2406 gtk_text_buffer_get_bounds (GtkTextBuffer *buffer,
2407                             GtkTextIter   *start,
2408                             GtkTextIter   *end)
2409 {
2410   g_return_if_fail (start != NULL);
2411   g_return_if_fail (end != NULL);
2412   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2413
2414   _gtk_text_btree_get_iter_at_char (get_btree (buffer), start, 0);
2415   _gtk_text_btree_get_end_iter (get_btree (buffer), end);
2416 }
2417
2418 /*
2419  * Modified flag
2420  */
2421
2422 /**
2423  * gtk_text_buffer_get_modified:
2424  * @buffer: a #GtkTextBuffer 
2425  * 
2426  * Indicates whether the buffer has been modified since the last call
2427  * to gtk_text_buffer_set_modified() set the modification flag to
2428  * %FALSE. Used for example to enable a "save" function in a text
2429  * editor.
2430  * 
2431  * Return value: %TRUE if the buffer has been modified
2432  **/
2433 gboolean
2434 gtk_text_buffer_get_modified (GtkTextBuffer *buffer)
2435 {
2436   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
2437
2438   return buffer->modified;
2439 }
2440
2441 /**
2442  * gtk_text_buffer_set_modified:
2443  * @buffer: a #GtkTextBuffer 
2444  * @setting: modification flag setting
2445  *
2446  * Used to keep track of whether the buffer has been modified since the
2447  * last time it was saved. Whenever the buffer is saved to disk, call
2448  * gtk_text_buffer_set_modified (@buffer, FALSE). When the buffer is modified,
2449  * it will automatically toggled on the modified bit again. When the modified
2450  * bit flips, the buffer emits a "modified_changed" signal.
2451  * 
2452  **/
2453 void
2454 gtk_text_buffer_set_modified (GtkTextBuffer      *buffer,
2455                               gboolean             setting)
2456 {
2457   gboolean fixed_setting;
2458
2459   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2460
2461   fixed_setting = setting != FALSE;
2462
2463   if (buffer->modified == fixed_setting)
2464     return;
2465   else
2466     {
2467       buffer->modified = fixed_setting;
2468       g_signal_emit (G_OBJECT (buffer), signals[MODIFIED_CHANGED], 0);
2469     }
2470 }
2471
2472
2473 /*
2474  * Assorted other stuff
2475  */
2476
2477 /**
2478  * gtk_text_buffer_get_line_count:
2479  * @buffer: a #GtkTextBuffer 
2480  * 
2481  * Obtains the number of lines in the buffer. This value is cached, so
2482  * the function is very fast.
2483  * 
2484  * Return value: number of lines in the buffer
2485  **/
2486 gint
2487 gtk_text_buffer_get_line_count (GtkTextBuffer *buffer)
2488 {
2489   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), 0);
2490
2491   return _gtk_text_btree_line_count (get_btree (buffer));
2492 }
2493
2494 /**
2495  * gtk_text_buffer_get_char_count:
2496  * @buffer: a #GtkTextBuffer 
2497  * 
2498  * Gets the number of characters in the buffer; note that characters
2499  * and bytes are not the same, you can't e.g. expect the contents of
2500  * the buffer in string form to be this many bytes long. The character
2501  * count is cached, so this function is very fast.
2502  * 
2503  * Return value: number of characters in the buffer
2504  **/
2505 gint
2506 gtk_text_buffer_get_char_count (GtkTextBuffer *buffer)
2507 {
2508   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), 0);
2509
2510   return _gtk_text_btree_char_count (get_btree (buffer));
2511 }
2512
2513 /* Called when we lose the primary selection.
2514  */
2515 static void
2516 clipboard_clear_selection_cb (GtkClipboard *clipboard,
2517                               gpointer      data)
2518 {
2519   /* Move selection_bound to the insertion point */
2520   GtkTextIter insert;
2521   GtkTextIter selection_bound;
2522   GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
2523
2524   gtk_text_buffer_get_iter_at_mark (buffer, &insert,
2525                                     gtk_text_buffer_get_mark (buffer, "insert"));
2526   gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound,
2527                                     gtk_text_buffer_get_mark (buffer, "selection_bound"));
2528
2529   if (!gtk_text_iter_equal (&insert, &selection_bound))
2530     gtk_text_buffer_move_mark (buffer,
2531                                gtk_text_buffer_get_mark (buffer, "selection_bound"),
2532                                &insert);
2533 }
2534
2535 /* Called when we have the primary selection and someone else wants our
2536  * data in order to paste it.
2537  */
2538 static void
2539 clipboard_get_selection_cb (GtkClipboard     *clipboard,
2540                             GtkSelectionData *selection_data,
2541                             guint             info,
2542                             gpointer          data)
2543 {
2544   GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
2545   GtkTextIter start, end;
2546
2547   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
2548     {
2549       if (selection_data->target ==
2550           gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
2551         {
2552           /* Provide the address of the buffer; this will only be
2553            * used within-process
2554            */
2555           gtk_selection_data_set (selection_data,
2556                                   gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE),
2557                                   8, /* bytes */
2558                                   (void*)&buffer,
2559                                   sizeof (buffer));
2560         }
2561       else
2562         {
2563           gchar *str;
2564           
2565           str = gtk_text_iter_get_visible_text (&start, &end);
2566           gtk_selection_data_set_text (selection_data, str);
2567           g_free (str);
2568         }
2569     }
2570 }
2571
2572 typedef struct
2573 {
2574   GtkClipboard *clipboard;
2575   GtkTextBuffer *buffer;
2576 } ContentsBuffer;
2577
2578 static void
2579 remove_all_clipboard_contents_buffers (GtkTextBuffer *buffer)
2580 {
2581   GSList *tmp_list = buffer->clipboard_contents_buffers;
2582   while (tmp_list)
2583     {
2584       ContentsBuffer *contents_buffer = tmp_list->data;
2585
2586       g_object_unref (contents_buffer->buffer);
2587       g_free (contents_buffer);
2588           
2589       tmp_list = tmp_list->next;
2590     }
2591
2592   g_slist_free (buffer->clipboard_contents_buffers);
2593   buffer->clipboard_contents_buffers = NULL;
2594 }
2595
2596 static void
2597 remove_clipboard_contents_buffer (GtkTextBuffer *buffer,
2598                                   GtkClipboard  *clipboard)
2599 {
2600   GSList *tmp_list = buffer->clipboard_contents_buffers;
2601   while (tmp_list)
2602     {
2603       ContentsBuffer *contents_buffer = tmp_list->data;
2604       
2605       if (contents_buffer->clipboard == clipboard)
2606         {
2607           buffer->clipboard_contents_buffers = g_slist_remove (buffer->clipboard_contents_buffers, contents_buffer);
2608           
2609           g_object_unref (contents_buffer->buffer);
2610           g_free (contents_buffer);
2611           
2612           return;
2613         }
2614
2615       tmp_list = tmp_list->next;
2616     }
2617 }
2618
2619 static GtkTextBuffer *
2620 get_clipboard_contents_buffer (GtkTextBuffer *buffer,
2621                                GtkClipboard  *clipboard,
2622                                gboolean       create)
2623 {
2624   ContentsBuffer *contents_buffer;
2625   GSList *tmp_list;
2626
2627   tmp_list = buffer->clipboard_contents_buffers;
2628   while (tmp_list)
2629     {
2630       contents_buffer = tmp_list->data;
2631       if (contents_buffer->clipboard == clipboard)
2632         return contents_buffer->buffer;
2633     }
2634   
2635   if (create)
2636     {
2637       contents_buffer = g_new (ContentsBuffer, 1);
2638       contents_buffer->clipboard = clipboard;
2639       contents_buffer->buffer = gtk_text_buffer_new (gtk_text_buffer_get_tag_table (buffer));
2640
2641       g_object_set_data (G_OBJECT (contents_buffer->buffer), "gtk-text-buffer-clipboard",
2642                          GUINT_TO_POINTER (1));
2643
2644       buffer->clipboard_contents_buffers = g_slist_prepend (buffer->clipboard_contents_buffers, contents_buffer);
2645
2646       
2647       return contents_buffer->buffer;
2648     }
2649   else
2650     return NULL;
2651 }
2652
2653 /* Provide cut/copied data */
2654 static void
2655 clipboard_get_contents_cb (GtkClipboard     *clipboard,
2656                            GtkSelectionData *selection_data,
2657                            guint             info,
2658                            gpointer          data)
2659 {
2660   GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
2661   GtkTextBuffer *contents = get_clipboard_contents_buffer (buffer, clipboard, FALSE);
2662
2663   g_assert (contents); /* This should never be called unless we own the clipboard */
2664
2665   if (selection_data->target ==
2666       gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
2667     {
2668       /* Provide the address of the clipboard buffer; this will only
2669        * be used within-process. OK to supply a NULL value for contents.
2670        */
2671       gtk_selection_data_set (selection_data,
2672                               gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE),
2673                               8, /* bytes */
2674                               (void*)&contents,
2675                               sizeof (contents));
2676     }
2677   else
2678     {
2679       gchar *str;
2680       GtkTextIter start, end;
2681       
2682       gtk_text_buffer_get_bounds (contents, &start, &end);
2683       
2684       str = gtk_text_iter_get_visible_text (&start, &end);
2685       gtk_selection_data_set_text (selection_data, str);
2686       g_free (str);
2687     }
2688 }
2689
2690 static void
2691 clipboard_clear_contents_cb (GtkClipboard *clipboard,
2692                              gpointer      data)
2693 {
2694   GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
2695
2696   remove_clipboard_contents_buffer (buffer, clipboard);
2697 }
2698
2699 static void
2700 get_paste_point (GtkTextBuffer *buffer,
2701                  GtkTextIter   *iter,
2702                  gboolean       clear_afterward)
2703 {
2704   GtkTextIter insert_point;
2705   GtkTextMark *paste_point_override;
2706
2707   paste_point_override = gtk_text_buffer_get_mark (buffer,
2708                                                    "gtk_paste_point_override");
2709
2710   if (paste_point_override != NULL)
2711     {
2712       gtk_text_buffer_get_iter_at_mark (buffer, &insert_point,
2713                                         paste_point_override);
2714       if (clear_afterward)
2715         gtk_text_buffer_delete_mark (buffer,
2716                                      gtk_text_buffer_get_mark (buffer,
2717                                                                "gtk_paste_point_override"));
2718     }
2719   else
2720     {
2721       gtk_text_buffer_get_iter_at_mark (buffer, &insert_point,
2722                                         gtk_text_buffer_get_mark (buffer,
2723                                                                   "insert"));
2724     }
2725
2726   *iter = insert_point;
2727 }
2728
2729 static void
2730 pre_paste_prep (ClipboardRequest *request_data,
2731                 GtkTextIter      *insert_point)
2732 {
2733   GtkTextBuffer *buffer = request_data->buffer;
2734   
2735   get_paste_point (buffer, insert_point, TRUE);
2736
2737   /* If we're going to replace the selection, we insert before it to
2738    * avoid messing it up, then we delete the selection after inserting.
2739    */
2740   if (request_data->replace_selection)
2741     {
2742       GtkTextIter start, end;
2743       
2744       if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
2745         *insert_point = start;
2746     }
2747 }
2748
2749 static void
2750 post_paste_cleanup (ClipboardRequest *request_data)
2751 {
2752   if (request_data->replace_selection)
2753     {
2754       GtkTextIter start, end;
2755       
2756       if (gtk_text_buffer_get_selection_bounds (request_data->buffer,
2757                                                 &start, &end))
2758         {
2759           if (request_data->interactive)
2760             gtk_text_buffer_delete_interactive (request_data->buffer,
2761                                                 &start,
2762                                                 &end,
2763                                                 request_data->default_editable);
2764           else
2765             gtk_text_buffer_delete (request_data->buffer, &start, &end);
2766         }
2767     }
2768 }
2769
2770 /* Called when we request a paste and receive the text data
2771  */
2772 static void
2773 clipboard_text_received (GtkClipboard *clipboard,
2774                          const gchar  *str,
2775                          gpointer      data)
2776 {
2777   ClipboardRequest *request_data = data;
2778   GtkTextBuffer *buffer = request_data->buffer;
2779
2780   if (str)
2781     {
2782       GtkTextIter insert_point;
2783
2784       pre_paste_prep (request_data, &insert_point);
2785       
2786       if (request_data->interactive)
2787         gtk_text_buffer_insert_interactive (buffer, &insert_point,
2788                                             str, -1, request_data->default_editable);
2789       else
2790         gtk_text_buffer_insert (buffer, &insert_point,
2791                                 str, -1);
2792
2793       post_paste_cleanup (request_data);
2794     }
2795
2796   g_object_unref (G_OBJECT (buffer));
2797   g_free (request_data);
2798 }
2799
2800 static GtkTextBuffer*
2801 selection_data_get_buffer (GtkSelectionData *selection_data,
2802                            ClipboardRequest *request_data)
2803 {
2804   GdkWindow *owner;
2805   GtkTextBuffer *src_buffer = NULL;
2806
2807   /* If we can get the owner, the selection is in-process */
2808   owner = gdk_selection_owner_get (selection_data->selection);
2809
2810   if (owner == NULL)
2811     return NULL;
2812   
2813   if (selection_data->type != gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE))
2814     return NULL;
2815
2816   if (selection_data->length != sizeof (src_buffer))
2817     return NULL;
2818           
2819   memcpy (&src_buffer, selection_data->data, sizeof (src_buffer));
2820
2821   if (src_buffer == NULL)
2822     return NULL;
2823   
2824   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (src_buffer), NULL);
2825
2826   if (gtk_text_buffer_get_tag_table (src_buffer) !=
2827       gtk_text_buffer_get_tag_table (request_data->buffer))
2828     return NULL;
2829   
2830   return src_buffer;
2831 }
2832
2833 #if 0
2834 /* These are pretty handy functions; maybe something like them
2835  * should be in the public API. Also, there are other places in this
2836  * file where they could be used.
2837  */
2838 static gpointer
2839 save_iter (const GtkTextIter *iter,
2840            gboolean           left_gravity)
2841 {
2842   return gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (iter),
2843                                       NULL,
2844                                       iter,
2845                                       TRUE);
2846 }
2847
2848 static void
2849 restore_iter (const GtkTextIter *iter,
2850               gpointer           save_id)
2851 {
2852   gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (save_id),
2853                                     (GtkTextIter*) iter,
2854                                     save_id);
2855   gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (save_id),
2856                                save_id);
2857 }
2858 #endif
2859
2860 static void
2861 paste_from_buffer (ClipboardRequest    *request_data,
2862                    GtkTextBuffer       *src_buffer,
2863                    const GtkTextIter   *start,
2864                    const GtkTextIter   *end)
2865 {
2866   GtkTextIter insert_point;
2867   
2868   /* We're about to emit a bunch of signals, so be safe */
2869   g_object_ref (G_OBJECT (src_buffer));
2870   
2871   pre_paste_prep (request_data, &insert_point);
2872   
2873   if (!gtk_text_iter_equal (start, end))
2874     {
2875       gtk_text_buffer_real_insert_range (request_data->buffer,
2876                                          &insert_point,
2877                                          start,
2878                                          end,
2879                                          request_data->interactive);
2880     }
2881
2882   post_paste_cleanup (request_data);
2883       
2884   g_object_unref (G_OBJECT (src_buffer));
2885 }
2886
2887 static void
2888 clipboard_clipboard_buffer_received (GtkClipboard     *clipboard,
2889                                      GtkSelectionData *selection_data,
2890                                      gpointer          data)
2891 {
2892   ClipboardRequest *request_data = data;
2893   GtkTextBuffer *src_buffer;
2894   
2895   src_buffer = selection_data_get_buffer (selection_data, request_data); 
2896
2897   if (src_buffer)
2898     {
2899       GtkTextIter start, end;
2900
2901       if (g_object_get_data (G_OBJECT (src_buffer), "gtk-text-buffer-clipboard"))
2902         {
2903           gtk_text_buffer_get_bounds (src_buffer, &start, &end);
2904           /* There's an extra newline on clipboard_contents */
2905           gtk_text_iter_backward_char (&end);
2906       
2907           paste_from_buffer (request_data, src_buffer,
2908                              &start, &end);
2909         }
2910       else
2911         {
2912           if (gtk_text_buffer_get_selection_bounds (src_buffer, &start, &end))
2913             paste_from_buffer (request_data, src_buffer,
2914                                &start, &end);
2915         }
2916     }
2917   else
2918     {
2919       /* Request the text selection instead */
2920       gtk_clipboard_request_text (clipboard,
2921                                   clipboard_text_received,
2922                                   data);
2923     }
2924 }
2925
2926 static const GtkTargetEntry targets[] = {
2927   { "STRING", 0, TARGET_STRING },
2928   { "TEXT",   0, TARGET_TEXT },
2929   { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
2930   { "UTF8_STRING", 0, TARGET_UTF8_STRING },
2931   { "GTK_TEXT_BUFFER_CONTENTS", 0, TARGET_TEXT_BUFFER_CONTENTS }
2932 };
2933
2934 typedef struct
2935 {
2936   GtkClipboard *clipboard;
2937   guint ref_count;
2938 } SelectionClipboard;
2939
2940 static void
2941 update_selection_clipboards (GtkTextBuffer *buffer)
2942 {
2943   GSList *tmp_list = buffer->selection_clipboards;
2944   while (tmp_list)
2945     {
2946       GtkTextIter start;
2947       GtkTextIter end;
2948       
2949       SelectionClipboard *selection_clipboard = tmp_list->data;
2950       GtkClipboard *clipboard = selection_clipboard->clipboard;
2951
2952       /* Determine whether we have a selection and adjust X selection
2953        * accordingly.
2954        */
2955       if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
2956         {
2957           if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (buffer))
2958             gtk_clipboard_clear (clipboard);
2959         }
2960       else
2961         {
2962           /* Even if we already have the selection, we need to update our
2963            * timestamp.
2964            */
2965           if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
2966                                              clipboard_get_selection_cb,
2967                                              clipboard_clear_selection_cb,
2968                                              G_OBJECT (buffer)))
2969             clipboard_clear_selection_cb (clipboard, buffer);
2970         }
2971
2972       tmp_list = tmp_list->next;
2973     }
2974 }
2975
2976 static SelectionClipboard *
2977 find_selection_clipboard (GtkTextBuffer *buffer,
2978                           GtkClipboard  *clipboard)
2979 {
2980   GSList *tmp_list = buffer->selection_clipboards;
2981   while (tmp_list)
2982     {
2983       SelectionClipboard *selection_clipboard = tmp_list->data;
2984       if (selection_clipboard->clipboard == clipboard)
2985         return selection_clipboard;
2986       
2987       tmp_list = tmp_list->next;
2988     }
2989
2990   return NULL;
2991 }
2992
2993 /**
2994  * gtk_text_buffer_add_selection_clipboard:
2995  * @buffer: a #GtkTextBuffer
2996  * @clipboard: a #GtkClipboard
2997  * 
2998  * Adds @clipboard to the list of clipboards in which the selection contents
2999  * of @buffer are available. In most cases, @clipboard will be the #GtkClipboard
3000  * of type %GDK_SELECTION_PRIMARY for a view of @buffer.
3001  **/
3002 void
3003 gtk_text_buffer_add_selection_clipboard (GtkTextBuffer *buffer,
3004                                          GtkClipboard  *clipboard)
3005 {
3006   SelectionClipboard *selection_clipboard;
3007
3008   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3009   g_return_if_fail (clipboard != NULL);
3010
3011   selection_clipboard = find_selection_clipboard (buffer, clipboard);
3012   if (selection_clipboard)
3013     {
3014       selection_clipboard->ref_count++;
3015     }
3016   else
3017     {
3018       selection_clipboard = g_new (SelectionClipboard, 1);
3019
3020       selection_clipboard->clipboard = clipboard;
3021       selection_clipboard->ref_count = 1;
3022
3023       buffer->selection_clipboards = g_slist_prepend (buffer->selection_clipboards, selection_clipboard);
3024     }
3025 }
3026
3027 /**
3028  * gtk_text_buffer_remove_selection_clipboard:
3029  * @buffer: a #GtkTextBuffer
3030  * @clipboard: a #GtkClipboard added to @buffer by gtk_text_buffer_add_selection_clipboard().
3031  * 
3032  * Removes a #GtkClipboard added with gtk_text_buffer_add_selection_clipboard()
3033  **/
3034 void 
3035 gtk_text_buffer_remove_selection_clipboard (GtkTextBuffer *buffer,
3036                                             GtkClipboard  *clipboard)
3037 {
3038   SelectionClipboard *selection_clipboard;
3039
3040   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3041   g_return_if_fail (clipboard != NULL);
3042
3043   selection_clipboard = find_selection_clipboard (buffer, clipboard);
3044   g_return_if_fail (selection_clipboard != NULL);
3045
3046   selection_clipboard->ref_count--;
3047   if (selection_clipboard->ref_count == 0)
3048     {
3049       if (gtk_clipboard_get_owner (selection_clipboard->clipboard) == G_OBJECT (buffer))
3050         gtk_clipboard_clear (selection_clipboard->clipboard);
3051
3052       buffer->selection_clipboards = g_slist_remove (buffer->selection_clipboards,
3053                                                      selection_clipboard);
3054       
3055       g_free (selection_clipboard);
3056     }
3057 }
3058
3059 static void
3060 remove_all_selection_clipboards (GtkTextBuffer *buffer)
3061 {
3062   GSList *tmp_list = buffer->selection_clipboards;
3063   while (tmp_list)
3064     {
3065       SelectionClipboard *selection_clipboard = tmp_list->data;
3066       
3067       if (gtk_clipboard_get_owner (selection_clipboard->clipboard) == G_OBJECT (buffer))
3068         gtk_clipboard_clear (selection_clipboard->clipboard);
3069       
3070       g_free (selection_clipboard);
3071
3072       tmp_list = tmp_list->next;
3073     }
3074
3075   g_slist_free (buffer->selection_clipboards);
3076   buffer->selection_clipboards = NULL;
3077 }
3078
3079 /**
3080  * gtk_text_buffer_paste_clipboard:
3081  * @buffer: a #GtkTextBuffer
3082  * @clipboard: the #GtkClipboard to paste from
3083  * @override_location: location to insert pasted text, or %NULL for at the cursor
3084  * @default_editable: whether the buffer is editable by default
3085  *
3086  * Pastes the contents of a clipboard at the insertion point, or at @override_location.
3087  * (Note: pasting is asynchronous, that is, we'll ask for the paste data and
3088  * return, and at some point later after the main loop runs, the paste
3089  * data will be inserted.)
3090  * 
3091  **/
3092 void
3093 gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer,
3094                                  GtkClipboard  *clipboard,
3095                                  GtkTextIter   *override_location,
3096                                  gboolean       default_editable)
3097 {
3098   ClipboardRequest *data = g_new (ClipboardRequest, 1);
3099   GtkTextIter paste_point;
3100   GtkTextIter start, end;
3101
3102   if (override_location != NULL)
3103     gtk_text_buffer_create_mark (buffer,
3104                                  "gtk_paste_point_override",
3105                                  override_location, FALSE);
3106
3107   data->buffer = buffer;
3108   g_object_ref (G_OBJECT (buffer));
3109   data->interactive = TRUE;
3110   data->default_editable = default_editable;
3111
3112   /* When pasting with the cursor inside the selection area, you
3113    * replace the selection with the new text, otherwise, you
3114    * simply insert the new text at the point where the click
3115    * occured, unselecting any selected text. The replace_selection
3116    * flag toggles this behavior.
3117    */
3118   data->replace_selection = FALSE;
3119   
3120   get_paste_point (buffer, &paste_point, FALSE);
3121   if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end) &&
3122       (gtk_text_iter_in_range (&paste_point, &start, &end) ||
3123        gtk_text_iter_equal (&paste_point, &end)))
3124     data->replace_selection = TRUE;
3125
3126   gtk_clipboard_request_contents (clipboard,
3127                                   gdk_atom_intern ("GTK_TEXT_BUFFER_CONTENTS", FALSE),
3128                                   clipboard_clipboard_buffer_received, data);
3129 }
3130
3131 /**
3132  * gtk_text_buffer_delete_selection:
3133  * @buffer: a #GtkTextBuffer 
3134  * @interactive: whether the deletion is caused by user interaction
3135  * @default_editable: whether the buffer is editable by default
3136  *
3137  * Deletes the range between the "insert" and "selection_bound" marks,
3138  * that is, the currently-selected text. If @interactive is %TRUE,
3139  * the editability of the selection will be considered (users can't delete
3140  * uneditable text).
3141  * 
3142  * Return value: whether there was a non-empty selection to delete
3143  **/
3144 gboolean
3145 gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
3146                                   gboolean interactive,
3147                                   gboolean default_editable)
3148 {
3149   GtkTextIter start;
3150   GtkTextIter end;
3151
3152   if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
3153     {
3154       return FALSE; /* No selection */
3155     }
3156   else
3157     {
3158       if (interactive)
3159         {
3160           gtk_text_buffer_begin_user_action (buffer);
3161           gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable);
3162           gtk_text_buffer_end_user_action (buffer);
3163         }
3164       else
3165         gtk_text_buffer_delete (buffer, &start, &end);
3166
3167       return TRUE; /* We deleted stuff */
3168     }
3169 }
3170
3171 static void
3172 cut_or_copy (GtkTextBuffer *buffer,
3173              GtkClipboard  *clipboard,
3174              gboolean       delete_region_after,
3175              gboolean       interactive,
3176              gboolean       default_editable)
3177 {
3178   /* We prefer to cut the selected region between selection_bound and
3179    * insertion point. If that region is empty, then we cut the region
3180    * between the "anchor" and the insertion point (this is for
3181    * C-space and M-w and other Emacs-style copy/yank keys). Note that
3182    * insert and selection_bound are guaranteed to exist, but the
3183    * anchor only exists sometimes.
3184    */
3185   GtkTextIter start;
3186   GtkTextIter end;
3187
3188   remove_clipboard_contents_buffer (buffer, clipboard);
3189   
3190   if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
3191     {
3192       /* Let's try the anchor thing */
3193       GtkTextMark * anchor = gtk_text_buffer_get_mark (buffer, "anchor");
3194
3195       if (anchor == NULL)
3196         return;
3197       else
3198         {
3199           gtk_text_buffer_get_iter_at_mark (buffer, &end, anchor);
3200           gtk_text_iter_order (&start, &end);
3201         }
3202     }
3203
3204   if (!gtk_text_iter_equal (&start, &end))
3205     {
3206       GtkTextIter ins;
3207       GtkTextBuffer *contents;
3208       
3209       contents = get_clipboard_contents_buffer (buffer, clipboard, TRUE);
3210
3211       gtk_text_buffer_get_iter_at_offset (contents, &ins, 0);
3212       
3213       gtk_text_buffer_insert_range (contents, &ins, &start, &end);
3214                                     
3215       if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
3216                                          clipboard_get_contents_cb,
3217                                          clipboard_clear_contents_cb,
3218                                          G_OBJECT (buffer)))
3219         clipboard_clear_contents_cb (clipboard, buffer);      
3220
3221       if (delete_region_after)
3222         {
3223           if (interactive)
3224             gtk_text_buffer_delete_interactive (buffer, &start, &end,
3225                                                 default_editable);
3226           else
3227             gtk_text_buffer_delete (buffer, &start, &end);
3228         }
3229     }
3230 }
3231
3232 /**
3233  * gtk_text_buffer_cut_clipboard:
3234  * @buffer: a #GtkTextBuffer
3235  * @clipboard: the #GtkClipboard object to cut to.
3236  * @default_editable: default editability of the buffer
3237  *
3238  * Copies the currently-selected text to a clipboard, then deletes
3239  * said text if it's editable.
3240  * 
3241  **/
3242 void
3243 gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer,
3244                                GtkClipboard  *clipboard,
3245                                gboolean       default_editable)
3246 {
3247   gtk_text_buffer_begin_user_action (buffer);
3248   cut_or_copy (buffer, clipboard, TRUE, TRUE, default_editable);
3249   gtk_text_buffer_end_user_action (buffer);
3250 }
3251
3252 /**
3253  * gtk_text_buffer_copy_clipboard:
3254  * @buffer: a #GtkTextBuffer 
3255  * @clipboard: the #GtkClipboard object to copy to.
3256  *
3257  * Copies the currently-selected text to a clipboard.
3258  * 
3259  **/
3260 void
3261 gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer,
3262                                 GtkClipboard  *clipboard)
3263 {
3264   gtk_text_buffer_begin_user_action (buffer);
3265   cut_or_copy (buffer, clipboard, FALSE, TRUE, TRUE);
3266   gtk_text_buffer_end_user_action (buffer);
3267 }
3268
3269
3270 /**
3271  * gtk_text_buffer_get_selection_bounds:
3272  * @buffer: a #GtkTextBuffer a #GtkTextBuffer
3273  * @start: iterator to initialize with selection start
3274  * @end: iterator to initialize with selection end
3275  *
3276  * Returns %TRUE if some text is selected; places the bounds
3277  * of the selection in @start and @end (if the selection has length 0,
3278  * then @start and @end are filled in with the same value).
3279  * @start and @end will be in ascending order. If @start and @end are
3280  * NULL, then they are not filled in, but the return value still indicates
3281  * whether text is selected.
3282  *
3283  * Return value: whether the selection has nonzero length
3284  **/
3285 gboolean
3286 gtk_text_buffer_get_selection_bounds   (GtkTextBuffer      *buffer,
3287                                         GtkTextIter        *start,
3288                                         GtkTextIter        *end)
3289 {
3290   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
3291
3292   return _gtk_text_btree_get_selection_bounds (get_btree (buffer), start, end);
3293 }
3294
3295 /**
3296  * gtk_text_buffer_begin_user_action:
3297  * @buffer: a #GtkTextBuffer
3298  * 
3299  * Called to indicate that the buffer operations between here and a
3300  * call to gtk_text_buffer_end_user_action() are part of a single
3301  * user-visible operation. The operations between
3302  * gtk_text_buffer_begin_user_action() and
3303  * gtk_text_buffer_end_user_action() can then be grouped when creating
3304  * an undo stack. #GtkTextBuffer maintains a count of calls to
3305  * gtk_text_buffer_begin_user_action() that have not been closed with
3306  * a call to gtk_text_buffer_end_user_action(), and emits the "begin_user_action"
3307  * and "end_user_action" signals only for the outermost pair of calls.
3308  * This allows you to build user actions from other user actions.
3309  *
3310  * The "interactive" buffer mutation functions, such as
3311  * gtk_text_buffer_insert_interactive(), automatically call begin/end
3312  * user action around the buffer operations they perform, so there's
3313  * no need to add extra calls if you user action consists solely of a
3314  * single call to one of those functions.
3315  **/
3316 void
3317 gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer)
3318 {
3319   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3320
3321   buffer->user_action_count += 1;
3322   
3323   if (buffer->user_action_count == 1)
3324     {
3325       /* Outermost nested user action begin emits the signal */
3326       g_signal_emit (G_OBJECT (buffer), signals[BEGIN_USER_ACTION], 0);
3327     }
3328 }
3329
3330 /**
3331  * gtk_text_buffer_end_user_action:
3332  * @buffer: a #GtkTextBuffer
3333  * 
3334  * Should be paired with a call to gtk_text_buffer_begin_user_action().
3335  * See that function for a full explanation.
3336  **/
3337 void
3338 gtk_text_buffer_end_user_action (GtkTextBuffer *buffer)
3339 {
3340   g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3341   g_return_if_fail (buffer->user_action_count > 0);
3342   
3343   buffer->user_action_count -= 1;
3344   
3345   if (buffer->user_action_count == 0)
3346     {
3347       /* Ended the outermost-nested user action end, so emit the signal */
3348       g_signal_emit (G_OBJECT (buffer), signals[END_USER_ACTION], 0);
3349     }
3350 }
3351
3352 /*
3353  * Logical attribute cache
3354  */
3355
3356 #define ATTR_CACHE_SIZE 2
3357
3358 typedef struct _CacheEntry CacheEntry;
3359 struct _CacheEntry
3360 {
3361   gint line;
3362   gint char_len;
3363   PangoLogAttr *attrs;
3364 };
3365
3366
3367 struct _GtkTextLogAttrCache
3368 {
3369   gint chars_changed_stamp;
3370   CacheEntry entries[ATTR_CACHE_SIZE];
3371 };
3372
3373 static void
3374 free_log_attr_cache (GtkTextLogAttrCache *cache)
3375 {
3376   gint i = 0;
3377   while (i < ATTR_CACHE_SIZE)
3378     {
3379       g_free (cache->entries[i].attrs);
3380       ++i;
3381     }
3382   g_free (cache);
3383 }
3384
3385 static void
3386 clear_log_attr_cache (GtkTextLogAttrCache *cache)
3387 {
3388   gint i = 0;
3389   while (i < ATTR_CACHE_SIZE)
3390     {
3391       g_free (cache->entries[i].attrs);
3392       cache->entries[i].attrs = NULL;
3393       ++i;
3394     }
3395 }
3396
3397 static PangoLogAttr*
3398 compute_log_attrs (const GtkTextIter *iter,
3399                    gint              *char_lenp)
3400 {
3401   GtkTextIter start;
3402   GtkTextIter end;
3403   gchar *paragraph;
3404   gint char_len, byte_len;
3405   PangoLogAttr *attrs = NULL;
3406   
3407   start = *iter;
3408   end = *iter;
3409
3410   gtk_text_iter_set_line_offset (&start, 0);
3411   gtk_text_iter_forward_line (&end);
3412
3413   paragraph = gtk_text_iter_get_slice (&start, &end);
3414   char_len = g_utf8_strlen (paragraph, -1);
3415   byte_len = strlen (paragraph);
3416
3417   g_assert (char_len > 0);
3418
3419   if (char_lenp)
3420     *char_lenp = char_len;
3421   
3422   attrs = g_new (PangoLogAttr, char_len);
3423   
3424   pango_get_log_attrs (paragraph, byte_len, -1,
3425                        gtk_text_iter_get_language (&start),
3426                        attrs);
3427   
3428   g_free (paragraph);
3429
3430   return attrs;
3431 }
3432
3433 /* The return value from this is valid until you call this a second time.
3434  */
3435 const PangoLogAttr*
3436 _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer     *buffer,
3437                                      const GtkTextIter *anywhere_in_line,
3438                                      gint              *char_len)
3439 {
3440   gint line;
3441   GtkTextLogAttrCache *cache;
3442   gint i;
3443   
3444   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
3445   g_return_val_if_fail (anywhere_in_line != NULL, NULL);
3446
3447   /* special-case for empty last line in buffer */
3448   if (gtk_text_iter_is_end (anywhere_in_line) &&
3449       gtk_text_iter_get_line_offset (anywhere_in_line) == 0)
3450     {
3451       if (char_len)
3452         *char_len = 0;
3453       return NULL;
3454     }
3455   
3456   /* FIXME we also need to recompute log attrs if the language tag at
3457    * the start of a paragraph changes
3458    */
3459   
3460   if (buffer->log_attr_cache == NULL)
3461     {
3462       buffer->log_attr_cache = g_new0 (GtkTextLogAttrCache, 1);
3463       buffer->log_attr_cache->chars_changed_stamp =
3464         _gtk_text_btree_get_chars_changed_stamp (get_btree (buffer));
3465     }
3466   else if (buffer->log_attr_cache->chars_changed_stamp !=
3467            _gtk_text_btree_get_chars_changed_stamp (get_btree (buffer)))
3468     {
3469       clear_log_attr_cache (buffer->log_attr_cache);
3470     }
3471   
3472   cache = buffer->log_attr_cache;
3473   line = gtk_text_iter_get_line (anywhere_in_line);
3474
3475   i = 0;
3476   while (i < ATTR_CACHE_SIZE)
3477     {
3478       if (cache->entries[i].attrs &&
3479           cache->entries[i].line == line)
3480         {
3481           if (char_len)
3482             *char_len = cache->entries[i].char_len;
3483           return cache->entries[i].attrs;
3484         }
3485       ++i;
3486     }
3487   
3488   /* Not in cache; open up the first cache entry */
3489   if (cache->entries[ATTR_CACHE_SIZE-1].attrs)
3490     g_free (cache->entries[ATTR_CACHE_SIZE-1].attrs);
3491   
3492   g_memmove (cache->entries + 1, cache->entries,
3493              sizeof (CacheEntry) * (ATTR_CACHE_SIZE - 1));
3494
3495   cache->entries[0].line = line;
3496   cache->entries[0].attrs = compute_log_attrs (anywhere_in_line,
3497                                                &cache->entries[0].char_len);
3498
3499   if (char_len)
3500     *char_len = cache->entries[0].char_len;
3501   
3502   return cache->entries[0].attrs;
3503 }
3504
3505 /*
3506  * Debug spew
3507  */
3508
3509 void
3510 _gtk_text_buffer_spew (GtkTextBuffer *buffer)
3511 {
3512   _gtk_text_btree_spew (get_btree (buffer));
3513 }
3514
3515
3516
3517
3518