gtktextbuffer.c 77 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 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

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)
{
132
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
133 134 135
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  parent_class = gtk_type_class (GTK_TYPE_OBJECT);
136

137 138 139 140 141
  signals[INSERT_TEXT] =
    gtk_signal_new ("insert_text",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, insert_text),
142
                    gtk_marshal_VOID__BOXED_BOXED_INT_BOOLEAN,
143
                    GTK_TYPE_NONE,
144
                    4,
145 146
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER,
147 148
                    GTK_TYPE_INT,
                    GTK_TYPE_BOOL);
149 150 151 152 153 154

  signals[DELETE_TEXT] =
    gtk_signal_new ("delete_text",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, delete_text),
155
                    gtk_marshal_VOID__BOXED_BOXED_BOOLEAN,
156
                    GTK_TYPE_NONE,
157
                    3,
158 159
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER,
160
                    GTK_TYPE_BOOL);
161 162 163 164 165 166

  signals[CHANGED] =
    gtk_signal_new ("changed",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, changed),
167
                    gtk_marshal_VOID__VOID,
168 169 170 171 172 173 174 175
                    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),
176
                    gtk_marshal_VOID__VOID,
177 178
                    GTK_TYPE_NONE,
                    0);
179

180 181 182 183 184
  signals[MARK_SET] =
    gtk_signal_new ("mark_set",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_set),
185
                    gtk_marshal_VOID__BOXED_OBJECT,
186 187
                    GTK_TYPE_NONE,
                    2,
188 189
                    GTK_TYPE_TEXT_ITER,
                    G_TYPE_OBJECT);
190 191 192 193 194 195

  signals[MARK_DELETED] =
    gtk_signal_new ("mark_deleted",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_deleted),
196
                    gtk_marshal_VOID__OBJECT,
197 198
                    GTK_TYPE_NONE,
                    1,
199
                    G_TYPE_OBJECT);
200 201 202 203 204 205

  signals[APPLY_TAG] =
    gtk_signal_new ("apply_tag",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, apply_tag),
206
                    gtk_marshal_VOID__OBJECT_BOXED_BOXED,
207 208
                    GTK_TYPE_NONE,
                    3,
209 210 211
                    G_TYPE_OBJECT,
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER);
212

213 214 215 216 217
  signals[REMOVE_TAG] =
    gtk_signal_new ("remove_tag",
                    GTK_RUN_LAST,
                    GTK_CLASS_TYPE (object_class),
                    GTK_SIGNAL_OFFSET (GtkTextBufferClass, remove_tag),
218
                    gtk_marshal_VOID__OBJECT_BOXED_BOXED,
219 220
                    GTK_TYPE_NONE,
                    3,
221 222 223
                    G_TYPE_OBJECT,
                    GTK_TYPE_TEXT_ITER,
                    GTK_TYPE_TEXT_ITER);
224

225 226 227 228 229 230 231 232
  gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);

  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;
233
  klass->changed = gtk_text_buffer_real_changed;
234 235 236 237 238
}

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

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

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

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

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

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

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

  buffer = GTK_TEXT_BUFFER (object);

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

  if (buffer->btree)
    {
292
      gtk_text_btree_unref (buffer->btree);
293 294
      buffer->btree = NULL;
    }
295
  
296 297 298
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

299 300 301 302 303 304 305
static GtkTextTagTable*
get_table (GtkTextBuffer *buffer)
{
  if (buffer->tag_table == NULL)
    {
      buffer->tag_table = gtk_text_tag_table_new ();

306 307
      gtk_object_ref (GTK_OBJECT (buffer->tag_table));
      gtk_object_sink (GTK_OBJECT (buffer->tag_table));
308 309 310 311 312 313 314 315 316
    }

  return buffer->tag_table;
}

static GtkTextBTree*
get_btree (GtkTextBuffer *buffer)
{
  if (buffer->btree == NULL)
317 318 319
    buffer->btree = gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer),
                                        buffer);

