gtktextbuffer.c 150 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2
 * gtktextbuffer.c Copyright (C) 2000 Red Hat, Inc.
3
 *                 Copyright (C) 2004 Nokia Corporation
4 5 6 7 8 9 10 11
 *
 * 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
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 14 15
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardon's avatar
Javier Jardon committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 18 19 20 21 22
 */

/*
 * 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
23
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24 25
 */

26
#include "config.h"
27
#include <string.h>
28
#include <stdarg.h>
29

30
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
31
#include "gtkclipboard.h"
32
#include "gtkdnd.h"
33
#include "gtkinvisible.h"
34
#include "gtkmarshalers.h"
35
#include "gtktextbuffer.h"
36
#include "gtktextbufferrichtext.h"
37 38
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
39
#include "gtktexttagprivate.h"
40
#include "gtkprivate.h"
41
#include "gtkintl.h"
42

43 44 45 46 47 48
/**
 * SECTION:gtktextbuffer
 * @Short_description: Stores attributed text for display in a GtkTextView
 * @Title: GtkTextBuffer
 * @See_also: #GtkTextView, #GtkTextIter, #GtkTextMark
 *
49 50 51
 * You may wish to begin by reading the
 * [text widget conceptual overview][TextWidget]
 * which gives an overview of all the objects and data
52 53 54
 * types related to the text widget and how they work together.
 */

55
typedef struct _GtkTextLogAttrCache GtkTextLogAttrCache;
56 57 58 59 60 61 62

struct _GtkTextBufferPrivate
{
  GtkTargetList  *copy_target_list;
  GtkTargetEntry *copy_target_entries;
  GtkTargetList  *paste_target_list;
  GtkTargetEntry *paste_target_entries;
63 64

  gint            n_copy_target_entries;
65
  gint            n_paste_target_entries;
66 67 68 69 70 71 72 73 74 75 76 77 78 79

  GtkTextTagTable *tag_table;
  GtkTextBTree *btree;

  GSList *clipboard_contents_buffers;
  GSList *selection_clipboards;

  GtkTextLogAttrCache *log_attr_cache;

  guint user_action_count;

  /* Whether the buffer has been modified since last save */
  guint modified : 1;
  guint has_selection : 1;
80 81
};

82 83 84 85 86
typedef struct _ClipboardRequest ClipboardRequest;

struct _ClipboardRequest
{
  GtkTextBuffer *buffer;
87 88 89
  guint interactive : 1;
  guint default_editable : 1;
  guint replace_selection : 1;
90 91
};

92 93
enum {
  INSERT_TEXT,
94
  INSERT_PIXBUF,
95
  INSERT_CHILD_ANCHOR,
96
  DELETE_RANGE,
97 98 99 100 101 102
  CHANGED,
  MODIFIED_CHANGED,
  MARK_SET,
  MARK_DELETED,
  APPLY_TAG,
  REMOVE_TAG,
103 104
  BEGIN_USER_ACTION,
  END_USER_ACTION,
105
  PASTE_DONE,
106 107 108 109
  LAST_SIGNAL
};

enum {
110 111 112
  PROP_0,

  /* Construct */
113
  PROP_TAG_TABLE,
114

115
  /* Normal */
116
  PROP_TEXT,
117
  PROP_HAS_SELECTION,
118
  PROP_CURSOR_POSITION,
119 120
  PROP_COPY_TARGET_LIST,
  PROP_PASTE_TARGET_LIST
121 122 123 124 125 126 127
};

static void gtk_text_buffer_finalize   (GObject            *object);

static void gtk_text_buffer_real_insert_text           (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *iter,
                                                        const gchar       *text,
128
                                                        gint               len);
129 130 131
static void gtk_text_buffer_real_insert_pixbuf         (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *iter,
                                                        GdkPixbuf         *pixbuf);
132 133 134
static void gtk_text_buffer_real_insert_anchor         (GtkTextBuffer     *buffer,
                                                        GtkTextIter       *iter,
                                                        GtkTextChildAnchor *anchor);
135
static void gtk_text_buffer_real_delete_range          (GtkTextBuffer     *buffer,
136
                                                        GtkTextIter       *start,
137
                                                        GtkTextIter       *end);
138 139 140 141 142 143 144 145
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);
146
static void gtk_text_buffer_real_changed               (GtkTextBuffer     *buffer);
147 148 149
static void gtk_text_buffer_real_mark_set              (GtkTextBuffer     *buffer,
                                                        const GtkTextIter *iter,
                                                        GtkTextMark       *mark);
150

151
static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
152
static void          free_log_attr_cache (GtkTextLogAttrCache *cache);
153

