gtktextbuffer.c 99.6 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
#include <string.h>
29
#include <stdarg.h>
30

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

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

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

50 51
enum {
  INSERT_TEXT,
52
  INSERT_PIXBUF,
53
  INSERT_CHILD_ANCHOR,
54
  DELETE_RANGE,
55 56 57 58 59 60
  CHANGED,
  MODIFIED_CHANGED,
  MARK_SET,
  MARK_DELETED,
  APPLY_TAG,
  REMOVE_TAG,
61 62
  BEGIN_USER_ACTION,
  END_USER_ACTION,
63 64 65 66 67 68 69 70 71 72 73 74
  LAST_SIGNAL
};

enum {
  ARG_0,
  LAST_ARG
};

enum {
  TARGET_STRING,
  TARGET_TEXT,
  TARGET_COMPOUND_TEXT,
75 76
  TARGET_UTF8_STRING,
  TARGET_TEXT_BUFFER_CONTENTS
77 78 79 80 81 82 83 84 85 86 87
};

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,
88
                                                        gint               len);
89 90 91
static void gtk_text_buffer_real_insert_pixbuf         (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *iter,
                                                        GdkPixbuf         *pixbuf);
92 93 94
static void gtk_text_buffer_real_insert_anchor         (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *iter,
                                                        GtkTextChildAnchor *anchor);
95
static void gtk_text_buffer_real_delete_range          (GtkTextBuffer     *buffer,
96
                                                        GtkTextIter       *start,
97
                                                        GtkTextIter       *end);
98 99 100 101 102 103 104 105
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);
106
static void gtk_text_buffer_real_changed               (GtkTextBuffer     *buffer);
107

108
static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
109
static void          free_log_attr_cache (GtkTextLogAttrCache *cache);
110 111 112 113

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

114
GType
115 116
gtk_text_buffer_get_type (void)
{
117
  static GType our_type = 0;
118 119 120

  if (our_type == 0)
    {
121
      static const GTypeInfo our_info =
122 123
      {
        sizeof (GtkTextBufferClass),
124 125 126 127 128 129 130 131
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gtk_text_buffer_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GtkTextBuffer),
        0,              /* n_preallocs */
        (GInstanceInitFunc) gtk_text_buffer_init
132 133
      };

134 135 136 137
      our_type = g_type_register_static (G_TYPE_OBJECT,
                                         "GtkTextBuffer",
                                         &our_info,
                                         0);
138 139 140 141 142 143 144 145
    }

  return our_type;
}

static void
gtk_text_buffer_class_init (GtkTextBufferClass *klass)
{
146
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
147

148
  parent_class = g_type_class_peek_parent (klass);
149

150
  object_class->finalize = gtk_text_buffer_finalize;
151 152

  klass->insert_text = gtk_text_buffer_real_insert_text;
153
  klass->insert_pixbuf = gtk_text_buffer_real_insert_pixbuf;
Havoc Pennington's avatar
Havoc Pennington committed
154
  klass->insert_child_anchor = gtk_text_buffer_real_insert_anchor;
155
  klass->delete_range = gtk_text_buffer_real_delete_range;
156 157 158 159
  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;

160
  signals[INSERT_TEXT] =
161
    g_signal_newc ("insert_text",
Tim Janik's avatar
Tim Janik committed
162
                   G_OBJECT_CLASS_TYPE (object_class),
163 164
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkTextBufferClass, insert_text),
165
                   NULL, NULL,
166
                   gtk_marshal_VOID__BOXED_STRING_INT,
167
                   GTK_TYPE_NONE,
168
                   3,
169 170
                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                   GTK_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
171
                   GTK_TYPE_INT);
172

173 174
  signals[INSERT_PIXBUF] =
    g_signal_newc ("insert_pixbuf",
Tim Janik's avatar
Tim Janik committed
175
                   G_OBJECT_CLASS_TYPE (object_class),
176 177
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkTextBufferClass, insert_pixbuf),
178
                   NULL, NULL,
179 180 181
                   gtk_marshal_VOID__BOXED_OBJECT,
                   GTK_TYPE_NONE,
                   2,
182
                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
183
                   GDK_TYPE_PIXBUF);
184 185 186

  signals[INSERT_CHILD_ANCHOR] =
    g_signal_newc ("insert_child_anchor",
Tim Janik's avatar
Tim Janik committed
187
                   G_OBJECT_CLASS_TYPE (object_class),
188 189
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkTextBufferClass, insert_child_anchor),
190
		   NULL, NULL,
