gtktextbuffer.c 152 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 Jardón's avatar
Javier Jardón 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 "gtktextbufferprivate.h"
37
#include "gtktextbufferrichtext.h"
38
39
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
40
#include "gtktexttagprivate.h"
41
#include "gtktexttagtableprivate.h"
42
#include "gtkprivate.h"
43
#include "gtkintl.h"
44

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

57
typedef struct _GtkTextLogAttrCache GtkTextLogAttrCache;
58
59
60
61
62
63
64

struct _GtkTextBufferPrivate
{
  GtkTargetList  *copy_target_list;
  GtkTargetEntry *copy_target_entries;
  GtkTargetList  *paste_target_list;
  GtkTargetEntry *paste_target_entries;
65
66

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

  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;
82
83
};

84
85
86
87
88
typedef struct _ClipboardRequest ClipboardRequest;

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

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

enum {
112
113
114
  PROP_0,

  /* Construct */
115
  PROP_TAG_TABLE,
116

117
  /* Normal */
Matthias Clasen's avatar
Matthias Clasen committed
118
  PROP_TEXT,
119
  PROP_HAS_SELECTION,
120
  PROP_CURSOR_POSITION,
121
  PROP_COPY_TARGET_LIST,
122
123
  PROP_PASTE_TARGET_LIST,
  LAST_PROP
124
125
126
127
128
129
130
};

static void gtk_text_buffer_finalize   (GObject            *object);

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

154
static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
155
static void          free_log_attr_cache (GtkTextLogAttrCache *cache);
156

157
158
159
static void remove_all_selection_clipboards       (GtkTextBuffer *buffer);
static void update_selection_clipboards           (GtkTextBuffer *buffer);

160
161
static GtkTextBuffer *create_clipboard_contents_buffer (GtkTextBuffer *buffer);

162
163
static void gtk_text_buffer_free_target_lists     (GtkTextBuffer *buffer);

164
165
166
167
168
169
170
171
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);
172
173
static void gtk_text_buffer_notify       (GObject         *object,
                                          GParamSpec      *pspec);
174

175
static guint signals[LAST_SIGNAL] = { 0 };
176
static GParamSpec *text_buffer_props[LAST_PROP];
177

178
G_DEFINE_TYPE_WITH_PRIVATE (GtkTextBuffer, gtk_text_buffer, G_TYPE_OBJECT)
179
180
181
182