154 155 156
static void remove_all_selection_clipboards       (GtkTextBuffer *buffer);
static void update_selection_clipboards           (GtkTextBuffer *buffer);

157 158
static GtkTextBuffer *create_clipboard_contents_buffer (GtkTextBuffer *buffer);

159 160
static void gtk_text_buffer_free_target_lists     (GtkTextBuffer *buffer);

161 162 163 164 165 166 167 168
static void gtk_text_buffer_set_property (GObject         *object,
				          guint            prop_id,
				          const GValue    *value,
				          GParamSpec      *pspec);
static void gtk_text_buffer_get_property (GObject         *object,
				          guint            prop_id,
				          GValue          *value,
				          GParamSpec      *pspec);
169 170
static void gtk_text_buffer_notify       (GObject         *object,
                                          GParamSpec      *pspec);
171

172 173
static guint signals[LAST_SIGNAL] = { 0 };

174
G_DEFINE_TYPE_WITH_PRIVATE (GtkTextBuffer, gtk_text_buffer, G_TYPE_OBJECT)
175 176 177 178

static void
gtk_text_buffer_class_init (GtkTextBufferClass *klass)
{
179
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
180

181
  object_class->finalize = gtk_text_buffer_finalize;
182 183
  object_class->set_property = gtk_text_buffer_set_property;
  object_class->get_property = gtk_text_buffer_get_property;
184
  object_class->notify       = gtk_text_buffer_notify;
185
 
186
  klass->insert_text = gtk_text_buffer_real_insert_text;
187
  klass->insert_pixbuf = gtk_text_buffer_real_insert_pixbuf;
Havoc Pennington's avatar
Havoc Pennington committed
188
  klass->insert_child_anchor = gtk_text_buffer_real_insert_anchor;
189
  klass->delete_range = gtk_text_buffer_real_delete_range;
190 191 192
  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;
193
  klass->mark_set = gtk_text_buffer_real_mark_set;
194

195 196 197
  /* Construct */
  g_object_class_install_property (object_class,
                                   PROP_TAG_TABLE,
198
                                   g_param_spec_object ("tag-table",
199 200
                                                        P_("Tag Table"),
                                                        P_("Text Tag Table"),
201
                                                        GTK_TYPE_TEXT_TAG_TABLE,
202
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
203

204 205 206 207 208 209
  /* Normal properties*/
  
  /**
   * GtkTextBuffer:text:
   *
   * The text content of the buffer. Without child widgets and images,
Matthias Clasen's avatar
Matthias Clasen committed
210
   * see gtk_text_buffer_get_text() for more information.
211 212 213 214 215 216 217 218 219 220 221
   *
   * Since: 2.8
   */
  g_object_class_install_property (object_class,
                                   PROP_TEXT,
                                   g_param_spec_string ("text",
                                                        P_("Text"),
                                                        P_("Current text of the buffer"),
							"",
                                                        GTK_PARAM_READWRITE));

222 223 224 225 226 227 228 229 230 231 232 233 234
  /**
   * GtkTextBuffer:has-selection:
   *
   * Whether the buffer has some text currently selected.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_HAS_SELECTION,
                                   g_param_spec_boolean ("has-selection",
                                                         P_("Has selection"),
                                                         P_("Whether the buffer has some text currently selected"),
                                                         FALSE,
235 236
                                                         GTK_PARAM_READABLE));

237 238 239
  /**
   * GtkTextBuffer:cursor-position:
   *
Matthias Clasen's avatar
Matthias Clasen committed
240 241 242
   * The position of the insert mark (as offset from the beginning 
   * of the buffer). It is useful for getting notified when the 
   * cursor moves.
243 244 245 246 247 248 249 250 251 252 253
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_CURSOR_POSITION,
                                   g_param_spec_int ("cursor-position",
                                                     P_("Cursor position"),
                                                     P_("The position of the insert mark (as offset from the beginning of the buffer)"),
						     0, G_MAXINT, 0,
                                                     GTK_PARAM_READABLE));

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
  /**
   * GtkTextBuffer:copy-target-list:
   *
   * The list of targets this buffer supports for clipboard copying
   * and as DND source.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_COPY_TARGET_LIST,
                                   g_param_spec_boxed ("copy-target-list",
                                                       P_("Copy target list"),
                                                       P_("The list of targets this buffer supports for clipboard copying and DND source"),
                                                       GTK_TYPE_TARGET_LIST,
                                                       GTK_PARAM_READABLE));

  /**
   * GtkTextBuffer:paste-target-list:
   *
   * The list of targets this buffer supports for clipboard pasting
   * and as DND destination.
   *
   * Since: 2.10
   */
  g_object_class_install_property (object_class,
                                   PROP_PASTE_TARGET_LIST,
                                   g_param_spec_boxed ("paste-target-list",
                                                       P_("Paste target list"),
                                                       P_("The list of targets this buffer supports for clipboard pasting and DND destination"),
                                                       GTK_TYPE_TARGET_LIST,
                                                       GTK_PARAM_READABLE));
285

286 287 288 289 290 291 292
  /**
   * GtkTextBuffer::insert-text:
   * @textbuffer: the object which received the signal
   * @location: position to insert @text in @textbuffer
   * @text: the UTF-8 text to be inserted
   * @len: length of the inserted text in bytes
   * 
293
   * The ::insert-text signal is emitted to insert text in a #GtkTextBuffer.
294 295 296 297 298 299 300 301 302 303 304
   * Insertion actually occurs in the default handler.  
   * 
   * Note that if your handler runs before the default handler it must not 
   * invalidate the @location iter (or has to revalidate it). 
   * The default signal handler revalidates it to point to the end of the 
   * inserted text.
   * 
   * See also: 
   * gtk_text_buffer_insert(), 
   * gtk_text_buffer_insert_range().
   */
305
  signals[INSERT_TEXT] =
306
    g_signal_new (I_("insert-text"),
307 308 309 310
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, insert_text),
                  NULL, NULL,
311
                  _gtk_marshal_VOID__BOXED_STRING_INT,
Manish Singh's avatar
Manish Singh committed
312
                  G_TYPE_NONE,
313 314
                  3,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
Manish Singh's avatar
Manish Singh committed
315 316
                  G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
                  G_TYPE_INT);
317 318
  g_signal_set_va_marshaller (signals[INSERT_TEXT], G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__BOXED_STRING_INTv);
319

320 321 322 323 324 325
  /**
   * GtkTextBuffer::insert-pixbuf:
   * @textbuffer: the object which received the signal
   * @location: position to insert @pixbuf in @textbuffer
   * @pixbuf: the #GdkPixbuf to be inserted
   * 
326
   * The ::insert-pixbuf signal is emitted to insert a #GdkPixbuf 
Matthias Clasen's avatar
Matthias Clasen committed
327
   * in a #GtkTextBuffer. Insertion actually occurs in the default handler.
328 329 330 331 332 333
   * 
   * Note that if your handler runs before the default handler it must not 
   * invalidate the @location iter (or has to revalidate it). 
   * The default signal handler revalidates it to be placed after the 
   * inserted @pixbuf.
   * 
Matthias Clasen's avatar
Matthias Clasen committed
334
   * See also: gtk_text_buffer_insert_pixbuf().
335
   */
336
  signals[INSERT_PIXBUF] =
337
    g_signal_new (I_("insert-pixbuf"),
338 339 340 341
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, insert_pixbuf),
                  NULL, NULL,
342
                  _gtk_marshal_VOID__BOXED_OBJECT,
Manish Singh's avatar
Manish Singh committed
343
                  G_TYPE_NONE,
344 345 346
                  2,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                  GDK_TYPE_PIXBUF);
347

348 349 350 351 352 353 354

  /**
   * GtkTextBuffer::insert-child-anchor:
   * @textbuffer: the object which received the signal
   * @location: position to insert @anchor in @textbuffer
   * @anchor: the #GtkTextChildAnchor to be inserted
   * 
355
   * The ::insert-child-anchor signal is emitted to insert a
356 357 358
   * #GtkTextChildAnchor in a #GtkTextBuffer.
   * Insertion actually occurs in the default handler.
   * 
Matthias Clasen's avatar
Matthias Clasen committed
359 360
   * Note that if your handler runs before the default handler it must
   * not invalidate the @location iter (or has to revalidate it). 
361 362 363
   * The default signal handler revalidates it to be placed after the 
   * inserted @anchor.
   * 
Matthias Clasen's avatar
Matthias Clasen committed
364
   * See also: gtk_text_buffer_insert_child_anchor().
365
   */
366
  signals[INSERT_CHILD_ANCHOR] =
367
    g_signal_new (I_("insert-child-anchor"),
368 369 370 371
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, insert_child_anchor),
                  NULL, NULL,
372
                  _gtk_marshal_VOID__BOXED_OBJECT,
Manish Singh's avatar
Manish Singh committed
373
                  G_TYPE_NONE,
374 375 376
                  2,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                  GTK_TYPE_TEXT_CHILD_ANCHOR);