191 192 193
                   gtk_marshal_VOID__BOXED_OBJECT,
                   GTK_TYPE_NONE,
                   2,
194
                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
195
                   GTK_TYPE_TEXT_CHILD_ANCHOR);
196 197 198
  
  signals[DELETE_RANGE] =
    g_signal_newc ("delete_range",
Tim Janik's avatar
Tim Janik committed
199
                   G_OBJECT_CLASS_TYPE (object_class),
200
                   G_SIGNAL_RUN_LAST,
201
                   G_STRUCT_OFFSET (GtkTextBufferClass, delete_range),
202
		   NULL, NULL,
203
                   gtk_marshal_VOID__BOXED_BOXED,
204
                   GTK_TYPE_NONE,
205
                   2,
206 207
                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                   GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
208 209

  signals[CHANGED] =
210
    g_signal_newc ("changed",
Tim Janik's avatar
Tim Janik committed
211
                   G_OBJECT_CLASS_TYPE (object_class),
212 213
                   G_SIGNAL_RUN_LAST,                   
                   G_STRUCT_OFFSET (GtkTextBufferClass, changed),
214
		   NULL, NULL,
215 216 217
                   gtk_marshal_VOID__VOID,
                   GTK_TYPE_NONE,
                   0);
218 219

  signals[MODIFIED_CHANGED] =
220
    g_signal_newc ("modified_changed",
Tim Janik's avatar
Tim Janik committed
221
                   G_OBJECT_CLASS_TYPE (object_class),
222 223
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkTextBufferClass, modified_changed),
224
		   NULL, NULL,
225 226 227
                   gtk_marshal_VOID__VOID,
                   GTK_TYPE_NONE,
                   0);
228

229
  signals[MARK_SET] =
230
    g_signal_newc ("mark_set",
Tim Janik's avatar
Tim Janik committed
231
                   G_OBJECT_CLASS_TYPE (object_class),
232 233
                   G_SIGNAL_RUN_LAST,                   
                   G_STRUCT_OFFSET (GtkTextBufferClass, mark_set),
234
		   NULL, NULL,
235 236 237 238
                   gtk_marshal_VOID__BOXED_OBJECT,
                   GTK_TYPE_NONE,
                   2,
                   GTK_TYPE_TEXT_ITER,
239
                   GTK_TYPE_TEXT_MARK);
240 241

  signals[MARK_DELETED] =
242
    g_signal_newc ("mark_deleted",
Tim Janik's avatar
Tim Janik committed
243
                   G_OBJECT_CLASS_TYPE (object_class),
244 245
                   G_SIGNAL_RUN_LAST,                   
                   G_STRUCT_OFFSET (GtkTextBufferClass, mark_deleted),
246
		   NULL, NULL,
247 248 249
                   gtk_marshal_VOID__OBJECT,
                   GTK_TYPE_NONE,
                   1,
250
                   GTK_TYPE_TEXT_MARK);
251
  
252
  signals[APPLY_TAG] =
253
    g_signal_newc ("apply_tag",
Tim Janik's avatar
Tim Janik committed
254
                   G_OBJECT_CLASS_TYPE (object_class),
255 256
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkTextBufferClass, apply_tag),
257
		   NULL, NULL,
258 259 260
                   gtk_marshal_VOID__OBJECT_BOXED_BOXED,
                   GTK_TYPE_NONE,
                   3,
261
                   GTK_TYPE_TEXT_TAG,
262 263
                   GTK_TYPE_TEXT_ITER,
                   GTK_TYPE_TEXT_ITER);
264

265
  signals[REMOVE_TAG] =
266
    g_signal_newc ("remove_tag",
Tim Janik's avatar
Tim Janik committed
267
                   G_OBJECT_CLASS_TYPE (object_class),
268 269
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkTextBufferClass, remove_tag),
270
		   NULL, NULL,
271 272 273
                   gtk_marshal_VOID__OBJECT_BOXED_BOXED,
                   GTK_TYPE_NONE,
                   3,
274
                   GTK_TYPE_TEXT_TAG,
275 276
                   GTK_TYPE_TEXT_ITER,
                   GTK_TYPE_TEXT_ITER);