static void
gtk_text_buffer_class_init (GtkTextBufferClass *klass)
{
183
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
184

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

199
  /* Construct */
200
201
202
203
204
205
206
207
208
  text_buffer_props[PROP_TAG_TABLE] =
      g_param_spec_object ("tag-table",
                           P_("Tag Table"),
                           P_("Text Tag Table"),
                           GTK_TYPE_TEXT_TAG_TABLE,
                           GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

  /* Normal properties */

209
210
211
212
  /**
   * GtkTextBuffer:text:
   *
   * The text content of the buffer. Without child widgets and images,
Matthias Clasen's avatar
Matthias Clasen committed
213
   * see gtk_text_buffer_get_text() for more information.
214
215
216
   *
   * Since: 2.8
   */
217
218
219
220
221
222
  text_buffer_props[PROP_TEXT] =
      g_param_spec_string ("text",
                           P_("Text"),
                           P_("Current text of the buffer"),
                           "",
                           GTK_PARAM_READWRITE);
223

Matthias Clasen's avatar
Matthias Clasen committed
224
225
226
227
228
229
230
  /**
   * GtkTextBuffer:has-selection:
   *
   * Whether the buffer has some text currently selected.
   *
   * Since: 2.10
   */
231
232
233
234
235
236
  text_buffer_props[PROP_HAS_SELECTION] =
      g_param_spec_boolean ("has-selection",
                            P_("Has selection"),
                            P_("Whether the buffer has some text currently selected"),
                            FALSE,
                            GTK_PARAM_READABLE);
237

238
239
240
  /**
   * GtkTextBuffer:cursor-position:
   *
241
242
   * The position of the insert mark (as offset from the beginning
   * of the buffer). It is useful for getting notified when the
Matthias Clasen's avatar
Matthias Clasen committed
243
   * cursor moves.
244
245
246
   *
   * Since: 2.10
   */
247
248
249
250
251
252
253
  text_buffer_props[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
  /**
   * GtkTextBuffer:copy-target-list:
   *
   * The list of targets this buffer supports for clipboard copying
   * and as DND source.
   *
   * Since: 2.10
   */
263
264
265
266
267
268
  text_buffer_props[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);
269
270
271
272
273
274
275
276
277

  /**
   * GtkTextBuffer:paste-target-list:
   *
   * The list of targets this buffer supports for clipboard pasting
   * and as DND destination.
   *
   * Since: 2.10
   */
278
279
280
281
282
283
284
285
  text_buffer_props[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);

  g_object_class_install_properties (object_class, LAST_PROP, text_buffer_props);
Matthias Clasen's avatar
Matthias Clasen committed
286

287
288
289
290
291
292
293
  /**
   * 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
   * 
294
   * The ::insert-text signal is emitted to insert text in a #GtkTextBuffer.
295
296
297
298
299
300
301
302
303
304
305
   * 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().
   */
306
  signals[INSERT_TEXT] =
307
    g_signal_new (I_("insert-text"),
308
309
310
311
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, insert_text),
                  NULL, NULL,
312
                  _gtk_marshal_VOID__BOXED_STRING_INT,
Manish Singh's avatar
Manish Singh committed
313
                  G_TYPE_NONE,
314
315
                  3,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
Manish Singh's avatar
Manish Singh committed
316
317
                  G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
                  G_TYPE_INT);
318
319
  g_signal_set_va_marshaller (signals[INSERT_TEXT], G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__BOXED_STRING_INTv);
320

321
322
323
324
325
326
  /**
   * GtkTextBuffer::insert-pixbuf:
   * @textbuffer: the object which received the signal
   * @location: position to insert @pixbuf in @textbuffer
   * @pixbuf: the #GdkPixbuf to be inserted
   * 
327
   * The ::insert-pixbuf signal is emitted to insert a #GdkPixbuf 
Matthias Clasen's avatar
Matthias Clasen committed
328
   * in a #GtkTextBuffer. Insertion actually occurs in the default handler.
329
330
331
332
333
334
   * 
   * 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
335
   * See also: gtk_text_buffer_insert_pixbuf().
336
   */
337
  signals[INSERT_PIXBUF] =
338
    g_signal_new (I_("insert-pixbuf"),
339
340
341
342
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, insert_pixbuf),
                  NULL, NULL,
343
                  _gtk_marshal_VOID__BOXED_OBJECT,
Manish Singh's avatar
Manish Singh committed
344
                  G_TYPE_NONE,
345
346
347
                  2,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                  GDK_TYPE_PIXBUF);
348
349
350
  g_signal_set_va_marshaller (signals[INSERT_PIXBUF],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__BOXED_OBJECTv);
351

352
353
354
355
356
357
358

  /**
   * GtkTextBuffer::insert-child-anchor:
   * @textbuffer: the object which received the signal
   * @location: position to insert @anchor in @textbuffer
   * @anchor: the #GtkTextChildAnchor to be inserted
   * 
359
   * The ::insert-child-anchor signal is emitted to insert a
360
361
362
   * #GtkTextChildAnchor in a #GtkTextBuffer.
   * Insertion actually occurs in the default handler.
   * 
Matthias Clasen's avatar
Matthias Clasen committed
363
364
   * Note that if your handler runs before the default handler it must
   * not invalidate the @location iter (or has to revalidate it). 
365
366
367
   * The default signal handler revalidates it to be placed after the 
   * inserted @anchor.
   * 
Matthias Clasen's avatar
Matthias Clasen committed
368
   * See also: gtk_text_buffer_insert_child_anchor().
369
   */
370
  signals[INSERT_CHILD_ANCHOR] =
371
    g_signal_new (I_("insert-child-anchor"),
372
373
374
375
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, insert_child_anchor),
                  NULL, NULL,
376
                  _gtk_marshal_VOID__BOXED_OBJECT,
Manish Singh's avatar
Manish Singh committed
377
                  G_TYPE_NONE,
378
379
380
                  2,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                  GTK_TYPE_TEXT_CHILD_ANCHOR);
381
382
383
  g_signal_set_va_marshaller (signals[INSERT_CHILD_ANCHOR],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__BOXED_OBJECTv);
384
  