377
  
378
  /**
379 380
   * GtkTextBuffer::delete-range:
   * @textbuffer: the object which received the signal
381 382
   * @start: the start of the range to be deleted
   * @end: the end of the range to be deleted
383
   * 
384
   * The ::delete-range signal is emitted to delete a range 
385 386 387 388 389
   * from a #GtkTextBuffer. 
   * 
   * Note that if your handler runs before the default handler it must not 
   * invalidate the @start and @end iters (or has to revalidate them). 
   * The default signal handler revalidates the @start and @end iters to 
Sébastien Wilmet's avatar
Sébastien Wilmet committed
390
   * both point to the location where text was deleted. Handlers
391 392 393
   * which run after the default handler (see g_signal_connect_after())
   * do not have access to the deleted text.
   * 
Matthias Clasen's avatar
Matthias Clasen committed
394
   * See also: gtk_text_buffer_delete().
395
   */
396
  signals[DELETE_RANGE] =
397
    g_signal_new (I_("delete-range"),
398 399 400 401
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, delete_range),
                  NULL, NULL,
402
                  _gtk_marshal_VOID__BOXED_BOXED,
Manish Singh's avatar
Manish Singh committed
403
                  G_TYPE_NONE,
404 405 406
                  2,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
407

408 409 410 411
  /**
   * GtkTextBuffer::changed:
   * @textbuffer: the object which received the signal
   * 
412
   * The ::changed signal is emitted when the content of a #GtkTextBuffer 
413 414
   * has changed.
   */