320 321 322 323 324 325 326 327 328
  return buffer->btree;
}

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

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

  return get_table (buffer);
}

345 346 347 348 349
/**
 * gtk_text_buffer_set_text:
 * @buffer: a #GtkTextBuffer
 * @text: UTF-8 text to insert
 * @len: length of @text in bytes
350
 *
351 352 353 354 355 356 357 358 359 360 361 362
 * 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;
363 364

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
365 366 367 368 369 370 371 372 373 374 375 376
  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);
377

378 379 380 381 382 383 384 385 386
  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);
    }
}

387 388 389 390 391
/*
 * Insertion
 */

static void
392

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

  gtk_text_btree_insert (iter, text, len);
403

404
  gtk_signal_emit (GTK_OBJECT (buffer), signals[CHANGED]);
405 406 407
}

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

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

421 422
  if (len > 0)
    {
423 424
      gtk_signal_emit (GTK_OBJECT (buffer), signals[INSERT_TEXT],
                       iter, text, len, interactive);
425 426 427
    }
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
428 429 430 431 432 433
/**
 * 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
434
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
435 436 437 438 439 440 441
 * 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.
442
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
443
 **/
444 445 446 447 448 449
void
gtk_text_buffer_insert (GtkTextBuffer *buffer,
                        GtkTextIter *iter,
                        const gchar *text,
                        gint len)
{
450 451 452 453 454
  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);
455 456
}

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

473 474
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (text != NULL);
475

476 477 478
  gtk_text_buffer_get_iter_at_mark (buffer, &iter,
                                    gtk_text_buffer_get_mark (buffer,
                                                              "insert"));
479

480
  gtk_text_buffer_insert (buffer, &iter, text, len);
481 482
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
483 484 485 486 487 488
/**
 * 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
489
 * @default_editable: default editability of buffer
490 491
 *
 * Like gtk_text_buffer_insert (), but the insertion will not occur if
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
492 493 494
 * @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).
495
 *
496 497 498 499
 * @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
500 501
 * Return value: whether text was actually inserted
 **/
502
gboolean
503 504 505 506 507 508 509 510 511
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);

512
  if (gtk_text_iter_editable (iter, default_editable))
513
    {
514
      gtk_text_buffer_emit_insert (buffer, iter, text, len, TRUE);
515 516 517 518
      return TRUE;
    }
  else
    return FALSE;
519 520
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
521 522 523 524 525 526
/**
 * 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
527 528
 *
 * Calls gtk_text_buffer_insert_interactive () at the cursor
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
529
 * position.
530
 *
531 532 533 534
 * @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
535 536
 * Return value: whether text was actually inserted
 **/
537 538 539 540 541
gboolean
gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer,
                                              const gchar   *text,
                                              gint           len,
                                              gboolean       default_editable)
542
{
543
  GtkTextIter iter;
544

545 546 547 548 549 550
  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"));
551

552 553
  return gtk_text_buffer_insert_interactive (buffer, &iter, text, len,
                                             default_editable);
554 555
}

556 557 558 559
static gboolean
possibly_not_text (gunichar ch,
                   gpointer user_data)
{
Havoc Pennington's avatar
Havoc Pennington committed
560
  return ch == GTK_TEXT_UNKNOWN_CHAR;
561 562 563 564 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
}

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
687
          else if (gtk_text_iter_get_char (&range_end) == GTK_TEXT_UNKNOWN_CHAR)
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
            {
              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;
                  
                  gtk_text_iter_next_char (&range_end);
                  
                  range_start = range_end;
                }
              else if (anchor)
                {
                  /* Just skip anchors */

                  gtk_text_iter_next_char (&range_end);
                  range_start = range_end;
                }
              else
                {
Havoc Pennington's avatar
Havoc Pennington committed
720
                  /* The GTK_TEXT_UNKNOWN_CHAR was in a text segment, so
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 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
                   * 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;
    }
}
839 840 841 842 843 844 845 846 847 848 849

/**
 * 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
850 851
 * images and tags. If @start and @end are in a different buffer from
 * @buffer, the two buffers must share the same tag table.
852
 *
853 854
 * Implemented via emissions of the insert_text and apply_tag signals,
 * so expect those.
855 856 857 858 859 860 861 862 863 864 865
 **/
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);
866
  g_return_if_fail (gtk_text_iter_get_buffer (start) ==
867
                    gtk_text_iter_get_buffer (end));