277 278 279

  signals[BEGIN_USER_ACTION] =
    g_signal_newc ("begin_user_action",
Tim Janik's avatar
Tim Janik committed
280
                   G_OBJECT_CLASS_TYPE (object_class),
281 282
                   G_SIGNAL_RUN_LAST,                   
                   G_STRUCT_OFFSET (GtkTextBufferClass, begin_user_action),
283
		   NULL, NULL,
284 285 286 287 288 289
                   gtk_marshal_VOID__VOID,
                   GTK_TYPE_NONE,
                   0);

  signals[END_USER_ACTION] =
    g_signal_newc ("end_user_action",
Tim Janik's avatar
Tim Janik committed
290
                   G_OBJECT_CLASS_TYPE (object_class),
291 292
                   G_SIGNAL_RUN_LAST,                   
                   G_STRUCT_OFFSET (GtkTextBufferClass, end_user_action),
293
		   NULL, NULL,
294 295 296
                   gtk_marshal_VOID__VOID,
                   GTK_TYPE_NONE,
                   0);  
297 298 299 300 301
}

void
gtk_text_buffer_init (GtkTextBuffer *buffer)
{
302
  buffer->clipboard_contents = NULL;
303 304
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
305 306 307
/**
 * gtk_text_buffer_new:
 * @table: a tag table, or NULL to create a new one
308
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
309
 * Creates a new text buffer.
310
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
311 312
 * Return value: a new text buffer
 **/
313 314 315 316
GtkTextBuffer*
gtk_text_buffer_new (GtkTextTagTable *table)
{
  GtkTextBuffer *text_buffer;
317

318
  text_buffer = GTK_TEXT_BUFFER (g_object_new (gtk_text_buffer_get_type (), NULL));
319 320

  if (table)
321 322
    {
      text_buffer->tag_table = table;
323

324
      g_object_ref (G_OBJECT (text_buffer->tag_table));
325 326
    }

327
  g_object_ref (G_OBJECT (text_buffer));
328
  
329 330 331 332
  return text_buffer;
}

static void
333
gtk_text_buffer_finalize (GObject *object)
334 335 336 337 338
{
  GtkTextBuffer *buffer;

  buffer = GTK_TEXT_BUFFER (object);

339 340 341 342 343 344
  if (buffer->clipboard_contents)
    {
      g_object_unref (G_OBJECT (buffer->clipboard_contents));
      buffer->clipboard_contents = NULL;
    }
  
345 346
  if (buffer->tag_table)
    {
347
      g_object_unref (G_OBJECT (buffer->tag_table));
348 349 350 351 352
      buffer->tag_table = NULL;
    }

  if (buffer->btree)
    {
353
      _gtk_text_btree_unref (buffer->btree);
354 355
      buffer->btree = NULL;
    }
356 357 358 359 360

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

  buffer->log_attr_cache = NULL;
361
  
362 363 364
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

365 366 367 368
static GtkTextTagTable*
get_table (GtkTextBuffer *buffer)
{
  if (buffer->tag_table == NULL)
369
    buffer->tag_table = gtk_text_tag_table_new ();
370 371 372 373 374 375 376 377

  return buffer->tag_table;
}

static GtkTextBTree*
get_btree (GtkTextBuffer *buffer)
{
  if (buffer->btree == NULL)
378
    buffer->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer),
379 380
                                        buffer);

381 382 383 384 385 386 387 388 389
  return buffer->btree;
}

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

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
390 391 392
/**
 * gtk_text_buffer_get_tag_table:
 * @buffer: a #GtkTextBuffer
393
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
394
 * Get the #GtkTextTagTable associated with this buffer.
395
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
396 397
 * Return value: the buffer's tag table
 **/
398 399 400
GtkTextTagTable*
gtk_text_buffer_get_tag_table (GtkTextBuffer  *buffer)
{
401
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
402 403 404 405

  return get_table (buffer);
}

406 407 408 409 410
/**
 * gtk_text_buffer_set_text:
 * @buffer: a #GtkTextBuffer
 * @text: UTF-8 text to insert
 * @len: length of @text in bytes
411
 *
412 413 414 415 416 417 418 419 420 421 422 423
 * 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;
424 425

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
426 427 428 429 430 431 432 433 434 435 436 437
  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);
438

439 440 441 442 443 444 445 446 447
  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);
    }
}

448 449 450 451 452
/*
 * Insertion
 */

static void
453

454 455 456
gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
                                  GtkTextIter *iter,
                                  const gchar *text,
457
                                  gint len)
