gtktextbuffer.c 82.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* GTK - The GIMP Toolkit
 * gtktextbuffer.c Copyright (C) 2000 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
13
14
15
16
17
18
19
20
21
22
23
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
24
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25
26
 */

27

28
29
#include <string.h>

30
#include "gtkclipboard.h"
31
32
33
34
35
#include "gtkinvisible.h"
#include "gtksignal.h"
#include "gtktextbuffer.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
36
#include <string.h>
37

38
39
40
41
42
43
44
45
typedef struct _ClipboardRequest ClipboardRequest;

struct _ClipboardRequest
{
  GtkTextBuffer *buffer;
  gboolean interactive;
  gboolean default_editable;
  gboolean is_clipboard;
46
  gboolean replace_selection;
47
48
};

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
enum {
  INSERT_TEXT,
  DELETE_TEXT,
  CHANGED,
  MODIFIED_CHANGED,
  MARK_SET,
  MARK_DELETED,
  APPLY_TAG,
  REMOVE_TAG,
  LAST_SIGNAL
};

enum {
  ARG_0,
  LAST_ARG
};

enum {
  TARGET_STRING,
  TARGET_TEXT,
  TARGET_COMPOUND_TEXT,
70
71
  TARGET_UTF8_STRING,
  TARGET_TEXT_BUFFER_CONTENTS
72
73
74
75
76
77
78
79
80
81
82
};

static void gtk_text_buffer_init       (GtkTextBuffer      *tkxt_buffer);
static void gtk_text_buffer_class_init (GtkTextBufferClass *klass);
static void gtk_text_buffer_finalize   (GObject            *object);


static void gtk_text_buffer_update_primary_selection   (GtkTextBuffer     *buffer);
static void gtk_text_buffer_real_insert_text           (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *iter,
                                                        const gchar       *text,
83
84
                                                        gint               len,
                                                        gboolean           interactive);
85
86
static void gtk_text_buffer_real_delete_text           (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *start,
87
88
                                                        GtkTextIter       *end,
                                                        gboolean           interactive);
89
90
91
92
93
94
95
96
static void gtk_text_buffer_real_apply_tag             (GtkTextBuffer     *buffer,
                                                        GtkTextTag        *tag,
                                                        const GtkTextIter *start_char,
                                                        const GtkTextIter *end_char);
static void gtk_text_buffer_real_remove_tag            (GtkTextBuffer     *buffer,
                                                        GtkTextTag        *tag,
                                                        const GtkTextIter *start_char,
                                                        const GtkTextIter *end_char);
97
static void gtk_text_buffer_real_changed               (GtkTextBuffer     *buffer);
98

99
static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
100
static void          free_log_attr_cache (GtkTextLogAttrCache *cache);
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

static GtkObjectClass *parent_class = NULL;
static guint signals[LAST_SIGNAL] = { 0 };