415
  signals[CHANGED] =
Matthias Clasen's avatar
Matthias Clasen committed
416
    g_signal_new (I_("changed"),
417 418 419 420
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, changed),
                  NULL, NULL,
421
                  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
422
                  G_TYPE_NONE,
423
                  0);
424

425 426 427 428
  /**
   * GtkTextBuffer::modified-changed:
   * @textbuffer: the object which received the signal
   * 
429
   * The ::modified-changed signal is emitted when the modified bit of a 
430 431 432 433 434
   * #GtkTextBuffer flips.
   * 
   * See also:
   * gtk_text_buffer_set_modified().
   */
435
  signals[MODIFIED_CHANGED] =
436
    g_signal_new (I_("modified-changed"),
437 438 439 440
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, modified_changed),
                  NULL, NULL,
441
                  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
442
                  G_TYPE_NONE,
443
                  0);
444

445 446 447 448 449 450
  /**
   * GtkTextBuffer::mark-set:
   * @textbuffer: the object which received the signal
   * @location: The location of @mark in @textbuffer
   * @mark: The mark that is set
   * 
451
   * The ::mark-set signal is emitted as notification
452 453 454 455 456 457
   * after a #GtkTextMark is set.
   * 
   * See also: 
   * gtk_text_buffer_create_mark(),
   * gtk_text_buffer_move_mark().
   */
458
  signals[MARK_SET] =
459
    g_signal_new (I_("mark-set"),
460 461 462 463
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, mark_set),
                  NULL, NULL,
464
                  _gtk_marshal_VOID__BOXED_OBJECT,
Manish Singh's avatar
Manish Singh committed
465
                  G_TYPE_NONE,
466 467 468
                  2,
                  GTK_TYPE_TEXT_ITER,
                  GTK_TYPE_TEXT_MARK);
469

470 471 472 473 474
  /**
   * GtkTextBuffer::mark-deleted:
   * @textbuffer: the object which received the signal
   * @mark: The mark that was deleted
   * 
475
   * The ::mark-deleted signal is emitted as notification
476 477 478 479 480
   * after a #GtkTextMark is deleted. 
   * 
   * See also:
   * gtk_text_buffer_delete_mark().
   */
481
  signals[MARK_DELETED] =
482
    g_signal_new (I_("mark-deleted"),
483 484 485 486
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, mark_deleted),
                  NULL, NULL,
487
                  _gtk_marshal_VOID__OBJECT,
Manish Singh's avatar
Manish Singh committed
488
                  G_TYPE_NONE,
489 490
                  1,
                  GTK_TYPE_TEXT_MARK);
491 492 493 494 495 496 497 498

   /**
   * GtkTextBuffer::apply-tag:
   * @textbuffer: the object which received the signal
   * @tag: the applied tag
   * @start: the start of the range the tag is applied to
   * @end: the end of the range the tag is applied to
   * 
499
   * The ::apply-tag signal is emitted to apply a tag to a
500 501 502 503 504 505 506 507 508 509 510
   * range of text in a #GtkTextBuffer. 
   * Applying actually occurs in the default handler.
   * 
   * Note that if your handler runs before the default handler it must not 
   * invalidate the @start and @end iters (or has to revalidate them). 
   * 
   * See also: 
   * gtk_text_buffer_apply_tag(),
   * gtk_text_buffer_insert_with_tags(),
   * gtk_text_buffer_insert_range().
   */ 
511
  signals[APPLY_TAG] =
512
    g_signal_new (I_("apply-tag"),
513 514 515 516
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, apply_tag),
                  NULL, NULL,
517
                  _gtk_marshal_VOID__OBJECT_BOXED_BOXED,