385
  /**
386
387
   * GtkTextBuffer::delete-range:
   * @textbuffer: the object which received the signal
388
389
   * @start: the start of the range to be deleted
   * @end: the end of the range to be deleted
390
   * 
391
   * The ::delete-range signal is emitted to delete a range 
392
393
394
395
396
   * 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
397
   * both point to the location where text was deleted. Handlers
398
399
400
   * 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
401
   * See also: gtk_text_buffer_delete().
402
   */
403
  signals[DELETE_RANGE] =
404
    g_signal_new (I_("delete-range"),
405
406
407
408
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, delete_range),
                  NULL, NULL,
409
                  _gtk_marshal_VOID__BOXED_BOXED,
Manish Singh's avatar
Manish Singh committed
410
                  G_TYPE_NONE,
411
412
413
                  2,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
                  GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
414
415
416
  g_signal_set_va_marshaller (signals[DELETE_RANGE],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__BOXED_BOXEDv);
417

418
419
420
421
  /**
   * GtkTextBuffer::changed:
   * @textbuffer: the object which received the signal
   * 
422
   * The ::changed signal is emitted when the content of a #GtkTextBuffer 
423
424
   * has changed.
   */
425
  signals[CHANGED] =
Matthias Clasen's avatar
Matthias Clasen committed
426
    g_signal_new (I_("changed"),
427
428
429
430
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, changed),
                  NULL, NULL,
431
                  NULL,
Manish Singh's avatar
Manish Singh committed
432
                  G_TYPE_NONE,
433
                  0);
434

435
436
437
438
  /**
   * GtkTextBuffer::modified-changed:
   * @textbuffer: the object which received the signal
   * 
439
   * The ::modified-changed signal is emitted when the modified bit of a 
440
441
442
443
444
   * #GtkTextBuffer flips.
   * 
   * See also:
   * gtk_text_buffer_set_modified().
   */
445
  signals[MODIFIED_CHANGED] =
446
    g_signal_new (I_("modified-changed"),
447
448
449
450
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, modified_changed),
                  NULL, NULL,
451
                  NULL,
Manish Singh's avatar
Manish Singh committed
452
                  G_TYPE_NONE,
453
                  0);
454

455
456
457
458
459
460
  /**
   * GtkTextBuffer::mark-set:
   * @textbuffer: the object which received the signal
   * @location: The location of @mark in @textbuffer
   * @mark: The mark that is set
   * 
461
   * The ::mark-set signal is emitted as notification
462
463
464
465
466
467
   * after a #GtkTextMark is set.
   * 
   * See also: 
   * gtk_text_buffer_create_mark(),
   * gtk_text_buffer_move_mark().
   */
468
  signals[MARK_SET] =
469
    g_signal_new (I_("mark-set"),
470
                  G_OBJECT_CLASS_TYPE (object_class),
471
                  G_SIGNAL_RUN_LAST,
472
473
                  G_STRUCT_OFFSET (GtkTextBufferClass, mark_set),
                  NULL, NULL,
474
                  _gtk_marshal_VOID__BOXED_OBJECT,
Manish Singh's avatar
Manish Singh committed
475
                  G_TYPE_NONE,
476
477
478
                  2,
                  GTK_TYPE_TEXT_ITER,
                  GTK_TYPE_TEXT_MARK);
479
480
481
  g_signal_set_va_marshaller (signals[MARK_SET],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__BOXED_OBJECTv);
482

483
484
485
486
487
  /**
   * GtkTextBuffer::mark-deleted:
   * @textbuffer: the object which received the signal
   * @mark: The mark that was deleted
   * 
488
   * The ::mark-deleted signal is emitted as notification
489
490
491
492
493
   * after a #GtkTextMark is deleted. 
   * 
   * See also:
   * gtk_text_buffer_delete_mark().
   */
494
  signals[MARK_DELETED] =
495
    g_signal_new (I_("mark-deleted"),
496
497
498
499
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, mark_deleted),
                  NULL, NULL,
500
                  NULL,
Manish Singh's avatar
Manish Singh committed
501
                  G_TYPE_NONE,
502
503
                  1,
                  GTK_TYPE_TEXT_MARK);
504
505
506
507
508
509
510
511

   /**
   * 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
   * 
512
   * The ::apply-tag signal is emitted to apply a tag to a
513
514
515
516
517
518
519
520
521
522
523
   * 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().
   */ 
524
  signals[APPLY_TAG] =