GtkType
gtk_text_buffer_get_type (void)
{
  static GtkType our_type = 0;

  if (our_type == 0)
    {
      static const GtkTypeInfo our_info =
      {
        "GtkTextBuffer",
        sizeof (GtkTextBuffer),
        sizeof (GtkTextBufferClass),
        (GtkClassInitFunc) gtk_text_buffer_class_init,
        (GtkObjectInitFunc) gtk_text_buffer_init,
        /* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL
      };

      our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
    }

  return our_type;
}

static void
gtk_text_buffer_class_init (GtkTextBufferClass *klass)
{
133
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
134
135
136
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  parent_class = gtk_type_class (GTK_TYPE_OBJECT);
137

138
139
140
141
142
143
144
145
  gobject_class->finalize = gtk_text_buffer_finalize;

  klass->insert_text = gtk_text_buffer_real_insert_text;
  klass->delete_text = gtk_text_buffer_real_delete_text;
  klass->apply_tag = gtk_text_buffer_real_apply_tag;
  klass->remove_tag = gtk_text_buffer_real_remove_tag;
  klass->changed = gtk_text_buffer_real_changed;

146
147
148
149
150
  signals[INSERT_TEXT] =
    gtk_signal_new ("insert_text",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, insert_text),
151
                    gtk_marshal_VOID__BOXED_BOXED_INT_BOOLEAN,
152
                    GTK_TYPE_NONE,
153
                    4,
154
155
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER,
156
157
                    GTK_TYPE_INT,
                    GTK_TYPE_BOOL);
158
159
160
161
162
163

  signals[DELETE_TEXT] =
    gtk_signal_new ("delete_text",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, delete_text),
164
                    gtk_marshal_VOID__BOXED_BOXED_BOOLEAN,
165
                    GTK_TYPE_NONE,
166
                    3,
167
168
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER,
169
                    GTK_TYPE_BOOL);
170
171
172
173
174
175

  signals[CHANGED] =
    gtk_signal_new ("changed",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, changed),
176
                    gtk_marshal_VOID__VOID,
177
178
179
180
181
182
183
184
                    GTK_TYPE_NONE,
                    0);

  signals[MODIFIED_CHANGED] =
    gtk_signal_new ("modified_changed",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, modified_changed),
185
                    gtk_marshal_VOID__VOID,
186
187
                    GTK_TYPE_NONE,
                    0);
188

189
190
191
192
193
  signals[MARK_SET] =
    gtk_signal_new ("mark_set",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_set),
194
                    gtk_marshal_VOID__BOXED_OBJECT,
195
196
                    GTK_TYPE_NONE,
                    2,
197
198
                    GTK_TYPE_TEXT_ITER,
                    G_TYPE_OBJECT);
199
200
201
202
203
204

  signals[MARK_DELETED] =
    gtk_signal_new ("mark_deleted",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_deleted),
205
                    gtk_marshal_VOID__OBJECT,
206
207
                    GTK_TYPE_NONE,
                    1,
208
                    G_TYPE_OBJECT);
209
210
211
212
213
214

  signals[APPLY_TAG] =
    gtk_signal_new ("apply_tag",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, apply_tag),
215
                    gtk_marshal_VOID__OBJECT_BOXED_BOXED,
216
217
                    GTK_TYPE_NONE,
                    3,
218
219
220
                    G_TYPE_OBJECT,
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER);
221

222
223
224
225
226
  signals[REMOVE_TAG] =
    gtk_signal_new ("remove_tag",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, remove_tag),
227
                    gtk_marshal_VOID__OBJECT_BOXED_BOXED,
228
229
                    GTK_TYPE_NONE,
                    3,
230
231
232
                    G_TYPE_OBJECT,
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER);
233
234
235
236
237
}

void
gtk_text_buffer_init (GtkTextBuffer *buffer)
{
238
  buffer->clipboard_contents = NULL;
239
240
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
241
242
243
/**
 * gtk_text_buffer_new:
 * @table: a tag table, or NULL to create a new one
244
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
245
 * Creates a new text buffer.
246
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
247
248
 * Return value: a new text buffer
 **/
249
250
251
252
GtkTextBuffer*
gtk_text_buffer_new (GtkTextTagTable *table)
{
  GtkTextBuffer *text_buffer;
253

254
255
256
  text_buffer = GTK_TEXT_BUFFER (gtk_type_new (gtk_text_buffer_get_type ()));

  if (table)
257
258
    {
      text_buffer->tag_table = table;
259
260
261
262
263

      gtk_object_ref (GTK_OBJECT (text_buffer->tag_table));
      gtk_object_sink (GTK_OBJECT (text_buffer->tag_table));
    }

264
265
266
  gtk_object_ref (GTK_OBJECT (text_buffer));
  gtk_object_sink (GTK_OBJECT (text_buffer));
  
267
268
269
270
  return text_buffer;
}

static void
271
gtk_text_buffer_finalize (GObject *object)
272
273
274
275
276
{
  GtkTextBuffer *buffer;

  buffer = GTK_TEXT_BUFFER (object);

277
278
279
280
281
282
  if (buffer->clipboard_contents)
    {
      g_object_unref (G_OBJECT (buffer->clipboard_contents));
      buffer->clipboard_contents = NULL;
    }
  
283
284
  if (buffer->tag_table)
    {
285
      gtk_object_unref (GTK_OBJECT (buffer->tag_table));
286
287
288
289
290
      buffer->tag_table = NULL;
    }

  if (buffer->btree)
    {
291
      _gtk_text_btree_unref (buffer->btree);
292
293
      buffer->btree = NULL;
    }
294
295
296
297
298

  if (buffer->log_attr_cache)
    free_log_attr_cache (buffer->log_attr_cache);

  buffer->log_attr_cache = NULL;
299
  
300
301
302
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

303
304
305
306
307
308
309
static GtkTextTagTable*
get_table (GtkTextBuffer *buffer)
{
  if (buffer->tag_table == NULL)
    {
      buffer->tag_table = gtk_text_tag_table_new ();

310
311
      gtk_object_ref (GTK_OBJECT (buffer->tag_table));
      gtk_object_sink (GTK_OBJECT (buffer->tag_table));
312
313
314
315
316
317
318
319
320
    }

  return buffer->tag_table;
}

static GtkTextBTree*
get_btree (GtkTextBuffer *buffer)
{
  if (buffer->btree == NULL)
321
    buffer->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer),
322
323
                                        buffer);

324
325
326
327
328
329
330
331
332
  return buffer->btree;
}

GtkTextBTree*
_gtk_text_buffer_get_btree (GtkTextBuffer *buffer)
{
  return get_btree (buffer);
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
333
334
335
/**
 * gtk_text_buffer_get_tag_table:
 * @buffer: a #GtkTextBuffer
336
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
337
 * Get the #GtkTextTagTable associated with this buffer.
338
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
339
340
 * Return value: the buffer's tag table
 **/
341
342
343
GtkTextTagTable*
gtk_text_buffer_get_tag_table (GtkTextBuffer  *buffer)
{
344
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
345
346
347
348

  return get_table (buffer);
}

349
350
351
352
353
/**
 * gtk_text_buffer_set_text:
 * @buffer: a #GtkTextBuffer
 * @text: UTF-8 text to insert
 * @len: length of @text in bytes
354
 *
355
356
357
358
359
360
361
362
363
364
365
366
 * Deletes current contents of @buffer, and inserts @text instead.  If
 * @text doesn't end with a newline, a newline is added;
 * #GtkTextBuffer contents must always end with a newline. If @text
 * ends with a newline, the new buffer contents will be exactly
 * @text. If @len is -1, @text must be nul-terminated.
 **/
void
gtk_text_buffer_set_text (GtkTextBuffer *buffer,
                          const gchar   *text,
                          gint           len)
{
  GtkTextIter start, end;
367
368

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
369
370
371
372
373
374
375
376
377
378
379
380
  g_return_if_fail (text != NULL);

  if (len < 0)
    len = strlen (text);

  /* Chop newline, since the buffer will already have one
   * in it.
   */
  if (len > 0 && text[len-1] == '\n')
    len -= 1;

  gtk_text_buffer_get_bounds (buffer, &start, &end);
381

382
383
384
385
386
387
388
389
390
  gtk_text_buffer_delete (buffer, &start, &end);

  if (len > 0)
    {
      gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
      gtk_text_buffer_insert (buffer, &start, text, len);
    }
}

391
392
393
394
395
/*
 * Insertion
 */

static void
396

397
398
399
400
401
gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
                                  GtkTextIter *iter,
                                  const gchar *text,
                                  gint len,
                                  gboolean interactive)
402
{
403
404
405
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);

406
  _gtk_text_btree_insert (iter, text, len);
407

408
  gtk_signal_emit (GTK_OBJECT (buffer), signals[CHANGED]);
409
410
411
}

static void
412
413
414
415
416
gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
                             GtkTextIter *iter,
                             const gchar *text,
                             gint len,
                             gboolean interactive)