Manish Singh's avatar
Manish Singh committed
518
                  G_TYPE_NONE,
519 520 521 522
                  3,
                  GTK_TYPE_TEXT_TAG,
                  GTK_TYPE_TEXT_ITER,
                  GTK_TYPE_TEXT_ITER);
523

524 525 526 527 528 529 530 531

   /**
   * GtkTextBuffer::remove-tag:
   * @textbuffer: the object which received the signal
   * @tag: the tag to be removed
   * @start: the start of the range the tag is removed from
   * @end: the end of the range the tag is removed from
   * 
532 533
   * The ::remove-tag signal is emitted to remove all occurrences of @tag from
   * a range of text in a #GtkTextBuffer. 
534 535 536 537 538 539 540 541
   * Removal actually occurs in the default handler.
   * 
   * Note that if your handler runs before the default handler it must not 
   * invalidate the @start and @end iters (or has to revalidate them). 
   * 
   * See also: 
   * gtk_text_buffer_remove_tag(). 
   */ 
542
  signals[REMOVE_TAG] =
543
    g_signal_new (I_("remove-tag"),
544 545 546 547
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, remove_tag),
                  NULL, NULL,
548
                  _gtk_marshal_VOID__OBJECT_BOXED_BOXED,
Manish Singh's avatar
Manish Singh committed
549
                  G_TYPE_NONE,
550 551 552 553
                  3,
                  GTK_TYPE_TEXT_TAG,
                  GTK_TYPE_TEXT_ITER,
                  GTK_TYPE_TEXT_ITER);
554

555 556 557 558
   /**
   * GtkTextBuffer::begin-user-action:
   * @textbuffer: the object which received the signal
   * 
559
   * The ::begin-user-action signal is emitted at the beginning of a single
560 561 562 563 564 565 566 567 568 569
   * user-visible operation on a #GtkTextBuffer.
   * 
   * See also: 
   * gtk_text_buffer_begin_user_action(),
   * gtk_text_buffer_insert_interactive(),
   * gtk_text_buffer_insert_range_interactive(),
   * gtk_text_buffer_delete_interactive(),
   * gtk_text_buffer_backspace(),
   * gtk_text_buffer_delete_selection().
   */ 
570
  signals[BEGIN_USER_ACTION] =
571
    g_signal_new (I_("begin-user-action"),
572 573 574 575
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, begin_user_action),
                  NULL, NULL,
576
                  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
577
                  G_TYPE_NONE,
578
                  0);
579

580 581 582 583
   /**
   * GtkTextBuffer::end-user-action:
   * @textbuffer: the object which received the signal
   * 
584
   * The ::end-user-action signal is emitted at the end of a single
Matthias Clasen's avatar
Matthias Clasen committed
585
   * user-visible operation on the #GtkTextBuffer.
586 587 588 589 590 591 592 593 594 595
   * 
   * See also: 
   * gtk_text_buffer_end_user_action(),
   * gtk_text_buffer_insert_interactive(),
   * gtk_text_buffer_insert_range_interactive(),
   * gtk_text_buffer_delete_interactive(),
   * gtk_text_buffer_backspace(),
   * gtk_text_buffer_delete_selection(),
   * gtk_text_buffer_backspace().
   */ 
596
  signals[END_USER_ACTION] =
597
    g_signal_new (I_("end-user-action"),
598 599 600 601
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, end_user_action),
                  NULL, NULL,
602
                  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
603
                  G_TYPE_NONE,
604 605
                  0);

606 607 608
   /**
   * GtkTextBuffer::paste-done:
   * @textbuffer: the object which received the signal
609
   * @clipboard: the #GtkClipboard pasted from
610 611 612 613 614 615 616 617
   * 
   * The paste-done signal is emitted after paste operation has been completed.
   * This is useful to properly scroll the view to the end of the pasted text.
   * See gtk_text_buffer_paste_clipboard() for more details.
   * 
   * Since: 2.16
   */ 
  signals[PASTE_DONE] =
618
    g_signal_new (I_("paste-done"),
619 620 621 622 623 624 625 626
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, paste_done),
                  NULL, NULL,
                  _gtk_marshal_VOID__OBJECT,
                  G_TYPE_NONE,
                  1,
                  GTK_TYPE_CLIPBOARD);
627 628
}

629
static void
630 631
gtk_text_buffer_init (GtkTextBuffer *buffer)
{
632
  buffer->priv = gtk_text_buffer_get_instance_private (buffer);
633 634
  buffer->priv->clipboard_contents_buffers = NULL;
  buffer->priv->tag_table = NULL;
635 636 637

  /* allow copying of arbiatray stuff in the internal rich text format */
  gtk_text_buffer_register_serialize_tagset (buffer, NULL);
638 639 640 641 642
}