868
  g_return_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
869
                    buffer->tag_table);  
870

871
  gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE);
872 873
}

874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
/**
 * 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
 **/
890 891 892 893 894 895 896 897 898 899 900
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);
901
  g_return_val_if_fail (gtk_text_iter_get_buffer (start) ==
902
                        gtk_text_iter_get_buffer (end), FALSE);
903
  g_return_val_if_fail (gtk_text_iter_get_buffer (start)->tag_table ==
904 905 906
                        buffer->tag_table, FALSE);


907 908 909 910 911 912 913
  if (gtk_text_iter_editable (iter, default_editable))
    {
      gtk_text_buffer_real_insert_range (buffer, iter, start, end, TRUE);
      return TRUE;
    }
  else
    return FALSE;
914 915 916 917 918 919 920
}

/**
 * gtk_text_buffer_insert_with_tags:
 * @buffer: a #GtkTextBuffer
 * @iter: an iterator in @buffer
 * @text: UTF-8 text
921
 * @len: length of @text, or -1
922
 * @first_tag: first tag to apply to @text
923 924
 * @Varargs: NULL-terminated list of tags to apply
 *
925 926
 * Inserts @text into @buffer at @iter, applying the list of tags to
 * the newly-inserted text. The last tag specified must be NULL to
927 928 929
 * 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.
930 931 932 933 934 935 936 937 938 939 940 941 942
 **/
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;
943 944

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
945 946 947 948
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  start_offset = gtk_text_iter_get_offset (iter);
949

950 951 952 953
  gtk_text_buffer_insert (buffer, iter, text, len);

  if (first_tag == NULL)
    return;
954

955
  gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
956

957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
  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
977 978
 *
 * Same as gtk_text_buffer_insert_with_tags (), but allows you
979 980 981 982 983 984 985 986 987 988 989 990 991 992
 * 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;
993 994

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
995 996 997 998
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);

  start_offset = gtk_text_iter_get_offset (iter);
999

1000 1001 1002 1003
  gtk_text_buffer_insert (buffer, iter, text, len);

  if (first_tag_name == NULL)
    return;
1004

1005
  gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1006

1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
  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;
        }
1021

1022 1023 1024 1025 1026 1027 1028 1029 1030
      gtk_text_buffer_apply_tag (buffer, tag, &start, iter);

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

  va_end (args);
}


1031 1032 1033 1034 1035
/*
 * Deletion
 */

static void
1036 1037 1038 1039
gtk_text_buffer_real_delete_text (GtkTextBuffer *buffer,
                                  GtkTextIter *start,
                                  GtkTextIter *end,
                                  gboolean interactive)
1040
{
1041 1042 1043 1044 1045
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);

  gtk_text_btree_delete (start, end);
1046 1047

  /* may have deleted the selection... */
1048 1049 1050
  gtk_text_buffer_update_primary_selection (buffer);

  gtk_signal_emit (GTK_OBJECT (buffer), signals[CHANGED]);
1051 1052 1053
}

static void
1054 1055 1056 1057
gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
                             GtkTextIter *start,
                             GtkTextIter *end,
                             gboolean interactive)