417
{
418
419
420
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);
421
422

  if (len < 0)
423
424
    len = strlen (text);

425
426
  if (len > 0)
    {
427
428
      gtk_signal_emit (GTK_OBJECT (buffer), signals[INSERT_TEXT],
                       iter, text, len, interactive);
429
430
431
    }
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
432
433
434
435
436
437
/**
 * gtk_text_buffer_insert:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in the buffer
 * @text: UTF-8 format text to insert
 * @len: length of text in bytes, or -1
438
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
439
440
441
442
443
444
445
 * Inserts @len bytes of @text at position @iter.  If @len is -1,
 * @text must be nul-terminated and will be inserted in its
 * entirety. Emits the "insert_text" signal; insertion actually occurs
 * in the default handler for the signal. @iter is invalidated when
 * insertion occurs (because the buffer contents change), but the
 * default signal handler revalidates it to point to the end of the
 * inserted text.
446
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
447
 **/
448
449
450
451
452
453
void
gtk_text_buffer_insert (GtkTextBuffer *buffer,
                        GtkTextIter *iter,
                        const gchar *text,
                        gint len)
{
454
455
456
457
458
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  gtk_text_buffer_emit_insert (buffer, iter, text, len, FALSE);
459
460
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
461
462
463
464
465
/**
 * gtk_text_buffer_insert_at_cursor:
 * @buffer: a #GtkTextBuffer
 * @text: some text in UTF-8 format
 * @len: length of text, in bytes
466
467
 *
 * Simply calls gtk_text_buffer_insert (), using the current
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
468
469
 * cursor position as the insertion point.
 **/
470
471
472
473
474
475
476
void
gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
                                  const gchar *text,
                                  gint len)
{
  GtkTextIter iter;

477
478
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (text != NULL);
479

480
481
482
  gtk_text_buffer_get_iter_at_mark (buffer, &iter,
                                    gtk_text_buffer_get_mark (buffer,
                                                              "insert"));
483

484
  gtk_text_buffer_insert (buffer, &iter, text, len);
485
486
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
487
488
489
490
491
492
/**
 * gtk_text_buffer_insert_interactive:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in @buffer
 * @text: some UTF-8 text
 * @len: length of text in bytes, or -1
493
 * @default_editable: default editability of buffer
494
495
 *
 * Like gtk_text_buffer_insert (), but the insertion will not occur if
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
496
497
498
 * @iter is at a non-editable location in the buffer.  Usually you
 * want to prevent insertions at ineditable locations if the insertion
 * results from a user action (is interactive).
499
 *
500
501
502
503
 * @default_editable indicates the editability of text that doesn't
 * have a tag affecting editability applied to it. Typically the
 * result of gtk_text_view_get_editable() is appropriate here.
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
504
505
 * Return value: whether text was actually inserted
 **/
506
gboolean
507
508
509
510
511
512
513
514
515
gtk_text_buffer_insert_interactive (GtkTextBuffer *buffer,
                                    GtkTextIter   *iter,
                                    const gchar   *text,
                                    gint           len,
                                    gboolean       default_editable)
{
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
  g_return_val_if_fail (text != NULL, FALSE);

516
  if (gtk_text_iter_editable (iter, default_editable))
517
    {
518
      gtk_text_buffer_emit_insert (buffer, iter, text, len, TRUE);
519
520
521
522
      return TRUE;
    }
  else
    return FALSE;
523
524
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
525
526
527
528
529
530
/**
 * gtk_text_buffer_insert_interactive_at_cursor:
 * @buffer: a #GtkTextBuffer
 * @text: text in UTF-8 format
 * @len: length of text in bytes, or -1
 * @default_editable: default editability of buffer
531
532
 *
 * Calls gtk_text_buffer_insert_interactive () at the cursor
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
533
 * position.
534
 *
535
536
537
538
 * @default_editable indicates the editability of text that doesn't
 * have a tag affecting editability applied to it. Typically the
 * result of gtk_text_view_get_editable() is appropriate here.
 * 
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
539
540
 * Return value: whether text was actually inserted
 **/
541
542
543
544
545
gboolean
gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer,
                                              const gchar   *text,
                                              gint           len,
                                              gboolean       default_editable)
546
{
547
  GtkTextIter iter;
548

549
550
551
552
553
554
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
  g_return_val_if_fail (text != NULL, FALSE);

  gtk_text_buffer_get_iter_at_mark (buffer, &iter,
                                    gtk_text_buffer_get_mark (buffer,
                                                              "insert"));
555

556
557
  return gtk_text_buffer_insert_interactive (buffer, &iter, text, len,
                                             default_editable);
558
559
}

560
561
562
563
static gboolean
possibly_not_text (gunichar ch,
                   gpointer user_data)
{
Havoc Pennington's avatar
Havoc Pennington committed
564
  return ch == GTK_TEXT_UNKNOWN_CHAR;
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
}

static void
insert_text_range (GtkTextBuffer     *buffer,
                   GtkTextIter       *iter,
                   const GtkTextIter *orig_start,
                   const GtkTextIter *orig_end,
                   gboolean           interactive)
{
  gchar *text;

  text = gtk_text_iter_get_text (orig_start, orig_end);

  gtk_text_buffer_emit_insert (buffer, iter, text, -1, interactive);

  g_free (text);
}

typedef struct _Range Range;
struct _Range
{
  GtkTextBuffer *buffer;
  GtkTextMark *start_mark;
  GtkTextMark *end_mark;
  GtkTextMark *whole_end_mark;
  GtkTextIter *range_start;
  GtkTextIter *range_end;
  GtkTextIter *whole_end;
};

static Range*
save_range (GtkTextIter *range_start,
            GtkTextIter *range_end,
            GtkTextIter *whole_end)
{
  Range *r;

  r = g_new (Range, 1);

  r->buffer = gtk_text_iter_get_buffer (range_start);
  g_object_ref (G_OBJECT (r->buffer));
  
  r->start_mark = 
    gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
                                 NULL,
                                 range_start,
                                 TRUE);
  r->end_mark = 
    gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
                                 NULL,
                                 range_end,
                                 FALSE);

  r->whole_end_mark = 
    gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
                                 NULL,
                                 whole_end,
                                 FALSE);

  r->range_start = range_start;
  r->range_end = range_end;
  r->whole_end = whole_end;

  return r;
}