458
{
459 460 461
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);

462
  _gtk_text_btree_insert (iter, text, len);
463

464
  g_signal_emit (G_OBJECT (buffer), signals[CHANGED], 0);
465 466 467
}

static void
468
gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
469 470
                             GtkTextIter   *iter,
                             const gchar   *text,
471
                             gint           len)
472
{
473 474 475
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);
476 477

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

Owen Taylor's avatar
Owen Taylor committed
480
  g_return_if_fail (g_utf8_validate (text, len, NULL));
481
  
482 483
  if (len > 0)
    {
484
      g_signal_emit (G_OBJECT (buffer), signals[INSERT_TEXT], 0,
485
                     iter, text, len);
486 487 488
    }
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
489 490 491 492 493 494
/**
 * 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
495
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
496 497 498 499 500 501 502
 * 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.
503
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
504
 **/
505 506 507 508 509 510
void
gtk_text_buffer_insert (GtkTextBuffer *buffer,
                        GtkTextIter *iter,
                        const gchar *text,
                        gint len)
{
511 512 513 514
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

515
  gtk_text_buffer_emit_insert (buffer, iter, text, len);
516 517
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
518 519 520 521 522
/**
 * gtk_text_buffer_insert_at_cursor:
 * @buffer: a #GtkTextBuffer
 * @text: some text in UTF-8 format
 * @len: length of text, in bytes
523 524
 *
 * Simply calls gtk_text_buffer_insert (), using the current
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
525 526
 * cursor position as the insertion point.
 **/
527 528 529 530 531 532 533
void
gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
                                  const gchar *text,
                                  gint len)
{
  GtkTextIter iter;

534 535
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (text != NULL);
536

537 538 539
  gtk_text_buffer_get_iter_at_mark (buffer, &iter,
                                    gtk_text_buffer_get_mark (buffer,
                                                              "insert"));
540

541
  gtk_text_buffer_insert (buffer, &iter, text, len);
542 543
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
544 545 546 547 548 549
/**
 * 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
550
 * @default_editable: default editability of buffer
551 552
 *
 * Like gtk_text_buffer_insert (), but the insertion will not occur if
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
553 554 555
 * @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).
556
 *
557 558 559 560
 * @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
561 562
 * Return value: whether text was actually inserted
 **/
563
gboolean
564 565 566 567 568 569 570 571 572
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);

573
  if (gtk_text_iter_editable (iter, default_editable))
574
    {
575 576 577
      gtk_text_buffer_begin_user_action (buffer);
      gtk_text_buffer_emit_insert (buffer, iter, text, len);
      gtk_text_buffer_end_user_action (buffer);
578 579 580 581
      return TRUE;
    }
  else
    return FALSE;
582 583
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
584 585 586 587 588 589
/**
 * 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
590 591
 *
 * Calls gtk_text_buffer_insert_interactive () at the cursor
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
592
 * position.
593
 *
594 595 596 597
 * @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
598 599
 * Return value: whether text was actually inserted
 **/
600 601 602 603 604
gboolean
gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer,
                                              const gchar   *text,
                                              gint           len,
                                              gboolean       default_editable)
605
{
606
  GtkTextIter iter;
607

608 609 610 611 612 613
  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"));
614

615 616
  return gtk_text_buffer_insert_interactive (buffer, &iter, text, len,
                                             default_editable);
617 618
}

619 620 621 622
static gboolean
possibly_not_text (gunichar ch,
                   gpointer user_data)
{
Havoc Pennington's avatar
Havoc Pennington committed
623
  return ch == GTK_TEXT_UNKNOWN_CHAR;
624 625 626 627 628 629 630 631 632 633 634 635 636
}

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);

637
  gtk_text_buffer_emit_insert (buffer, iter, text, -1);
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 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 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

  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
