]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbuffer.c
Test program to make sure GtkTextBuffer is in working order.
[~andy/gtk] / gtk / gtktextbuffer.c
1 /*  gtktextbuffer.c - the "model" in the MVC text widget architecture 
2  *  Copyright (c) 2000 Red Hat, Inc.
3  *  Developed by Havoc Pennington
4  */
5
6 #include "gtkinvisible.h"
7 #include "gtkselection.h"
8 #include "gtksignal.h"
9 #include "gtktextbuffer.h"
10 #include "gtktextbtree.h"
11 #include "gtktextiterprivate.h"
12
13 enum {
14   INSERT_TEXT,
15   DELETE_TEXT,
16   CHANGED,
17   MODIFIED_CHANGED,
18   MARK_SET,
19   MARK_DELETED,
20   APPLY_TAG,
21   REMOVE_TAG,
22   LAST_SIGNAL
23 };
24
25 enum {
26   ARG_0,
27   LAST_ARG
28 };
29
30 enum {
31   TARGET_STRING,
32   TARGET_TEXT,
33   TARGET_COMPOUND_TEXT,
34   TARGET_UTF8_STRING
35 };
36
37 static void gtk_text_buffer_init       (GtkTextBuffer      *tkxt_buffer);
38 static void gtk_text_buffer_class_init (GtkTextBufferClass *klass);
39 static void gtk_text_buffer_destroy    (GtkObject          *object);
40 static void gtk_text_buffer_finalize   (GObject            *object);
41
42
43 static void gtk_text_buffer_update_primary_selection   (GtkTextBuffer     *buffer);
44 static void gtk_text_buffer_update_clipboard_selection (GtkTextBuffer     *buffer);
45 static void gtk_text_buffer_real_insert_text           (GtkTextBuffer     *buffer,
46                                                         GtkTextIter       *iter,
47                                                         const gchar       *text,
48                                                         gint               len);
49 static void gtk_text_buffer_real_delete_text           (GtkTextBuffer     *buffer,
50                                                         GtkTextIter       *start,
51                                                         GtkTextIter       *end);
52 static void gtk_text_buffer_real_apply_tag             (GtkTextBuffer     *buffer,
53                                                         GtkTextTag        *tag,
54                                                         const GtkTextIter *start_char,
55                                                         const GtkTextIter *end_char);
56 static void gtk_text_buffer_real_remove_tag            (GtkTextBuffer     *buffer,
57                                                         GtkTextTag        *tag,
58                                                         const GtkTextIter *start_char,
59                                                         const GtkTextIter *end_char);
60
61
62 void gtk_marshal_NONE__INT_POINTER_INT (GtkObject  *object,
63                                         GtkSignalFunc func,
64                                         gpointer func_data,
65                                         GtkArg  *args);
66
67 static GdkAtom clipboard_atom = GDK_NONE;
68 static GdkAtom text_atom = GDK_NONE;
69 static GdkAtom ctext_atom = GDK_NONE;
70 static GdkAtom utf8_atom = GDK_NONE;
71
72 static GtkObjectClass *parent_class = NULL;
73 static guint signals[LAST_SIGNAL] = { 0 };
74
75 GtkType
76 gtk_text_buffer_get_type (void)
77 {
78   static GtkType our_type = 0;
79
80   if (our_type == 0)
81     {
82       static const GtkTypeInfo our_info =
83       {
84         "GtkTextBuffer",
85         sizeof (GtkTextBuffer),
86         sizeof (GtkTextBufferClass),
87         (GtkClassInitFunc) gtk_text_buffer_class_init,
88         (GtkObjectInitFunc) gtk_text_buffer_init,
89         /* reserved_1 */ NULL,
90         /* reserved_2 */ NULL,
91         (GtkClassInitFunc) NULL
92       };
93
94       our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
95     }
96
97   return our_type;
98 }
99
100 static void
101 gtk_text_buffer_class_init (GtkTextBufferClass *klass)
102 {
103   GtkObjectClass *object_class = (GtkObjectClass*) klass;
104   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
105
106   parent_class = gtk_type_class (GTK_TYPE_OBJECT);
107   
108   signals[INSERT_TEXT] =
109     gtk_signal_new ("insert_text",
110                     GTK_RUN_LAST,
111                     GTK_CLASS_TYPE (object_class),
112                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, insert_text),
113                     gtk_marshal_NONE__INT_POINTER_INT,
114                     GTK_TYPE_NONE,
115                     3,
116                     GTK_TYPE_INT,
117                     GTK_TYPE_POINTER,
118                     GTK_TYPE_INT);
119
120   signals[DELETE_TEXT] =
121     gtk_signal_new ("delete_text",
122                     GTK_RUN_LAST,
123                     GTK_CLASS_TYPE (object_class),
124                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, delete_text),
125                     gtk_marshal_NONE__INT_INT,
126                     GTK_TYPE_NONE,
127                     2,
128                     GTK_TYPE_INT,
129                     GTK_TYPE_INT);
130
131   signals[CHANGED] =
132     gtk_signal_new ("changed",
133                     GTK_RUN_LAST,
134                     GTK_CLASS_TYPE (object_class),
135                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, changed),
136                     gtk_marshal_NONE__NONE,
137                     GTK_TYPE_NONE,
138                     0);
139
140   signals[MODIFIED_CHANGED] =
141     gtk_signal_new ("modified_changed",
142                     GTK_RUN_LAST,
143                     GTK_CLASS_TYPE (object_class),
144                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, modified_changed),
145                     gtk_marshal_NONE__NONE,
146                     GTK_TYPE_NONE,
147                     0);
148   
149   signals[MARK_SET] =
150     gtk_signal_new ("mark_set",
151                     GTK_RUN_LAST,
152                     GTK_CLASS_TYPE (object_class),
153                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_set),
154                     gtk_marshal_NONE__POINTER_POINTER,
155                     GTK_TYPE_NONE,
156                     2,
157                     GTK_TYPE_POINTER,
158                     GTK_TYPE_POINTER);
159
160   signals[MARK_DELETED] =
161     gtk_signal_new ("mark_deleted",
162                     GTK_RUN_LAST,
163                     GTK_CLASS_TYPE (object_class),
164                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_deleted),
165                     gtk_marshal_NONE__POINTER,
166                     GTK_TYPE_NONE,
167                     1,
168                     GTK_TYPE_POINTER);
169
170   signals[APPLY_TAG] =
171     gtk_signal_new ("apply_tag",
172                     GTK_RUN_LAST,
173                     GTK_CLASS_TYPE (object_class),
174                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, apply_tag),
175                     gtk_marshal_NONE__POINTER_INT_INT,
176                     GTK_TYPE_NONE,
177                     3,
178                     GTK_TYPE_POINTER,
179                     GTK_TYPE_INT,
180                     GTK_TYPE_INT);
181     
182   signals[REMOVE_TAG] =
183     gtk_signal_new ("remove_tag",
184                     GTK_RUN_LAST,
185                     GTK_CLASS_TYPE (object_class),
186                     GTK_SIGNAL_OFFSET (GtkTextBufferClass, remove_tag),
187                     gtk_marshal_NONE__POINTER_INT_INT,
188                     GTK_TYPE_NONE,
189                     3,
190                     GTK_TYPE_POINTER,
191                     GTK_TYPE_INT,
192                     GTK_TYPE_INT);
193   
194   gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
195
196   object_class->destroy = gtk_text_buffer_destroy;
197
198   gobject_class->finalize = gtk_text_buffer_finalize;
199
200   klass->insert_text = gtk_text_buffer_real_insert_text;
201   klass->delete_text = gtk_text_buffer_real_delete_text;
202   klass->apply_tag = gtk_text_buffer_real_apply_tag;
203   klass->remove_tag = gtk_text_buffer_real_remove_tag;
204 }
205
206
207 typedef gint (*GtkSignal_NONE__INT_POINTER_INT) (GtkObject  *object,
208                                                  gint pos,
209                                                  const gchar *text,
210                                                  gint len,
211                                                  gpointer user_data);
212
213 void 
214 gtk_marshal_NONE__INT_POINTER_INT (GtkObject  *object,
215                                    GtkSignalFunc func,
216                                    gpointer func_data,
217                                    GtkArg  *args)
218 {
219   GtkSignal_NONE__INT_POINTER_INT rfunc;
220
221   rfunc = (GtkSignal_NONE__INT_POINTER_INT) func;
222
223   (*rfunc) (object,
224             GTK_VALUE_INT (args[0]),
225             GTK_VALUE_POINTER (args[1]),
226             GTK_VALUE_INT (args[2]),
227             func_data);
228 }
229
230 void
231 gtk_text_buffer_init (GtkTextBuffer *buffer)
232 {
233   static const GtkTargetEntry targets[] = {
234     { "STRING", 0, TARGET_STRING },
235     { "TEXT",   0, TARGET_TEXT }, 
236     { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
237     { "UTF8_STRING", 0, TARGET_UTF8_STRING }
238   };
239   static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
240
241   if (!clipboard_atom)
242     clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
243
244   if (!text_atom)
245     text_atom = gdk_atom_intern ("TEXT", FALSE);
246
247   if (!ctext_atom)
248     ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
249
250   if (!utf8_atom)
251     utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
252   
253   buffer->selection_widget = gtk_invisible_new();
254   
255   gtk_selection_add_targets (buffer->selection_widget,
256                              GDK_SELECTION_PRIMARY,
257                              targets, n_targets);
258   gtk_selection_add_targets (buffer->selection_widget,
259                              clipboard_atom,
260                              targets, n_targets);
261 }
262
263 GtkTextBuffer*
264 gtk_text_buffer_new (GtkTextTagTable *table)
265 {
266   GtkTextBuffer *text_buffer;
267
268   /* This is broken, need construct_only argument for the tag table
269      so language bindings can set it.
270   */
271   
272   text_buffer = GTK_TEXT_BUFFER (gtk_type_new (gtk_text_buffer_get_type ()));
273
274   if (table)
275     text_buffer->tag_table = table;
276   else
277     text_buffer->tag_table = gtk_text_tag_table_new();
278
279   gtk_object_ref (GTK_OBJECT(text_buffer->tag_table));
280   gtk_object_sink (GTK_OBJECT(text_buffer->tag_table));
281   
282   text_buffer->tree = gtk_text_btree_new(text_buffer->tag_table,
283                                          text_buffer);  
284   
285   return text_buffer;
286 }
287
288 static void
289 gtk_text_buffer_destroy (GtkObject *object)
290 {
291   GtkTextBuffer *buffer;
292
293   buffer = GTK_TEXT_BUFFER (object);
294
295   gtk_widget_destroy(buffer->selection_widget);
296   buffer->selection_widget = NULL;
297
298   gtk_object_unref(GTK_OBJECT(buffer->tag_table));
299   buffer->tag_table = NULL;
300   
301   gtk_text_btree_unref(buffer->tree);
302   buffer->tree = NULL;
303   
304   (* parent_class->destroy) (object);
305 }
306
307 static void
308 gtk_text_buffer_finalize (GObject *object)
309 {
310   GtkTextBuffer *tkxt_buffer;
311
312   tkxt_buffer = GTK_TEXT_BUFFER (object);
313
314   G_OBJECT_CLASS (parent_class)->finalize (object);
315 }
316
317 /*
318  * Insertion
319  */
320
321 static void
322 gtk_text_buffer_real_insert_text(GtkTextBuffer *buffer,
323                                  GtkTextIter *iter,
324                                  const gchar *text,
325                                  gint len)
326 {
327   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
328   g_return_if_fail(iter != NULL);
329
330   gtk_text_btree_insert(iter, text, len);
331   
332   gtk_signal_emit(GTK_OBJECT(buffer), signals[CHANGED]);
333
334   gtk_text_buffer_set_modified(buffer, TRUE);
335 }
336
337 static void
338 gtk_text_buffer_emit_insert(GtkTextBuffer *buffer,
339                             GtkTextIter *iter,
340                             const gchar *text,
341                             gint len)
342 {
343   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
344   g_return_if_fail(iter != NULL);
345   g_return_if_fail(text != NULL);
346
347   if (len < 0)
348     len = strlen(text);
349   
350   if (len > 0)
351     {
352       gtk_signal_emit(GTK_OBJECT(buffer), signals[INSERT_TEXT],
353                       iter, text, len);
354     }
355 }
356
357 void
358 gtk_text_buffer_insert (GtkTextBuffer *buffer,
359                         GtkTextIter *iter,
360                         const gchar *text,
361                         gint len)
362 {
363   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
364   g_return_if_fail(iter != NULL);
365   g_return_if_fail(text != NULL);
366   
367   gtk_text_buffer_emit_insert(buffer, iter, text, len);
368 }
369
370 void
371 gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
372                                   const gchar *text,
373                                   gint len)
374 {
375   GtkTextIter iter;
376
377   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
378   g_return_if_fail(text != NULL);
379
380   gtk_text_buffer_get_iter_at_mark(buffer, &iter,
381                                    gtk_text_buffer_get_mark (buffer,
382                                                              "insert"));
383
384   gtk_text_buffer_insert(buffer, &iter, text, len);
385 }
386
387 void
388 gtk_text_buffer_insert_at_char         (GtkTextBuffer      *buffer,
389                                         gint                 char_pos,
390                                         const gchar         *text,
391                                         gint                 len)
392 {
393   GtkTextIter iter;
394
395   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
396   g_return_if_fail(text != NULL);
397   
398   gtk_text_buffer_get_iter_at_char(buffer, &iter, char_pos);
399
400   gtk_text_buffer_insert(buffer, &iter, text, len);
401 }
402
403 void
404 gtk_text_buffer_insert_after_line(GtkTextBuffer *buffer,
405                                   gint after_this_line,
406                                   const gchar *text,
407                                   gint len)
408 {
409   GtkTextIter line;
410   
411   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
412   g_return_if_fail(text != NULL);
413   
414   gtk_text_buffer_get_iter_at_line(buffer,
415                                    &line,
416                                    after_this_line);
417
418   /* Start of the next line */
419   gtk_text_iter_forward_line(&line);
420
421   gtk_text_buffer_insert(buffer, &line, text, len);
422 }
423
424 /*
425  * Deletion
426  */
427
428 static void
429 gtk_text_buffer_real_delete_text(GtkTextBuffer *buffer,
430                                  GtkTextIter *start,
431                                  GtkTextIter *end)
432 {
433   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
434   g_return_if_fail(start != NULL);
435   g_return_if_fail(end != NULL);
436
437   gtk_text_btree_delete(start, end);
438
439   /* may have deleted the selection... */
440   gtk_text_buffer_update_primary_selection(buffer);
441   
442   gtk_signal_emit(GTK_OBJECT(buffer), signals[CHANGED]);
443   
444   gtk_text_buffer_set_modified(buffer, TRUE);
445 }
446
447 static void
448 gtk_text_buffer_emit_delete(GtkTextBuffer *buffer,
449                             GtkTextIter *start,
450                             GtkTextIter *end)
451 {
452   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
453   g_return_if_fail(start != NULL);
454   g_return_if_fail(end != NULL);
455
456   if (gtk_text_iter_equal(start, end))
457     return;
458
459   gtk_signal_emit(GTK_OBJECT(buffer),
460                   signals[DELETE_TEXT],
461                   start, end);
462 }
463
464 void
465 gtk_text_buffer_delete (GtkTextBuffer *buffer,
466                         GtkTextIter *start,
467                         GtkTextIter *end)
468 {
469   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
470   g_return_if_fail(start != NULL);
471   g_return_if_fail(end != NULL);
472   
473   gtk_text_buffer_emit_delete(buffer, start, end);
474 }
475
476 void
477 gtk_text_buffer_delete_chars (GtkTextBuffer      *buffer,
478                               gint                 start_char,
479                               gint                 end_char)
480 {
481   GtkTextIter start;
482   GtkTextIter end;
483   
484   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
485
486   if (start_char == end_char)
487     return;
488
489   gtk_text_buffer_get_iter_at_char(buffer, &start, start_char);
490   gtk_text_buffer_get_iter_at_char(buffer, &end, end_char);
491
492   gtk_text_buffer_emit_delete(buffer, &start, &end);
493 }
494
495 void
496 gtk_text_buffer_delete_lines(GtkTextBuffer *buffer,
497                              gint start_line,
498                              gint end_line)
499 {
500   GtkTextIter start;
501   GtkTextIter end;
502   
503   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
504
505   if (start_line == end_line)
506     return;
507
508   /* start of the start line */
509   gtk_text_buffer_get_iter_at_line(buffer, &start, start_line);
510   
511   /* start of the end line; note that we don't delete the end_line, we
512      want to delete up to the start of it */
513   gtk_text_buffer_get_iter_at_line(buffer, &end, end_line);
514   
515   gtk_text_buffer_delete (buffer, &start, &end);
516 }
517
518 void
519 gtk_text_buffer_delete_from_line(GtkTextBuffer *buffer,
520                                  gint line,
521                                  gint start_char, gint end_char)
522 {
523   GtkTextIter start;
524   GtkTextIter end;
525   
526   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
527   
528   if (start_char == end_char)
529     return;
530
531   gtk_text_buffer_get_iter_at_line_char(buffer, &start, line, start_char);
532   gtk_text_buffer_get_iter_at_line_char(buffer, &end, line, end_char);
533
534   gtk_text_buffer_delete(buffer, &start, &end);
535 }
536
537 /*
538  * Extracting textual buffer contents
539  */
540
541 gchar*
542 gtk_text_buffer_get_text (GtkTextBuffer      *buffer,
543                           const GtkTextIter *start,
544                           const GtkTextIter *end,
545                           gboolean             include_hidden_chars)
546 {
547   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
548   g_return_val_if_fail(start != NULL, NULL);
549   g_return_val_if_fail(end != NULL, NULL);
550
551   if (include_hidden_chars)
552     return gtk_text_iter_get_text(start, end);
553   else
554     return gtk_text_iter_get_visible_text(start, end);
555 }
556
557 gchar*
558 gtk_text_buffer_get_text_chars         (GtkTextBuffer      *buffer,
559                                         gint                 start_char,
560                                         gint                 end_char,
561                                         gboolean             include_hidden_chars)
562 {
563   GtkTextIter start;
564   GtkTextIter end;
565   
566   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
567   
568   if (start_char == end_char)
569     return g_strdup("");
570   
571   gtk_text_buffer_get_iter_at_char (buffer, &start, start_char);
572   gtk_text_buffer_get_iter_at_char (buffer, &end, end_char);
573   
574   return gtk_text_buffer_get_text(buffer, &start, &end,
575                                   include_hidden_chars);
576 }
577
578 gchar*
579 gtk_text_buffer_get_text_from_line    (GtkTextBuffer      *buffer,
580                                        gint                 line,
581                                        gint                 start_char,
582                                        gint                 end_char,
583                                        gboolean             include_hidden_chars)
584 {
585   GtkTextIter start;
586   GtkTextIter end;
587   
588   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
589   
590   if (start_char == end_char)
591     return g_strdup("");
592   
593   gtk_text_buffer_get_iter_at_line_char (buffer, &start, line, start_char);
594   gtk_text_buffer_get_iter_at_line_char (buffer, &end, line, end_char);
595   
596   return gtk_text_buffer_get_text(buffer, &start, &end,
597                                   include_hidden_chars);
598 }
599
600 gchar*
601 gtk_text_buffer_get_slice (GtkTextBuffer      *buffer,
602                            const GtkTextIter *start,
603                            const GtkTextIter *end,
604                            gboolean             include_hidden_chars)
605 {
606   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
607   g_return_val_if_fail(start != NULL, NULL);
608   g_return_val_if_fail(end != NULL, NULL);
609
610   if (include_hidden_chars)
611     return gtk_text_iter_get_slice(start, end);
612   else
613     return gtk_text_iter_get_visible_slice(start, end);
614 }
615
616 gchar*
617 gtk_text_buffer_get_slice_chars         (GtkTextBuffer      *buffer,
618                                          gint                 start_char,
619                                          gint                 end_char,
620                                          gboolean             include_hidden_chars)
621 {
622   GtkTextIter start;
623   GtkTextIter end;
624   
625   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
626   
627   if (start_char == end_char)
628     return g_strdup("");
629   
630   gtk_text_buffer_get_iter_at_char (buffer, &start, start_char);
631   gtk_text_buffer_get_iter_at_char (buffer, &end, end_char);
632   
633   return gtk_text_buffer_get_slice(buffer, &start, &end,
634                                    include_hidden_chars);
635 }
636
637 gchar*
638 gtk_text_buffer_get_slice_from_line    (GtkTextBuffer      *buffer,
639                                         gint                 line,
640                                         gint                 start_char,
641                                         gint                 end_char,
642                                         gboolean             include_hidden_chars)
643 {
644   GtkTextIter start;
645   GtkTextIter end;
646   
647   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
648   
649   if (start_char == end_char)
650     return g_strdup("");
651   
652   gtk_text_buffer_get_iter_at_line_char (buffer, &start, line, start_char);
653   gtk_text_buffer_get_iter_at_line_char (buffer, &end, line, end_char);
654   
655   return gtk_text_buffer_get_slice(buffer, &start, &end,
656                                    include_hidden_chars);
657 }
658
659 /*
660  * Pixmaps
661  */
662
663 void
664 gtk_text_buffer_insert_pixmap         (GtkTextBuffer      *buffer,
665                                        GtkTextIter *iter,
666                                        GdkPixmap           *pixmap,
667                                        GdkBitmap           *mask)
668 {
669   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
670   g_return_if_fail(iter != NULL);
671   g_return_if_fail(pixmap != NULL);
672
673   gtk_text_btree_insert_pixmap(iter, pixmap, mask);
674
675   /* FIXME pixmap-specific signal like insert_text */
676   
677   gtk_signal_emit(GTK_OBJECT(buffer), signals[CHANGED]);
678   
679   gtk_text_buffer_set_modified(buffer, TRUE);
680 }
681
682 void
683 gtk_text_buffer_insert_pixmap_at_char (GtkTextBuffer      *buffer,
684                                        gint                 char_pos,
685                                        GdkPixmap           *pixmap,
686                                        GdkBitmap           *mask)
687 {
688   GtkTextIter iter;
689
690   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
691   g_return_if_fail(pixmap != NULL);
692   
693   gtk_text_buffer_get_iter_at_char(buffer, &iter, char_pos);
694
695   gtk_text_buffer_insert_pixmap(buffer, &iter, pixmap, mask);
696 }
697
698 /*
699  * Mark manipulation
700  */
701
702 static void
703 gtk_text_buffer_mark_set (GtkTextBuffer     *buffer,
704                           const GtkTextIter *location,
705                           GtkTextMark       *mark)
706 {
707   /* IMO this should NOT work like insert_text and delete_text,
708      where the real action happens in the default handler.
709
710      The reason is that the default handler would be _required_,
711      i.e. the whole widget would start breaking and segfaulting
712      if the default handler didn't get run. So you can't really
713      override the default handler or stop the emission; that is,
714      this signal is purely for notification, and not to allow users
715      to modify the default behavior. */
716   gtk_signal_emit(GTK_OBJECT(buffer),
717                   signals[MARK_SET],
718                   &location,
719                   mark);
720
721 }
722
723 /**
724  * gtk_text_buffer_set_mark:
725  * @buffer:       a #GtkTextBuffer
726  * @mark_name:    name of the mark
727  * @iter:         location for the mark.
728  * @left_gravity: if the mark is created by this function, gravity for the new
729  *                mark.
730  * @should_exist: if %TRUE, warn if the mark does not exist, and return
731  *                immediately.
732  * 
733  * Move the mark to the given position, if not @should_exist, create the mark.
734  * 
735  * Return value: 
736  **/
737 static GtkTextMark*
738 gtk_text_buffer_set_mark(GtkTextBuffer *buffer,
739                          GtkTextMark *existing_mark,
740                          const gchar *mark_name,
741                          const GtkTextIter *iter,
742                          gboolean left_gravity,
743                          gboolean should_exist)
744 {
745   GtkTextIter location;
746   GtkTextMark *mark;
747   
748   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
749   
750   mark = gtk_text_btree_set_mark(buffer->tree,
751                                  existing_mark,
752                                  mark_name,
753                                  left_gravity,
754                                  iter,
755                                  should_exist);
756
757   if (gtk_text_btree_mark_is_insert(buffer->tree, mark) ||
758       gtk_text_btree_mark_is_selection_bound(buffer->tree, mark))
759     {
760       gtk_text_buffer_update_primary_selection(buffer);
761     }
762   
763   gtk_text_btree_get_iter_at_mark(buffer->tree,
764                                   &location,
765                                   mark);
766   
767   gtk_text_buffer_mark_set (buffer, &location, mark);
768
769   return (GtkTextMark*)mark;
770 }
771
772 GtkTextMark*
773 gtk_text_buffer_create_mark(GtkTextBuffer *buffer,
774                             const gchar *mark_name,
775                             const GtkTextIter *where,
776                             gboolean left_gravity)
777 {
778   return gtk_text_buffer_set_mark(buffer, NULL, mark_name, where,
779                                   left_gravity, FALSE);
780 }
781
782 void
783 gtk_text_buffer_move_mark(GtkTextBuffer *buffer,
784                           GtkTextMark *mark,
785                           const GtkTextIter *where)
786 {
787   g_return_if_fail (mark != NULL);
788   
789   gtk_text_buffer_set_mark(buffer, mark, NULL, where, FALSE, TRUE);
790 }
791
792 void
793 gtk_text_buffer_get_iter_at_mark(GtkTextBuffer *buffer,
794                                  GtkTextIter *iter,
795                                  GtkTextMark *mark)
796 {
797   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
798
799   gtk_text_btree_get_iter_at_mark(buffer->tree,
800                                   iter,
801                                   mark);
802 }
803
804 void
805 gtk_text_buffer_delete_mark(GtkTextBuffer *buffer,
806                             GtkTextMark *mark)
807 {
808   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
809
810   gtk_text_btree_remove_mark (buffer->tree, mark);
811
812   /* See rationale above for MARK_SET on why we emit this after
813      removing the mark, rather than removing the mark in a default
814      handler. */
815   gtk_signal_emit(GTK_OBJECT(buffer), signals[MARK_DELETED],
816                   mark);
817 }
818
819 GtkTextMark*
820 gtk_text_buffer_get_mark (GtkTextBuffer      *buffer,
821                           const gchar         *name)
822 {
823   GtkTextMark *mark;
824
825   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
826   g_return_val_if_fail(name != NULL, NULL);
827   
828   mark = gtk_text_btree_get_mark_by_name(buffer->tree, name);
829
830   return mark;
831 }
832
833 void
834 gtk_text_buffer_place_cursor (GtkTextBuffer     *buffer,
835                               const GtkTextIter *where)
836 {
837   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
838   
839   gtk_text_btree_place_cursor(buffer->tree, where);
840   gtk_text_buffer_mark_set (buffer, where,
841                             gtk_text_buffer_get_mark (buffer,
842                                                       "insert"));
843   gtk_text_buffer_mark_set (buffer, where,
844                             gtk_text_buffer_get_mark (buffer,
845                                                       "selection_bound"));
846 }
847
848 /*
849  * Tags
850  */
851
852 GtkTextTag*
853 gtk_text_buffer_create_tag(GtkTextBuffer *buffer,
854                            const gchar *tag_name)
855 {
856   GtkTextTag *tag;
857   
858   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
859   g_return_val_if_fail(tag_name != NULL, NULL);
860   
861   tag = gtk_text_tag_new(tag_name);
862
863   gtk_text_tag_table_add(buffer->tag_table, tag);
864
865   return tag;
866 }
867
868 static void
869 gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
870                                 GtkTextTag *tag,
871                                 const GtkTextIter *start,
872                                 const GtkTextIter *end)
873 {
874   gtk_text_btree_tag(start, end, tag, TRUE);
875 }
876
877 static void
878 gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
879                                  GtkTextTag *tag,
880                                  const GtkTextIter *start,
881                                  const GtkTextIter *end)
882 {
883   gtk_text_btree_tag(start, end, tag, FALSE);
884 }
885
886
887 static void
888 gtk_text_buffer_emit_tag(GtkTextBuffer *buffer,
889                          const gchar *name,
890                          gboolean apply,
891                          const GtkTextIter *start,
892                          const GtkTextIter *end)
893 {
894   GtkTextTag *tag;
895   
896   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
897   g_return_if_fail(name != NULL);
898   g_return_if_fail(start != NULL);
899   g_return_if_fail(end != NULL);
900   
901   tag = gtk_text_tag_table_lookup(buffer->tag_table,
902                                   name);
903
904   if (tag == NULL)
905     {
906       g_warning("Unknown tag `%s'", name);
907       return;
908     }
909
910   if (apply)
911     gtk_signal_emit(GTK_OBJECT(buffer), signals[APPLY_TAG],
912                     tag, start, end);
913   else
914     gtk_signal_emit(GTK_OBJECT(buffer), signals[REMOVE_TAG],
915                     tag, start, end);
916 }
917
918
919 void
920 gtk_text_buffer_apply_tag(GtkTextBuffer *buffer,
921                           const gchar *name,
922                           const GtkTextIter *start,
923                           const GtkTextIter *end)
924 {
925   gtk_text_buffer_emit_tag(buffer, name, TRUE, start, end);
926 }
927
928 void
929 gtk_text_buffer_remove_tag(GtkTextBuffer *buffer,
930                            const gchar *name,
931                            const GtkTextIter *start,
932                            const GtkTextIter *end)
933
934 {
935   gtk_text_buffer_emit_tag(buffer, name, FALSE, start, end);
936 }
937
938 void
939 gtk_text_buffer_apply_tag_to_chars(GtkTextBuffer *buffer,
940                                    const gchar *name,
941                                    gint start_char, gint end_char)
942 {
943   GtkTextIter start;
944   GtkTextIter end;
945
946   gtk_text_buffer_get_iter_at_char(buffer, &start, start_char);
947   gtk_text_buffer_get_iter_at_char(buffer, &end, end_char);
948   
949   gtk_text_buffer_apply_tag(buffer, name, &start, &end);
950 }
951
952 void
953 gtk_text_buffer_remove_tag_from_chars(GtkTextBuffer *buffer,
954                                       const gchar *name,
955                                       gint start_char, gint end_char)
956 {
957   GtkTextIter start;
958   GtkTextIter end;
959
960   gtk_text_buffer_get_iter_at_char(buffer, &start, start_char);
961   gtk_text_buffer_get_iter_at_char(buffer, &end, end_char);
962   
963   gtk_text_buffer_remove_tag(buffer, name, &start, &end);
964 }
965
966 /*
967  * Obtain various iterators
968  */
969
970 void
971 gtk_text_buffer_get_iter_at_line_char    (GtkTextBuffer      *buffer,
972                                           GtkTextIter        *iter,
973                                           gint                 line_number,
974                                           gint                 char_number)
975 {  
976   g_return_if_fail(iter != NULL);
977   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
978
979   gtk_text_btree_get_iter_at_line_char(buffer->tree,
980                                        iter, line_number, char_number);
981 }
982
983 void
984 gtk_text_buffer_get_iter_at_line    (GtkTextBuffer      *buffer,
985                                      GtkTextIter        *iter,
986                                      gint                 line_number)
987 {
988   g_return_if_fail(iter != NULL);
989   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
990
991   gtk_text_buffer_get_iter_at_line_char(buffer, iter, line_number, 0);
992 }
993
994 void
995 gtk_text_buffer_get_iter_at_char         (GtkTextBuffer      *buffer,
996                                           GtkTextIter        *iter,
997                                           gint                 char_index)
998 {
999   g_return_if_fail(iter != NULL);
1000   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
1001
1002   gtk_text_btree_get_iter_at_char(buffer->tree, iter, char_index);
1003 }
1004
1005 void
1006 gtk_text_buffer_get_last_iter         (GtkTextBuffer      *buffer,
1007                                        GtkTextIter        *iter)
1008 {  
1009   g_return_if_fail(iter != NULL);
1010   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
1011
1012   gtk_text_btree_get_last_iter(buffer->tree, iter);
1013 }
1014
1015 void
1016 gtk_text_buffer_get_bounds (GtkTextBuffer *buffer,
1017                             GtkTextIter   *start,
1018                             GtkTextIter   *end)
1019 {
1020   g_return_if_fail(start != NULL);
1021   g_return_if_fail(end != NULL);
1022   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
1023
1024   gtk_text_btree_get_iter_at_char(buffer->tree, start, 0);
1025   gtk_text_btree_get_last_iter(buffer->tree, end);
1026 }
1027
1028
1029 gboolean
1030 gtk_text_buffer_get_iter_from_string(GtkTextBuffer *buffer,
1031                                      GtkTextIter *iter,
1032                                      const gchar *index_string)
1033 {
1034   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), FALSE);
1035
1036   return gtk_text_btree_get_iter_from_string(buffer->tree,
1037                                              iter,
1038                                              index_string);
1039 }
1040
1041 /*
1042  * Modified flag
1043  */
1044
1045 gboolean
1046 gtk_text_buffer_modified (GtkTextBuffer      *buffer)
1047 {
1048   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), FALSE);
1049   
1050   return buffer->modified;
1051 }
1052
1053 void
1054 gtk_text_buffer_set_modified (GtkTextBuffer      *buffer,
1055                               gboolean             setting)
1056 {
1057   gboolean fixed_setting;
1058   
1059   g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
1060
1061   fixed_setting = setting != FALSE;
1062   
1063   if (buffer->modified == fixed_setting)
1064     return;
1065   else
1066     {
1067       buffer->modified = fixed_setting;
1068       gtk_signal_emit(GTK_OBJECT(buffer), signals[MODIFIED_CHANGED]);
1069     }
1070 }
1071
1072
1073 /*
1074  * Clipboard
1075  */
1076
1077 static void
1078 set_clipboard_contents_nocopy(GtkTextBuffer *buffer,
1079                               gchar *text)
1080 {
1081   if (text && *text == '\0')
1082     {
1083       g_free(text);
1084       text = NULL;
1085     }
1086   
1087   if (buffer->clipboard_text != NULL)
1088     g_free(buffer->clipboard_text);  
1089
1090   buffer->clipboard_text = text;
1091   
1092   gtk_text_buffer_update_clipboard_selection(buffer);
1093 }
1094
1095 void
1096 gtk_text_buffer_set_clipboard_contents (GtkTextBuffer      *buffer,
1097                                         const gchar         *text)
1098 {
1099   set_clipboard_contents_nocopy(buffer, text ? g_strdup(text) : NULL);
1100 }
1101
1102 const gchar*
1103 gtk_text_buffer_get_clipboard_contents (GtkTextBuffer      *buffer)
1104 {
1105   return buffer->clipboard_text;
1106 }
1107
1108 /*
1109  * Assorted other stuff
1110  */
1111
1112 gint
1113 gtk_text_buffer_get_line_count(GtkTextBuffer *buffer)
1114 {
1115   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), 0);
1116   
1117   return gtk_text_btree_line_count(buffer->tree);
1118 }
1119
1120 gint
1121 gtk_text_buffer_get_char_count(GtkTextBuffer *buffer)
1122 {
1123   g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), 0);
1124
1125   return gtk_text_btree_char_count(buffer->tree);
1126 }
1127
1128 GSList*
1129 gtk_text_buffer_get_tags               (GtkTextBuffer      *buffer,
1130                                         const GtkTextIter  *iter)
1131 {
1132   GSList *retval = NULL;
1133   GtkTextTag** tags;
1134   gint count = 0;
1135   
1136   tags = gtk_text_btree_get_tags(iter, &count);
1137
1138   if (count > 0)
1139     {
1140       gint i;
1141
1142       gtk_text_tag_array_sort(tags, count);
1143       
1144       i = 0;
1145       while (i < count)
1146         {
1147           retval = g_slist_prepend(retval, tags[i]);
1148           ++i;
1149         }
1150
1151       retval = g_slist_reverse(retval);
1152     }
1153
1154   if (tags)
1155     g_free(tags);
1156
1157   return retval;
1158 }
1159
1160 /*
1161  * Selection
1162  */
1163
1164 static gboolean
1165 have_primary_x_selection(GtkWidget *widget)
1166 {
1167   return (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == 
1168           widget->window);
1169 }
1170
1171 static gboolean
1172 request_primary_x_selection(GtkWidget *widget, guint32 time)
1173 {
1174   return gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, time);
1175 }
1176
1177 static gboolean
1178 release_primary_x_selection(GtkWidget *widget, guint32 time)
1179 {
1180   if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == 
1181       widget->window)
1182     {
1183       gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time);
1184       return TRUE;
1185     }
1186   else
1187     return FALSE;
1188 }
1189
1190
1191 static gboolean
1192 have_clipboard_x_selection(GtkWidget *widget)
1193 {
1194   return (gdk_selection_owner_get (clipboard_atom) == 
1195           widget->window);
1196 }
1197
1198 static gboolean
1199 request_clipboard_x_selection(GtkWidget *widget, guint32 time)
1200 {
1201   return gtk_selection_owner_set (widget, clipboard_atom, time);
1202 }
1203
1204 static gboolean
1205 release_clipboard_x_selection(GtkWidget *widget, guint32 time)
1206 {
1207   if (gdk_selection_owner_get (clipboard_atom) == 
1208       widget->window)
1209     {
1210       gtk_selection_owner_set (NULL, clipboard_atom, time);
1211       return TRUE;
1212     }
1213   else
1214     return FALSE;
1215 }
1216
1217 /* Called when we lose the selection */
1218 static gint
1219 selection_clear_event(GtkWidget *widget, GdkEventSelection *event,
1220                       gpointer data)
1221 {
1222   GtkTextBuffer *buffer;
1223   
1224   buffer = GTK_TEXT_BUFFER(data);
1225
1226   /* Let the selection handling code know that the selection
1227    * has been changed, since we've overriden the default handler */
1228   if (!gtk_selection_clear (widget, event))
1229     return FALSE;
1230
1231   buffer->have_selection = FALSE;
1232   
1233   if (event->selection == GDK_SELECTION_PRIMARY)
1234     {
1235       /* Move selection_bound to the insertion point */
1236       GtkTextIter insert;
1237       GtkTextIter selection_bound;
1238
1239       gtk_text_buffer_get_iter_at_mark(buffer, &insert,
1240                                        gtk_text_buffer_get_mark (buffer, "insert"));
1241       gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound,
1242                                        gtk_text_buffer_get_mark (buffer, "selection_bound"));
1243       
1244       if (!gtk_text_iter_equal(&insert, &selection_bound))
1245         gtk_text_buffer_move_mark(buffer,
1246                                   gtk_text_buffer_get_mark (buffer, "selection_bound"),
1247                                   &insert);
1248     }
1249   else if (event->selection == clipboard_atom)
1250     {
1251       gtk_text_buffer_set_clipboard_contents(buffer, NULL);
1252     }
1253
1254   return TRUE;
1255 }
1256
1257 /* Called when we have the selection and someone else wants our
1258    data in order to paste it */
1259 static void
1260 selection_get (GtkWidget         *widget,
1261                GtkSelectionData  *selection_data,
1262                guint              info,
1263                guint              time,
1264                gpointer data)
1265 {
1266   GtkTextBuffer *buffer;
1267   gchar *str;
1268   guint length;
1269   
1270   buffer = GTK_TEXT_BUFFER(data);
1271   
1272   if (selection_data->selection == GDK_SELECTION_PRIMARY)
1273     {
1274       GtkTextIter start;
1275       GtkTextIter end;
1276
1277       if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
1278         {
1279           /* Extract the selected text */
1280           str = gtk_text_iter_get_visible_text(&start, &end);
1281           
1282           length = strlen(str);
1283         }
1284       else
1285         return;
1286     }
1287   else
1288     {
1289       const gchar *cstr;
1290       
1291       cstr = gtk_text_buffer_get_clipboard_contents(buffer);
1292
1293       if (cstr == NULL)
1294         return;
1295
1296       str = g_strdup(cstr);
1297       
1298       length = strlen (str);
1299     }
1300
1301   if (str)
1302     {
1303       if (info == TARGET_UTF8_STRING)
1304         {
1305           /* Pass raw UTF8 */
1306           gtk_selection_data_set (selection_data,
1307                                   utf8_atom,
1308                                   8*sizeof(gchar), (guchar *)str, length);
1309
1310         }
1311       else if (info == TARGET_STRING ||
1312                info == TARGET_TEXT)
1313         {
1314           gchar *latin1;
1315
1316           latin1 = gtk_text_utf_to_latin1(str, length);
1317           
1318           gtk_selection_data_set (selection_data,
1319                                   GDK_SELECTION_TYPE_STRING,
1320                                   8*sizeof(gchar), latin1, strlen(latin1));
1321           g_free(latin1);
1322         }
1323       else if (info == TARGET_COMPOUND_TEXT)
1324         {
1325           /* FIXME convert UTF8 directly to current locale, not via
1326              latin1 */
1327           
1328           guchar *text;
1329           GdkAtom encoding;
1330           gint format;
1331           gint new_length;
1332           gchar *latin1;
1333
1334           latin1 = gtk_text_utf_to_latin1(str, length);
1335           
1336           gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
1337           gtk_selection_data_set (selection_data, encoding, format, text, new_length);
1338           gdk_free_compound_text (text);
1339
1340           g_free(latin1);
1341         }
1342
1343       g_free (str);
1344     }
1345 }
1346
1347 /* Called when we request a paste and receive the data */
1348 static void
1349 selection_received (GtkWidget        *widget,
1350                     GtkSelectionData *selection_data,
1351                     guint             time,
1352                     gpointer data)
1353 {
1354   GtkTextBuffer *buffer;
1355   gboolean reselect;
1356   GtkTextIter insert_point;
1357   GtkTextMark *paste_point_override;
1358   enum {INVALID, STRING, CTEXT, UTF8} type;
1359
1360   g_return_if_fail (widget != NULL);
1361   
1362   buffer = GTK_TEXT_BUFFER(data);
1363
1364   if (selection_data->type == GDK_TARGET_STRING)
1365     type = STRING;
1366   else if (selection_data->type == ctext_atom)
1367     type = CTEXT;
1368   else if (selection_data->type == utf8_atom)
1369     type = UTF8;
1370   else
1371     type = INVALID;
1372
1373   if (type == INVALID || selection_data->length < 0)
1374     {
1375       /* If we asked for UTF8 and didn't get it, try text; if we asked
1376          for text and didn't get it, try string.  If we asked for
1377          anything else and didn't get it, give up. */
1378       if (selection_data->target == utf8_atom)
1379         gtk_selection_convert (widget, selection_data->selection,
1380                                GDK_TARGET_STRING, time);
1381       return;
1382     }
1383
1384   paste_point_override = gtk_text_buffer_get_mark (buffer,
1385                                                    "__paste_point_override");
1386   
1387   if (paste_point_override != NULL)
1388     {
1389       gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
1390                                        paste_point_override);
1391       gtk_text_buffer_delete_mark(buffer,
1392                                   gtk_text_buffer_get_mark (buffer,
1393                                                             "__paste_point_override"));
1394     }
1395   else
1396     {
1397       gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
1398                                        gtk_text_buffer_get_mark (buffer,
1399                                                                  "insert"));
1400     }
1401   
1402   reselect = FALSE;
1403
1404   if ((TRUE/* Text is selected FIXME */) && 
1405       (!buffer->have_selection ||
1406        (selection_data->selection == clipboard_atom)))
1407     {
1408       reselect = TRUE;
1409
1410       if (buffer->have_selection)
1411         {
1412           /* FIXME Delete currently-selected chars but don't give up X
1413              selection since we'll use the newly-pasted stuff as
1414              a new X selection */
1415
1416         }
1417       else
1418         ; /* FIXME Delete selected chars and give up X selection */
1419     }
1420
1421   switch (type)
1422     {
1423     case STRING:
1424       {
1425         gchar *utf;
1426         
1427         utf = gtk_text_latin1_to_utf((const gchar*)selection_data->data,
1428                                      selection_data->length);
1429         gtk_text_buffer_insert (buffer, &insert_point,
1430                                 utf, -1);
1431         g_free(utf);
1432       }
1433       break;
1434       
1435     case UTF8:
1436       gtk_text_buffer_insert (buffer, &insert_point,
1437                               (const gchar *)selection_data->data,
1438                               selection_data->length);
1439       break;
1440       
1441     case CTEXT:
1442       {
1443         gchar **list;
1444         gint count;
1445         gint i;
1446
1447         count = gdk_text_property_to_text_list (selection_data->type,
1448                                                 selection_data->format, 
1449                                                 selection_data->data,
1450                                                 selection_data->length,
1451                                                 &list);
1452         for (i=0; i<count; i++)
1453           {
1454             /* FIXME this is broken, it assumes the CTEXT is latin1
1455                when it probably isn't. */
1456             gchar *utf;
1457
1458             utf = gtk_text_latin1_to_utf(list[i], strlen(list[i]));
1459             
1460             gtk_text_buffer_insert(buffer, &insert_point, utf, -1);
1461
1462             g_free(utf);
1463           }
1464
1465         if (count > 0)
1466           gdk_free_text_list (list);
1467       }
1468       break;
1469       
1470     case INVALID:               /* quiet compiler */
1471       break;
1472     }
1473
1474   if (reselect)
1475     ; /* FIXME Select the region of text we just inserted */
1476
1477 }
1478
1479 static void
1480 ensure_handlers(GtkTextBuffer *buffer)
1481 {
1482   if (!buffer->selection_handlers_installed)
1483     {
1484       buffer->selection_handlers_installed = TRUE;
1485
1486       gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
1487                          "selection_clear_event",
1488                          GTK_SIGNAL_FUNC(selection_clear_event),
1489                          buffer);
1490
1491       gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
1492                          "selection_received",
1493                          GTK_SIGNAL_FUNC(selection_received),
1494                          buffer);
1495
1496       gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
1497                          "selection_get",
1498                          GTK_SIGNAL_FUNC(selection_get),
1499                          buffer);
1500     }
1501 }
1502
1503 /* FIXME GDK_CURRENT_TIME should probably go away and we should
1504    figure out how to get the events in here */
1505 static void
1506 gtk_text_buffer_update_primary_selection(GtkTextBuffer *buffer)
1507 {
1508   GtkTextIter start;
1509   GtkTextIter end;
1510
1511   ensure_handlers(buffer);
1512   
1513   /* Determine whether we have a selection and adjust X selection
1514      accordingly. */
1515   
1516   if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
1517     {
1518       release_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
1519       buffer->have_selection = FALSE;
1520     }
1521   else
1522     {
1523       /* Even if we already have the selection, we need to update our
1524          timestamp. */
1525       buffer->have_selection = FALSE;
1526       request_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
1527       if (have_primary_x_selection(buffer->selection_widget))
1528         buffer->have_selection = TRUE;
1529     }
1530 }
1531
1532 static void
1533 gtk_text_buffer_update_clipboard_selection(GtkTextBuffer *buffer)
1534
1535   if (buffer->clipboard_text == NULL ||
1536       buffer->clipboard_text[0] == '\0')
1537     {
1538       release_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
1539     }
1540   else
1541     {
1542       /* Even if we already have the selection, we need to update our
1543          timestamp. */
1544       request_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
1545     }
1546 }
1547
1548 static void
1549 paste(GtkTextBuffer *buffer, GdkAtom selection, guint32 time)
1550 {
1551   ensure_handlers(buffer);
1552   
1553   gtk_selection_convert (buffer->selection_widget, selection,
1554                          utf8_atom, time);
1555 }
1556
1557 void
1558 gtk_text_buffer_paste_primary_selection(GtkTextBuffer      *buffer,
1559                                         GtkTextIter        *override_location,
1560                                         guint32 time)
1561 {
1562   if (override_location != NULL)
1563     gtk_text_buffer_create_mark(buffer,
1564                                 "__paste_point_override",
1565                                 override_location, FALSE);
1566   
1567   paste(buffer, GDK_SELECTION_PRIMARY, time);
1568 }
1569
1570 void
1571 gtk_text_buffer_paste_clipboard        (GtkTextBuffer      *buffer,
1572                                         guint32              time)
1573 {
1574   paste(buffer, clipboard_atom, time);
1575 }
1576
1577 gboolean
1578 gtk_text_buffer_delete_selection (GtkTextBuffer      *buffer)
1579 {
1580   GtkTextIter start;
1581   GtkTextIter end;
1582   
1583   if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
1584     {
1585       return FALSE; /* No selection */
1586     }
1587   else
1588     {
1589       gtk_text_buffer_delete(buffer, &start, &end);
1590       gtk_text_buffer_update_primary_selection(buffer);
1591       return TRUE; /* We deleted stuff */
1592     }
1593 }
1594
1595 static void
1596 cut_or_copy(GtkTextBuffer *buffer,
1597             guint32 time,
1598             gboolean delete_region_after)
1599 {
1600   /* We prefer to cut the selected region between selection_bound and
1601      insertion point. If that region is empty, then we cut the region
1602      between the "anchor" and the insertion point (this is for C-space
1603      and M-w and other Emacs-style copy/yank keys). Note that insert
1604      and selection_bound are guaranteed to exist, but the anchor only
1605      exists sometimes. */
1606   GtkTextIter start;
1607   GtkTextIter end;
1608
1609   if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
1610     {
1611       /* Let's try the anchor thing */
1612       GtkTextMark * anchor = gtk_text_buffer_get_mark (buffer, "anchor");
1613       
1614       if (anchor == NULL)
1615         return;
1616       else
1617         {
1618           gtk_text_buffer_get_iter_at_mark(buffer, &end, anchor);
1619           gtk_text_iter_reorder(&start, &end);
1620         }
1621     }
1622
1623   if (!gtk_text_iter_equal(&start, &end))
1624     {
1625       gchar *text;
1626       
1627       text = gtk_text_iter_get_visible_text(&start, &end);
1628       
1629       set_clipboard_contents_nocopy(buffer, text);
1630       
1631       if (delete_region_after)
1632         gtk_text_buffer_delete(buffer, &start, &end);
1633     }
1634 }
1635
1636 void
1637 gtk_text_buffer_cut (GtkTextBuffer      *buffer,
1638                      guint32              time)
1639 {
1640   cut_or_copy(buffer, time, TRUE);
1641 }
1642
1643 void
1644 gtk_text_buffer_copy                   (GtkTextBuffer      *buffer,
1645                                         guint32              time)
1646 {
1647   cut_or_copy(buffer, time, FALSE);
1648 }
1649
1650
1651 gboolean
1652 gtk_text_buffer_get_selection_bounds   (GtkTextBuffer      *buffer,
1653                                         GtkTextIter        *start,
1654                                         GtkTextIter        *end)
1655 {
1656   g_return_val_if_fail (buffer != NULL, FALSE);
1657   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1658
1659   return gtk_text_btree_get_selection_bounds (buffer->tree, start, end);
1660 }
1661
1662
1663 /*
1664  * Debug spew
1665  */
1666
1667 void
1668 gtk_text_buffer_spew(GtkTextBuffer *buffer)
1669 {
1670   gtk_text_btree_spew(buffer->tree);
1671 }