1058
{
1059 1060 1061
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (start != NULL);
  g_return_if_fail (end != NULL);
1062

1063
  if (gtk_text_iter_equal (start, end))
1064 1065
    return;

1066
  gtk_text_iter_reorder (start, end);
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1067

1068 1069 1070 1071 1072 1073 1074
  /* 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
1075
   */
1076 1077 1078 1079
  gtk_signal_emit (GTK_OBJECT (buffer),
                   signals[DELETE_TEXT],
                   start, end,
                   interactive);
1080 1081
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1082 1083 1084 1085 1086 1087 1088
/**
 * 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
1089
 * is not actually relevant; gtk_text_buffer_delete () will reorder
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
 * 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.
1100
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1101
 **/
1102 1103
void
gtk_text_buffer_delete (GtkTextBuffer *buffer,
1104 1105
                        GtkTextIter   *start,
                        GtkTextIter   *end)
1106
{
1107 1108 1109 1110 1111
  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);
1112 1113
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1114 1115 1116 1117 1118
/**
 * gtk_text_buffer_delete_interactive:
 * @buffer: a #GtkTextBuffer
 * @start_iter: start of range to delete
 * @end_iter: end of range
1119 1120
 * @default_editable: whether the buffer is editable by default
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1121
 * Deletes all <emphasis>editable</emphasis> text in the given range.
1122
 * Calls gtk_text_buffer_delete () for each editable sub-range of
1123 1124 1125
 * [@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.
1126
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1127 1128
 * Return value: whether some text was actually deleted
 **/
1129 1130 1131 1132 1133
gboolean
gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
                                    GtkTextIter   *start_iter,
                                    GtkTextIter   *end_iter,
                                    gboolean       default_editable)
1134
{
1135 1136 1137 1138 1139 1140 1141
  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 */
1142 1143

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1144 1145
  g_return_val_if_fail (start_iter != NULL, FALSE);
  g_return_val_if_fail (end_iter != NULL, FALSE);
1146

1147
  gtk_text_iter_reorder (start_iter, end_iter);
1148 1149

  start_mark = gtk_text_buffer_create_mark (buffer, NULL,
1150
                                            start_iter, TRUE);
1151
  end_mark = gtk_text_buffer_create_mark (buffer, NULL,
1152 1153
                                          end_iter, FALSE);
  iter = *start_iter;
1154

1155
  current_state = gtk_text_iter_editable (&iter, default_editable);
1156

1157 1158 1159 1160 1161
  while (TRUE)
    {
      gboolean new_state;
      gboolean done = FALSE;
      GtkTextIter end;
1162

1163
      gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
1164

1165
      gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
1166

1167 1168 1169 1170 1171
      if (gtk_text_iter_compare (&iter, &end) >= 0)
        {
          done = TRUE;
          iter = end; /* clamp to the last boundary */
        }
1172

1173
      new_state = gtk_text_iter_editable (&iter, default_editable);
1174

1175 1176 1177 1178 1179 1180 1181 1182
      if (current_state == new_state)
        {
          if (done)
            {
              if (current_state)
                {
                  /* We're ending an editable region. Delete said region. */
                  GtkTextIter start;
1183

1184
                  gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1185

1186
                  gtk_text_buffer_emit_delete (buffer, &start, &iter, TRUE);
1187

1188
                  deleted_stuff = TRUE;
1189 1190 1191 1192

                  /* revalidate user's iterators. */
                  *start_iter = start;
                  *end_iter = iter;
1193 1194 1195 1196 1197 1198 1199
                }

              break;
            }
          else
            continue;
        }
1200

1201 1202 1203 1204
      if (current_state && !new_state)
        {
          /* End of an editable region. Delete it. */
          GtkTextIter start;
1205

1206
          gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1207

1208
          gtk_text_buffer_emit_delete (buffer, &start, &iter, TRUE);
1209

1210 1211
          current_state = FALSE;
          deleted_stuff = TRUE;
1212 1213 1214 1215

          /* revalidate user's iterators. */
          *start_iter = start;
          *end_iter = iter;
1216 1217 1218 1219 1220 1221
        }
      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.
           */
Havoc Pennington's avatar