static void
set_table (GtkTextBuffer *buffer, GtkTextTagTable *table)
{
643 644 645
  GtkTextBufferPrivate *priv = buffer->priv;

  g_return_if_fail (priv->tag_table == NULL);
646 647 648

  if (table)
    {
649 650
      priv->tag_table = table;
      g_object_ref (priv->tag_table);
651 652 653 654 655 656 657
      _gtk_text_tag_table_add_buffer (table, buffer);
    }
}

static GtkTextTagTable*
get_table (GtkTextBuffer *buffer)
{
658 659 660
  GtkTextBufferPrivate *priv = buffer->priv;

  if (priv->tag_table == NULL)
661
    {
662 663
      priv->tag_table = gtk_text_tag_table_new ();
      _gtk_text_tag_table_add_buffer (priv->tag_table, buffer);
664 665
    }

666
  return priv->tag_table;
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
}

static void
gtk_text_buffer_set_property (GObject         *object,
                              guint            prop_id,
                              const GValue    *value,
                              GParamSpec      *pspec)
{
  GtkTextBuffer *text_buffer;

  text_buffer = GTK_TEXT_BUFFER (object);

  switch (prop_id)
    {
    case PROP_TAG_TABLE:
      set_table (text_buffer, g_value_get_object (value));
      break;
684

685
    case PROP_TEXT:
686
      gtk_text_buffer_set_text (text_buffer,
687 688
				g_value_get_string (value), -1);
      break;
689 690

    default:
691
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
692 693 694 695 696 697 698 699 700 701 702
      break;
    }
}

static void
gtk_text_buffer_get_property (GObject         *object,
                              guint            prop_id,
                              GValue          *value,
                              GParamSpec      *pspec)
{
  GtkTextBuffer *text_buffer;
703
  GtkTextIter iter;
704 705 706 707 708 709 710 711

  text_buffer = GTK_TEXT_BUFFER (object);

  switch (prop_id)
    {
    case PROP_TAG_TABLE:
      g_value_set_object (value, get_table (text_buffer));
      break;
712

713
    case PROP_TEXT:
714 715 716 717 718 719
      {
        GtkTextIter start, end;

        gtk_text_buffer_get_start_iter (text_buffer, &start);
        gtk_text_buffer_get_end_iter (text_buffer, &end);

Matthias Clasen's avatar
Matthias Clasen committed
720
        g_value_take_string (value,
721 722 723 724
                            gtk_text_buffer_get_text (text_buffer,
                                                      &start, &end, FALSE));
        break;
      }
725

726
    case PROP_HAS_SELECTION:
727
      g_value_set_boolean (value, text_buffer->priv->has_selection);
728 729
      break;

730 731 732 733 734 735
    case PROP_CURSOR_POSITION:
      gtk_text_buffer_get_iter_at_mark (text_buffer, &iter, 
    				        gtk_text_buffer_get_insert (text_buffer));
      g_value_set_int (value, gtk_text_iter_get_offset (&iter));
      break;

736 737 738 739 740 741 742 743
    case PROP_COPY_TARGET_LIST:
      g_value_set_boxed (value, gtk_text_buffer_get_copy_target_list (text_buffer));
      break;

    case PROP_PASTE_TARGET_LIST:
      g_value_set_boxed (value, gtk_text_buffer_get_paste_target_list (text_buffer));
      break;

744
    default:
745
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
746 747
      break;
    }
748 749
}