static void
restore_range (Range *r)
{
  gtk_text_buffer_get_iter_at_mark (r->buffer,
                                    r->range_start,
                                    r->start_mark);
      
  gtk_text_buffer_get_iter_at_mark (r->buffer,
                                    r->range_end,
                                    r->end_mark);
      
  gtk_text_buffer_get_iter_at_mark (r->buffer,
                                    r->whole_end,
                                    r->whole_end_mark);
      
  gtk_text_buffer_delete_mark (r->buffer, r->start_mark);
  gtk_text_buffer_delete_mark (r->buffer, r->end_mark);
  gtk_text_buffer_delete_mark (r->buffer, r->whole_end_mark);

  g_object_unref (G_OBJECT (r->buffer));
  g_free (r); 
}

static void
insert_range_untagged (GtkTextBuffer     *buffer,
                       GtkTextIter       *iter,
                       const GtkTextIter *orig_start,
                       const GtkTextIter *orig_end,
                       gboolean           interactive)
{
  GtkTextIter range_start;
  GtkTextIter range_end;
  GtkTextIter start, end;
  GtkTextBuffer *src_buffer;
  Range *r;
  
  if (gtk_text_iter_equal (orig_start, orig_end))
    return;

  start = *orig_start;
  end = *orig_end;
  
  src_buffer = gtk_text_iter_get_buffer (&start);
  
  range_start = start;
  range_end = start;
  
  while (TRUE)
    {
      if (gtk_text_iter_equal (&range_start, &range_end))
        {
          /* Figure out how to move forward */

          g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
          
          if (gtk_text_iter_equal (&range_end, &end))
            {
              /* nothing left to do */
              break;
            }
Havoc Pennington's avatar
Havoc Pennington committed
691
          else if (gtk_text_iter_get_char (&range_end) == GTK_TEXT_UNKNOWN_CHAR)
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
            {
              GdkPixbuf *pixbuf = NULL;
              GtkTextChildAnchor *anchor = NULL;
              pixbuf = gtk_text_iter_get_pixbuf (&range_end);
              anchor = gtk_text_iter_get_child_anchor (&range_end);

              if (pixbuf)
                {
                  r = save_range (&range_start,
                                  &range_end,
                                  &end);

                  gtk_text_buffer_insert_pixbuf (buffer,
                                                 iter,
                                                 pixbuf);

                  restore_range (r);
                  r = NULL;
                  
711
                  gtk_text_iter_forward_char (&range_end);
712
713
714
715
716
717
718
                  
                  range_start = range_end;
                }
              else if (anchor)
                {
                  /* Just skip anchors */

719
                  gtk_text_iter_forward_char (&range_end);
720
721
722
723
                  range_start = range_end;
                }
              else
                {
Havoc Pennington's avatar
Havoc Pennington committed
724
                  /* The GTK_TEXT_UNKNOWN_CHAR was in a text segment, so
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
                   * keep going. 
                   */
                  gtk_text_iter_forward_find_char (&range_end,
                                                   possibly_not_text, NULL,
                                                   &end);
                  
                  g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
                }
            }
          else
            {
              /* Text segment starts here, so forward search to
               * find its possible endpoint
               */
              gtk_text_iter_forward_find_char (&range_end,
                                               possibly_not_text, NULL,
                                               &end);
              
              g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
            }
        }
      else
        {
          r = save_range (&range_start,
                          &range_end,
                          &end);
          
          insert_text_range (buffer,
                             iter,
                             &range_start,
                             &range_end,
                             interactive);

          restore_range (r);
          r = NULL;
          
          range_start = range_end;
        }
    }
}