525
    g_signal_new (I_("apply-tag"),
526
527
528
529
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, apply_tag),
                  NULL, NULL,
530
                  _gtk_marshal_VOID__OBJECT_BOXED_BOXED,
Manish Singh's avatar
Manish Singh committed
531
                  G_TYPE_NONE,
532
533
534
535
                  3,
                  GTK_TYPE_TEXT_TAG,
                  GTK_TYPE_TEXT_ITER,
                  GTK_TYPE_TEXT_ITER);
536
537
538
  g_signal_set_va_marshaller (signals[APPLY_TAG],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__OBJECT_BOXED_BOXEDv);
539

540
541
542
543
544
545
546
547

   /**
   * 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
   * 
548
549
   * The ::remove-tag signal is emitted to remove all occurrences of @tag from
   * a range of text in a #GtkTextBuffer. 
550
551
552
553
554
555
556
557
   * 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(). 
   */ 
558
  signals[REMOVE_TAG] =
559
    g_signal_new (I_("remove-tag"),
560
561
562
563
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, remove_tag),
                  NULL, NULL,
564
                  _gtk_marshal_VOID__OBJECT_BOXED_BOXED,
Manish Singh's avatar
Manish Singh committed
565
                  G_TYPE_NONE,
566
567
568
569
                  3,
                  GTK_TYPE_TEXT_TAG,
                  GTK_TYPE_TEXT_ITER,
                  GTK_TYPE_TEXT_ITER);
570
571
572
  g_signal_set_va_marshaller (signals[REMOVE_TAG],
                              G_TYPE_FROM_CLASS (klass),
                              _gtk_marshal_VOID__OBJECT_BOXED_BOXEDv);
573

574
575
576
577
   /**
   * GtkTextBuffer::begin-user-action:
   * @textbuffer: the object which received the signal
   * 
578
   * The ::begin-user-action signal is emitted at the beginning of a single
579
580
581
582
583
584
585
586
587
588
   * 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().
   */ 
589
  signals[BEGIN_USER_ACTION] =
590
    g_signal_new (I_("begin-user-action"),
591
592
593
594
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, begin_user_action),
                  NULL, NULL,
595
                  NULL,
Manish Singh's avatar
Manish Singh committed
596
                  G_TYPE_NONE,
597
                  0);
598

599
600
601
602
   /**
   * GtkTextBuffer::end-user-action:
   * @textbuffer: the object which received the signal
   * 
603
   * The ::end-user-action signal is emitted at the end of a single
Matthias Clasen's avatar
Matthias Clasen committed
604
   * user-visible operation on the #GtkTextBuffer.
605
606
607
608
609
610
611
612
613
614
   * 
   * 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().
   */ 
615
  signals[END_USER_ACTION] =
616
    g_signal_new (I_("end-user-action"),
617
618
619
620
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,                   
                  G_STRUCT_OFFSET (GtkTextBufferClass, end_user_action),
                  NULL, NULL,
621
                  NULL,
Manish Singh's avatar
Manish Singh committed
622
                  G_TYPE_NONE,
623
624
                  0);

625
626
627
   /**
   * GtkTextBuffer::paste-done:
   * @textbuffer: the object which received the signal
628
   * @clipboard: the #GtkClipboard pasted from
629
630
631
632
633
634
635
636
   * 
   * 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] =
637
    g_signal_new (I_("paste-done"),
638
639
640
641
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkTextBufferClass, paste_done),
                  NULL, NULL,
642
                  NULL,
643
644
645
                  G_TYPE_NONE,
                  1,
                  GTK_TYPE_CLIPBOARD);
646
647
}

648
static void
649
650
gtk_text_buffer_init (GtkTextBuffer *buffer)
{
651
  buffer->priv = gtk_text_buffer_get_instance_private (buffer);
652
653
  buffer->priv->clipboard_contents_buffers = NULL;
  buffer->priv->tag_table = NULL;
654
655
656

  /* allow copying of arbiatray stuff in the internal rich text format */
  gtk_text_buffer_register_serialize_tagset (buffer, NULL);
657
658
659
660
661
}

static void
set_table (GtkTextBuffer *buffer, GtkTextTagTable *table)
{
662
663
664
  GtkTextBufferPrivate *priv = buffer->priv;

  g_return_if_fail (priv->tag_table == NULL);
665
666
667

  if (table)
    {
668
669
      priv->tag_table = table;
      g_object_ref (priv->tag_table);
670
671
672
673
674
675
676
      _gtk_text_tag_table_add_buffer (table, buffer);
    }
}