750
          else if (gtk_text_iter_get_char (&range_end) == GTK_TEXT_UNKNOWN_CHAR)
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
            {
              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;
                  
770
                  gtk_text_iter_forward_char (&range_end);
771 772 773 774 775 776 777
                  
                  range_start = range_end;
                }
              else if (anchor)
                {
                  /* Just skip anchors */

778
                  gtk_text_iter_forward_char (&range_end);
779 780 781 782
                  range_start = range_end;
                }
              else
                {
Havoc Pennington's avatar
Havoc Pennington committed
783
                  /* The GTK_TEXT_UNKNOWN_CHAR was in a text segment, so
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 843 844
                   * 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;

845 846 847
  if (interactive)
    gtk_text_buffer_begin_user_action (buffer);
  
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
  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;
    }
904 905 906

  if (interactive)
    gtk_text_buffer_end_user_action (buffer);
907
}
908 909 910 911 912 913 914 915 916 917 918

/**
 * 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
919 920
 * images and tags. If @start and @end are in a different buffer from
 * @buffer, the two buffers must share the same tag table.
921
 *
922 923
 * Implemented via emissions of the insert_text and apply_tag signals,
 * so expect those.
924 925 926 927 928 929 930 931 932 933 934
 **/
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);
935
  g_return_if_fail (gtk_text_iter_get_buffer (start) ==
936
                    gtk_text_iter_get_buffer (end));
937
  g_return_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
938
                    buffer->tag_table);  
939

940
  gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE);
941 942
}

943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
/**
 * 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
 **/
959 960 961 962 963 964 965 966 967 968 969
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);
970
  g_return_val_if_fail (gtk_text_iter_get_buffer (start) ==
971
                        gtk_text_iter_get_buffer (end), FALSE);
972
  g_return_val_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
973 974 975
                        buffer->tag_table, FALSE);


976 977 978 979 980 981 982
  if (gtk_text_iter_editable (iter, default_editable))
    {
      gtk_text_buffer_real_insert_range (buffer, iter, start, end, TRUE);
      return TRUE;
    }
  else
    return FALSE;
983 984 985 986 987 988 989
}

/**
 * gtk_text_buffer_insert_with_tags:
 * @buffer: a #GtkTextBuffer
 * @iter: an iterator in @buffer
 * @text: UTF-8 text
990
 * @len: length of @text, or -1
991
 * @first_tag: first tag to apply to @text
992 993
 * @Varargs: NULL-terminated list of tags to apply
 *
994 995
 * Inserts @text into @buffer at @iter, applying the list of tags to
 * the newly-inserted text. The last tag specified must be NULL to
996 997 998
 * 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.
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
 **/
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;
1012 1013

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1014 1015 1016 1017
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  start_offset = gtk_text_iter_get_offset (iter);
1018

1019 1020 1021 1022
  gtk_text_buffer_insert (buffer, iter, text, len);

  if (first_tag == NULL)
    return;
1023

1024
  gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1025

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
  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
1046 1047
 *
 * Same as gtk_text_buffer_insert_with_tags (), but allows you
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
 * 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;
1062 1063

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1064 1065 1066 1067
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  start_offset = gtk_text_iter_get_offset (iter);
1068

1069 1070 1071 1072
  gtk_text_buffer_insert (buffer, iter, text, len);

  if (first_tag_name == NULL)
    return;
1073

1074
  gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1075

1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
  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;
        }
1090

1091 1092 1093 1094 1095 1096 1097 1098 1099
      gtk_text_buffer_apply_tag (buffer, tag, &start, iter);

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

  va_end (args);
}


1100 1101 1102 1103 1104
/*
 * Deletion
 */

static void
1105 1106 1107
gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer,
                                   GtkTextIter   *start,
                                   GtkTextIter   *end)
1108
{
1109 1110 1111 1112
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);

1113
  _gtk_text_btree_delete (start, end);
1114 1115

  /* may have deleted the selection... */
1116 1117
  gtk_text_buffer_update_primary_selection (buffer);

1118
  g_signal_emit (G_OBJECT (buffer), signals[CHANGED], 0);
1119 1120 1121
}

static void
1122 1123
gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
                             GtkTextIter *start,
1124
                             GtkTextIter *end)
1125
{
1126 1127 1128
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);
1129

1130
  if (gtk_text_iter_equal (start, end))
1131 1132
    return;

1133
  gtk_text_iter_reorder (start, end);
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1134

1135 1136 1137 1138 1139 1140 1141
  /* 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
1142
   */
1143
  g_signal_emit (G_OBJECT (buffer),
1144
                 signals[DELETE_RANGE],
1145
                 0,
1146
                 start, end);
1147 1148
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1149 1150 1151 1152 1153 1154 1155
/**
 * 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
1156
 * is not actually relevant; gtk_text_buffer_delete () will reorder
1157
 * them. This function actually emits the "delete_range" signal, and
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1158 1159 1160 1161 1162 1163 1164 1165 1166
 * 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.
1167
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1168
 **/