static void
gtk_text_buffer_real_insert_range (GtkTextBuffer     *buffer,
                                   GtkTextIter       *iter,
                                   const GtkTextIter *orig_start,
                                   const GtkTextIter *orig_end,
                                   gboolean           interactive)
{
  /* Find each range of uniformly-tagged text, insert it,
   * then apply the tags.
   */
  GtkTextIter start = *orig_start;
  GtkTextIter end = *orig_end;
  GtkTextIter range_start;
  GtkTextIter range_end;
  GtkTextBuffer *src_buffer;
  Range *r;
  
  if (gtk_text_iter_equal (orig_start, orig_end))
    return;

  src_buffer = gtk_text_iter_get_buffer (orig_start);
  
  gtk_text_iter_reorder (&start, &end);

  range_start = start;
  range_end = start;

  while (TRUE)
    {
      gint start_offset;
      GtkTextIter start_iter;
      GSList *tags;
      GSList *tmp_list;
      
      if (gtk_text_iter_equal (&range_start, &end))
        break; /* All done */

      g_assert (gtk_text_iter_compare (&range_start, &end) < 0);
      
      gtk_text_iter_forward_to_tag_toggle (&range_end, NULL);

      g_assert (!gtk_text_iter_equal (&range_start, &range_end));

      /* Clamp to the end iterator */
      if (gtk_text_iter_compare (&range_end, &end) > 0)
        range_end = end;
      
      /* We have a range with unique tags; insert it, and
       * apply all tags.
       */
      start_offset = gtk_text_iter_get_offset (iter);

      r = save_range (&range_start, &range_end, &end);
      
      insert_range_untagged (buffer, iter, &range_start, &range_end, interactive);

      restore_range (r);
      r = NULL;
      
      gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
      
      tags = gtk_text_iter_get_tags (&range_start);
      tmp_list = tags;
      while (tmp_list != NULL)
        {
          gtk_text_buffer_apply_tag (buffer,
                                     tmp_list->data,
                                     &start_iter,
                                     iter);
          
          tmp_list = g_slist_next (tmp_list);
        }
      g_slist_free (tags);

      range_start = range_end;
    }
}
843
844
845
846
847
848
849
850
851
852
853

/**
 * gtk_text_buffer_insert_range:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in @buffer
 * @start: a position in a #GtkTextBuffer
 * @end: another position in the same buffer as @start
 *
 * Copies text, tags, and pixbufs between @start and @end (the order
 * of @start and @end doesn't matter) and inserts the copy at @iter.
 * Used instead of simply getting/inserting text because it preserves
854
855
 * images and tags. If @start and @end are in a different buffer from
 * @buffer, the two buffers must share the same tag table.
856
 *
857
858
 * Implemented via emissions of the insert_text and apply_tag signals,
 * so expect those.
859
860
861
862
863
864
865
866
867
868
869
 **/
void
gtk_text_buffer_insert_range (GtkTextBuffer     *buffer,
                              GtkTextIter       *iter,
                              const GtkTextIter *start,
                              const GtkTextIter *end)
{
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);
870
  g_return_if_fail (gtk_text_iter_get_buffer (start) ==
871
                    gtk_text_iter_get_buffer (end));
872
  g_return_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
873
                    buffer->tag_table);  
874

875
  gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE);
876
877
}

878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
/**
 * gtk_text_buffer_insert_range_interactive:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in @buffer
 * @start: a position in a #GtkTextBuffer
 * @end: another position in the same buffer as @start
 * @default_editable: default editability of the buffer
 *
 * Same as gtk_text_buffer_insert_range(), but does nothing if the
 * insertion point isn't editable. The @default_editable parameter
 * indicates whether the text is editable at @iter if no tags
 * enclosing @iter affect editability. Typically the result of
 * gtk_text_view_get_editable() is appropriate here.
 *
 * Returns: whether an insertion was possible at @iter
 **/
894
895
896
897
898
899
900
901
902
903
904
gboolean
gtk_text_buffer_insert_range_interactive (GtkTextBuffer     *buffer,
                                          GtkTextIter       *iter,
                                          const GtkTextIter *start,
                                          const GtkTextIter *end,
                                          gboolean           default_editable)
{
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
  g_return_val_if_fail (iter != NULL, FALSE);
  g_return_val_if_fail (start != NULL, FALSE);
  g_return_val_if_fail (end != NULL, FALSE);
905
  g_return_val_if_fail (gtk_text_iter_get_buffer (start) ==
906
                        gtk_text_iter_get_buffer (end), FALSE);
907
  g_return_val_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
908
909
910
                        buffer->tag_table, FALSE);


911
912
913
914
915
916
917
  if (gtk_text_iter_editable (iter, default_editable))
    {
      gtk_text_buffer_real_insert_range (buffer, iter, start, end, TRUE);
      return TRUE;
    }
  else
    return FALSE;
918
919
920
921
922
923
924
}

/**
 * gtk_text_buffer_insert_with_tags:
 * @buffer: a #GtkTextBuffer
 * @iter: an iterator in @buffer
 * @text: UTF-8 text
925
 * @len: length of @text, or -1
926
 * @first_tag: first tag to apply to @text
927
928
 * @Varargs: NULL-terminated list of tags to apply
 *
929
930
 * Inserts @text into @buffer at @iter, applying the list of tags to
 * the newly-inserted text. The last tag specified must be NULL to
931
932
933
 * terminate the list. Equivalent to calling gtk_text_buffer_insert (),
 * then gtk_text_buffer_apply_tag () on the inserted text;
 * gtk_text_buffer_insert_with_tags () is just a convenience function.
934
935
936
937
938
939
940
941
942
943
944
945
946
 **/