750 751 752 753 754 755 756 757 758 759 760
static void
gtk_text_buffer_notify (GObject    *object,
                        GParamSpec *pspec)
{
  if (!strcmp (pspec->name, "copy-target-list") ||
      !strcmp (pspec->name, "paste-target-list"))
    {
      gtk_text_buffer_free_target_lists (GTK_TEXT_BUFFER (object));
    }
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
761 762
/**
 * gtk_text_buffer_new:
Johan Dahlin's avatar
Johan Dahlin committed
763
 * @table: (allow-none): a tag table, or %NULL to create a new one
764
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
765
 * Creates a new text buffer.
766
 *
767
 * Returns: a new text buffer
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
768
 **/
769 770 771 772
GtkTextBuffer*
gtk_text_buffer_new (GtkTextTagTable *table)
{
  GtkTextBuffer *text_buffer;
773

774
  text_buffer = g_object_new (GTK_TYPE_TEXT_BUFFER, "tag-table", table, NULL);
775

776 777 778 779
  return text_buffer;
}

static void
780
gtk_text_buffer_finalize (GObject *object)
781 782
{
  GtkTextBuffer *buffer;
783
  GtkTextBufferPrivate *priv;
784 785

  buffer = GTK_TEXT_BUFFER (object);
786
  priv = buffer->priv;
787

Matthias Clasen's avatar
Matthias Clasen committed
788 789
  remove_all_selection_clipboards (buffer);

790
  if (priv->tag_table)
791
    {
792 793 794
      _gtk_text_tag_table_remove_buffer (priv->tag_table, buffer);
      g_object_unref (priv->tag_table);
      priv->tag_table = NULL;
795 796
    }

797
  if (priv->btree)
798
    {
799 800
      _gtk_text_btree_unref (priv->btree);
      priv->btree = NULL;
801
    }
802

803 804
  if (priv->log_attr_cache)
    free_log_attr_cache (priv->log_attr_cache);
805

806
  priv->log_attr_cache = NULL;
807 808 809

  gtk_text_buffer_free_target_lists (buffer);

Matthias Clasen's avatar
Matthias Clasen committed
810
  G_OBJECT_CLASS (gtk_text_buffer_parent_class)->finalize (object);
811 812
}

813 814 815
static GtkTextBTree*
get_btree (GtkTextBuffer *buffer)
{
816 817 818 819 820
  GtkTextBufferPrivate *priv = buffer->priv;

  if (priv->btree == NULL)
    priv->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer),
                                       buffer);
821

822
  return priv->btree;
823 824 825 826 827 828 829 830
}

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

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
831 832 833
/**
 * gtk_text_buffer_get_tag_table:
 * @buffer: a #GtkTextBuffer
834
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
835
 * Get the #GtkTextTagTable associated with this buffer.
836
 *
837
 * Returns: (transfer none): the buffer’s tag table
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
838
 **/
839
GtkTextTagTable*
Matthias Clasen's avatar
Matthias Clasen committed
840
gtk_text_buffer_get_tag_table (GtkTextBuffer *buffer)
841
{
842
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
843 844 845 846

  return get_table (buffer);
}

847 848 849 850 851
/**
 * gtk_text_buffer_set_text:
 * @buffer: a #GtkTextBuffer
 * @text: UTF-8 text to insert
 * @len: length of @text in bytes
852
 *
853 854
 * Deletes current contents of @buffer, and inserts @text instead. If
 * @len is -1, @text must be nul-terminated. @text must be valid UTF-8.
855 856 857 858 859 860 861
 **/
void
gtk_text_buffer_set_text (GtkTextBuffer *buffer,
                          const gchar   *text,
                          gint           len)
{
  GtkTextIter start, end;
862 863

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
864 865 866 867 868 869
  g_return_if_fail (text != NULL);

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

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

871 872 873 874 875 876 877 878 879
  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);
    }
}

880 881
 

882 883 884 885 886
/*
 * Insertion
 */

static void
887
gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
Matthias Clasen's avatar
Matthias Clasen committed
888 889 890
                                  GtkTextIter   *iter,
                                  const gchar   *text,
                                  gint           len)
891
{
892 893
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
894
  
895
  _gtk_text_btree_insert (iter, text, len);
896

Manish Singh's avatar
Manish Singh committed
897
  g_signal_emit (buffer, signals[CHANGED], 0);
898
  g_object_notify (G_OBJECT (buffer), "cursor-position");
899 900 901
}

static void
902
gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
903 904
                             GtkTextIter   *iter,
                             const gchar   *text,
905
                             gint           len)
906
{
907 908 909
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);
910 911

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

Owen Taylor's avatar
Owen Taylor committed
914
  g_return_if_fail (g_utf8_validate (text, len, NULL));
915
  
916 917
  if (len > 0)
    {
Manish Singh's avatar
Manish Singh committed
918
      g_signal_emit (buffer, signals[INSERT_TEXT], 0,
919
                     iter, text, len);
920 921 922
    }
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
923 924 925 926
/**
 * gtk_text_buffer_insert:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in the buffer
927
 * @text: text in UTF-8 format
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
928
 * @len: length of text in bytes, or -1
929
 *
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
930 931
 * Inserts @len bytes of @text at position @iter.  If @len is -1,
 * @text must be nul-terminated and will be inserted in its
William Jon McCann's avatar
William Jon McCann committed
932
 * entirety. Emits the “insert-text” signal; insertion actually occurs
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
933 934 935 936 937
 * 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.
 **/
938 939
void
gtk_text_buffer_insert (GtkTextBuffer *buffer,
Matthias Clasen's avatar
Matthias Clasen committed
940 941 942
                        GtkTextIter   *iter,
                        const gchar   *text,
                        gint           len)