static GtkTextTagTable*
get_table (GtkTextBuffer *buffer)
{
677
678
679
  GtkTextBufferPrivate *priv = buffer->priv;

  if (priv->tag_table == NULL)
680
    {
681
682
      priv->tag_table = gtk_text_tag_table_new ();
      _gtk_text_tag_table_add_buffer (priv->tag_table, buffer);
683
684
    }

685
  return priv->tag_table;
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
}

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

704
    case PROP_TEXT:
705
      gtk_text_buffer_set_text (text_buffer,
706
707
				g_value_get_string (value), -1);
      break;
708
709

    default:
710
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
711
712
713
714
715
716
717
718
719
720
721
      break;
    }
}

static void
gtk_text_buffer_get_property (GObject         *object,
                              guint            prop_id,
                              GValue          *value,
                              GParamSpec      *pspec)
{
  GtkTextBuffer *text_buffer;
722
  GtkTextIter iter;
723
724
725
726
727
728
729
730

  text_buffer = GTK_TEXT_BUFFER (object);

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

732
    case PROP_TEXT:
733
734
735
736
737
738
      {
        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
739
        g_value_take_string (value,
740
741
742
743
                            gtk_text_buffer_get_text (text_buffer,
                                                      &start, &end, FALSE));
        break;
      }
744

Matthias Clasen's avatar
Matthias Clasen committed
745
    case PROP_HAS_SELECTION:
746
      g_value_set_boolean (value, text_buffer->priv->has_selection);
Matthias Clasen's avatar
Matthias Clasen committed
747
748
      break;

749
750
751
752
753
754
    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;

755
756
757
758
759
760
761
762
    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;

763
    default:
764
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
765
766
      break;
    }
767
768
}

769
770
771
772
773
774
775
776
777
778
779
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
780
781
/**
 * gtk_text_buffer_new:
Johan Dahlin's avatar
Johan Dahlin committed
782
 * @table: (allow-none): a tag table, or %NULL to create a new one
783
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
784
 * Creates a new text buffer.
785
 *
786
 * Returns: a new text buffer
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
787
 **/
788
789
790
791
GtkTextBuffer*
gtk_text_buffer_new (GtkTextTagTable *table)
{
  GtkTextBuffer *text_buffer;
792

793
  text_buffer = g_object_new (GTK_TYPE_TEXT_BUFFER, "tag-table", table, NULL);
794

795
796
797
798
  return text_buffer;
}

static void
799
gtk_text_buffer_finalize (GObject *object)
800
801
{
  GtkTextBuffer *buffer;
802
  GtkTextBufferPrivate *priv;
803
804

  buffer = GTK_TEXT_BUFFER (object);
805
  priv = buffer->priv;
806

Matthias Clasen's avatar
Matthias Clasen committed
807
808
  remove_all_selection_clipboards (buffer);

809
  if (priv->tag_table)
810
    {
811
812
813
      _gtk_text_tag_table_remove_buffer (priv->tag_table, buffer);
      g_object_unref (priv->tag_table);
      priv->tag_table = NULL;
814
815
    }

816
  if (priv->btree)
817
    {
818
819
      _gtk_text_btree_unref (priv->btree);
      priv->btree = NULL;
820
    }
821

822
823
  if (priv->log_attr_cache)
    free_log_attr_cache (priv->log_attr_cache);
824

825
  priv->log_attr_cache = NULL;
826
827
828

  gtk_text_buffer_free_target_lists (buffer);

Matthias Clasen's avatar
Matthias Clasen committed
829
  G_OBJECT_CLASS (gtk_text_buffer_parent_class)->finalize (object);
830
831
}

832
833
834
static GtkTextBTree*
get_btree (GtkTextBuffer *buffer)
{
835
836
837
838
839
  GtkTextBufferPrivate *priv = buffer->priv;

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

841
  return priv->btree;
842
843
844
845
846
847
848
849
}

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

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
850
851
852
/**
 * gtk_text_buffer_get_tag_table:
 * @buffer: a #GtkTextBuffer
853
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
854
 * Get the #GtkTextTagTable associated with this buffer.
855
 *
856
 * Returns: (transfer none): the buffer’s tag table
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
857
 **/