void
gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer,
                                  GtkTextIter   *iter,
                                  const gchar   *text,
                                  gint           len,
                                  GtkTextTag    *first_tag,
                                  ...)
{
  gint start_offset;
  GtkTextIter start;
  va_list args;
  GtkTextTag *tag;
947
948

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
949
950
951
952
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  start_offset = gtk_text_iter_get_offset (iter);
953

954
955
956
957
  gtk_text_buffer_insert (buffer, iter, text, len);

  if (first_tag == NULL)
    return;
958

959
  gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
960

961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  va_start (args, first_tag);
  tag = first_tag;
  while (tag)
    {
      gtk_text_buffer_apply_tag (buffer, tag, &start, iter);

      tag = va_arg (args, GtkTextTag*);
    }

  va_end (args);
}

/**
 * gtk_text_buffer_insert_with_tags_by_name:
 * @buffer: a #GtkTextBuffer
 * @iter: position in @buffer
 * @text: UTF-8 text
 * @len: length of @text, or -1
 * @first_tag_name: name of a tag to apply to @text
 * @Varargs: more tag names
981
982
 *
 * Same as gtk_text_buffer_insert_with_tags (), but allows you
983
984
985
986
987
988
989
990
991
992
993
994
995
996
 * to pass in tag names instead of tag objects.
 **/
void
gtk_text_buffer_insert_with_tags_by_name  (GtkTextBuffer *buffer,
                                           GtkTextIter   *iter,
                                           const gchar   *text,
                                           gint           len,
                                           const gchar   *first_tag_name,
                                           ...)
{
  gint start_offset;
  GtkTextIter start;
  va_list args;
  const gchar *tag_name;
997
998

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
999
1000
1001
1002
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  start_offset = gtk_text_iter_get_offset (iter);
1003

1004
1005
1006
1007
  gtk_text_buffer_insert (buffer, iter, text, len);

  if (first_tag_name == NULL)
    return;
1008

1009
  gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1010

1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
  va_start (args, first_tag_name);
  tag_name = first_tag_name;
  while (tag_name)
    {
      GtkTextTag *tag;

      tag = gtk_text_tag_table_lookup (buffer->tag_table,
                                       tag_name);

      if (tag == NULL)
        {
          g_warning ("%s: no tag with name '%s'!", G_STRLOC, tag_name);
          return;
        }
1025

1026
1027
1028
1029
1030
1031
1032
1033
1034
      gtk_text_buffer_apply_tag (buffer, tag, &start, iter);

      tag_name = va_arg (args, const gchar*);
    }

  va_end (args);
}


1035
1036
1037
1038
1039
/*
 * Deletion
 */

static void
1040
1041
1042
1043
gtk_text_buffer_real_delete_text (GtkTextBuffer *buffer,
                                  GtkTextIter *start,
                                  GtkTextIter *end,
                                  gboolean interactive)
1044
{
1045
1046
1047
1048
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);

1049
  _gtk_text_btree_delete (start, end);
1050
1051

  /* may have deleted the selection... */
1052
1053
1054
  gtk_text_buffer_update_primary_selection (buffer);

  gtk_signal_emit (GTK_OBJECT (buffer), signals[CHANGED]);
1055
1056
1057
}

static void
1058
1059
1060
1061
gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
                             GtkTextIter *start,
                             GtkTextIter *end,
                             gboolean interactive)
1062
{
1063
1064
1065
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);
1066

1067
  if (gtk_text_iter_equal (start, end))
1068
1069
    return;

1070
  gtk_text_iter_reorder (start, end);
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1071

1072
1073
1074
1075
1076
1077
1078
  /* Somewhat annoyingly, if you try to delete the final newline
   * the BTree will put it back; which means you can't deduce the
   * final contents of the buffer purely by monitoring insert/delete
   * signals on the buffer. But if you delete the final newline, any
   * tags on the newline will go away, oddly. See comment in
   * gtktextbtree.c. This is all sort of annoying, but really hard
   * to fix.
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1079
   */
1080
1081
1082
1083
  gtk_signal_emit (GTK_OBJECT (buffer),
                   signals[DELETE_TEXT],
                   start, end,
                   interactive);
1084
1085
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1086
1087
1088
1089
1090
1091
1092
/**
 * gtk_text_buffer_delete:
 * @buffer: a #GtkTextBuffer
 * @start: a position in @buffer
 * @end: another position in @buffer
 *
 * Deletes text between @start and @end. The order of @start and @end
1093
 * is not actually relevant; gtk_text_buffer_delete () will reorder
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
 * them. This function actually emits the "delete_text" signal, and
 * the default handler of that signal deletes the text. Because the
 * buffer is modified, all outstanding iterators become invalid after
 * calling this function; however, the @start and @end will be
 * re-initialized to point to the location where text was deleted.
 *
 * Note that the final newline in the buffer may not be deleted; a
 * #GtkTextBuffer always contains at least one newline.  You can
 * safely include the final newline in the range [@start,@end) but it
 * won't be affected by the deletion.
1104
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1105
 **/
1106
1107
void
gtk_text_buffer_delete (GtkTextBuffer *buffer,
1108
1109
                        GtkTextIter   *start,
                        GtkTextIter   *end)