943
{
944 945 946
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
947 948
  g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
  
949
  gtk_text_buffer_emit_insert (buffer, iter, text, len);
950 951
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
952 953 954
/**
 * gtk_text_buffer_insert_at_cursor:
 * @buffer: a #GtkTextBuffer
955
 * @text: text in UTF-8 format
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
956
 * @len: length of text, in bytes
957
 *
Matthias Clasen's avatar
Matthias Clasen committed
958
 * Simply calls gtk_text_buffer_insert(), using the current
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
959 960
 * cursor position as the insertion point.
 **/
961 962
void
gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
Matthias Clasen's avatar
Matthias Clasen committed
963 964
                                  const gchar   *text,
                                  gint           len)
965 966 967
{
  GtkTextIter iter;

968 969
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (text != NULL);
970

971
  gtk_text_buffer_get_iter_at_mark (buffer, &iter,
972
                                    gtk_text_buffer_get_insert (buffer));
973

974
  gtk_text_buffer_insert (buffer, &iter, text, len);
975 976
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
977 978 979 980 981 982
/**
 * 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
983
 * @default_editable: default editability of buffer
984
 *
985
 * Like gtk_text_buffer_insert(), but the insertion will not occur if
Matthias Clasen's avatar
Matthias Clasen committed
986
 * @iter is at a non-editable location in the buffer. Usually you
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
987 988
 * want to prevent insertions at ineditable locations if the insertion
 * results from a user action (is interactive).
989
 *
990 991 992 993
 * @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.
 *
994
 * Returns: whether text was actually inserted
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
995
 **/
996
gboolean
997 998 999 1000 1001 1002 1003 1004
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);
Havoc Pennington's avatar
Havoc Pennington committed
1005
  g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, FALSE);
1006

1007
  if (gtk_text_iter_can_insert (iter, default_editable))
1008
    {
1009 1010 1011
      gtk_text_buffer_begin_user_action (buffer);
      gtk_text_buffer_emit_insert (buffer, iter, text, len);
      gtk_text_buffer_end_user_action (buffer);
1012 1013 1014 1015
      return TRUE;
    }
  else
    return FALSE;
1016 1017
}

Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1018 1019 1020 1021 1022 1023
/**
 * 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
1024
 *
Matthias Clasen's avatar
Matthias Clasen committed
1025
 * Calls gtk_text_buffer_insert_interactive() at the cursor
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1026
 * position.
1027
 *
1028 1029 1030 1031
 * @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.
 * 
1032
 * Returns: whether text was actually inserted
Havoc Pennington's avatar
Docs  
Havoc Pennington committed
1033
 **/
1034 1035 1036 1037 1038
gboolean
gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer,
                                              const gchar   *text,
                                              gint           len,
                                              gboolean       default_editable)
1039
{
1040
  GtkTextIter iter;
1041

1042 1043 1044 1045
  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,
1046
                                    gtk_text_buffer_get_insert (buffer));
1047

1048 1049
  return gtk_text_buffer_insert_interactive (buffer, &iter, text, len,
                                             default_editable);
1050 1051
}

1052 1053 1054 1055
static gboolean
possibly_not_text (gunichar ch,
                   gpointer user_data)
{
Havoc Pennington's avatar
Havoc Pennington committed
1056
  return ch == GTK_TEXT_UNKNOWN_CHAR;
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
}

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

1070
  gtk_text_buffer_emit_insert (buffer, iter, text, -1);
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093

  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;

Sébastien Wilmet's avatar
Sébastien Wilmet committed
1094
  r = g_slice_new (Range);
1095 1096

  r->buffer = gtk_text_iter_get_buffer (range_start);
Manish Singh's avatar
Manish Singh committed
1097
  g_object_ref (r->buffer);
1098 1099 1100 1101 1102
  
  r->start_mark = 
    gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
                                 NULL,
                                 range_start,
1103
                                 FALSE);
1104 1105 1106 1107
  r->end_mark = 
    gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
                                 NULL,
                                 range_end,
1108
                                 TRUE);
1109 1110 1111 1112 1113

  r->whole_end_mark = 
    gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
                                 NULL,
                                 whole_end,
1114
                                 TRUE);
1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135

  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,
1136 1137
                                    r->whole_end_mark);  
  
1138 1139 1140 1141
  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);

1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
  /* Due to the gravities on the marks, the ordering could have
   * gotten mangled; we switch to an empty range in that
   * case
   */
  
  if (gtk_text_iter_compare (r->range_start, r->range_end) > 0)
    *r->range_start = *r->range_end;

  if (gtk_text_iter_compare (r->range_end, r->whole_end) > 0)
    *r->range_end = *r->whole_end;
  
Manish Singh's avatar
Manish Singh committed
1153
  g_object_unref (r->buffer);
Sébastien Wilmet's avatar
Sébastien Wilmet committed
1154
  g_slice_free (Range, r);
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
}

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;
  Range *r