1169 1170
void
gtk_text_buffer_delete (GtkTextBuffer *buffer,
1171 1172
                        GtkTextIter   *start,
                        GtkTextIter   *end)
1173
{
1174 1175 1176 1177
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);

1178
  gtk_text_buffer_emit_delete (buffer, start, end);
1179 1180
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1181 1182 1183 1184 1185
/**
 * gtk_text_buffer_delete_interactive:
 * @buffer: a #GtkTextBuffer
 * @start_iter: start of range to delete
 * @end_iter: end of range
1186 1187
 * @default_editable: whether the buffer is editable by default
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1188
 * Deletes all <emphasis>editable</emphasis> text in the given range.
1189
 * Calls gtk_text_buffer_delete () for each editable sub-range of
1190 1191 1192
 * [@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.
1193
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1194 1195
 * Return value: whether some text was actually deleted
 **/
1196 1197 1198 1199 1200
gboolean
gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
                                    GtkTextIter   *start_iter,
                                    GtkTextIter   *end_iter,
                                    gboolean       default_editable)
1201
{
1202 1203 1204 1205 1206 1207 1208
  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 */
1209 1210

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1211 1212
  g_return_val_if_fail (start_iter != NULL, FALSE);
  g_return_val_if_fail (end_iter != NULL, FALSE);
1213

1214 1215
  gtk_text_buffer_begin_user_action (buffer);
  
1216
  gtk_text_iter_reorder (start_iter, end_iter);
1217 1218

  start_mark = gtk_text_buffer_create_mark (buffer, NULL,
1219
                                            start_iter, TRUE);
1220
  end_mark = gtk_text_buffer_create_mark (buffer, NULL,
1221 1222
                                          end_iter, FALSE);
  iter = *start_iter;
1223

1224
  current_state = gtk_text_iter_editable (&iter, default_editable);
1225

1226 1227 1228 1229 1230
  while (TRUE)
    {
      gboolean new_state;
      gboolean done = FALSE;
      GtkTextIter end;
1231

1232
      gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
1233

1234
      gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
1235

1236 1237 1238 1239 1240
      if (gtk_text_iter_compare (&iter, &end) >= 0)
        {
          done = TRUE;
          iter = end; /* clamp to the last boundary */
        }
1241

1242
      new_state = gtk_text_iter_editable (&iter, default_editable);
1243

1244 1245 1246 1247 1248 1249 1250 1251
      if (current_state == new_state)
        {
          if (done)
            {
              if (current_state)
                {
                  /* We're ending an editable region. Delete said region. */
                  GtkTextIter start;
1252

1253
                  gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1254

1255
                  gtk_text_buffer_emit_delete (buffer, &start, &iter);
1256

1257
                  deleted_stuff = TRUE;
1258 1259 1260 1261

                  /* revalidate user's iterators. */
                  *start_iter = start;
                  *end_iter = iter;
1262 1263 1264 1265 1266 1267 1268
                }

              break;
            }
          else
            continue;
        }
1269

1270 1271 1272 1273
      if (current_state && !new_state)
        {
          /* End of an editable region. Delete it. */
          GtkTextIter start;
1274

1275
          gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1276

1277
          gtk_text_buffer_emit_delete (buffer, &start, &iter);
1278

1279 1280
          current_state = FALSE;
          deleted_stuff = TRUE;
1281 1282 1283 1284

          /* revalidate user's iterators. */
          *start_iter = start;
          *end_iter = iter;
1285 1286 1287 1288 1289 1290
        }
      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.
           */
1291

1292 1293
          g_assert (!current_state && new_state);

1294 1295
          gtk_text_buffer_move_mark (buffer, start_mark,
                                     &iter);
1296 1297


1298 1299 1300 1301 1302 1303 1304
          current_state = TRUE;
        }

      if (done)
        break;
    }

1305

1306 1307
  gtk_text_buffer_delete_mark (buffer, start_mark);
  gtk_text_buffer_delete_mark (buffer, end_mark);
1308

1309 1310
  gtk_text_buffer_end_user_action (buffer);
  
1311
  return deleted_stuff;
1312 1313 1314 1315 1316 1317
}

/*
 * Extracting textual buffer contents
 */

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1318 1319 1320 1321 1322 1323
/**
 * 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
1324
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1325 1326 1327 1328 1329 1330
 * 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
1331 1332
 * gtk_text_buffer_get_slice ().
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1333 1334