1110
{
1111
1112
1113
1114
1115
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);

  gtk_text_buffer_emit_delete (buffer, start, end, FALSE);
1116
1117
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1118
1119
1120
1121
1122
/**
 * gtk_text_buffer_delete_interactive:
 * @buffer: a #GtkTextBuffer
 * @start_iter: start of range to delete
 * @end_iter: end of range
1123
1124
 * @default_editable: whether the buffer is editable by default
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1125
 * Deletes all <emphasis>editable</emphasis> text in the given range.
1126
 * Calls gtk_text_buffer_delete () for each editable sub-range of
1127
1128
1129
 * [@start,@end). @start and @end are revalidated to point to
 * the location of the last deleted range, or left untouched if
 * no text was deleted.
1130
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1131
1132
 * Return value: whether some text was actually deleted
 **/
1133
1134
1135
1136
1137
gboolean
gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
                                    GtkTextIter   *start_iter,
                                    GtkTextIter   *end_iter,
                                    gboolean       default_editable)
1138
{
1139
1140
1141
1142
1143
1144
1145
  GtkTextMark *end_mark;
  GtkTextMark *start_mark;
  GtkTextIter iter;
  gboolean current_state;
  gboolean deleted_stuff = FALSE;

  /* Delete all editable text in the range start_iter, end_iter */
1146
1147

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1148
1149
  g_return_val_if_fail (start_iter != NULL, FALSE);
  g_return_val_if_fail (end_iter != NULL, FALSE);
1150

1151
  gtk_text_iter_reorder (start_iter, end_iter);
1152
1153

  start_mark = gtk_text_buffer_create_mark (buffer, NULL,
1154
                                            start_iter, TRUE);
1155
  end_mark = gtk_text_buffer_create_mark (buffer, NULL,
1156
1157
                                          end_iter, FALSE);
  iter = *start_iter;
1158

1159
  current_state = gtk_text_iter_editable (&iter, default_editable);
1160

1161
1162
1163
1164
1165
  while (TRUE)
    {
      gboolean new_state;
      gboolean done = FALSE;
      GtkTextIter end;
1166

1167
      gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
1168

1169
      gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
1170

1171
1172
1173
1174
1175
      if (gtk_text_iter_compare (&iter, &end) >= 0)
        {
          done = TRUE;
          iter = end; /* clamp to the last boundary */
        }
1176

1177
      new_state = gtk_text_iter_editable (&iter, default_editable);
1178

1179
1180
1181
1182
1183
1184
1185
1186
      if (current_state == new_state)
        {
          if (done)
            {
              if (current_state)
                {
                  /* We're ending an editable region. Delete said region. */
                  GtkTextIter start;
1187

1188
                  gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1189

1190
                  gtk_text_buffer_emit_delete (buffer, &start, &iter, TRUE);
1191

1192
                  deleted_stuff = TRUE;
1193
1194
1195
1196

                  /* revalidate user's iterators. */
                  *start_iter = start;
                  *end_iter = iter;
1197
1198
1199
1200
1201
1202
1203
                }

              break;
            }
          else
            continue;
        }
1204

1205
1206
1207
1208
      if (current_state && !new_state)
        {
          /* End of an editable region. Delete it. */
          GtkTextIter start;
1209

1210
          gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1211

1212
          gtk_text_buffer_emit_delete (buffer, &start, &iter, TRUE);
1213

1214
1215
          current_state = FALSE;
          deleted_stuff = TRUE;
1216
1217
1218
1219

          /* revalidate user's iterators. */
          *start_iter = start;
          *end_iter = iter;
1220
1221
1222
1223
1224
1225
        }
      else
        {
          /* We are at the start of an editable region. We won't be deleting
           * the previous region. Move start mark to start of this region.
           */
1226

1227
1228
          g_assert (!current_state && new_state);

1229
1230
          gtk_text_buffer_move_mark (buffer, start_mark,
                                     &iter);
1231
1232


1233
1234
1235
1236
1237
1238
1239
          current_state = TRUE;
        }

      if (done)
        break;
    }

1240

1241
1242
  gtk_text_buffer_delete_mark (buffer, start_mark);
  gtk_text_buffer_delete_mark (buffer, end_mark);
1243

1244
  return deleted_stuff;
1245
1246
1247
1248
1249
1250
}

/*
 * Extracting textual buffer contents
 */

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1251
1252
1253
1254
1255
1256
/**
 * gtk_text_buffer_get_text:
 * @buffer: a #GtkTextBuffer
 * @start: start of a range
 * @end: end of a range
 * @include_hidden_chars: whether to include invisible text
1257
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1258
1259
1260
1261
1262
1263
 * Returns the text in the range [@start,@end). Excludes undisplayed
 * text (text marked with tags that set the invisibility attribute) if
 * @include_hidden_chars is FALSE. Does not include characters
 * representing embedded images, so byte and character indexes into
 * the returned string do <emphasis>not</emphasis> correspond to byte
 * and character indexes into the buffer. Contrast with
1264
1265
 * gtk_text_buffer_get_slice ().
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1266
1267
 * Return value: an allocated UTF-8 string
 **/
1268
1269
1270
1271
1272
1273
gchar*
gtk_text_buffer_get_text (GtkTextBuffer      *buffer,
                          const GtkTextIter *start,
                          const GtkTextIter *end,
                          gboolean             include_hidden_chars)
{
1274
1275
1276
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);
1277
1278

  if (include_hidden_chars)
1279
    return gtk_text_iter_get_text (start, end);