858
GtkTextTagTable*
Matthias Clasen's avatar
Matthias Clasen committed
859
gtk_text_buffer_get_tag_table (GtkTextBuffer *buffer)
860
{
861
  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
862
863
864
865

  return get_table (buffer);
}

866
867
868
869
870
/**
 * gtk_text_buffer_set_text:
 * @buffer: a #GtkTextBuffer
 * @text: UTF-8 text to insert
 * @len: length of @text in bytes
871
 *
872
873
 * Deletes current contents of @buffer, and inserts @text instead. If
 * @len is -1, @text must be nul-terminated. @text must be valid UTF-8.
874
875
876
877
878
879
880
 **/
void
gtk_text_buffer_set_text (GtkTextBuffer *buffer,
                          const gchar   *text,
                          gint           len)
{
  GtkTextIter start, end;
881
882

  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
883
884
885
886
887
888
  g_return_if_fail (text != NULL);

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

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

890
891
892
893
894
895
896
897
898
  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);
    }
}

899
900
 

901
902
903
904
905
/*
 * Insertion
 */

static void
906
gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
Matthias Clasen's avatar
Matthias Clasen committed
907
908
909
                                  GtkTextIter   *iter,
                                  const gchar   *text,
                                  gint           len)
910
{
911
912
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
Havoc Pennington's avatar
Havoc Pennington committed
913
  
914
  _gtk_text_btree_insert (iter, text, len);
915

Manish Singh's avatar
Manish Singh committed
916
  g_signal_emit (buffer, signals[CHANGED], 0);
917
  g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_CURSOR_POSITION]);
918
919
920
}

static void
921
gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
922
923
                             GtkTextIter   *iter,
                             const gchar   *text,
924
                             gint           len)
925
{
926
927
928
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (iter != NULL);
  g_return_if_fail (text != NULL);
929
930

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

Owen Taylor's avatar
Owen Taylor committed
933
  g_return_if_fail (g_utf8_validate (text, len, NULL));
934
  
935
936
  if (len > 0)
    {
Manish Singh's avatar
Manish Singh committed
937
      g_signal_emit (buffer, signals[INSERT_TEXT], 0,
938
                     iter, text, len);
939
940
941
    }
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
942
943
944
945
/**
 * gtk_text_buffer_insert:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in the buffer
946
 * @text: text in UTF-8 format
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
947
 * @len: length of text in bytes, or -1
948
 *
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
949
950
 * 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
951
 * entirety. Emits the “insert-text” signal; insertion actually occurs
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
952
953
954
955
956
 * 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.
 **/
957
958
void
gtk_text_buffer_insert (GtkTextBuffer *buffer,
Matthias Clasen's avatar
Matthias Clasen committed
959
960
961
                        GtkTextIter   *iter,
                        const gchar   *text,
                        gint           len)
962
{
963
964
965
  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
966
967
  g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
  
968
  gtk_text_buffer_emit_insert (buffer, iter, text, len);
969
970
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
971
972
973
/**
 * gtk_text_buffer_insert_at_cursor:
 * @buffer: a #GtkTextBuffer
974
 * @text: text in UTF-8 format
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
975
 * @len: length of text, in bytes
976
 *
Matthias Clasen's avatar
Matthias Clasen committed
977
 * Simply calls gtk_text_buffer_insert(), using the current
Havoc Pennington's avatar
Docs    
Havoc Pennington committed
978
979
 * cursor position as the insertion point.
 **/
980
981
void
gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
Matthias Clasen's avatar
Matthias Clasen committed
982
983
                                  const gchar   *text,
                                  gint           len)
984
985
986
{
  GtkTextIter iter;

987
988
  g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
  g_return_if_fail (text != NULL);
989

990
  gtk_text_buffer_get_iter_at_mark (buffer, &iter,
991
                                    gtk_text_buffer_get_insert (buffer));
992

993
  gtk_text_buffer_insert (buffer, &iter, text, len);
994
995
}

Havoc Pennington's avatar
Docs    
Havoc Pennington committed
996
997
998
999
1000
/**
 * gtk_text_buffer_insert_interactive:
 * @buffer: a #GtkTextBuffer
 * @iter: a position in @buffer
 * @text: some UTF-8 text
For faster browsing, not all history is shown. View entire blame