1280
  else
1281
    return gtk_text_iter_get_visible_text (start, end);
1282
1283
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1284
1285
1286
1287
1288
/**
 * gtk_text_buffer_get_slice:
 * @buffer: a #GtkTextBuffer
 * @start: start of a range
 * @end: end of a range
1289
 * @include_hidden_chars: whether to include invisible text
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1290
1291
1292
1293
 *
 * Returns the text in the range [@start,@end). Excludes undisplayed
 * text (text marked with tags that set the invisibility attribute) if
 * @include_hidden_chars is FALSE. The returned string includes a
Havoc Pennington's avatar
Havoc Pennington committed
1294
 * 0xFFFC character whenever the buffer contains
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1295
1296
1297
 * embedded images, so byte and character indexes into
 * the returned string <emphasis>do</emphasis> correspond to byte
 * and character indexes into the buffer. Contrast with
Havoc Pennington's avatar
Havoc Pennington committed
1298
 * gtk_text_buffer_get_text (). Note that 0xFFFC can occur in normal
1299
1300
 * text as well, so it is not a reliable indicator that a pixbuf or
 * widget is in the buffer.
1301
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
1302
1303
 * Return value: an allocated UTF-8 string
 **/
1304
1305
1306
1307
1308
1309
gchar*
gtk_text_buffer_get_slice (GtkTextBuffer      *buffer,
                           const GtkTextIter *start,
                           const GtkTextIter *end,
                           gboolean             include_hidden_chars)
{
1310
1311
1312
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);
1313
1314

  if (include_hidden_chars)
1315
    return gtk_text_iter_get_slice (start, end);
1316
  else
1317
    return gtk_text_iter_get_visible_slice (start, end);
1318
1319
1320
1321
1322
1323
1324
}

/*
 * Pixmaps
 */

void
Havoc Pennington's avatar
Havoc Pennington committed
1325
1326
1327
gtk_text_buffer_insert_pixbuf         (GtkTextBuffer      *buffer,
                                       GtkTextIter        *iter,
                                       GdkPixbuf          *pixbuf)
1328
{
1329
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
Havoc Pennington's avatar
Havoc Pennington committed
1330
1331
  g_return_if_fail (iter != NULL);
  g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1332

1333
  __gtk_text_btree_insert_pixbuf (iter, pixbuf);
1334

Havoc Pennington's avatar
Havoc Pennington committed
1335
  /* FIXME pixbuf-specific signal like insert_text */
1336
1337

  gtk_signal_emit (GTK_OBJECT (buffer), signals[CHANGED]);
1338
}
1339

1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
/*
 * Child anchor
 */

GtkTextChildAnchor*
gtk_text_buffer_create_child_anchor (GtkTextBuffer *buffer,
                                     GtkTextIter   *iter)
{
  GtkTextChildAnchor *anchor;
  
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
  g_return_val_if_fail (iter != NULL, NULL);

1353
  anchor = _gtk_text_btree_create_child_anchor (iter);
1354
1355
1356
1357
1358
1359

  /* FIXME child-anchor-specific signal */
  
  gtk_signal_emit (GTK_OBJECT (buffer), signals[CHANGED]);

  return anchor;
1360
1361
}

1362

1363
1364
1365
1366
1367
1368
/*
 * Mark manipulation
 */

static void
gtk_text_buffer_mark_set (GtkTextBuffer     *buffer,
1369
                          const GtkTextIter *location,
1370
                          GtkTextMark       *mark)
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
{
  /* IMO this should NOT work like insert_text and delete_text,
     where the real action happens in the default handler.

     The reason is that the default handler would be _required_,
     i.e. the whole widget would start breaking and segfaulting
     if the default handler didn't get run. So you can't really
     override the default handler or stop the emission; that is,
     this signal is purely for notification, and not to allow users
     to modify the default behavior. */
1381

Havoc Pennington's avatar
Havoc Pennington committed
1382
  g_object_ref (G_OBJECT (mark));
1383
1384
1385
1386
1387

  gtk_signal_emit (GTK_OBJECT (buffer),
                   signals[MARK_SET],
                   location,
                   mark);
1388

Havoc Pennington's avatar
Havoc Pennington committed
1389
  g_object_unref (G_OBJECT (mark));
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
}

/**
 * gtk_text_buffer_set_mark:
 * @buffer:       a #GtkTextBuffer
 * @mark_name:    name of the mark
 * @iter:         location for the mark.
 * @left_gravity: if the mark is created by this function, gravity for the new
 *                mark.
 * @should_exist: if %TRUE, warn if the mark does not exist, and return
 *                immediately.
1401
 *
1402
 * Move the mark to the given position, if not @should_exist, create the mark.
1403
 *
1404
 * Return value: mark
1405
1406
 **/
static GtkTextMark*
1407
1408
1409
1410
1411
1412
gtk_text_buffer_set_mark (GtkTextBuffer *buffer,
                          GtkTextMark *existing_mark,
                          const gchar *mark_name,
                          const GtkTextIter *iter,
                          gboolean left_gravity,
                          gboolean should_exist)
1413
1414
{
  GtkTextIter location;
1415
  GtkTextMark *mark;
1416

1417
  mark = _gtk_text_btree_set_mark (get_btree (buffer),
Havoc Pennington's avatar
Havoc Pennington committed
1418
1419
1420
1421
1422
                                  existing_mark,
                                  mark_name,
                                  left_gravity,
                                  iter,
                                  should_exist);