1 /* GTK - The GIMP Toolkit
2 * gtktextbuffer.c Copyright (C) 2000 Red Hat, Inc.
3 * Copyright (C) 2004 Nokia Corporation
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * 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 #include "gtktextbtree.h"
39 #include "gtktextiterprivate.h"
40 #include "gtktexttagprivate.h"
41 #include "gtktexttagtableprivate.h"
42 #include "gtkprivate.h"
43 #include "gtkintl.h"
44
45 /**
46 * SECTION:gtktextbuffer
47 * @Short_description: Stores attributed text for display in a GtkTextView
48 * @Title: GtkTextBuffer
49 * @See_also: #GtkTextView, #GtkTextIter, #GtkTextMark
50 *
51 * You may wish to begin by reading the
52 * [text widget conceptual overview][TextWidget]
53 * which gives an overview of all the objects and data
54 * types related to the text widget and how they work together.
55 */
56
57 typedef struct _GtkTextLogAttrCache GtkTextLogAttrCache;
58
59 struct _GtkTextBufferPrivate
60 {
61 GtkTargetList *copy_target_list;
62 GtkTargetEntry *copy_target_entries;
63 GtkTargetList *paste_target_list;
64 GtkTargetEntry *paste_target_entries;
65
66 gint n_copy_target_entries;
67 gint n_paste_target_entries;
68
69 GtkTextTagTable *tag_table;
70 GtkTextBTree *btree;
71
72 GSList *clipboard_contents_buffers;
73 GSList *selection_clipboards;
74
75 GtkTextLogAttrCache *log_attr_cache;
76
77 guint user_action_count;
78
79 /* Whether the buffer has been modified since last save */
80 guint modified : 1;
81 guint has_selection : 1;
82 };
83
84 typedef struct _ClipboardRequest ClipboardRequest;
85
86 struct _ClipboardRequest
87 {
88 GtkTextBuffer *buffer;
89 guint interactive : 1;
90 guint default_editable : 1;
91 guint replace_selection : 1;
92 };
93
94 enum {
95 INSERT_TEXT,
96 INSERT_PIXBUF,
97 INSERT_CHILD_ANCHOR,
98 DELETE_RANGE,
99 CHANGED,
100 MODIFIED_CHANGED,
101 MARK_SET,
102 MARK_DELETED,
103 APPLY_TAG,
104 REMOVE_TAG,
105 BEGIN_USER_ACTION,
106 END_USER_ACTION,
107 PASTE_DONE,
108 LAST_SIGNAL
109 };
110
111 enum {
112 PROP_0,
113
114 /* Construct */
115 PROP_TAG_TABLE,
116
117 /* Normal */
118 PROP_TEXT,
119 PROP_HAS_SELECTION,
120 PROP_CURSOR_POSITION,
121 PROP_COPY_TARGET_LIST,
122 PROP_PASTE_TARGET_LIST,
123 LAST_PROP
124 };
125
126 static void gtk_text_buffer_finalize (GObject *object);
127
128 static void gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
129 GtkTextIter *iter,
130 const gchar *text,
131 gint len);
132 static void gtk_text_buffer_real_insert_pixbuf (GtkTextBuffer *buffer,
133 GtkTextIter *iter,
134 GdkPixbuf *pixbuf);
135 static void gtk_text_buffer_real_insert_anchor (GtkTextBuffer *buffer,
136 GtkTextIter *iter,
137 GtkTextChildAnchor *anchor);
138 static void gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer,
139 GtkTextIter *start,
140 GtkTextIter *end);
141 static void gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
142 GtkTextTag *tag,
143 const GtkTextIter *start_char,
144 const GtkTextIter *end_char);
145 static void gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
146 GtkTextTag *tag,
147 const GtkTextIter *start_char,
148 const GtkTextIter *end_char);
149 static void gtk_text_buffer_real_changed (GtkTextBuffer *buffer);
150 static void gtk_text_buffer_real_mark_set (GtkTextBuffer *buffer,
151 const GtkTextIter *iter,
152 GtkTextMark *mark);
153
154 static GtkTextBTree* get_btree (GtkTextBuffer *buffer);
155 static void free_log_attr_cache (GtkTextLogAttrCache *cache);
156
157 static void remove_all_selection_clipboards (GtkTextBuffer *buffer);
158 static void update_selection_clipboards (GtkTextBuffer *buffer);
159
160 static GtkTextBuffer *create_clipboard_contents_buffer (GtkTextBuffer *buffer);
161
162 static void gtk_text_buffer_free_target_lists (GtkTextBuffer *buffer);
163
164 static void gtk_text_buffer_set_property (GObject *object,
165 guint prop_id,
166 const GValue *value,
167 GParamSpec *pspec);
168 static void gtk_text_buffer_get_property (GObject *object,
169 guint prop_id,
170 GValue *value,
171 GParamSpec *pspec);
172 static void gtk_text_buffer_notify (GObject *object,
173 GParamSpec *pspec);
174
175 static guint signals[LAST_SIGNAL] = { 0 };
176 static GParamSpec *text_buffer_props[LAST_PROP];
177
G_DEFINE_TYPE_WITH_PRIVATE(GtkTextBuffer,gtk_text_buffer,G_TYPE_OBJECT)178 G_DEFINE_TYPE_WITH_PRIVATE (GtkTextBuffer, gtk_text_buffer, G_TYPE_OBJECT)
179
180 static void
181 gtk_text_buffer_class_init (GtkTextBufferClass *klass)
182 {
183 GObjectClass *object_class = G_OBJECT_CLASS (klass);
184
185 object_class->finalize = gtk_text_buffer_finalize;
186 object_class->set_property = gtk_text_buffer_set_property;
187 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;
192 klass->insert_child_anchor = gtk_text_buffer_real_insert_anchor;
193 klass->delete_range = gtk_text_buffer_real_delete_range;
194 klass->apply_tag = gtk_text_buffer_real_apply_tag;
195 klass->remove_tag = gtk_text_buffer_real_remove_tag;
196 klass->changed = gtk_text_buffer_real_changed;
197 klass->mark_set = gtk_text_buffer_real_mark_set;
198
199 /* Construct */
200 text_buffer_props[PROP_TAG_TABLE] =
201 g_param_spec_object ("tag-table",
202 P_("Tag Table"),
203 P_("Text Tag Table"),
204 GTK_TYPE_TEXT_TAG_TABLE,
205 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
206
207 /* Normal properties */
208
209 /**
210 * GtkTextBuffer:text:
211 *
212 * The text content of the buffer. Without child widgets and images,
213 * see gtk_text_buffer_get_text() for more information.
214 *
215 * Since: 2.8
216 */
217 text_buffer_props[PROP_TEXT] =
218 g_param_spec_string ("text",
219 P_("Text"),
220 P_("Current text of the buffer"),
221 "",
222 GTK_PARAM_READWRITE);
223
224 /**
225 * GtkTextBuffer:has-selection:
226 *
227 * Whether the buffer has some text currently selected.
228 *
229 * Since: 2.10
230 */
231 text_buffer_props[PROP_HAS_SELECTION] =
232 g_param_spec_boolean ("has-selection",
233 P_("Has selection"),
234 P_("Whether the buffer has some text currently selected"),
235 FALSE,
236 GTK_PARAM_READABLE);
237
238 /**
239 * GtkTextBuffer:cursor-position:
240 *
241 * The position of the insert mark (as offset from the beginning
242 * of the buffer). It is useful for getting notified when the
243 * cursor moves.
244 *
245 * Since: 2.10
246 */
247 text_buffer_props[PROP_CURSOR_POSITION] =
248 g_param_spec_int ("cursor-position",
249 P_("Cursor position"),
250 P_("The position of the insert mark (as offset from the beginning of the buffer)"),
251 0, G_MAXINT,
252 0,
253 GTK_PARAM_READABLE);
254
255 /**
256 * GtkTextBuffer:copy-target-list:
257 *
258 * The list of targets this buffer supports for clipboard copying
259 * and as DND source.
260 *
261 * Since: 2.10
262 */
263 text_buffer_props[PROP_COPY_TARGET_LIST] =
264 g_param_spec_boxed ("copy-target-list",
265 P_("Copy target list"),
266 P_("The list of targets this buffer supports for clipboard copying and DND source"),
267 GTK_TYPE_TARGET_LIST,
268 GTK_PARAM_READABLE);
269
270 /**
271 * GtkTextBuffer:paste-target-list:
272 *
273 * The list of targets this buffer supports for clipboard pasting
274 * and as DND destination.
275 *
276 * Since: 2.10
277 */
278 text_buffer_props[PROP_PASTE_TARGET_LIST] =
279 g_param_spec_boxed ("paste-target-list",
280 P_("Paste target list"),
281 P_("The list of targets this buffer supports for clipboard pasting and DND destination"),
282 GTK_TYPE_TARGET_LIST,
283 GTK_PARAM_READABLE);
284
285 g_object_class_install_properties (object_class, LAST_PROP, text_buffer_props);
286
287 /**
288 * GtkTextBuffer::insert-text:
289 * @textbuffer: the object which received the signal
290 * @location: position to insert @text in @textbuffer
291 * @text: the UTF-8 text to be inserted
292 * @len: length of the inserted text in bytes
293 *
294 * The ::insert-text signal is emitted to insert text in a #GtkTextBuffer.
295 * Insertion actually occurs in the default handler.
296 *
297 * Note that if your handler runs before the default handler it must not
298 * invalidate the @location iter (or has to revalidate it).
299 * The default signal handler revalidates it to point to the end of the
300 * inserted text.
301 *
302 * See also:
303 * gtk_text_buffer_insert(),
304 * gtk_text_buffer_insert_range().
305 */
306 signals[INSERT_TEXT] =
307 g_signal_new (I_("insert-text"),
308 G_OBJECT_CLASS_TYPE (object_class),
309 G_SIGNAL_RUN_LAST,
310 G_STRUCT_OFFSET (GtkTextBufferClass, insert_text),
311 NULL, NULL,
312 _gtk_marshal_VOID__BOXED_STRING_INT,
313 G_TYPE_NONE,
314 3,
315 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
316 G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
317 G_TYPE_INT);
318 g_signal_set_va_marshaller (signals[INSERT_TEXT], G_TYPE_FROM_CLASS (klass),
319 _gtk_marshal_VOID__BOXED_STRING_INTv);
320
321 /**
322 * GtkTextBuffer::insert-pixbuf:
323 * @textbuffer: the object which received the signal
324 * @location: position to insert @pixbuf in @textbuffer
325 * @pixbuf: the #GdkPixbuf to be inserted
326 *
327 * The ::insert-pixbuf signal is emitted to insert a #GdkPixbuf
328 * in a #GtkTextBuffer. Insertion actually occurs in the default handler.
329 *
330 * Note that if your handler runs before the default handler it must not
331 * invalidate the @location iter (or has to revalidate it).
332 * The default signal handler revalidates it to be placed after the
333 * inserted @pixbuf.
334 *
335 * See also: gtk_text_buffer_insert_pixbuf().
336 */
337 signals[INSERT_PIXBUF] =
338 g_signal_new (I_("insert-pixbuf"),
339 G_OBJECT_CLASS_TYPE (object_class),
340 G_SIGNAL_RUN_LAST,
341 G_STRUCT_OFFSET (GtkTextBufferClass, insert_pixbuf),
342 NULL, NULL,
343 _gtk_marshal_VOID__BOXED_OBJECT,
344 G_TYPE_NONE,
345 2,
346 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
347 GDK_TYPE_PIXBUF);
348 g_signal_set_va_marshaller (signals[INSERT_PIXBUF],
349 G_TYPE_FROM_CLASS (klass),
350 _gtk_marshal_VOID__BOXED_OBJECTv);
351
352
353 /**
354 * GtkTextBuffer::insert-child-anchor:
355 * @textbuffer: the object which received the signal
356 * @location: position to insert @anchor in @textbuffer
357 * @anchor: the #GtkTextChildAnchor to be inserted
358 *
359 * The ::insert-child-anchor signal is emitted to insert a
360 * #GtkTextChildAnchor in a #GtkTextBuffer.
361 * Insertion actually occurs in the default handler.
362 *
363 * Note that if your handler runs before the default handler it must
364 * not invalidate the @location iter (or has to revalidate it).
365 * The default signal handler revalidates it to be placed after the
366 * inserted @anchor.
367 *
368 * See also: gtk_text_buffer_insert_child_anchor().
369 */
370 signals[INSERT_CHILD_ANCHOR] =
371 g_signal_new (I_("insert-child-anchor"),
372 G_OBJECT_CLASS_TYPE (object_class),
373 G_SIGNAL_RUN_LAST,
374 G_STRUCT_OFFSET (GtkTextBufferClass, insert_child_anchor),
375 NULL, NULL,
376 _gtk_marshal_VOID__BOXED_OBJECT,
377 G_TYPE_NONE,
378 2,
379 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
380 GTK_TYPE_TEXT_CHILD_ANCHOR);
381 g_signal_set_va_marshaller (signals[INSERT_CHILD_ANCHOR],
382 G_TYPE_FROM_CLASS (klass),
383 _gtk_marshal_VOID__BOXED_OBJECTv);
384
385 /**
386 * GtkTextBuffer::delete-range:
387 * @textbuffer: the object which received the signal
388 * @start: the start of the range to be deleted
389 * @end: the end of the range to be deleted
390 *
391 * The ::delete-range signal is emitted to delete a range
392 * from a #GtkTextBuffer.
393 *
394 * Note that if your handler runs before the default handler it must not
395 * invalidate the @start and @end iters (or has to revalidate them).
396 * The default signal handler revalidates the @start and @end iters to
397 * both point to the location where text was deleted. Handlers
398 * which run after the default handler (see g_signal_connect_after())
399 * do not have access to the deleted text.
400 *
401 * See also: gtk_text_buffer_delete().
402 */
403 signals[DELETE_RANGE] =
404 g_signal_new (I_("delete-range"),
405 G_OBJECT_CLASS_TYPE (object_class),
406 G_SIGNAL_RUN_LAST,
407 G_STRUCT_OFFSET (GtkTextBufferClass, delete_range),
408 NULL, NULL,
409 _gtk_marshal_VOID__BOXED_BOXED,
410 G_TYPE_NONE,
411 2,
412 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE,
413 GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE);
414 g_signal_set_va_marshaller (signals[DELETE_RANGE],
415 G_TYPE_FROM_CLASS (klass),
416 _gtk_marshal_VOID__BOXED_BOXEDv);
417
418 /**
419 * GtkTextBuffer::changed:
420 * @textbuffer: the object which received the signal
421 *
422 * The ::changed signal is emitted when the content of a #GtkTextBuffer
423 * has changed.
424 */
425 signals[CHANGED] =
426 g_signal_new (I_("changed"),
427 G_OBJECT_CLASS_TYPE (object_class),
428 G_SIGNAL_RUN_LAST,
429 G_STRUCT_OFFSET (GtkTextBufferClass, changed),
430 NULL, NULL,
431 NULL,
432 G_TYPE_NONE,
433 0);
434
435 /**
436 * GtkTextBuffer::modified-changed:
437 * @textbuffer: the object which received the signal
438 *
439 * The ::modified-changed signal is emitted when the modified bit of a
440 * #GtkTextBuffer flips.
441 *
442 * See also:
443 * gtk_text_buffer_set_modified().
444 */
445 signals[MODIFIED_CHANGED] =
446 g_signal_new (I_("modified-changed"),
447 G_OBJECT_CLASS_TYPE (object_class),
448 G_SIGNAL_RUN_LAST,
449 G_STRUCT_OFFSET (GtkTextBufferClass, modified_changed),
450 NULL, NULL,
451 NULL,
452 G_TYPE_NONE,
453 0);
454
455 /**
456 * GtkTextBuffer::mark-set:
457 * @textbuffer: the object which received the signal
458 * @location: The location of @mark in @textbuffer
459 * @mark: The mark that is set
460 *
461 * The ::mark-set signal is emitted as notification
462 * after a #GtkTextMark is set.
463 *
464 * See also:
465 * gtk_text_buffer_create_mark(),
466 * gtk_text_buffer_move_mark().
467 */
468 signals[MARK_SET] =
469 g_signal_new (I_("mark-set"),
470 G_OBJECT_CLASS_TYPE (object_class),
471 G_SIGNAL_RUN_LAST,
472 G_STRUCT_OFFSET (GtkTextBufferClass, mark_set),
473 NULL, NULL,
474 _gtk_marshal_VOID__BOXED_OBJECT,
475 G_TYPE_NONE,
476 2,
477 GTK_TYPE_TEXT_ITER,
478 GTK_TYPE_TEXT_MARK);
479 g_signal_set_va_marshaller (signals[MARK_SET],
480 G_TYPE_FROM_CLASS (klass),
481 _gtk_marshal_VOID__BOXED_OBJECTv);
482
483 /**
484 * GtkTextBuffer::mark-deleted:
485 * @textbuffer: the object which received the signal
486 * @mark: The mark that was deleted
487 *
488 * The ::mark-deleted signal is emitted as notification
489 * after a #GtkTextMark is deleted.
490 *
491 * See also:
492 * gtk_text_buffer_delete_mark().
493 */
494 signals[MARK_DELETED] =
495 g_signal_new (I_("mark-deleted"),
496 G_OBJECT_CLASS_TYPE (object_class),
497 G_SIGNAL_RUN_LAST,
498 G_STRUCT_OFFSET (GtkTextBufferClass, mark_deleted),
499 NULL, NULL,
500 NULL,
501 G_TYPE_NONE,
502 1,
503 GTK_TYPE_TEXT_MARK);
504
505 /**
506 * GtkTextBuffer::apply-tag:
507 * @textbuffer: the object which received the signal
508 * @tag: the applied tag
509 * @start: the start of the range the tag is applied to
510 * @end: the end of the range the tag is applied to
511 *
512 * The ::apply-tag signal is emitted to apply a tag to a
513 * range of text in a #GtkTextBuffer.
514 * Applying actually occurs in the default handler.
515 *
516 * Note that if your handler runs before the default handler it must not
517 * invalidate the @start and @end iters (or has to revalidate them).
518 *
519 * See also:
520 * gtk_text_buffer_apply_tag(),
521 * gtk_text_buffer_insert_with_tags(),
522 * gtk_text_buffer_insert_range().
523 */
524 signals[APPLY_TAG] =
525 g_signal_new (I_("apply-tag"),
526 G_OBJECT_CLASS_TYPE (object_class),
527 G_SIGNAL_RUN_LAST,
528 G_STRUCT_OFFSET (GtkTextBufferClass, apply_tag),
529 NULL, NULL,
530 _gtk_marshal_VOID__OBJECT_BOXED_BOXED,
531 G_TYPE_NONE,
532 3,
533 GTK_TYPE_TEXT_TAG,
534 GTK_TYPE_TEXT_ITER,
535 GTK_TYPE_TEXT_ITER);
536 g_signal_set_va_marshaller (signals[APPLY_TAG],
537 G_TYPE_FROM_CLASS (klass),
538 _gtk_marshal_VOID__OBJECT_BOXED_BOXEDv);
539
540
541 /**
542 * GtkTextBuffer::remove-tag:
543 * @textbuffer: the object which received the signal
544 * @tag: the tag to be removed
545 * @start: the start of the range the tag is removed from
546 * @end: the end of the range the tag is removed from
547 *
548 * The ::remove-tag signal is emitted to remove all occurrences of @tag from
549 * a range of text in a #GtkTextBuffer.
550 * Removal actually occurs in the default handler.
551 *
552 * Note that if your handler runs before the default handler it must not
553 * invalidate the @start and @end iters (or has to revalidate them).
554 *
555 * See also:
556 * gtk_text_buffer_remove_tag().
557 */
558 signals[REMOVE_TAG] =
559 g_signal_new (I_("remove-tag"),
560 G_OBJECT_CLASS_TYPE (object_class),
561 G_SIGNAL_RUN_LAST,
562 G_STRUCT_OFFSET (GtkTextBufferClass, remove_tag),
563 NULL, NULL,
564 _gtk_marshal_VOID__OBJECT_BOXED_BOXED,
565 G_TYPE_NONE,
566 3,
567 GTK_TYPE_TEXT_TAG,
568 GTK_TYPE_TEXT_ITER,
569 GTK_TYPE_TEXT_ITER);
570 g_signal_set_va_marshaller (signals[REMOVE_TAG],
571 G_TYPE_FROM_CLASS (klass),
572 _gtk_marshal_VOID__OBJECT_BOXED_BOXEDv);
573
574 /**
575 * GtkTextBuffer::begin-user-action:
576 * @textbuffer: the object which received the signal
577 *
578 * The ::begin-user-action signal is emitted at the beginning of a single
579 * user-visible operation on a #GtkTextBuffer.
580 *
581 * See also:
582 * gtk_text_buffer_begin_user_action(),
583 * gtk_text_buffer_insert_interactive(),
584 * gtk_text_buffer_insert_range_interactive(),
585 * gtk_text_buffer_delete_interactive(),
586 * gtk_text_buffer_backspace(),
587 * gtk_text_buffer_delete_selection().
588 */
589 signals[BEGIN_USER_ACTION] =
590 g_signal_new (I_("begin-user-action"),
591 G_OBJECT_CLASS_TYPE (object_class),
592 G_SIGNAL_RUN_LAST,
593 G_STRUCT_OFFSET (GtkTextBufferClass, begin_user_action),
594 NULL, NULL,
595 NULL,
596 G_TYPE_NONE,
597 0);
598
599 /**
600 * GtkTextBuffer::end-user-action:
601 * @textbuffer: the object which received the signal
602 *
603 * The ::end-user-action signal is emitted at the end of a single
604 * user-visible operation on the #GtkTextBuffer.
605 *
606 * See also:
607 * gtk_text_buffer_end_user_action(),
608 * gtk_text_buffer_insert_interactive(),
609 * gtk_text_buffer_insert_range_interactive(),
610 * gtk_text_buffer_delete_interactive(),
611 * gtk_text_buffer_backspace(),
612 * gtk_text_buffer_delete_selection(),
613 * gtk_text_buffer_backspace().
614 */
615 signals[END_USER_ACTION] =
616 g_signal_new (I_("end-user-action"),
617 G_OBJECT_CLASS_TYPE (object_class),
618 G_SIGNAL_RUN_LAST,
619 G_STRUCT_OFFSET (GtkTextBufferClass, end_user_action),
620 NULL, NULL,
621 NULL,
622 G_TYPE_NONE,
623 0);
624
625 /**
626 * GtkTextBuffer::paste-done:
627 * @textbuffer: the object which received the signal
628 * @clipboard: the #GtkClipboard pasted from
629 *
630 * The paste-done signal is emitted after paste operation has been completed.
631 * This is useful to properly scroll the view to the end of the pasted text.
632 * See gtk_text_buffer_paste_clipboard() for more details.
633 *
634 * Since: 2.16
635 */
636 signals[PASTE_DONE] =
637 g_signal_new (I_("paste-done"),
638 G_OBJECT_CLASS_TYPE (object_class),
639 G_SIGNAL_RUN_LAST,
640 G_STRUCT_OFFSET (GtkTextBufferClass, paste_done),
641 NULL, NULL,
642 NULL,
643 G_TYPE_NONE,
644 1,
645 GTK_TYPE_CLIPBOARD);
646 }
647
648 static void
gtk_text_buffer_init(GtkTextBuffer * buffer)649 gtk_text_buffer_init (GtkTextBuffer *buffer)
650 {
651 buffer->priv = gtk_text_buffer_get_instance_private (buffer);
652 buffer->priv->clipboard_contents_buffers = NULL;
653 buffer->priv->tag_table = NULL;
654
655 /* allow copying of arbiatray stuff in the internal rich text format */
656 gtk_text_buffer_register_serialize_tagset (buffer, NULL);
657 }
658
659 static void
set_table(GtkTextBuffer * buffer,GtkTextTagTable * table)660 set_table (GtkTextBuffer *buffer, GtkTextTagTable *table)
661 {
662 GtkTextBufferPrivate *priv = buffer->priv;
663
664 g_return_if_fail (priv->tag_table == NULL);
665
666 if (table)
667 {
668 priv->tag_table = table;
669 g_object_ref (priv->tag_table);
670 _gtk_text_tag_table_add_buffer (table, buffer);
671 }
672 }
673
674 static GtkTextTagTable*
get_table(GtkTextBuffer * buffer)675 get_table (GtkTextBuffer *buffer)
676 {
677 GtkTextBufferPrivate *priv = buffer->priv;
678
679 if (priv->tag_table == NULL)
680 {
681 priv->tag_table = gtk_text_tag_table_new ();
682 _gtk_text_tag_table_add_buffer (priv->tag_table, buffer);
683 }
684
685 return priv->tag_table;
686 }
687
688 static void
gtk_text_buffer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)689 gtk_text_buffer_set_property (GObject *object,
690 guint prop_id,
691 const GValue *value,
692 GParamSpec *pspec)
693 {
694 GtkTextBuffer *text_buffer;
695
696 text_buffer = GTK_TEXT_BUFFER (object);
697
698 switch (prop_id)
699 {
700 case PROP_TAG_TABLE:
701 set_table (text_buffer, g_value_get_object (value));
702 break;
703
704 case PROP_TEXT:
705 gtk_text_buffer_set_text (text_buffer,
706 g_value_get_string (value), -1);
707 break;
708
709 default:
710 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
711 break;
712 }
713 }
714
715 static void
gtk_text_buffer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)716 gtk_text_buffer_get_property (GObject *object,
717 guint prop_id,
718 GValue *value,
719 GParamSpec *pspec)
720 {
721 GtkTextBuffer *text_buffer;
722 GtkTextIter iter;
723
724 text_buffer = GTK_TEXT_BUFFER (object);
725
726 switch (prop_id)
727 {
728 case PROP_TAG_TABLE:
729 g_value_set_object (value, get_table (text_buffer));
730 break;
731
732 case PROP_TEXT:
733 {
734 GtkTextIter start, end;
735
736 gtk_text_buffer_get_start_iter (text_buffer, &start);
737 gtk_text_buffer_get_end_iter (text_buffer, &end);
738
739 g_value_take_string (value,
740 gtk_text_buffer_get_text (text_buffer,
741 &start, &end, FALSE));
742 break;
743 }
744
745 case PROP_HAS_SELECTION:
746 g_value_set_boolean (value, text_buffer->priv->has_selection);
747 break;
748
749 case PROP_CURSOR_POSITION:
750 gtk_text_buffer_get_iter_at_mark (text_buffer, &iter,
751 gtk_text_buffer_get_insert (text_buffer));
752 g_value_set_int (value, gtk_text_iter_get_offset (&iter));
753 break;
754
755 case PROP_COPY_TARGET_LIST:
756 g_value_set_boxed (value, gtk_text_buffer_get_copy_target_list (text_buffer));
757 break;
758
759 case PROP_PASTE_TARGET_LIST:
760 g_value_set_boxed (value, gtk_text_buffer_get_paste_target_list (text_buffer));
761 break;
762
763 default:
764 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
765 break;
766 }
767 }
768
769 static void
gtk_text_buffer_notify(GObject * object,GParamSpec * pspec)770 gtk_text_buffer_notify (GObject *object,
771 GParamSpec *pspec)
772 {
773 if (!strcmp (pspec->name, "copy-target-list") ||
774 !strcmp (pspec->name, "paste-target-list"))
775 {
776 gtk_text_buffer_free_target_lists (GTK_TEXT_BUFFER (object));
777 }
778 }
779
780 /**
781 * gtk_text_buffer_new:
782 * @table: (allow-none): a tag table, or %NULL to create a new one
783 *
784 * Creates a new text buffer.
785 *
786 * Returns: a new text buffer
787 **/
788 GtkTextBuffer*
gtk_text_buffer_new(GtkTextTagTable * table)789 gtk_text_buffer_new (GtkTextTagTable *table)
790 {
791 GtkTextBuffer *text_buffer;
792
793 text_buffer = g_object_new (GTK_TYPE_TEXT_BUFFER, "tag-table", table, NULL);
794
795 return text_buffer;
796 }
797
798 static void
gtk_text_buffer_finalize(GObject * object)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
807 remove_all_selection_clipboards (buffer);
808
809 if (priv->tag_table)
810 {
811 _gtk_text_tag_table_remove_buffer (priv->tag_table, buffer);
812 g_object_unref (priv->tag_table);
813 priv->tag_table = NULL;
814 }
815
816 if (priv->btree)
817 {
818 _gtk_text_btree_unref (priv->btree);
819 priv->btree = NULL;
820 }
821
822 if (priv->log_attr_cache)
823 free_log_attr_cache (priv->log_attr_cache);
824
825 priv->log_attr_cache = NULL;
826
827 gtk_text_buffer_free_target_lists (buffer);
828
829 G_OBJECT_CLASS (gtk_text_buffer_parent_class)->finalize (object);
830 }
831
832 static GtkTextBTree*
get_btree(GtkTextBuffer * buffer)833 get_btree (GtkTextBuffer *buffer)
834 {
835 GtkTextBufferPrivate *priv = buffer->priv;
836
837 if (priv->btree == NULL)
838 priv->btree = _gtk_text_btree_new (gtk_text_buffer_get_tag_table (buffer),
839 buffer);
840
841 return priv->btree;
842 }
843
844 GtkTextBTree*
_gtk_text_buffer_get_btree(GtkTextBuffer * buffer)845 _gtk_text_buffer_get_btree (GtkTextBuffer *buffer)
846 {
847 return get_btree (buffer);
848 }
849
850 /**
851 * gtk_text_buffer_get_tag_table:
852 * @buffer: a #GtkTextBuffer
853 *
854 * Get the #GtkTextTagTable associated with this buffer.
855 *
856 * Returns: (transfer none): the buffer’s tag table
857 **/
858 GtkTextTagTable*
gtk_text_buffer_get_tag_table(GtkTextBuffer * buffer)859 gtk_text_buffer_get_tag_table (GtkTextBuffer *buffer)
860 {
861 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
862
863 return get_table (buffer);
864 }
865
866 /**
867 * gtk_text_buffer_set_text:
868 * @buffer: a #GtkTextBuffer
869 * @text: UTF-8 text to insert
870 * @len: length of @text in bytes
871 *
872 * Deletes current contents of @buffer, and inserts @text instead. If
873 * @len is -1, @text must be nul-terminated. @text must be valid UTF-8.
874 **/
875 void
gtk_text_buffer_set_text(GtkTextBuffer * buffer,const gchar * text,gint len)876 gtk_text_buffer_set_text (GtkTextBuffer *buffer,
877 const gchar *text,
878 gint len)
879 {
880 GtkTextIter start, end;
881
882 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
883 g_return_if_fail (text != NULL);
884
885 if (len < 0)
886 len = strlen (text);
887
888 gtk_text_buffer_get_bounds (buffer, &start, &end);
889
890 gtk_text_buffer_delete (buffer, &start, &end);
891
892 if (len > 0)
893 {
894 gtk_text_buffer_get_iter_at_offset (buffer, &start, 0);
895 gtk_text_buffer_insert (buffer, &start, text, len);
896 }
897 }
898
899
900
901 /*
902 * Insertion
903 */
904
905 static void
gtk_text_buffer_real_insert_text(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len)906 gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
907 GtkTextIter *iter,
908 const gchar *text,
909 gint len)
910 {
911 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
912 g_return_if_fail (iter != NULL);
913
914 _gtk_text_btree_insert (iter, text, len);
915
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
gtk_text_buffer_emit_insert(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len)921 gtk_text_buffer_emit_insert (GtkTextBuffer *buffer,
922 GtkTextIter *iter,
923 const gchar *text,
924 gint len)
925 {
926 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
927 g_return_if_fail (iter != NULL);
928 g_return_if_fail (text != NULL);
929
930 if (len < 0)
931 len = strlen (text);
932
933 g_return_if_fail (g_utf8_validate (text, len, NULL));
934
935 if (len > 0)
936 {
937 g_signal_emit (buffer, signals[INSERT_TEXT], 0,
938 iter, text, len);
939 }
940 }
941
942 /**
943 * gtk_text_buffer_insert:
944 * @buffer: a #GtkTextBuffer
945 * @iter: a position in the buffer
946 * @text: text in UTF-8 format
947 * @len: length of text in bytes, or -1
948 *
949 * Inserts @len bytes of @text at position @iter. If @len is -1,
950 * @text must be nul-terminated and will be inserted in its
951 * entirety. Emits the “insert-text” signal; insertion actually occurs
952 * in the default handler for the signal. @iter is invalidated when
953 * insertion occurs (because the buffer contents change), but the
954 * default signal handler revalidates it to point to the end of the
955 * inserted text.
956 **/
957 void
gtk_text_buffer_insert(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len)958 gtk_text_buffer_insert (GtkTextBuffer *buffer,
959 GtkTextIter *iter,
960 const gchar *text,
961 gint len)
962 {
963 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
964 g_return_if_fail (iter != NULL);
965 g_return_if_fail (text != NULL);
966 g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
967
968 gtk_text_buffer_emit_insert (buffer, iter, text, len);
969 }
970
971 /**
972 * gtk_text_buffer_insert_at_cursor:
973 * @buffer: a #GtkTextBuffer
974 * @text: text in UTF-8 format
975 * @len: length of text, in bytes
976 *
977 * Simply calls gtk_text_buffer_insert(), using the current
978 * cursor position as the insertion point.
979 **/
980 void
gtk_text_buffer_insert_at_cursor(GtkTextBuffer * buffer,const gchar * text,gint len)981 gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
982 const gchar *text,
983 gint len)
984 {
985 GtkTextIter iter;
986
987 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
988 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
996 /**
997 * gtk_text_buffer_insert_interactive:
998 * @buffer: a #GtkTextBuffer
999 * @iter: a position in @buffer
1000 * @text: some UTF-8 text
1001 * @len: length of text in bytes, or -1
1002 * @default_editable: default editability of buffer
1003 *
1004 * Like gtk_text_buffer_insert(), but the insertion will not occur if
1005 * @iter is at a non-editable location in the buffer. Usually you
1006 * want to prevent insertions at ineditable locations if the insertion
1007 * results from a user action (is interactive).
1008 *
1009 * @default_editable indicates the editability of text that doesn't
1010 * have a tag affecting editability applied to it. Typically the
1011 * result of gtk_text_view_get_editable() is appropriate here.
1012 *
1013 * Returns: whether text was actually inserted
1014 **/
1015 gboolean
gtk_text_buffer_insert_interactive(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len,gboolean default_editable)1016 gtk_text_buffer_insert_interactive (GtkTextBuffer *buffer,
1017 GtkTextIter *iter,
1018 const gchar *text,
1019 gint len,
1020 gboolean default_editable)
1021 {
1022 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1023 g_return_val_if_fail (text != NULL, FALSE);
1024 g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, FALSE);
1025
1026 if (gtk_text_iter_can_insert (iter, default_editable))
1027 {
1028 gtk_text_buffer_begin_user_action (buffer);
1029 gtk_text_buffer_emit_insert (buffer, iter, text, len);
1030 gtk_text_buffer_end_user_action (buffer);
1031 return TRUE;
1032 }
1033 else
1034 return FALSE;
1035 }
1036
1037 /**
1038 * gtk_text_buffer_insert_interactive_at_cursor:
1039 * @buffer: a #GtkTextBuffer
1040 * @text: text in UTF-8 format
1041 * @len: length of text in bytes, or -1
1042 * @default_editable: default editability of buffer
1043 *
1044 * Calls gtk_text_buffer_insert_interactive() at the cursor
1045 * position.
1046 *
1047 * @default_editable indicates the editability of text that doesn't
1048 * have a tag affecting editability applied to it. Typically the
1049 * result of gtk_text_view_get_editable() is appropriate here.
1050 *
1051 * Returns: whether text was actually inserted
1052 **/
1053 gboolean
gtk_text_buffer_insert_interactive_at_cursor(GtkTextBuffer * buffer,const gchar * text,gint len,gboolean default_editable)1054 gtk_text_buffer_insert_interactive_at_cursor (GtkTextBuffer *buffer,
1055 const gchar *text,
1056 gint len,
1057 gboolean default_editable)
1058 {
1059 GtkTextIter iter;
1060
1061 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1062 g_return_val_if_fail (text != NULL, FALSE);
1063
1064 gtk_text_buffer_get_iter_at_mark (buffer, &iter,
1065 gtk_text_buffer_get_insert (buffer));
1066
1067 return gtk_text_buffer_insert_interactive (buffer, &iter, text, len,
1068 default_editable);
1069 }
1070
1071 static gboolean
possibly_not_text(gunichar ch,gpointer user_data)1072 possibly_not_text (gunichar ch,
1073 gpointer user_data)
1074 {
1075 return ch == GTK_TEXT_UNKNOWN_CHAR;
1076 }
1077
1078 static void
insert_text_range(GtkTextBuffer * buffer,GtkTextIter * iter,const GtkTextIter * orig_start,const GtkTextIter * orig_end,gboolean interactive)1079 insert_text_range (GtkTextBuffer *buffer,
1080 GtkTextIter *iter,
1081 const GtkTextIter *orig_start,
1082 const GtkTextIter *orig_end,
1083 gboolean interactive)
1084 {
1085 gchar *text;
1086
1087 text = gtk_text_iter_get_text (orig_start, orig_end);
1088
1089 gtk_text_buffer_emit_insert (buffer, iter, text, -1);
1090
1091 g_free (text);
1092 }
1093
1094 typedef struct _Range Range;
1095 struct _Range
1096 {
1097 GtkTextBuffer *buffer;
1098 GtkTextMark *start_mark;
1099 GtkTextMark *end_mark;
1100 GtkTextMark *whole_end_mark;
1101 GtkTextIter *range_start;
1102 GtkTextIter *range_end;
1103 GtkTextIter *whole_end;
1104 };
1105
1106 static Range*
save_range(GtkTextIter * range_start,GtkTextIter * range_end,GtkTextIter * whole_end)1107 save_range (GtkTextIter *range_start,
1108 GtkTextIter *range_end,
1109 GtkTextIter *whole_end)
1110 {
1111 Range *r;
1112
1113 r = g_slice_new (Range);
1114
1115 r->buffer = gtk_text_iter_get_buffer (range_start);
1116 g_object_ref (r->buffer);
1117
1118 r->start_mark =
1119 gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
1120 NULL,
1121 range_start,
1122 FALSE);
1123 r->end_mark =
1124 gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
1125 NULL,
1126 range_end,
1127 TRUE);
1128
1129 r->whole_end_mark =
1130 gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (range_start),
1131 NULL,
1132 whole_end,
1133 TRUE);
1134
1135 r->range_start = range_start;
1136 r->range_end = range_end;
1137 r->whole_end = whole_end;
1138
1139 return r;
1140 }
1141
1142 static void
restore_range(Range * r)1143 restore_range (Range *r)
1144 {
1145 gtk_text_buffer_get_iter_at_mark (r->buffer,
1146 r->range_start,
1147 r->start_mark);
1148
1149 gtk_text_buffer_get_iter_at_mark (r->buffer,
1150 r->range_end,
1151 r->end_mark);
1152
1153 gtk_text_buffer_get_iter_at_mark (r->buffer,
1154 r->whole_end,
1155 r->whole_end_mark);
1156
1157 gtk_text_buffer_delete_mark (r->buffer, r->start_mark);
1158 gtk_text_buffer_delete_mark (r->buffer, r->end_mark);
1159 gtk_text_buffer_delete_mark (r->buffer, r->whole_end_mark);
1160
1161 /* Due to the gravities on the marks, the ordering could have
1162 * gotten mangled; we switch to an empty range in that
1163 * case
1164 */
1165
1166 if (gtk_text_iter_compare (r->range_start, r->range_end) > 0)
1167 *r->range_start = *r->range_end;
1168
1169 if (gtk_text_iter_compare (r->range_end, r->whole_end) > 0)
1170 *r->range_end = *r->whole_end;
1171
1172 g_object_unref (r->buffer);
1173 g_slice_free (Range, r);
1174 }
1175
1176 static void
insert_range_untagged(GtkTextBuffer * buffer,GtkTextIter * iter,const GtkTextIter * orig_start,const GtkTextIter * orig_end,gboolean interactive)1177 insert_range_untagged (GtkTextBuffer *buffer,
1178 GtkTextIter *iter,
1179 const GtkTextIter *orig_start,
1180 const GtkTextIter *orig_end,
1181 gboolean interactive)
1182 {
1183 GtkTextIter range_start;
1184 GtkTextIter range_end;
1185 GtkTextIter start, end;
1186 Range *r;
1187
1188 if (gtk_text_iter_equal (orig_start, orig_end))
1189 return;
1190
1191 start = *orig_start;
1192 end = *orig_end;
1193
1194 range_start = start;
1195 range_end = start;
1196
1197 while (TRUE)
1198 {
1199 if (gtk_text_iter_equal (&range_start, &range_end))
1200 {
1201 /* Figure out how to move forward */
1202
1203 g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
1204
1205 if (gtk_text_iter_equal (&range_end, &end))
1206 {
1207 /* nothing left to do */
1208 break;
1209 }
1210 else if (gtk_text_iter_get_char (&range_end) == GTK_TEXT_UNKNOWN_CHAR)
1211 {
1212 GdkPixbuf *pixbuf = NULL;
1213 GtkTextChildAnchor *anchor = NULL;
1214 pixbuf = gtk_text_iter_get_pixbuf (&range_end);
1215 anchor = gtk_text_iter_get_child_anchor (&range_end);
1216
1217 if (pixbuf)
1218 {
1219 r = save_range (&range_start,
1220 &range_end,
1221 &end);
1222
1223 gtk_text_buffer_insert_pixbuf (buffer,
1224 iter,
1225 pixbuf);
1226
1227 restore_range (r);
1228 r = NULL;
1229
1230 gtk_text_iter_forward_char (&range_end);
1231
1232 range_start = range_end;
1233 }
1234 else if (anchor)
1235 {
1236 /* Just skip anchors */
1237
1238 gtk_text_iter_forward_char (&range_end);
1239 range_start = range_end;
1240 }
1241 else
1242 {
1243 /* The GTK_TEXT_UNKNOWN_CHAR was in a text segment, so
1244 * keep going.
1245 */
1246 gtk_text_iter_forward_find_char (&range_end,
1247 possibly_not_text, NULL,
1248 &end);
1249
1250 g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
1251 }
1252 }
1253 else
1254 {
1255 /* Text segment starts here, so forward search to
1256 * find its possible endpoint
1257 */
1258 gtk_text_iter_forward_find_char (&range_end,
1259 possibly_not_text, NULL,
1260 &end);
1261
1262 g_assert (gtk_text_iter_compare (&range_end, &end) <= 0);
1263 }
1264 }
1265 else
1266 {
1267 r = save_range (&range_start,
1268 &range_end,
1269 &end);
1270
1271 insert_text_range (buffer,
1272 iter,
1273 &range_start,
1274 &range_end,
1275 interactive);
1276
1277 restore_range (r);
1278 r = NULL;
1279
1280 range_start = range_end;
1281 }
1282 }
1283 }
1284
1285 static void
insert_range_not_inside_self(GtkTextBuffer * buffer,GtkTextIter * iter,const GtkTextIter * orig_start,const GtkTextIter * orig_end,gboolean interactive)1286 insert_range_not_inside_self (GtkTextBuffer *buffer,
1287 GtkTextIter *iter,
1288 const GtkTextIter *orig_start,
1289 const GtkTextIter *orig_end,
1290 gboolean interactive)
1291 {
1292 /* Find each range of uniformly-tagged text, insert it,
1293 * then apply the tags.
1294 */
1295 GtkTextIter start = *orig_start;
1296 GtkTextIter end = *orig_end;
1297 GtkTextIter range_start;
1298 GtkTextIter range_end;
1299
1300 if (gtk_text_iter_equal (orig_start, orig_end))
1301 return;
1302
1303 gtk_text_iter_order (&start, &end);
1304
1305 range_start = start;
1306 range_end = start;
1307
1308 while (TRUE)
1309 {
1310 gint start_offset;
1311 GtkTextIter start_iter;
1312 GSList *tags;
1313 GSList *tmp_list;
1314 Range *r;
1315
1316 if (gtk_text_iter_equal (&range_start, &end))
1317 break; /* All done */
1318
1319 g_assert (gtk_text_iter_compare (&range_start, &end) < 0);
1320
1321 gtk_text_iter_forward_to_tag_toggle (&range_end, NULL);
1322
1323 g_assert (!gtk_text_iter_equal (&range_start, &range_end));
1324
1325 /* Clamp to the end iterator */
1326 if (gtk_text_iter_compare (&range_end, &end) > 0)
1327 range_end = end;
1328
1329 /* We have a range with unique tags; insert it, and
1330 * apply all tags.
1331 */
1332 start_offset = gtk_text_iter_get_offset (iter);
1333
1334 r = save_range (&range_start, &range_end, &end);
1335
1336 insert_range_untagged (buffer, iter, &range_start, &range_end, interactive);
1337
1338 restore_range (r);
1339 r = NULL;
1340
1341 gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
1342
1343 tags = gtk_text_iter_get_tags (&range_start);
1344 tmp_list = tags;
1345 while (tmp_list != NULL)
1346 {
1347 gtk_text_buffer_apply_tag (buffer,
1348 tmp_list->data,
1349 &start_iter,
1350 iter);
1351
1352 tmp_list = tmp_list->next;
1353 }
1354 g_slist_free (tags);
1355
1356 range_start = range_end;
1357 }
1358 }
1359
1360 static void
gtk_text_buffer_real_insert_range(GtkTextBuffer * buffer,GtkTextIter * iter,const GtkTextIter * orig_start,const GtkTextIter * orig_end,gboolean interactive)1361 gtk_text_buffer_real_insert_range (GtkTextBuffer *buffer,
1362 GtkTextIter *iter,
1363 const GtkTextIter *orig_start,
1364 const GtkTextIter *orig_end,
1365 gboolean interactive)
1366 {
1367 GtkTextBuffer *src_buffer;
1368
1369 /* Find each range of uniformly-tagged text, insert it,
1370 * then apply the tags.
1371 */
1372 if (gtk_text_iter_equal (orig_start, orig_end))
1373 return;
1374
1375 if (interactive)
1376 gtk_text_buffer_begin_user_action (buffer);
1377
1378 src_buffer = gtk_text_iter_get_buffer (orig_start);
1379
1380 if (gtk_text_iter_get_buffer (iter) != src_buffer ||
1381 !gtk_text_iter_in_range (iter, orig_start, orig_end))
1382 {
1383 insert_range_not_inside_self (buffer, iter, orig_start, orig_end, interactive);
1384 }
1385 else
1386 {
1387 /* If you insert a range into itself, it could loop infinitely
1388 * because the region being copied keeps growing as we insert. So
1389 * we have to separately copy the range before and after
1390 * the insertion point.
1391 */
1392 GtkTextIter start = *orig_start;
1393 GtkTextIter end = *orig_end;
1394 GtkTextIter range_start;
1395 GtkTextIter range_end;
1396 Range *first_half;
1397 Range *second_half;
1398
1399 gtk_text_iter_order (&start, &end);
1400
1401 range_start = start;
1402 range_end = *iter;
1403 first_half = save_range (&range_start, &range_end, &end);
1404
1405 range_start = *iter;
1406 range_end = end;
1407 second_half = save_range (&range_start, &range_end, &end);
1408
1409 restore_range (first_half);
1410 insert_range_not_inside_self (buffer, iter, &range_start, &range_end, interactive);
1411
1412 restore_range (second_half);
1413 insert_range_not_inside_self (buffer, iter, &range_start, &range_end, interactive);
1414 }
1415
1416 if (interactive)
1417 gtk_text_buffer_end_user_action (buffer);
1418 }
1419
1420 /**
1421 * gtk_text_buffer_insert_range:
1422 * @buffer: a #GtkTextBuffer
1423 * @iter: a position in @buffer
1424 * @start: a position in a #GtkTextBuffer
1425 * @end: another position in the same buffer as @start
1426 *
1427 * Copies text, tags, and pixbufs between @start and @end (the order
1428 * of @start and @end doesn’t matter) and inserts the copy at @iter.
1429 * Used instead of simply getting/inserting text because it preserves
1430 * images and tags. If @start and @end are in a different buffer from
1431 * @buffer, the two buffers must share the same tag table.
1432 *
1433 * Implemented via emissions of the insert_text and apply_tag signals,
1434 * so expect those.
1435 **/
1436 void
gtk_text_buffer_insert_range(GtkTextBuffer * buffer,GtkTextIter * iter,const GtkTextIter * start,const GtkTextIter * end)1437 gtk_text_buffer_insert_range (GtkTextBuffer *buffer,
1438 GtkTextIter *iter,
1439 const GtkTextIter *start,
1440 const GtkTextIter *end)
1441 {
1442 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1443 g_return_if_fail (iter != NULL);
1444 g_return_if_fail (start != NULL);
1445 g_return_if_fail (end != NULL);
1446 g_return_if_fail (gtk_text_iter_get_buffer (start) ==
1447 gtk_text_iter_get_buffer (end));
1448 g_return_if_fail (gtk_text_iter_get_buffer (start)->priv->tag_table ==
1449 buffer->priv->tag_table);
1450 g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1451
1452 gtk_text_buffer_real_insert_range (buffer, iter, start, end, FALSE);
1453 }
1454
1455 /**
1456 * gtk_text_buffer_insert_range_interactive:
1457 * @buffer: a #GtkTextBuffer
1458 * @iter: a position in @buffer
1459 * @start: a position in a #GtkTextBuffer
1460 * @end: another position in the same buffer as @start
1461 * @default_editable: default editability of the buffer
1462 *
1463 * Same as gtk_text_buffer_insert_range(), but does nothing if the
1464 * insertion point isn’t editable. The @default_editable parameter
1465 * indicates whether the text is editable at @iter if no tags
1466 * enclosing @iter affect editability. Typically the result of
1467 * gtk_text_view_get_editable() is appropriate here.
1468 *
1469 * Returns: whether an insertion was possible at @iter
1470 **/
1471 gboolean
gtk_text_buffer_insert_range_interactive(GtkTextBuffer * buffer,GtkTextIter * iter,const GtkTextIter * start,const GtkTextIter * end,gboolean default_editable)1472 gtk_text_buffer_insert_range_interactive (GtkTextBuffer *buffer,
1473 GtkTextIter *iter,
1474 const GtkTextIter *start,
1475 const GtkTextIter *end,
1476 gboolean default_editable)
1477 {
1478 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1479 g_return_val_if_fail (iter != NULL, FALSE);
1480 g_return_val_if_fail (start != NULL, FALSE);
1481 g_return_val_if_fail (end != NULL, FALSE);
1482 g_return_val_if_fail (gtk_text_iter_get_buffer (start) ==
1483 gtk_text_iter_get_buffer (end), FALSE);
1484 g_return_val_if_fail (gtk_text_iter_get_buffer (start)->priv->tag_table ==
1485 buffer->priv->tag_table, FALSE);
1486
1487 if (gtk_text_iter_can_insert (iter, default_editable))
1488 {
1489 gtk_text_buffer_real_insert_range (buffer, iter, start, end, TRUE);
1490 return TRUE;
1491 }
1492 else
1493 return FALSE;
1494 }
1495
1496 /**
1497 * gtk_text_buffer_insert_with_tags:
1498 * @buffer: a #GtkTextBuffer
1499 * @iter: an iterator in @buffer
1500 * @text: UTF-8 text
1501 * @len: length of @text, or -1
1502 * @first_tag: first tag to apply to @text
1503 * @...: %NULL-terminated list of tags to apply
1504 *
1505 * Inserts @text into @buffer at @iter, applying the list of tags to
1506 * the newly-inserted text. The last tag specified must be %NULL to
1507 * terminate the list. Equivalent to calling gtk_text_buffer_insert(),
1508 * then gtk_text_buffer_apply_tag() on the inserted text;
1509 * gtk_text_buffer_insert_with_tags() is just a convenience function.
1510 **/
1511 void
gtk_text_buffer_insert_with_tags(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len,GtkTextTag * first_tag,...)1512 gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer,
1513 GtkTextIter *iter,
1514 const gchar *text,
1515 gint len,
1516 GtkTextTag *first_tag,
1517 ...)
1518 {
1519 gint start_offset;
1520 GtkTextIter start;
1521 va_list args;
1522 GtkTextTag *tag;
1523
1524 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1525 g_return_if_fail (iter != NULL);
1526 g_return_if_fail (text != NULL);
1527 g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1528
1529 start_offset = gtk_text_iter_get_offset (iter);
1530
1531 gtk_text_buffer_insert (buffer, iter, text, len);
1532
1533 if (first_tag == NULL)
1534 return;
1535
1536 gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1537
1538 va_start (args, first_tag);
1539 tag = first_tag;
1540 while (tag)
1541 {
1542 gtk_text_buffer_apply_tag (buffer, tag, &start, iter);
1543
1544 tag = va_arg (args, GtkTextTag*);
1545 }
1546
1547 va_end (args);
1548 }
1549
1550 /**
1551 * gtk_text_buffer_insert_with_tags_by_name:
1552 * @buffer: a #GtkTextBuffer
1553 * @iter: position in @buffer
1554 * @text: UTF-8 text
1555 * @len: length of @text, or -1
1556 * @first_tag_name: name of a tag to apply to @text
1557 * @...: more tag names
1558 *
1559 * Same as gtk_text_buffer_insert_with_tags(), but allows you
1560 * to pass in tag names instead of tag objects.
1561 **/
1562 void
gtk_text_buffer_insert_with_tags_by_name(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len,const gchar * first_tag_name,...)1563 gtk_text_buffer_insert_with_tags_by_name (GtkTextBuffer *buffer,
1564 GtkTextIter *iter,
1565 const gchar *text,
1566 gint len,
1567 const gchar *first_tag_name,
1568 ...)
1569 {
1570 gint start_offset;
1571 GtkTextIter start;
1572 va_list args;
1573 const gchar *tag_name;
1574
1575 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1576 g_return_if_fail (iter != NULL);
1577 g_return_if_fail (text != NULL);
1578 g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1579
1580 start_offset = gtk_text_iter_get_offset (iter);
1581
1582 gtk_text_buffer_insert (buffer, iter, text, len);
1583
1584 if (first_tag_name == NULL)
1585 return;
1586
1587 gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
1588
1589 va_start (args, first_tag_name);
1590 tag_name = first_tag_name;
1591 while (tag_name)
1592 {
1593 GtkTextTag *tag;
1594
1595 tag = gtk_text_tag_table_lookup (buffer->priv->tag_table,
1596 tag_name);
1597
1598 if (tag == NULL)
1599 {
1600 g_warning ("%s: no tag with name '%s'!", G_STRLOC, tag_name);
1601 va_end (args);
1602 return;
1603 }
1604
1605 gtk_text_buffer_apply_tag (buffer, tag, &start, iter);
1606
1607 tag_name = va_arg (args, const gchar*);
1608 }
1609
1610 va_end (args);
1611 }
1612
1613
1614 /*
1615 * Deletion
1616 */
1617
1618 static void
gtk_text_buffer_real_delete_range(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end)1619 gtk_text_buffer_real_delete_range (GtkTextBuffer *buffer,
1620 GtkTextIter *start,
1621 GtkTextIter *end)
1622 {
1623 gboolean has_selection;
1624
1625 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1626 g_return_if_fail (start != NULL);
1627 g_return_if_fail (end != NULL);
1628
1629 _gtk_text_btree_delete (start, end);
1630
1631 /* may have deleted the selection... */
1632 update_selection_clipboards (buffer);
1633
1634 has_selection = gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL);
1635 if (has_selection != buffer->priv->has_selection)
1636 {
1637 buffer->priv->has_selection = has_selection;
1638 g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_HAS_SELECTION]);
1639 }
1640
1641 g_signal_emit (buffer, signals[CHANGED], 0);
1642 g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_CURSOR_POSITION]);
1643 }
1644
1645 static void
gtk_text_buffer_emit_delete(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end)1646 gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
1647 GtkTextIter *start,
1648 GtkTextIter *end)
1649 {
1650 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1651 g_return_if_fail (start != NULL);
1652 g_return_if_fail (end != NULL);
1653
1654 if (gtk_text_iter_equal (start, end))
1655 return;
1656
1657 gtk_text_iter_order (start, end);
1658
1659 g_signal_emit (buffer,
1660 signals[DELETE_RANGE],
1661 0,
1662 start, end);
1663 }
1664
1665 /**
1666 * gtk_text_buffer_delete:
1667 * @buffer: a #GtkTextBuffer
1668 * @start: a position in @buffer
1669 * @end: another position in @buffer
1670 *
1671 * Deletes text between @start and @end. The order of @start and @end
1672 * is not actually relevant; gtk_text_buffer_delete() will reorder
1673 * them. This function actually emits the “delete-range” signal, and
1674 * the default handler of that signal deletes the text. Because the
1675 * buffer is modified, all outstanding iterators become invalid after
1676 * calling this function; however, the @start and @end will be
1677 * re-initialized to point to the location where text was deleted.
1678 **/
1679 void
gtk_text_buffer_delete(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end)1680 gtk_text_buffer_delete (GtkTextBuffer *buffer,
1681 GtkTextIter *start,
1682 GtkTextIter *end)
1683 {
1684 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1685 g_return_if_fail (start != NULL);
1686 g_return_if_fail (end != NULL);
1687 g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
1688 g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
1689
1690 gtk_text_buffer_emit_delete (buffer, start, end);
1691 }
1692
1693 /**
1694 * gtk_text_buffer_delete_interactive:
1695 * @buffer: a #GtkTextBuffer
1696 * @start_iter: start of range to delete
1697 * @end_iter: end of range
1698 * @default_editable: whether the buffer is editable by default
1699 *
1700 * Deletes all editable text in the given range.
1701 * Calls gtk_text_buffer_delete() for each editable sub-range of
1702 * [@start,@end). @start and @end are revalidated to point to
1703 * the location of the last deleted range, or left untouched if
1704 * no text was deleted.
1705 *
1706 * Returns: whether some text was actually deleted
1707 **/
1708 gboolean
gtk_text_buffer_delete_interactive(GtkTextBuffer * buffer,GtkTextIter * start_iter,GtkTextIter * end_iter,gboolean default_editable)1709 gtk_text_buffer_delete_interactive (GtkTextBuffer *buffer,
1710 GtkTextIter *start_iter,
1711 GtkTextIter *end_iter,
1712 gboolean default_editable)
1713 {
1714 GtkTextMark *end_mark;
1715 GtkTextMark *start_mark;
1716 GtkTextIter iter;
1717 gboolean current_state;
1718 gboolean deleted_stuff = FALSE;
1719
1720 /* Delete all editable text in the range start_iter, end_iter */
1721
1722 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
1723 g_return_val_if_fail (start_iter != NULL, FALSE);
1724 g_return_val_if_fail (end_iter != NULL, FALSE);
1725 g_return_val_if_fail (gtk_text_iter_get_buffer (start_iter) == buffer, FALSE);
1726 g_return_val_if_fail (gtk_text_iter_get_buffer (end_iter) == buffer, FALSE);
1727
1728
1729 gtk_text_buffer_begin_user_action (buffer);
1730
1731 gtk_text_iter_order (start_iter, end_iter);
1732
1733 start_mark = gtk_text_buffer_create_mark (buffer, NULL,
1734 start_iter, TRUE);
1735 end_mark = gtk_text_buffer_create_mark (buffer, NULL,
1736 end_iter, FALSE);
1737
1738 gtk_text_buffer_get_iter_at_mark (buffer, &iter, start_mark);
1739
1740 current_state = gtk_text_iter_editable (&iter, default_editable);
1741
1742 while (TRUE)
1743 {
1744 gboolean new_state;
1745 gboolean done = FALSE;
1746 GtkTextIter end;
1747
1748 gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
1749
1750 gtk_text_buffer_get_iter_at_mark (buffer, &end, end_mark);
1751
1752 if (gtk_text_iter_compare (&iter, &end) >= 0)
1753 {
1754 done = TRUE;
1755 iter = end; /* clamp to the last boundary */
1756 }
1757
1758 new_state = gtk_text_iter_editable (&iter, default_editable);
1759
1760 if (current_state == new_state)
1761 {
1762 if (done)
1763 {
1764 if (current_state)
1765 {
1766 /* We're ending an editable region. Delete said region. */
1767 GtkTextIter start;
1768
1769 gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1770
1771 gtk_text_buffer_emit_delete (buffer, &start, &iter);
1772
1773 deleted_stuff = TRUE;
1774
1775 /* revalidate user's iterators. */
1776 *start_iter = start;
1777 *end_iter = iter;
1778 }
1779
1780 break;
1781 }
1782 else
1783 continue;
1784 }
1785
1786 if (current_state && !new_state)
1787 {
1788 /* End of an editable region. Delete it. */
1789 GtkTextIter start;
1790
1791 gtk_text_buffer_get_iter_at_mark (buffer, &start, start_mark);
1792
1793 gtk_text_buffer_emit_delete (buffer, &start, &iter);
1794
1795 /* It's more robust to ask for the state again then to assume that
1796 * we're on the next not-editable segment. We don't know what the
1797 * ::delete-range handler did.... maybe it deleted the following
1798 * not-editable segment because it was associated with the editable
1799 * segment.
1800 */
1801 current_state = gtk_text_iter_editable (&iter, default_editable);
1802 deleted_stuff = TRUE;
1803
1804 /* revalidate user's iterators. */
1805 *start_iter = start;
1806 *end_iter = iter;
1807 }
1808 else
1809 {
1810 /* We are at the start of an editable region. We won't be deleting
1811 * the previous region. Move start mark to start of this region.
1812 */
1813
1814 g_assert (!current_state && new_state);
1815
1816 gtk_text_buffer_move_mark (buffer, start_mark, &iter);
1817
1818 current_state = TRUE;
1819 }
1820
1821 if (done)
1822 break;
1823 }
1824
1825 gtk_text_buffer_delete_mark (buffer, start_mark);
1826 gtk_text_buffer_delete_mark (buffer, end_mark);
1827
1828 gtk_text_buffer_end_user_action (buffer);
1829
1830 return deleted_stuff;
1831 }
1832
1833 /*
1834 * Extracting textual buffer contents
1835 */
1836
1837 /**
1838 * gtk_text_buffer_get_text:
1839 * @buffer: a #GtkTextBuffer
1840 * @start: start of a range
1841 * @end: end of a range
1842 * @include_hidden_chars: whether to include invisible text
1843 *
1844 * Returns the text in the range [@start,@end). Excludes undisplayed
1845 * text (text marked with tags that set the invisibility attribute) if
1846 * @include_hidden_chars is %FALSE. Does not include characters
1847 * representing embedded images, so byte and character indexes into
1848 * the returned string do not correspond to byte
1849 * and character indexes into the buffer. Contrast with
1850 * gtk_text_buffer_get_slice().
1851 *
1852 * Returns: (transfer full): an allocated UTF-8 string
1853 **/
1854 gchar*
gtk_text_buffer_get_text(GtkTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gboolean include_hidden_chars)1855 gtk_text_buffer_get_text (GtkTextBuffer *buffer,
1856 const GtkTextIter *start,
1857 const GtkTextIter *end,
1858 gboolean include_hidden_chars)
1859 {
1860 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1861 g_return_val_if_fail (start != NULL, NULL);
1862 g_return_val_if_fail (end != NULL, NULL);
1863 g_return_val_if_fail (gtk_text_iter_get_buffer (start) == buffer, NULL);
1864 g_return_val_if_fail (gtk_text_iter_get_buffer (end) == buffer, NULL);
1865
1866 if (include_hidden_chars)
1867 return gtk_text_iter_get_text (start, end);
1868 else
1869 return gtk_text_iter_get_visible_text (start, end);
1870 }
1871
1872 /**
1873 * gtk_text_buffer_get_slice:
1874 * @buffer: a #GtkTextBuffer
1875 * @start: start of a range
1876 * @end: end of a range
1877 * @include_hidden_chars: whether to include invisible text
1878 *
1879 * Returns the text in the range [@start,@end). Excludes undisplayed
1880 * text (text marked with tags that set the invisibility attribute) if
1881 * @include_hidden_chars is %FALSE. The returned string includes a
1882 * 0xFFFC character whenever the buffer contains
1883 * embedded images, so byte and character indexes into
1884 * the returned string do correspond to byte
1885 * and character indexes into the buffer. Contrast with
1886 * gtk_text_buffer_get_text(). Note that 0xFFFC can occur in normal
1887 * text as well, so it is not a reliable indicator that a pixbuf or
1888 * widget is in the buffer.
1889 *
1890 * Returns: (transfer full): an allocated UTF-8 string
1891 **/
1892 gchar*
gtk_text_buffer_get_slice(GtkTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gboolean include_hidden_chars)1893 gtk_text_buffer_get_slice (GtkTextBuffer *buffer,
1894 const GtkTextIter *start,
1895 const GtkTextIter *end,
1896 gboolean include_hidden_chars)
1897 {
1898 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
1899 g_return_val_if_fail (start != NULL, NULL);
1900 g_return_val_if_fail (end != NULL, NULL);
1901 g_return_val_if_fail (gtk_text_iter_get_buffer (start) == buffer, NULL);
1902 g_return_val_if_fail (gtk_text_iter_get_buffer (end) == buffer, NULL);
1903
1904 if (include_hidden_chars)
1905 return gtk_text_iter_get_slice (start, end);
1906 else
1907 return gtk_text_iter_get_visible_slice (start, end);
1908 }
1909
1910 /*
1911 * Pixbufs
1912 */
1913
1914 static void
gtk_text_buffer_real_insert_pixbuf(GtkTextBuffer * buffer,GtkTextIter * iter,GdkPixbuf * pixbuf)1915 gtk_text_buffer_real_insert_pixbuf (GtkTextBuffer *buffer,
1916 GtkTextIter *iter,
1917 GdkPixbuf *pixbuf)
1918 {
1919 _gtk_text_btree_insert_pixbuf (iter, pixbuf);
1920
1921 g_signal_emit (buffer, signals[CHANGED], 0);
1922 }
1923
1924 /**
1925 * gtk_text_buffer_insert_pixbuf:
1926 * @buffer: a #GtkTextBuffer
1927 * @iter: location to insert the pixbuf
1928 * @pixbuf: a #GdkPixbuf
1929 *
1930 * Inserts an image into the text buffer at @iter. The image will be
1931 * counted as one character in character counts, and when obtaining
1932 * the buffer contents as a string, will be represented by the Unicode
1933 * “object replacement character” 0xFFFC. Note that the “slice”
1934 * variants for obtaining portions of the buffer as a string include
1935 * this character for pixbufs, but the “text” variants do
1936 * not. e.g. see gtk_text_buffer_get_slice() and
1937 * gtk_text_buffer_get_text().
1938 **/
1939 void
gtk_text_buffer_insert_pixbuf(GtkTextBuffer * buffer,GtkTextIter * iter,GdkPixbuf * pixbuf)1940 gtk_text_buffer_insert_pixbuf (GtkTextBuffer *buffer,
1941 GtkTextIter *iter,
1942 GdkPixbuf *pixbuf)
1943 {
1944 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1945 g_return_if_fail (iter != NULL);
1946 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1947 g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1948
1949 g_signal_emit (buffer, signals[INSERT_PIXBUF], 0,
1950 iter, pixbuf);
1951 }
1952
1953 /*
1954 * Child anchor
1955 */
1956
1957
1958 static void
gtk_text_buffer_real_insert_anchor(GtkTextBuffer * buffer,GtkTextIter * iter,GtkTextChildAnchor * anchor)1959 gtk_text_buffer_real_insert_anchor (GtkTextBuffer *buffer,
1960 GtkTextIter *iter,
1961 GtkTextChildAnchor *anchor)
1962 {
1963 _gtk_text_btree_insert_child_anchor (iter, anchor);
1964
1965 g_signal_emit (buffer, signals[CHANGED], 0);
1966 }
1967
1968 /**
1969 * gtk_text_buffer_insert_child_anchor:
1970 * @buffer: a #GtkTextBuffer
1971 * @iter: location to insert the anchor
1972 * @anchor: a #GtkTextChildAnchor
1973 *
1974 * Inserts a child widget anchor into the text buffer at @iter. The
1975 * anchor will be counted as one character in character counts, and
1976 * when obtaining the buffer contents as a string, will be represented
1977 * by the Unicode “object replacement character” 0xFFFC. Note that the
1978 * “slice” variants for obtaining portions of the buffer as a string
1979 * include this character for child anchors, but the “text” variants do
1980 * not. E.g. see gtk_text_buffer_get_slice() and
1981 * gtk_text_buffer_get_text(). Consider
1982 * gtk_text_buffer_create_child_anchor() as a more convenient
1983 * alternative to this function. The buffer will add a reference to
1984 * the anchor, so you can unref it after insertion.
1985 **/
1986 void
gtk_text_buffer_insert_child_anchor(GtkTextBuffer * buffer,GtkTextIter * iter,GtkTextChildAnchor * anchor)1987 gtk_text_buffer_insert_child_anchor (GtkTextBuffer *buffer,
1988 GtkTextIter *iter,
1989 GtkTextChildAnchor *anchor)
1990 {
1991 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
1992 g_return_if_fail (iter != NULL);
1993 g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
1994 g_return_if_fail (gtk_text_iter_get_buffer (iter) == buffer);
1995
1996 g_signal_emit (buffer, signals[INSERT_CHILD_ANCHOR], 0,
1997 iter, anchor);
1998 }
1999
2000 /**
2001 * gtk_text_buffer_create_child_anchor:
2002 * @buffer: a #GtkTextBuffer
2003 * @iter: location in the buffer
2004 *
2005 * This is a convenience function which simply creates a child anchor
2006 * with gtk_text_child_anchor_new() and inserts it into the buffer
2007 * with gtk_text_buffer_insert_child_anchor(). The new anchor is
2008 * owned by the buffer; no reference count is returned to
2009 * the caller of gtk_text_buffer_create_child_anchor().
2010 *
2011 * Returns: (transfer none): the created child anchor
2012 **/
2013 GtkTextChildAnchor*
gtk_text_buffer_create_child_anchor(GtkTextBuffer * buffer,GtkTextIter * iter)2014 gtk_text_buffer_create_child_anchor (GtkTextBuffer *buffer,
2015 GtkTextIter *iter)
2016 {
2017 GtkTextChildAnchor *anchor;
2018
2019 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
2020 g_return_val_if_fail (iter != NULL, NULL);
2021 g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, NULL);
2022
2023 anchor = gtk_text_child_anchor_new ();
2024
2025 gtk_text_buffer_insert_child_anchor (buffer, iter, anchor);
2026
2027 g_object_unref (anchor);
2028
2029 return anchor;
2030 }
2031
2032 /*
2033 * Mark manipulation
2034 */
2035
2036 static void
gtk_text_buffer_mark_set(GtkTextBuffer * buffer,const GtkTextIter * location,GtkTextMark * mark)2037 gtk_text_buffer_mark_set (GtkTextBuffer *buffer,
2038 const GtkTextIter *location,
2039 GtkTextMark *mark)
2040 {
2041 /* IMO this should NOT work like insert_text and delete_range,
2042 * where the real action happens in the default handler.
2043 *
2044 * The reason is that the default handler would be _required_,
2045 * i.e. the whole widget would start breaking and segfaulting if the
2046 * default handler didn't get run. So you can't really override the
2047 * default handler or stop the emission; that is, this signal is
2048 * purely for notification, and not to allow users to modify the
2049 * default behavior.
2050 */
2051
2052 g_object_ref (mark);
2053
2054 g_signal_emit (buffer,
2055 signals[MARK_SET],
2056 0,
2057 location,
2058 mark);
2059
2060 g_object_unref (mark);
2061 }
2062
2063 /**
2064 * gtk_text_buffer_set_mark:
2065 * @buffer: a #GtkTextBuffer
2066 * @mark_name: name of the mark
2067 * @iter: location for the mark
2068 * @left_gravity: if the mark is created by this function, gravity for
2069 * the new mark
2070 * @should_exist: if %TRUE, warn if the mark does not exist, and return
2071 * immediately
2072 *
2073 * Move the mark to the given position, if not @should_exist,
2074 * create the mark.
2075 *
2076 * Returns: mark
2077 **/
2078 static GtkTextMark*
gtk_text_buffer_set_mark(GtkTextBuffer * buffer,GtkTextMark * existing_mark,const gchar * mark_name,const GtkTextIter * iter,gboolean left_gravity,gboolean should_exist)2079 gtk_text_buffer_set_mark (GtkTextBuffer *buffer,
2080 GtkTextMark *existing_mark,
2081 const gchar *mark_name,
2082 const GtkTextIter *iter,
2083 gboolean left_gravity,
2084 gboolean should_exist)
2085 {
2086 GtkTextIter location;
2087 GtkTextMark *mark;
2088
2089 g_return_val_if_fail (gtk_text_iter_get_buffer (iter) == buffer, NULL);
2090
2091 mark = _gtk_text_btree_set_mark (get_btree (buffer),
2092 existing_mark,
2093 mark_name,
2094 left_gravity,
2095 iter,
2096 should_exist);
2097
2098 _gtk_text_btree_get_iter_at_mark (get_btree (buffer),
2099 &location,
2100 mark);
2101
2102 gtk_text_buffer_mark_set (buffer, &location, mark);
2103
2104 return mark;
2105 }
2106
2107 /**
2108 * gtk_text_buffer_create_mark:
2109 * @buffer: a #GtkTextBuffer
2110 * @mark_name: (allow-none): name for mark, or %NULL
2111 * @where: location to place mark
2112 * @left_gravity: whether the mark has left gravity
2113 *
2114 * Creates a mark at position @where. If @mark_name is %NULL, the mark
2115 * is anonymous; otherwise, the mark can be retrieved by name using
2116 * gtk_text_buffer_get_mark(). If a mark has left gravity, and text is
2117 * inserted at the mark’s current location, the mark will be moved to
2118 * the left of the newly-inserted text. If the mark has right gravity
2119 * (@left_gravity = %FALSE), the mark will end up on the right of
2120 * newly-inserted text. The standard left-to-right cursor is a mark
2121 * with right gravity (when you type, the cursor stays on the right
2122 * side of the text you’re typing).
2123 *
2124 * The caller of this function does not own a
2125 * reference to the returned #GtkTextMark, so you can ignore the
2126 * return value if you like. Marks are owned by the buffer and go
2127 * away when the buffer does.
2128 *
2129 * Emits the #GtkTextBuffer::mark-set signal as notification of the mark's
2130 * initial placement.
2131 *
2132 * Returns: (transfer none): the new #GtkTextMark object
2133 **/
2134 GtkTextMark*
gtk_text_buffer_create_mark(GtkTextBuffer * buffer,const gchar * mark_name,const GtkTextIter * where,gboolean left_gravity)2135 gtk_text_buffer_create_mark (GtkTextBuffer *buffer,
2136 const gchar *mark_name,
2137 const GtkTextIter *where,
2138 gboolean left_gravity)
2139 {
2140 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
2141
2142 return gtk_text_buffer_set_mark (buffer, NULL, mark_name, where,
2143 left_gravity, FALSE);
2144 }
2145
2146 /**
2147 * gtk_text_buffer_add_mark:
2148 * @buffer: a #GtkTextBuffer
2149 * @mark: the mark to add
2150 * @where: location to place mark
2151 *
2152 * Adds the mark at position @where. The mark must not be added to
2153 * another buffer, and if its name is not %NULL then there must not
2154 * be another mark in the buffer with the same name.
2155 *
2156 * Emits the #GtkTextBuffer::mark-set signal as notification of the mark's
2157 * initial placement.
2158 *
2159 * Since: 2.12
2160 **/
2161 void
gtk_text_buffer_add_mark(GtkTextBuffer * buffer,GtkTextMark * mark,const GtkTextIter * where)2162 gtk_text_buffer_add_mark (GtkTextBuffer *buffer,
2163 GtkTextMark *mark,
2164 const GtkTextIter *where)
2165 {
2166 const gchar *name;
2167
2168 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2169 g_return_if_fail (GTK_IS_TEXT_MARK (mark));
2170 g_return_if_fail (where != NULL);
2171 g_return_if_fail (gtk_text_mark_get_buffer (mark) == NULL);
2172
2173 name = gtk_text_mark_get_name (mark);
2174
2175 if (name != NULL && gtk_text_buffer_get_mark (buffer, name) != NULL)
2176 {
2177 g_critical ("Mark %s already exists in the buffer", name);
2178 return;
2179 }
2180
2181 gtk_text_buffer_set_mark (buffer, mark, NULL, where, FALSE, FALSE);
2182 }
2183
2184 /**
2185 * gtk_text_buffer_move_mark:
2186 * @buffer: a #GtkTextBuffer
2187 * @mark: a #GtkTextMark
2188 * @where: new location for @mark in @buffer
2189 *
2190 * Moves @mark to the new location @where. Emits the #GtkTextBuffer::mark-set
2191 * signal as notification of the move.
2192 **/
2193 void
gtk_text_buffer_move_mark(GtkTextBuffer * buffer,GtkTextMark * mark,const GtkTextIter * where)2194 gtk_text_buffer_move_mark (GtkTextBuffer *buffer,
2195 GtkTextMark *mark,
2196 const GtkTextIter *where)
2197 {
2198 g_return_if_fail (GTK_IS_TEXT_MARK (mark));
2199 g_return_if_fail (!gtk_text_mark_get_deleted (mark));
2200 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2201
2202 gtk_text_buffer_set_mark (buffer, mark, NULL, where, FALSE, TRUE);
2203 }
2204
2205 /**
2206 * gtk_text_buffer_get_iter_at_mark:
2207 * @buffer: a #GtkTextBuffer
2208 * @iter: (out): iterator to initialize
2209 * @mark: a #GtkTextMark in @buffer
2210 *
2211 * Initializes @iter with the current position of @mark.
2212 **/
2213 void
gtk_text_buffer_get_iter_at_mark(GtkTextBuffer * buffer,GtkTextIter * iter,GtkTextMark * mark)2214 gtk_text_buffer_get_iter_at_mark (GtkTextBuffer *buffer,
2215 GtkTextIter *iter,
2216 GtkTextMark *mark)
2217 {
2218 g_return_if_fail (GTK_IS_TEXT_MARK (mark));
2219 g_return_if_fail (!gtk_text_mark_get_deleted (mark));
2220 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2221
2222 _gtk_text_btree_get_iter_at_mark (get_btree (buffer),
2223 iter,
2224 mark);
2225 }
2226
2227 /**
2228 * gtk_text_buffer_delete_mark:
2229 * @buffer: a #GtkTextBuffer
2230 * @mark: a #GtkTextMark in @buffer
2231 *
2232 * Deletes @mark, so that it’s no longer located anywhere in the
2233 * buffer. Removes the reference the buffer holds to the mark, so if
2234 * you haven’t called g_object_ref() on the mark, it will be freed. Even
2235 * if the mark isn’t freed, most operations on @mark become
2236 * invalid, until it gets added to a buffer again with
2237 * gtk_text_buffer_add_mark(). Use gtk_text_mark_get_deleted() to
2238 * find out if a mark has been removed from its buffer.
2239 * The #GtkTextBuffer::mark-deleted signal will be emitted as notification after
2240 * the mark is deleted.
2241 **/
2242 void
gtk_text_buffer_delete_mark(GtkTextBuffer * buffer,GtkTextMark * mark)2243 gtk_text_buffer_delete_mark (GtkTextBuffer *buffer,
2244 GtkTextMark *mark)
2245 {
2246 g_return_if_fail (GTK_IS_TEXT_MARK (mark));
2247 g_return_if_fail (!gtk_text_mark_get_deleted (mark));
2248 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2249
2250 g_object_ref (mark);
2251
2252 _gtk_text_btree_remove_mark (get_btree (buffer), mark);
2253
2254 /* See rationale above for MARK_SET on why we emit this after
2255 * removing the mark, rather than removing the mark in a default
2256 * handler.
2257 */
2258 g_signal_emit (buffer, signals[MARK_DELETED],
2259 0,
2260 mark);
2261
2262 g_object_unref (mark);
2263 }
2264
2265 /**
2266 * gtk_text_buffer_get_mark:
2267 * @buffer: a #GtkTextBuffer
2268 * @name: a mark name
2269 *
2270 * Returns the mark named @name in buffer @buffer, or %NULL if no such
2271 * mark exists in the buffer.
2272 *
2273 * Returns: (nullable) (transfer none): a #GtkTextMark, or %NULL
2274 **/
2275 GtkTextMark*
gtk_text_buffer_get_mark(GtkTextBuffer * buffer,const gchar * name)2276 gtk_text_buffer_get_mark (GtkTextBuffer *buffer,
2277 const gchar *name)
2278 {
2279 GtkTextMark *mark;
2280
2281 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
2282 g_return_val_if_fail (name != NULL, NULL);
2283
2284 mark = _gtk_text_btree_get_mark_by_name (get_btree (buffer), name);
2285
2286 return mark;
2287 }
2288
2289 /**
2290 * gtk_text_buffer_move_mark_by_name:
2291 * @buffer: a #GtkTextBuffer
2292 * @name: name of a mark
2293 * @where: new location for mark
2294 *
2295 * Moves the mark named @name (which must exist) to location @where.
2296 * See gtk_text_buffer_move_mark() for details.
2297 **/
2298 void
gtk_text_buffer_move_mark_by_name(GtkTextBuffer * buffer,const gchar * name,const GtkTextIter * where)2299 gtk_text_buffer_move_mark_by_name (GtkTextBuffer *buffer,
2300 const gchar *name,
2301 const GtkTextIter *where)
2302 {
2303 GtkTextMark *mark;
2304
2305 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2306 g_return_if_fail (name != NULL);
2307
2308 mark = _gtk_text_btree_get_mark_by_name (get_btree (buffer), name);
2309
2310 if (mark == NULL)
2311 {
2312 g_warning ("%s: no mark named '%s'", G_STRLOC, name);
2313 return;
2314 }
2315
2316 gtk_text_buffer_move_mark (buffer, mark, where);
2317 }
2318
2319 /**
2320 * gtk_text_buffer_delete_mark_by_name:
2321 * @buffer: a #GtkTextBuffer
2322 * @name: name of a mark in @buffer
2323 *
2324 * Deletes the mark named @name; the mark must exist. See
2325 * gtk_text_buffer_delete_mark() for details.
2326 **/
2327 void
gtk_text_buffer_delete_mark_by_name(GtkTextBuffer * buffer,const gchar * name)2328 gtk_text_buffer_delete_mark_by_name (GtkTextBuffer *buffer,
2329 const gchar *name)
2330 {
2331 GtkTextMark *mark;
2332
2333 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2334 g_return_if_fail (name != NULL);
2335
2336 mark = _gtk_text_btree_get_mark_by_name (get_btree (buffer), name);
2337
2338 if (mark == NULL)
2339 {
2340 g_warning ("%s: no mark named '%s'", G_STRLOC, name);
2341 return;
2342 }
2343
2344 gtk_text_buffer_delete_mark (buffer, mark);
2345 }
2346
2347 /**
2348 * gtk_text_buffer_get_insert:
2349 * @buffer: a #GtkTextBuffer
2350 *
2351 * Returns the mark that represents the cursor (insertion point).
2352 * Equivalent to calling gtk_text_buffer_get_mark() to get the mark
2353 * named “insert”, but very slightly more efficient, and involves less
2354 * typing.
2355 *
2356 * Returns: (transfer none): insertion point mark
2357 **/
2358 GtkTextMark*
gtk_text_buffer_get_insert(GtkTextBuffer * buffer)2359 gtk_text_buffer_get_insert (GtkTextBuffer *buffer)
2360 {
2361 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
2362
2363 return _gtk_text_btree_get_insert (get_btree (buffer));
2364 }
2365
2366 /**
2367 * gtk_text_buffer_get_selection_bound:
2368 * @buffer: a #GtkTextBuffer
2369 *
2370 * Returns the mark that represents the selection bound. Equivalent
2371 * to calling gtk_text_buffer_get_mark() to get the mark named
2372 * “selection_bound”, but very slightly more efficient, and involves
2373 * less typing.
2374 *
2375 * The currently-selected text in @buffer is the region between the
2376 * “selection_bound” and “insert” marks. If “selection_bound” and
2377 * “insert” are in the same place, then there is no current selection.
2378 * gtk_text_buffer_get_selection_bounds() is another convenient function
2379 * for handling the selection, if you just want to know whether there’s a
2380 * selection and what its bounds are.
2381 *
2382 * Returns: (transfer none): selection bound mark
2383 **/
2384 GtkTextMark*
gtk_text_buffer_get_selection_bound(GtkTextBuffer * buffer)2385 gtk_text_buffer_get_selection_bound (GtkTextBuffer *buffer)
2386 {
2387 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
2388
2389 return _gtk_text_btree_get_selection_bound (get_btree (buffer));
2390 }
2391
2392 /**
2393 * gtk_text_buffer_get_iter_at_child_anchor:
2394 * @buffer: a #GtkTextBuffer
2395 * @iter: (out): an iterator to be initialized
2396 * @anchor: a child anchor that appears in @buffer
2397 *
2398 * Obtains the location of @anchor within @buffer.
2399 **/
2400 void
gtk_text_buffer_get_iter_at_child_anchor(GtkTextBuffer * buffer,GtkTextIter * iter,GtkTextChildAnchor * anchor)2401 gtk_text_buffer_get_iter_at_child_anchor (GtkTextBuffer *buffer,
2402 GtkTextIter *iter,
2403 GtkTextChildAnchor *anchor)
2404 {
2405 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2406 g_return_if_fail (iter != NULL);
2407 g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor));
2408 g_return_if_fail (!gtk_text_child_anchor_get_deleted (anchor));
2409
2410 _gtk_text_btree_get_iter_at_child_anchor (get_btree (buffer),
2411 iter,
2412 anchor);
2413 }
2414
2415 /**
2416 * gtk_text_buffer_place_cursor:
2417 * @buffer: a #GtkTextBuffer
2418 * @where: where to put the cursor
2419 *
2420 * This function moves the “insert” and “selection_bound” marks
2421 * simultaneously. If you move them to the same place in two steps
2422 * with gtk_text_buffer_move_mark(), you will temporarily select a
2423 * region in between their old and new locations, which can be pretty
2424 * inefficient since the temporarily-selected region will force stuff
2425 * to be recalculated. This function moves them as a unit, which can
2426 * be optimized.
2427 **/
2428 void
gtk_text_buffer_place_cursor(GtkTextBuffer * buffer,const GtkTextIter * where)2429 gtk_text_buffer_place_cursor (GtkTextBuffer *buffer,
2430 const GtkTextIter *where)
2431 {
2432 gtk_text_buffer_select_range (buffer, where, where);
2433 }
2434
2435 /**
2436 * gtk_text_buffer_select_range:
2437 * @buffer: a #GtkTextBuffer
2438 * @ins: where to put the “insert” mark
2439 * @bound: where to put the “selection_bound” mark
2440 *
2441 * This function moves the “insert” and “selection_bound” marks
2442 * simultaneously. If you move them in two steps
2443 * with gtk_text_buffer_move_mark(), you will temporarily select a
2444 * region in between their old and new locations, which can be pretty
2445 * inefficient since the temporarily-selected region will force stuff
2446 * to be recalculated. This function moves them as a unit, which can
2447 * be optimized.
2448 *
2449 * Since: 2.4
2450 **/
2451 void
gtk_text_buffer_select_range(GtkTextBuffer * buffer,const GtkTextIter * ins,const GtkTextIter * bound)2452 gtk_text_buffer_select_range (GtkTextBuffer *buffer,
2453 const GtkTextIter *ins,
2454 const GtkTextIter *bound)
2455 {
2456 GtkTextIter real_ins;
2457 GtkTextIter real_bound;
2458
2459 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2460
2461 real_ins = *ins;
2462 real_bound = *bound;
2463
2464 _gtk_text_btree_select_range (get_btree (buffer), &real_ins, &real_bound);
2465 gtk_text_buffer_mark_set (buffer, &real_ins,
2466 gtk_text_buffer_get_insert (buffer));
2467 gtk_text_buffer_mark_set (buffer, &real_bound,
2468 gtk_text_buffer_get_selection_bound (buffer));
2469 }
2470
2471 /*
2472 * Tags
2473 */
2474
2475 /**
2476 * gtk_text_buffer_create_tag:
2477 * @buffer: a #GtkTextBuffer
2478 * @tag_name: (allow-none): name of the new tag, or %NULL
2479 * @first_property_name: (allow-none): name of first property to set, or %NULL
2480 * @...: %NULL-terminated list of property names and values
2481 *
2482 * Creates a tag and adds it to the tag table for @buffer.
2483 * Equivalent to calling gtk_text_tag_new() and then adding the
2484 * tag to the buffer’s tag table. The returned tag is owned by
2485 * the buffer’s tag table, so the ref count will be equal to one.
2486 *
2487 * If @tag_name is %NULL, the tag is anonymous.
2488 *
2489 * If @tag_name is non-%NULL, a tag called @tag_name must not already
2490 * exist in the tag table for this buffer.
2491 *
2492 * The @first_property_name argument and subsequent arguments are a list
2493 * of properties to set on the tag, as with g_object_set().
2494 *
2495 * Returns: (transfer none): a new tag
2496 */
2497 GtkTextTag*
gtk_text_buffer_create_tag(GtkTextBuffer * buffer,const gchar * tag_name,const gchar * first_property_name,...)2498 gtk_text_buffer_create_tag (GtkTextBuffer *buffer,
2499 const gchar *tag_name,
2500 const gchar *first_property_name,
2501 ...)
2502 {
2503 GtkTextTag *tag;
2504 va_list list;
2505
2506 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
2507
2508 tag = gtk_text_tag_new (tag_name);
2509
2510 if (!gtk_text_tag_table_add (get_table (buffer), tag))
2511 {
2512 g_object_unref (tag);
2513 return NULL;
2514 }
2515
2516 if (first_property_name)
2517 {
2518 va_start (list, first_property_name);
2519 g_object_set_valist (G_OBJECT (tag), first_property_name, list);
2520 va_end (list);
2521 }
2522
2523 g_object_unref (tag);
2524
2525 return tag;
2526 }
2527
2528 static void
gtk_text_buffer_real_apply_tag(GtkTextBuffer * buffer,GtkTextTag * tag,const GtkTextIter * start,const GtkTextIter * end)2529 gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
2530 GtkTextTag *tag,
2531 const GtkTextIter *start,
2532 const GtkTextIter *end)
2533 {
2534 if (tag->priv->table != buffer->priv->tag_table)
2535 {
2536 g_warning ("Can only apply tags that are in the tag table for the buffer");
2537 return;
2538 }
2539
2540 _gtk_text_btree_tag (start, end, tag, TRUE);
2541 }
2542
2543 static void
gtk_text_buffer_real_remove_tag(GtkTextBuffer * buffer,GtkTextTag * tag,const GtkTextIter * start,const GtkTextIter * end)2544 gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
2545 GtkTextTag *tag,
2546 const GtkTextIter *start,
2547 const GtkTextIter *end)
2548 {
2549 if (tag->priv->table != buffer->priv->tag_table)
2550 {
2551 g_warning ("Can only remove tags that are in the tag table for the buffer");
2552 return;
2553 }
2554
2555 _gtk_text_btree_tag (start, end, tag, FALSE);
2556 }
2557
2558 static void
gtk_text_buffer_real_changed(GtkTextBuffer * buffer)2559 gtk_text_buffer_real_changed (GtkTextBuffer *buffer)
2560 {
2561 gtk_text_buffer_set_modified (buffer, TRUE);
2562
2563 g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_TEXT]);
2564 }
2565
2566 static void
gtk_text_buffer_real_mark_set(GtkTextBuffer * buffer,const GtkTextIter * iter,GtkTextMark * mark)2567 gtk_text_buffer_real_mark_set (GtkTextBuffer *buffer,
2568 const GtkTextIter *iter,
2569 GtkTextMark *mark)
2570 {
2571 GtkTextMark *insert;
2572
2573 insert = gtk_text_buffer_get_insert (buffer);
2574
2575 if (mark == insert || mark == gtk_text_buffer_get_selection_bound (buffer))
2576 {
2577 gboolean has_selection;
2578
2579 update_selection_clipboards (buffer);
2580
2581 has_selection = gtk_text_buffer_get_selection_bounds (buffer,
2582 NULL,
2583 NULL);
2584
2585 if (has_selection != buffer->priv->has_selection)
2586 {
2587 buffer->priv->has_selection = has_selection;
2588 g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_HAS_SELECTION]);
2589 }
2590 }
2591
2592 if (mark == insert)
2593 g_object_notify_by_pspec (G_OBJECT (buffer), text_buffer_props[PROP_CURSOR_POSITION]);
2594 }
2595
2596 static void
gtk_text_buffer_emit_tag(GtkTextBuffer * buffer,GtkTextTag * tag,gboolean apply,const GtkTextIter * start,const GtkTextIter * end)2597 gtk_text_buffer_emit_tag (GtkTextBuffer *buffer,
2598 GtkTextTag *tag,
2599 gboolean apply,
2600 const GtkTextIter *start,
2601 const GtkTextIter *end)
2602 {
2603 GtkTextIter start_tmp = *start;
2604 GtkTextIter end_tmp = *end;
2605
2606 g_return_if_fail (tag != NULL);
2607
2608 gtk_text_iter_order (&start_tmp, &end_tmp);
2609
2610 if (apply)
2611 g_signal_emit (buffer, signals[APPLY_TAG],
2612 0,
2613 tag, &start_tmp, &end_tmp);
2614 else
2615 g_signal_emit (buffer, signals[REMOVE_TAG],
2616 0,
2617 tag, &start_tmp, &end_tmp);
2618 }
2619
2620 /**
2621 * gtk_text_buffer_apply_tag:
2622 * @buffer: a #GtkTextBuffer
2623 * @tag: a #GtkTextTag
2624 * @start: one bound of range to be tagged
2625 * @end: other bound of range to be tagged
2626 *
2627 * Emits the “apply-tag” signal on @buffer. The default
2628 * handler for the signal applies @tag to the given range.
2629 * @start and @end do not have to be in order.
2630 **/
2631 void
gtk_text_buffer_apply_tag(GtkTextBuffer * buffer,GtkTextTag * tag,const GtkTextIter * start,const GtkTextIter * end)2632 gtk_text_buffer_apply_tag (GtkTextBuffer *buffer,
2633 GtkTextTag *tag,
2634 const GtkTextIter *start,
2635 const GtkTextIter *end)
2636 {
2637 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2638 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
2639 g_return_if_fail (start != NULL);
2640 g_return_if_fail (end != NULL);
2641 g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2642 g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2643 g_return_if_fail (tag->priv->table == buffer->priv->tag_table);
2644
2645 gtk_text_buffer_emit_tag (buffer, tag, TRUE, start, end);
2646 }
2647
2648 /**
2649 * gtk_text_buffer_remove_tag:
2650 * @buffer: a #GtkTextBuffer
2651 * @tag: a #GtkTextTag
2652 * @start: one bound of range to be untagged
2653 * @end: other bound of range to be untagged
2654 *
2655 * Emits the “remove-tag” signal. The default handler for the signal
2656 * removes all occurrences of @tag from the given range. @start and
2657 * @end don’t have to be in order.
2658 **/
2659 void
gtk_text_buffer_remove_tag(GtkTextBuffer * buffer,GtkTextTag * tag,const GtkTextIter * start,const GtkTextIter * end)2660 gtk_text_buffer_remove_tag (GtkTextBuffer *buffer,
2661 GtkTextTag *tag,
2662 const GtkTextIter *start,
2663 const GtkTextIter *end)
2664
2665 {
2666 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2667 g_return_if_fail (GTK_IS_TEXT_TAG (tag));
2668 g_return_if_fail (start != NULL);
2669 g_return_if_fail (end != NULL);
2670 g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2671 g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2672 g_return_if_fail (tag->priv->table == buffer->priv->tag_table);
2673
2674 gtk_text_buffer_emit_tag (buffer, tag, FALSE, start, end);
2675 }
2676
2677 /**
2678 * gtk_text_buffer_apply_tag_by_name:
2679 * @buffer: a #GtkTextBuffer
2680 * @name: name of a named #GtkTextTag
2681 * @start: one bound of range to be tagged
2682 * @end: other bound of range to be tagged
2683 *
2684 * Calls gtk_text_tag_table_lookup() on the buffer’s tag table to
2685 * get a #GtkTextTag, then calls gtk_text_buffer_apply_tag().
2686 **/
2687 void
gtk_text_buffer_apply_tag_by_name(GtkTextBuffer * buffer,const gchar * name,const GtkTextIter * start,const GtkTextIter * end)2688 gtk_text_buffer_apply_tag_by_name (GtkTextBuffer *buffer,
2689 const gchar *name,
2690 const GtkTextIter *start,
2691 const GtkTextIter *end)
2692 {
2693 GtkTextTag *tag;
2694
2695 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2696 g_return_if_fail (name != NULL);
2697 g_return_if_fail (start != NULL);
2698 g_return_if_fail (end != NULL);
2699 g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2700 g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2701
2702 tag = gtk_text_tag_table_lookup (get_table (buffer),
2703 name);
2704
2705 if (tag == NULL)
2706 {
2707 g_warning ("Unknown tag '%s'", name);
2708 return;
2709 }
2710
2711 gtk_text_buffer_emit_tag (buffer, tag, TRUE, start, end);
2712 }
2713
2714 /**
2715 * gtk_text_buffer_remove_tag_by_name:
2716 * @buffer: a #GtkTextBuffer
2717 * @name: name of a #GtkTextTag
2718 * @start: one bound of range to be untagged
2719 * @end: other bound of range to be untagged
2720 *
2721 * Calls gtk_text_tag_table_lookup() on the buffer’s tag table to
2722 * get a #GtkTextTag, then calls gtk_text_buffer_remove_tag().
2723 **/
2724 void
gtk_text_buffer_remove_tag_by_name(GtkTextBuffer * buffer,const gchar * name,const GtkTextIter * start,const GtkTextIter * end)2725 gtk_text_buffer_remove_tag_by_name (GtkTextBuffer *buffer,
2726 const gchar *name,
2727 const GtkTextIter *start,
2728 const GtkTextIter *end)
2729 {
2730 GtkTextTag *tag;
2731
2732 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2733 g_return_if_fail (name != NULL);
2734 g_return_if_fail (start != NULL);
2735 g_return_if_fail (end != NULL);
2736 g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2737 g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2738
2739 tag = gtk_text_tag_table_lookup (get_table (buffer),
2740 name);
2741
2742 if (tag == NULL)
2743 {
2744 g_warning ("Unknown tag '%s'", name);
2745 return;
2746 }
2747
2748 gtk_text_buffer_emit_tag (buffer, tag, FALSE, start, end);
2749 }
2750
2751 static gint
pointer_cmp(gconstpointer a,gconstpointer b)2752 pointer_cmp (gconstpointer a,
2753 gconstpointer b)
2754 {
2755 if (a < b)
2756 return -1;
2757 else if (a > b)
2758 return 1;
2759 else
2760 return 0;
2761 }
2762
2763 /**
2764 * gtk_text_buffer_remove_all_tags:
2765 * @buffer: a #GtkTextBuffer
2766 * @start: one bound of range to be untagged
2767 * @end: other bound of range to be untagged
2768 *
2769 * Removes all tags in the range between @start and @end. Be careful
2770 * with this function; it could remove tags added in code unrelated to
2771 * the code you’re currently writing. That is, using this function is
2772 * probably a bad idea if you have two or more unrelated code sections
2773 * that add tags.
2774 **/
2775 void
gtk_text_buffer_remove_all_tags(GtkTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end)2776 gtk_text_buffer_remove_all_tags (GtkTextBuffer *buffer,
2777 const GtkTextIter *start,
2778 const GtkTextIter *end)
2779 {
2780 GtkTextIter first, second, tmp;
2781 GSList *tags;
2782 GSList *tmp_list;
2783 GSList *prev, *next;
2784 GtkTextTag *tag;
2785
2786 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2787 g_return_if_fail (start != NULL);
2788 g_return_if_fail (end != NULL);
2789 g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
2790 g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
2791
2792 first = *start;
2793 second = *end;
2794
2795 gtk_text_iter_order (&first, &second);
2796
2797 /* Get all tags turned on at the start */
2798 tags = gtk_text_iter_get_tags (&first);
2799
2800 /* Find any that are toggled on within the range */
2801 tmp = first;
2802 while (gtk_text_iter_forward_to_tag_toggle (&tmp, NULL))
2803 {
2804 GSList *toggled;
2805 GSList *tmp_list2;
2806
2807 if (gtk_text_iter_compare (&tmp, &second) >= 0)
2808 break; /* past the end of the range */
2809
2810 toggled = gtk_text_iter_get_toggled_tags (&tmp, TRUE);
2811
2812 /* We could end up with a really big-ass list here.
2813 * Fix it someday.
2814 */
2815 tmp_list2 = toggled;
2816 while (tmp_list2 != NULL)
2817 {
2818 tags = g_slist_prepend (tags, tmp_list2->data);
2819
2820 tmp_list2 = tmp_list2->next;
2821 }
2822
2823 g_slist_free (toggled);
2824 }
2825
2826 /* Sort the list */
2827 tags = g_slist_sort (tags, pointer_cmp);
2828
2829 /* Strip duplicates */
2830 tag = NULL;
2831 prev = NULL;
2832 tmp_list = tags;
2833 while (tmp_list != NULL)
2834 {
2835 if (tag == tmp_list->data)
2836 {
2837 /* duplicate */
2838 next = tmp_list->next;
2839 if (prev)
2840 prev->next = next;
2841
2842 tmp_list->next = NULL;
2843
2844 g_slist_free (tmp_list);
2845
2846 tmp_list = next;
2847 /* prev is unchanged */
2848 }
2849 else
2850 {
2851 /* not a duplicate */
2852 tag = GTK_TEXT_TAG (tmp_list->data);
2853 prev = tmp_list;
2854 tmp_list = tmp_list->next;
2855 }
2856 }
2857
2858 g_slist_foreach (tags, (GFunc) g_object_ref, NULL);
2859
2860 tmp_list = tags;
2861 while (tmp_list != NULL)
2862 {
2863 tag = GTK_TEXT_TAG (tmp_list->data);
2864
2865 gtk_text_buffer_remove_tag (buffer, tag, &first, &second);
2866
2867 tmp_list = tmp_list->next;
2868 }
2869
2870 g_slist_free_full (tags, g_object_unref);
2871 }
2872
2873
2874 /*
2875 * Obtain various iterators
2876 */
2877
2878 /**
2879 * gtk_text_buffer_get_iter_at_line_offset:
2880 * @buffer: a #GtkTextBuffer
2881 * @iter: (out): iterator to initialize
2882 * @line_number: line number counting from 0
2883 * @char_offset: char offset from start of line
2884 *
2885 * Obtains an iterator pointing to @char_offset within the given line. Note
2886 * characters, not bytes; UTF-8 may encode one character as multiple bytes.
2887 *
2888 * Before the 3.20 version, it was not allowed to pass an invalid location.
2889 *
2890 * Since the 3.20 version, if @line_number is greater than the number of lines
2891 * in the @buffer, the end iterator is returned. And if @char_offset is off the
2892 * end of the line, the iterator at the end of the line is returned.
2893 **/
2894 void
gtk_text_buffer_get_iter_at_line_offset(GtkTextBuffer * buffer,GtkTextIter * iter,gint line_number,gint char_offset)2895 gtk_text_buffer_get_iter_at_line_offset (GtkTextBuffer *buffer,
2896 GtkTextIter *iter,
2897 gint line_number,
2898 gint char_offset)
2899 {
2900 GtkTextIter end_line_iter;
2901
2902 g_return_if_fail (iter != NULL);
2903 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2904
2905 if (line_number >= gtk_text_buffer_get_line_count (buffer))
2906 {
2907 gtk_text_buffer_get_end_iter (buffer, iter);
2908 return;
2909 }
2910
2911 _gtk_text_btree_get_iter_at_line_char (get_btree (buffer), iter, line_number, 0);
2912
2913 end_line_iter = *iter;
2914 if (!gtk_text_iter_ends_line (&end_line_iter))
2915 gtk_text_iter_forward_to_line_end (&end_line_iter);
2916
2917 if (char_offset <= gtk_text_iter_get_line_offset (&end_line_iter))
2918 gtk_text_iter_set_line_offset (iter, char_offset);
2919 else
2920 *iter = end_line_iter;
2921 }
2922
2923 /**
2924 * gtk_text_buffer_get_iter_at_line_index:
2925 * @buffer: a #GtkTextBuffer
2926 * @iter: (out): iterator to initialize
2927 * @line_number: line number counting from 0
2928 * @byte_index: byte index from start of line
2929 *
2930 * Obtains an iterator pointing to @byte_index within the given line.
2931 * @byte_index must be the start of a UTF-8 character. Note bytes, not
2932 * characters; UTF-8 may encode one character as multiple bytes.
2933 *
2934 * Before the 3.20 version, it was not allowed to pass an invalid location.
2935 *
2936 * Since the 3.20 version, if @line_number is greater than the number of lines
2937 * in the @buffer, the end iterator is returned. And if @byte_index is off the
2938 * end of the line, the iterator at the end of the line is returned.
2939 **/
2940 void
gtk_text_buffer_get_iter_at_line_index(GtkTextBuffer * buffer,GtkTextIter * iter,gint line_number,gint byte_index)2941 gtk_text_buffer_get_iter_at_line_index (GtkTextBuffer *buffer,
2942 GtkTextIter *iter,
2943 gint line_number,
2944 gint byte_index)
2945 {
2946 GtkTextIter end_line_iter;
2947
2948 g_return_if_fail (iter != NULL);
2949 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2950
2951 if (line_number >= gtk_text_buffer_get_line_count (buffer))
2952 {
2953 gtk_text_buffer_get_end_iter (buffer, iter);
2954 return;
2955 }
2956
2957 gtk_text_buffer_get_iter_at_line (buffer, iter, line_number);
2958
2959 end_line_iter = *iter;
2960 if (!gtk_text_iter_ends_line (&end_line_iter))
2961 gtk_text_iter_forward_to_line_end (&end_line_iter);
2962
2963 if (byte_index <= gtk_text_iter_get_line_index (&end_line_iter))
2964 gtk_text_iter_set_line_index (iter, byte_index);
2965 else
2966 *iter = end_line_iter;
2967 }
2968
2969 /**
2970 * gtk_text_buffer_get_iter_at_line:
2971 * @buffer: a #GtkTextBuffer
2972 * @iter: (out): iterator to initialize
2973 * @line_number: line number counting from 0
2974 *
2975 * Initializes @iter to the start of the given line. If @line_number is greater
2976 * than the number of lines in the @buffer, the end iterator is returned.
2977 **/
2978 void
gtk_text_buffer_get_iter_at_line(GtkTextBuffer * buffer,GtkTextIter * iter,gint line_number)2979 gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer,
2980 GtkTextIter *iter,
2981 gint line_number)
2982 {
2983 g_return_if_fail (iter != NULL);
2984 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
2985
2986 gtk_text_buffer_get_iter_at_line_offset (buffer, iter, line_number, 0);
2987 }
2988
2989 /**
2990 * gtk_text_buffer_get_iter_at_offset:
2991 * @buffer: a #GtkTextBuffer
2992 * @iter: (out): iterator to initialize
2993 * @char_offset: char offset from start of buffer, counting from 0, or -1
2994 *
2995 * Initializes @iter to a position @char_offset chars from the start
2996 * of the entire buffer. If @char_offset is -1 or greater than the number
2997 * of characters in the buffer, @iter is initialized to the end iterator,
2998 * the iterator one past the last valid character in the buffer.
2999 **/
3000 void
gtk_text_buffer_get_iter_at_offset(GtkTextBuffer * buffer,GtkTextIter * iter,gint char_offset)3001 gtk_text_buffer_get_iter_at_offset (GtkTextBuffer *buffer,
3002 GtkTextIter *iter,
3003 gint char_offset)
3004 {
3005 g_return_if_fail (iter != NULL);
3006 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3007
3008 _gtk_text_btree_get_iter_at_char (get_btree (buffer), iter, char_offset);
3009 }
3010
3011 /**
3012 * gtk_text_buffer_get_start_iter:
3013 * @buffer: a #GtkTextBuffer
3014 * @iter: (out): iterator to initialize
3015 *
3016 * Initialized @iter with the first position in the text buffer. This
3017 * is the same as using gtk_text_buffer_get_iter_at_offset() to get
3018 * the iter at character offset 0.
3019 **/
3020 void
gtk_text_buffer_get_start_iter(GtkTextBuffer * buffer,GtkTextIter * iter)3021 gtk_text_buffer_get_start_iter (GtkTextBuffer *buffer,
3022 GtkTextIter *iter)
3023 {
3024 g_return_if_fail (iter != NULL);
3025 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3026
3027 _gtk_text_btree_get_iter_at_char (get_btree (buffer), iter, 0);
3028 }
3029
3030 /**
3031 * gtk_text_buffer_get_end_iter:
3032 * @buffer: a #GtkTextBuffer
3033 * @iter: (out): iterator to initialize
3034 *
3035 * Initializes @iter with the “end iterator,” one past the last valid
3036 * character in the text buffer. If dereferenced with
3037 * gtk_text_iter_get_char(), the end iterator has a character value of 0.
3038 * The entire buffer lies in the range from the first position in
3039 * the buffer (call gtk_text_buffer_get_start_iter() to get
3040 * character position 0) to the end iterator.
3041 **/
3042 void
gtk_text_buffer_get_end_iter(GtkTextBuffer * buffer,GtkTextIter * iter)3043 gtk_text_buffer_get_end_iter (GtkTextBuffer *buffer,
3044 GtkTextIter *iter)
3045 {
3046 g_return_if_fail (iter != NULL);
3047 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3048
3049 _gtk_text_btree_get_end_iter (get_btree (buffer), iter);
3050 }
3051
3052 /**
3053 * gtk_text_buffer_get_bounds:
3054 * @buffer: a #GtkTextBuffer
3055 * @start: (out): iterator to initialize with first position in the buffer
3056 * @end: (out): iterator to initialize with the end iterator
3057 *
3058 * Retrieves the first and last iterators in the buffer, i.e. the
3059 * entire buffer lies within the range [@start,@end).
3060 **/
3061 void
gtk_text_buffer_get_bounds(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end)3062 gtk_text_buffer_get_bounds (GtkTextBuffer *buffer,
3063 GtkTextIter *start,
3064 GtkTextIter *end)
3065 {
3066 g_return_if_fail (start != NULL);
3067 g_return_if_fail (end != NULL);
3068 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3069
3070 _gtk_text_btree_get_iter_at_char (get_btree (buffer), start, 0);
3071 _gtk_text_btree_get_end_iter (get_btree (buffer), end);
3072 }
3073
3074 /*
3075 * Modified flag
3076 */
3077
3078 /**
3079 * gtk_text_buffer_get_modified:
3080 * @buffer: a #GtkTextBuffer
3081 *
3082 * Indicates whether the buffer has been modified since the last call
3083 * to gtk_text_buffer_set_modified() set the modification flag to
3084 * %FALSE. Used for example to enable a “save” function in a text
3085 * editor.
3086 *
3087 * Returns: %TRUE if the buffer has been modified
3088 **/
3089 gboolean
gtk_text_buffer_get_modified(GtkTextBuffer * buffer)3090 gtk_text_buffer_get_modified (GtkTextBuffer *buffer)
3091 {
3092 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
3093
3094 return buffer->priv->modified;
3095 }
3096
3097 /**
3098 * gtk_text_buffer_set_modified:
3099 * @buffer: a #GtkTextBuffer
3100 * @setting: modification flag setting
3101 *
3102 * Used to keep track of whether the buffer has been modified since the
3103 * last time it was saved. Whenever the buffer is saved to disk, call
3104 * gtk_text_buffer_set_modified (@buffer, FALSE). When the buffer is modified,
3105 * it will automatically toggled on the modified bit again. When the modified
3106 * bit flips, the buffer emits the #GtkTextBuffer::modified-changed signal.
3107 **/
3108 void
gtk_text_buffer_set_modified(GtkTextBuffer * buffer,gboolean setting)3109 gtk_text_buffer_set_modified (GtkTextBuffer *buffer,
3110 gboolean setting)
3111 {
3112 gboolean fixed_setting;
3113
3114 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3115
3116 fixed_setting = setting != FALSE;
3117
3118 if (buffer->priv->modified == fixed_setting)
3119 return;
3120 else
3121 {
3122 buffer->priv->modified = fixed_setting;
3123 g_signal_emit (buffer, signals[MODIFIED_CHANGED], 0);
3124 }
3125 }
3126
3127 /**
3128 * gtk_text_buffer_get_has_selection:
3129 * @buffer: a #GtkTextBuffer
3130 *
3131 * Indicates whether the buffer has some text currently selected.
3132 *
3133 * Returns: %TRUE if the there is text selected
3134 *
3135 * Since: 2.10
3136 **/
3137 gboolean
gtk_text_buffer_get_has_selection(GtkTextBuffer * buffer)3138 gtk_text_buffer_get_has_selection (GtkTextBuffer *buffer)
3139 {
3140 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
3141
3142 return buffer->priv->has_selection;
3143 }
3144
3145
3146 /*
3147 * Assorted other stuff
3148 */
3149
3150 /**
3151 * gtk_text_buffer_get_line_count:
3152 * @buffer: a #GtkTextBuffer
3153 *
3154 * Obtains the number of lines in the buffer. This value is cached, so
3155 * the function is very fast.
3156 *
3157 * Returns: number of lines in the buffer
3158 **/
3159 gint
gtk_text_buffer_get_line_count(GtkTextBuffer * buffer)3160 gtk_text_buffer_get_line_count (GtkTextBuffer *buffer)
3161 {
3162 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), 0);
3163
3164 return _gtk_text_btree_line_count (get_btree (buffer));
3165 }
3166
3167 /**
3168 * gtk_text_buffer_get_char_count:
3169 * @buffer: a #GtkTextBuffer
3170 *
3171 * Gets the number of characters in the buffer; note that characters
3172 * and bytes are not the same, you can’t e.g. expect the contents of
3173 * the buffer in string form to be this many bytes long. The character
3174 * count is cached, so this function is very fast.
3175 *
3176 * Returns: number of characters in the buffer
3177 **/
3178 gint
gtk_text_buffer_get_char_count(GtkTextBuffer * buffer)3179 gtk_text_buffer_get_char_count (GtkTextBuffer *buffer)
3180 {
3181 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), 0);
3182
3183 return _gtk_text_btree_char_count (get_btree (buffer));
3184 }
3185
3186 /* Called when we lose the primary selection.
3187 */
3188 static void
clipboard_clear_selection_cb(GtkClipboard * clipboard,gpointer data)3189 clipboard_clear_selection_cb (GtkClipboard *clipboard,
3190 gpointer data)
3191 {
3192 /* Move selection_bound to the insertion point */
3193 GtkTextIter insert;
3194 GtkTextIter selection_bound;
3195 GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
3196
3197 gtk_text_buffer_get_iter_at_mark (buffer, &insert,
3198 gtk_text_buffer_get_insert (buffer));
3199 gtk_text_buffer_get_iter_at_mark (buffer, &selection_bound,
3200 gtk_text_buffer_get_selection_bound (buffer));
3201
3202 if (!gtk_text_iter_equal (&insert, &selection_bound))
3203 gtk_text_buffer_move_mark (buffer,
3204 gtk_text_buffer_get_selection_bound (buffer),
3205 &insert);
3206 }
3207
3208 /* Called when we have the primary selection and someone else wants our
3209 * data in order to paste it.
3210 */
3211 static void
clipboard_get_selection_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)3212 clipboard_get_selection_cb (GtkClipboard *clipboard,
3213 GtkSelectionData *selection_data,
3214 guint info,
3215 gpointer data)
3216 {
3217 GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
3218 GtkTextIter start, end;
3219
3220 if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
3221 {
3222 if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
3223 {
3224 /* Provide the address of the buffer; this will only be
3225 * used within-process
3226 */
3227 gtk_selection_data_set (selection_data,
3228 gtk_selection_data_get_target (selection_data),
3229 8, /* bytes */
3230 (void*)&buffer,
3231 sizeof (buffer));
3232 }
3233 else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
3234 {
3235 guint8 *str;
3236 gsize len;
3237
3238 str = gtk_text_buffer_serialize (buffer, buffer,
3239 gtk_selection_data_get_target (selection_data),
3240 &start, &end, &len);
3241
3242 gtk_selection_data_set (selection_data,
3243 gtk_selection_data_get_target (selection_data),
3244 8, /* bytes */
3245 str, len);
3246 g_free (str);
3247 }
3248 else
3249 {
3250 gchar *str;
3251
3252 str = gtk_text_iter_get_visible_text (&start, &end);
3253 gtk_selection_data_set_text (selection_data, str, -1);
3254 g_free (str);
3255 }
3256 }
3257 }
3258
3259 static GtkTextBuffer *
create_clipboard_contents_buffer(GtkTextBuffer * buffer)3260 create_clipboard_contents_buffer (GtkTextBuffer *buffer)
3261 {
3262 GtkTextBuffer *contents;
3263
3264 contents = gtk_text_buffer_new (gtk_text_buffer_get_tag_table (buffer));
3265
3266 g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard-source"),
3267 buffer);
3268 g_object_set_data (G_OBJECT (contents), I_("gtk-text-buffer-clipboard"),
3269 GINT_TO_POINTER (1));
3270
3271 /* Ref the source buffer as long as the clipboard contents buffer
3272 * exists, because it's needed for serializing the contents buffer.
3273 * See http://bugzilla.gnome.org/show_bug.cgi?id=339195
3274 */
3275 g_object_ref (buffer);
3276 g_object_weak_ref (G_OBJECT (contents), (GWeakNotify) g_object_unref, buffer);
3277
3278 return contents;
3279 }
3280
3281 /* Provide cut/copied data */
3282 static void
clipboard_get_contents_cb(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer data)3283 clipboard_get_contents_cb (GtkClipboard *clipboard,
3284 GtkSelectionData *selection_data,
3285 guint info,
3286 gpointer data)
3287 {
3288 GtkTextBuffer *contents = GTK_TEXT_BUFFER (data);
3289
3290 g_assert (contents); /* This should never be called unless we own the clipboard */
3291
3292 if (info == GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS)
3293 {
3294 /* Provide the address of the clipboard buffer; this will only
3295 * be used within-process. OK to supply a NULL value for contents.
3296 */
3297 gtk_selection_data_set (selection_data,
3298 gtk_selection_data_get_target (selection_data),
3299 8, /* bytes */
3300 (void*)&contents,
3301 sizeof (contents));
3302 }
3303 else if (info == GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT)
3304 {
3305 GtkTextBuffer *clipboard_source_buffer;
3306 GtkTextIter start, end;
3307 guint8 *str;
3308 gsize len;
3309
3310 clipboard_source_buffer = g_object_get_data (G_OBJECT (contents),
3311 "gtk-text-buffer-clipboard-source");
3312
3313 gtk_text_buffer_get_bounds (contents, &start, &end);
3314
3315 str = gtk_text_buffer_serialize (clipboard_source_buffer, contents,
3316 gtk_selection_data_get_target (selection_data),
3317 &start, &end, &len);
3318
3319 gtk_selection_data_set (selection_data,
3320 gtk_selection_data_get_target (selection_data),
3321 8, /* bytes */
3322 str, len);
3323 g_free (str);
3324 }
3325 else
3326 {
3327 gchar *str;
3328 GtkTextIter start, end;
3329
3330 gtk_text_buffer_get_bounds (contents, &start, &end);
3331
3332 str = gtk_text_iter_get_visible_text (&start, &end);
3333 gtk_selection_data_set_text (selection_data, str, -1);
3334 g_free (str);
3335 }
3336 }
3337
3338 static void
clipboard_clear_contents_cb(GtkClipboard * clipboard,gpointer data)3339 clipboard_clear_contents_cb (GtkClipboard *clipboard,
3340 gpointer data)
3341 {
3342 GtkTextBuffer *contents = GTK_TEXT_BUFFER (data);
3343
3344 g_object_unref (contents);
3345 }
3346
3347 static void
get_paste_point(GtkTextBuffer * buffer,GtkTextIter * iter,gboolean clear_afterward)3348 get_paste_point (GtkTextBuffer *buffer,
3349 GtkTextIter *iter,
3350 gboolean clear_afterward)
3351 {
3352 GtkTextIter insert_point;
3353 GtkTextMark *paste_point_override;
3354
3355 paste_point_override = gtk_text_buffer_get_mark (buffer,
3356 "gtk_paste_point_override");
3357
3358 if (paste_point_override != NULL)
3359 {
3360 gtk_text_buffer_get_iter_at_mark (buffer, &insert_point,
3361 paste_point_override);
3362 if (clear_afterward)
3363 gtk_text_buffer_delete_mark (buffer, paste_point_override);
3364 }
3365 else
3366 {
3367 gtk_text_buffer_get_iter_at_mark (buffer, &insert_point,
3368 gtk_text_buffer_get_insert (buffer));
3369 }
3370
3371 *iter = insert_point;
3372 }
3373
3374 static void
pre_paste_prep(ClipboardRequest * request_data,GtkTextIter * insert_point)3375 pre_paste_prep (ClipboardRequest *request_data,
3376 GtkTextIter *insert_point)
3377 {
3378 GtkTextBuffer *buffer = request_data->buffer;
3379
3380 get_paste_point (buffer, insert_point, TRUE);
3381
3382 if (request_data->replace_selection)
3383 {
3384 GtkTextIter start, end;
3385
3386 if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
3387 {
3388 if (request_data->interactive)
3389 gtk_text_buffer_delete_interactive (request_data->buffer,
3390 &start,
3391 &end,
3392 request_data->default_editable);
3393 else
3394 gtk_text_buffer_delete (request_data->buffer, &start, &end);
3395
3396 *insert_point = start;
3397 }
3398 }
3399 }
3400
3401 static void
emit_paste_done(GtkTextBuffer * buffer,GtkClipboard * clipboard)3402 emit_paste_done (GtkTextBuffer *buffer,
3403 GtkClipboard *clipboard)
3404 {
3405 g_signal_emit (buffer, signals[PASTE_DONE], 0, clipboard);
3406 }
3407
3408 static void
free_clipboard_request(ClipboardRequest * request_data)3409 free_clipboard_request (ClipboardRequest *request_data)
3410 {
3411 g_object_unref (request_data->buffer);
3412 g_slice_free (ClipboardRequest, request_data);
3413 }
3414
3415 /* Called when we request a paste and receive the text data
3416 */
3417 static void
clipboard_text_received(GtkClipboard * clipboard,const gchar * str,gpointer data)3418 clipboard_text_received (GtkClipboard *clipboard,
3419 const gchar *str,
3420 gpointer data)
3421 {
3422 ClipboardRequest *request_data = data;
3423 GtkTextBuffer *buffer = request_data->buffer;
3424
3425 if (str)
3426 {
3427 GtkTextIter insert_point;
3428
3429 if (request_data->interactive)
3430 gtk_text_buffer_begin_user_action (buffer);
3431
3432 pre_paste_prep (request_data, &insert_point);
3433
3434 if (request_data->interactive)
3435 gtk_text_buffer_insert_interactive (buffer, &insert_point,
3436 str, -1, request_data->default_editable);
3437 else
3438 gtk_text_buffer_insert (buffer, &insert_point,
3439 str, -1);
3440
3441 if (request_data->interactive)
3442 gtk_text_buffer_end_user_action (buffer);
3443
3444 emit_paste_done (buffer, clipboard);
3445 }
3446 else
3447 {
3448 /* It may happen that we set a point override but we are not inserting
3449 any text, so we must remove it afterwards */
3450 GtkTextMark *paste_point_override;
3451
3452 paste_point_override = gtk_text_buffer_get_mark (buffer,
3453 "gtk_paste_point_override");
3454
3455 if (paste_point_override != NULL)
3456 gtk_text_buffer_delete_mark (buffer, paste_point_override);
3457 }
3458
3459 free_clipboard_request (request_data);
3460 }
3461
3462 static GtkTextBuffer*
selection_data_get_buffer(GtkSelectionData * selection_data,ClipboardRequest * request_data)3463 selection_data_get_buffer (GtkSelectionData *selection_data,
3464 ClipboardRequest *request_data)
3465 {
3466 GdkWindow *owner;
3467 GtkTextBuffer *src_buffer = NULL;
3468
3469 /* If we can get the owner, the selection is in-process */
3470 owner = gdk_selection_owner_get_for_display (gtk_selection_data_get_display (selection_data),
3471 gtk_selection_data_get_selection (selection_data));
3472
3473 if (owner == NULL)
3474 return NULL;
3475
3476 if (gdk_window_get_window_type (owner) == GDK_WINDOW_FOREIGN)
3477 return NULL;
3478
3479 if (gtk_selection_data_get_data_type (selection_data) != gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"))
3480 return NULL;
3481
3482 if (gtk_selection_data_get_length (selection_data) != sizeof (src_buffer))
3483 return NULL;
3484
3485 memcpy (&src_buffer, gtk_selection_data_get_data (selection_data), sizeof (src_buffer));
3486
3487 if (src_buffer == NULL)
3488 return NULL;
3489
3490 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (src_buffer), NULL);
3491
3492 if (gtk_text_buffer_get_tag_table (src_buffer) !=
3493 gtk_text_buffer_get_tag_table (request_data->buffer))
3494 return NULL;
3495
3496 return src_buffer;
3497 }
3498
3499 #if 0
3500 /* These are pretty handy functions; maybe something like them
3501 * should be in the public API. Also, there are other places in this
3502 * file where they could be used.
3503 */
3504 static gpointer
3505 save_iter (const GtkTextIter *iter,
3506 gboolean left_gravity)
3507 {
3508 return gtk_text_buffer_create_mark (gtk_text_iter_get_buffer (iter),
3509 NULL,
3510 iter,
3511 TRUE);
3512 }
3513
3514 static void
3515 restore_iter (const GtkTextIter *iter,
3516 gpointer save_id)
3517 {
3518 gtk_text_buffer_get_iter_at_mark (gtk_text_mark_get_buffer (save_id),
3519 (GtkTextIter*) iter,
3520 save_id);
3521 gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (save_id),
3522 save_id);
3523 }
3524 #endif
3525
3526 static void
clipboard_rich_text_received(GtkClipboard * clipboard,GdkAtom format,const guint8 * text,gsize length,gpointer data)3527 clipboard_rich_text_received (GtkClipboard *clipboard,
3528 GdkAtom format,
3529 const guint8 *text,
3530 gsize length,
3531 gpointer data)
3532 {
3533 ClipboardRequest *request_data = data;
3534 GtkTextIter insert_point;
3535 gboolean retval = TRUE;
3536 GError *error = NULL;
3537
3538 if (text != NULL && length > 0)
3539 {
3540 if (request_data->interactive)
3541 gtk_text_buffer_begin_user_action (request_data->buffer);
3542
3543 pre_paste_prep (request_data, &insert_point);
3544
3545 if (!request_data->interactive ||
3546 gtk_text_iter_can_insert (&insert_point,
3547 request_data->default_editable))
3548 {
3549 retval = gtk_text_buffer_deserialize (request_data->buffer,
3550 request_data->buffer,
3551 format,
3552 &insert_point,
3553 text, length,
3554 &error);
3555 }
3556
3557 if (!retval)
3558 {
3559 g_warning ("error pasting: %s\n", error->message);
3560 g_clear_error (&error);
3561 }
3562
3563 if (request_data->interactive)
3564 gtk_text_buffer_end_user_action (request_data->buffer);
3565
3566 emit_paste_done (request_data->buffer, clipboard);
3567
3568 if (retval)
3569 return;
3570 }
3571
3572 /* Request the text selection instead */
3573 gtk_clipboard_request_text (clipboard,
3574 clipboard_text_received,
3575 data);
3576 }
3577
3578 static void
paste_from_buffer(GtkClipboard * clipboard,ClipboardRequest * request_data,GtkTextBuffer * src_buffer,const GtkTextIter * start,const GtkTextIter * end)3579 paste_from_buffer (GtkClipboard *clipboard,
3580 ClipboardRequest *request_data,
3581 GtkTextBuffer *src_buffer,
3582 const GtkTextIter *start,
3583 const GtkTextIter *end)
3584 {
3585 GtkTextIter insert_point;
3586 GtkTextBuffer *buffer = request_data->buffer;
3587
3588 /* We're about to emit a bunch of signals, so be safe */
3589 g_object_ref (src_buffer);
3590
3591 /* Replacing the selection with itself */
3592 if (request_data->replace_selection &&
3593 buffer == src_buffer)
3594 {
3595 /* Clear the paste point if needed */
3596 get_paste_point (buffer, &insert_point, TRUE);
3597 goto done;
3598 }
3599
3600 if (request_data->interactive)
3601 gtk_text_buffer_begin_user_action (buffer);
3602
3603 pre_paste_prep (request_data, &insert_point);
3604
3605 if (!gtk_text_iter_equal (start, end))
3606 {
3607 if (!request_data->interactive ||
3608 (gtk_text_iter_can_insert (&insert_point,
3609 request_data->default_editable)))
3610 gtk_text_buffer_real_insert_range (buffer,
3611 &insert_point,
3612 start,
3613 end,
3614 request_data->interactive);
3615 }
3616
3617 if (request_data->interactive)
3618 gtk_text_buffer_end_user_action (buffer);
3619
3620 done:
3621 emit_paste_done (buffer, clipboard);
3622
3623 g_object_unref (src_buffer);
3624
3625 free_clipboard_request (request_data);
3626 }
3627
3628 static void
clipboard_clipboard_buffer_received(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer data)3629 clipboard_clipboard_buffer_received (GtkClipboard *clipboard,
3630 GtkSelectionData *selection_data,
3631 gpointer data)
3632 {
3633 ClipboardRequest *request_data = data;
3634 GtkTextBuffer *src_buffer;
3635
3636 src_buffer = selection_data_get_buffer (selection_data, request_data);
3637
3638 if (src_buffer)
3639 {
3640 GtkTextIter start, end;
3641
3642 if (g_object_get_data (G_OBJECT (src_buffer), "gtk-text-buffer-clipboard"))
3643 {
3644 gtk_text_buffer_get_bounds (src_buffer, &start, &end);
3645
3646 paste_from_buffer (clipboard, request_data, src_buffer,
3647 &start, &end);
3648 }
3649 else
3650 {
3651 if (gtk_text_buffer_get_selection_bounds (src_buffer, &start, &end))
3652 paste_from_buffer (clipboard, request_data, src_buffer,
3653 &start, &end);
3654 }
3655 }
3656 else
3657 {
3658 if (gtk_clipboard_wait_is_rich_text_available (clipboard,
3659 request_data->buffer))
3660 {
3661 /* Request rich text */
3662 gtk_clipboard_request_rich_text (clipboard,
3663 request_data->buffer,
3664 clipboard_rich_text_received,
3665 data);
3666 }
3667 else
3668 {
3669 /* Request the text selection instead */
3670 gtk_clipboard_request_text (clipboard,
3671 clipboard_text_received,
3672 data);
3673 }
3674 }
3675 }
3676
3677 typedef struct
3678 {
3679 GtkClipboard *clipboard;
3680 guint ref_count;
3681 } SelectionClipboard;
3682
3683 static void
update_selection_clipboards(GtkTextBuffer * buffer)3684 update_selection_clipboards (GtkTextBuffer *buffer)
3685 {
3686 GtkTextBufferPrivate *priv;
3687 gboolean has_selection;
3688 GtkTextIter start;
3689 GtkTextIter end;
3690 GSList *tmp_list;
3691
3692 priv = buffer->priv;
3693
3694 gtk_text_buffer_get_copy_target_list (buffer);
3695 has_selection = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
3696 tmp_list = buffer->priv->selection_clipboards;
3697
3698 while (tmp_list)
3699 {
3700 SelectionClipboard *selection_clipboard = tmp_list->data;
3701 GtkClipboard *clipboard = selection_clipboard->clipboard;
3702
3703 if (has_selection)
3704 {
3705 /* Even if we already have the selection, we need to update our
3706 * timestamp.
3707 */
3708 gtk_clipboard_set_with_owner (clipboard,
3709 priv->copy_target_entries,
3710 priv->n_copy_target_entries,
3711 clipboard_get_selection_cb,
3712 clipboard_clear_selection_cb,
3713 G_OBJECT (buffer));
3714 }
3715 else if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (buffer))
3716 gtk_clipboard_clear (clipboard);
3717
3718 tmp_list = tmp_list->next;
3719 }
3720 }
3721
3722 static SelectionClipboard *
find_selection_clipboard(GtkTextBuffer * buffer,GtkClipboard * clipboard)3723 find_selection_clipboard (GtkTextBuffer *buffer,
3724 GtkClipboard *clipboard)
3725 {
3726 GSList *tmp_list = buffer->priv->selection_clipboards;
3727 while (tmp_list)
3728 {
3729 SelectionClipboard *selection_clipboard = tmp_list->data;
3730 if (selection_clipboard->clipboard == clipboard)
3731 return selection_clipboard;
3732
3733 tmp_list = tmp_list->next;
3734 }
3735
3736 return NULL;
3737 }
3738
3739 /**
3740 * gtk_text_buffer_add_selection_clipboard:
3741 * @buffer: a #GtkTextBuffer
3742 * @clipboard: a #GtkClipboard
3743 *
3744 * Adds @clipboard to the list of clipboards in which the selection
3745 * contents of @buffer are available. In most cases, @clipboard will be
3746 * the #GtkClipboard of type %GDK_SELECTION_PRIMARY for a view of @buffer.
3747 **/
3748 void
gtk_text_buffer_add_selection_clipboard(GtkTextBuffer * buffer,GtkClipboard * clipboard)3749 gtk_text_buffer_add_selection_clipboard (GtkTextBuffer *buffer,
3750 GtkClipboard *clipboard)
3751 {
3752 SelectionClipboard *selection_clipboard;
3753
3754 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3755 g_return_if_fail (clipboard != NULL);
3756
3757 selection_clipboard = find_selection_clipboard (buffer, clipboard);
3758 if (selection_clipboard)
3759 {
3760 selection_clipboard->ref_count++;
3761 }
3762 else
3763 {
3764 selection_clipboard = g_slice_new (SelectionClipboard);
3765
3766 selection_clipboard->clipboard = clipboard;
3767 selection_clipboard->ref_count = 1;
3768
3769 buffer->priv->selection_clipboards = g_slist_prepend (buffer->priv->selection_clipboards,
3770 selection_clipboard);
3771 }
3772 }
3773
3774 /**
3775 * gtk_text_buffer_remove_selection_clipboard:
3776 * @buffer: a #GtkTextBuffer
3777 * @clipboard: a #GtkClipboard added to @buffer by
3778 * gtk_text_buffer_add_selection_clipboard()
3779 *
3780 * Removes a #GtkClipboard added with
3781 * gtk_text_buffer_add_selection_clipboard().
3782 **/
3783 void
gtk_text_buffer_remove_selection_clipboard(GtkTextBuffer * buffer,GtkClipboard * clipboard)3784 gtk_text_buffer_remove_selection_clipboard (GtkTextBuffer *buffer,
3785 GtkClipboard *clipboard)
3786 {
3787 SelectionClipboard *selection_clipboard;
3788
3789 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
3790 g_return_if_fail (clipboard != NULL);
3791
3792 selection_clipboard = find_selection_clipboard (buffer, clipboard);
3793 g_return_if_fail (selection_clipboard != NULL);
3794
3795 selection_clipboard->ref_count--;
3796 if (selection_clipboard->ref_count == 0)
3797 {
3798 if (gtk_clipboard_get_owner (selection_clipboard->clipboard) == G_OBJECT (buffer))
3799 gtk_clipboard_clear (selection_clipboard->clipboard);
3800
3801 buffer->priv->selection_clipboards = g_slist_remove (buffer->priv->selection_clipboards,
3802 selection_clipboard);
3803
3804 g_slice_free (SelectionClipboard, selection_clipboard);
3805 }
3806 }
3807
3808 static void
remove_all_selection_clipboards(GtkTextBuffer * buffer)3809 remove_all_selection_clipboards (GtkTextBuffer *buffer)
3810 {
3811 GtkTextBufferPrivate *priv = buffer->priv;
3812 GSList *l;
3813
3814 for (l = priv->selection_clipboards; l != NULL; l = l->next)
3815 {
3816 SelectionClipboard *selection_clipboard = l->data;
3817 g_slice_free (SelectionClipboard, selection_clipboard);
3818 }
3819
3820 g_slist_free (priv->selection_clipboards);
3821 priv->selection_clipboards = NULL;
3822 }
3823
3824 /**
3825 * gtk_text_buffer_paste_clipboard:
3826 * @buffer: a #GtkTextBuffer
3827 * @clipboard: the #GtkClipboard to paste from
3828 * @override_location: (allow-none): location to insert pasted text, or %NULL
3829 * @default_editable: whether the buffer is editable by default
3830 *
3831 * Pastes the contents of a clipboard. If @override_location is %NULL, the
3832 * pasted text will be inserted at the cursor position, or the buffer selection
3833 * will be replaced if the selection is non-empty.
3834 *
3835 * Note: pasting is asynchronous, that is, we’ll ask for the paste data and
3836 * return, and at some point later after the main loop runs, the paste data will
3837 * be inserted.
3838 **/
3839 void
gtk_text_buffer_paste_clipboard(GtkTextBuffer * buffer,GtkClipboard * clipboard,GtkTextIter * override_location,gboolean default_editable)3840 gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer,
3841 GtkClipboard *clipboard,
3842 GtkTextIter *override_location,
3843 gboolean default_editable)
3844 {
3845 ClipboardRequest *data = g_slice_new (ClipboardRequest);
3846 GtkTextIter paste_point;
3847 GtkTextIter start, end;
3848
3849 if (override_location != NULL)
3850 gtk_text_buffer_create_mark (buffer,
3851 "gtk_paste_point_override",
3852 override_location, FALSE);
3853
3854 data->buffer = g_object_ref (buffer);
3855 data->interactive = TRUE;
3856 data->default_editable = !!default_editable;
3857
3858 /* When pasting with the cursor inside the selection area, you
3859 * replace the selection with the new text, otherwise, you
3860 * simply insert the new text at the point where the click
3861 * occurred, unselecting any selected text. The replace_selection
3862 * flag toggles this behavior.
3863 */
3864 data->replace_selection = FALSE;
3865
3866 get_paste_point (buffer, &paste_point, FALSE);
3867 if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end) &&
3868 (gtk_text_iter_in_range (&paste_point, &start, &end) ||
3869 gtk_text_iter_equal (&paste_point, &end)))
3870 data->replace_selection = TRUE;
3871
3872 gtk_clipboard_request_contents (clipboard,
3873 gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
3874 clipboard_clipboard_buffer_received, data);
3875 }
3876
3877 /**
3878 * gtk_text_buffer_delete_selection:
3879 * @buffer: a #GtkTextBuffer
3880 * @interactive: whether the deletion is caused by user interaction
3881 * @default_editable: whether the buffer is editable by default
3882 *
3883 * Deletes the range between the “insert” and “selection_bound” marks,
3884 * that is, the currently-selected text. If @interactive is %TRUE,
3885 * the editability of the selection will be considered (users can’t delete
3886 * uneditable text).
3887 *
3888 * Returns: whether there was a non-empty selection to delete
3889 **/
3890 gboolean
gtk_text_buffer_delete_selection(GtkTextBuffer * buffer,gboolean interactive,gboolean default_editable)3891 gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
3892 gboolean interactive,
3893 gboolean default_editable)
3894 {
3895 GtkTextIter start;
3896 GtkTextIter end;
3897
3898 if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
3899 {
3900 return FALSE; /* No selection */
3901 }
3902 else
3903 {
3904 if (interactive)
3905 gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable);
3906 else
3907 gtk_text_buffer_delete (buffer, &start, &end);
3908
3909 return TRUE; /* We deleted stuff */
3910 }
3911 }
3912
3913 /**
3914 * gtk_text_buffer_backspace:
3915 * @buffer: a #GtkTextBuffer
3916 * @iter: a position in @buffer
3917 * @interactive: whether the deletion is caused by user interaction
3918 * @default_editable: whether the buffer is editable by default
3919 *
3920 * Performs the appropriate action as if the user hit the delete
3921 * key with the cursor at the position specified by @iter. In the
3922 * normal case a single character will be deleted, but when
3923 * combining accents are involved, more than one character can
3924 * be deleted, and when precomposed character and accent combinations
3925 * are involved, less than one character will be deleted.
3926 *
3927 * Because the buffer is modified, all outstanding iterators become
3928 * invalid after calling this function; however, the @iter will be
3929 * re-initialized to point to the location where text was deleted.
3930 *
3931 * Returns: %TRUE if the buffer was modified
3932 *
3933 * Since: 2.6
3934 **/
3935 gboolean
gtk_text_buffer_backspace(GtkTextBuffer * buffer,GtkTextIter * iter,gboolean interactive,gboolean default_editable)3936 gtk_text_buffer_backspace (GtkTextBuffer *buffer,
3937 GtkTextIter *iter,
3938 gboolean interactive,
3939 gboolean default_editable)
3940 {
3941 gchar *cluster_text;
3942 GtkTextIter start;
3943 GtkTextIter end;
3944 gboolean retval = FALSE;
3945 const PangoLogAttr *attrs;
3946 gint offset;
3947 gboolean backspace_deletes_character;
3948
3949 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
3950 g_return_val_if_fail (iter != NULL, FALSE);
3951
3952 start = *iter;
3953 end = *iter;
3954
3955 attrs = _gtk_text_buffer_get_line_log_attrs (buffer, &start, NULL);
3956 offset = gtk_text_iter_get_line_offset (&start);
3957 backspace_deletes_character = attrs[offset].backspace_deletes_character;
3958
3959 gtk_text_iter_backward_cursor_position (&start);
3960
3961 if (gtk_text_iter_equal (&start, &end))
3962 return FALSE;
3963
3964 cluster_text = gtk_text_iter_get_text (&start, &end);
3965
3966 if (interactive)
3967 gtk_text_buffer_begin_user_action (buffer);
3968
3969 if (gtk_text_buffer_delete_interactive (buffer, &start, &end,
3970 default_editable))
3971 {
3972 /* special case \r\n, since we never want to reinsert \r */
3973 if (backspace_deletes_character && strcmp ("\r\n", cluster_text))
3974 {
3975 gchar *normalized_text = g_utf8_normalize (cluster_text,
3976 strlen (cluster_text),
3977 G_NORMALIZE_NFD);
3978 glong len = g_utf8_strlen (normalized_text, -1);
3979
3980 if (len > 1)
3981 gtk_text_buffer_insert_interactive (buffer,
3982 &start,
3983 normalized_text,
3984 g_utf8_offset_to_pointer (normalized_text, len - 1) - normalized_text,
3985 default_editable);
3986
3987 g_free (normalized_text);
3988 }
3989
3990 retval = TRUE;
3991 }
3992
3993 if (interactive)
3994 gtk_text_buffer_end_user_action (buffer);
3995
3996 g_free (cluster_text);
3997
3998 /* Revalidate the users iter */
3999 *iter = start;
4000
4001 return retval;
4002 }
4003
4004 static void
cut_or_copy(GtkTextBuffer * buffer,GtkClipboard * clipboard,gboolean delete_region_after,gboolean interactive,gboolean default_editable)4005 cut_or_copy (GtkTextBuffer *buffer,
4006 GtkClipboard *clipboard,
4007 gboolean delete_region_after,
4008 gboolean interactive,
4009 gboolean default_editable)
4010 {
4011 GtkTextBufferPrivate *priv;
4012
4013 /* We prefer to cut the selected region between selection_bound and
4014 * insertion point. If that region is empty, then we cut the region
4015 * between the "anchor" and the insertion point (this is for
4016 * C-space and M-w and other Emacs-style copy/yank keys). Note that
4017 * insert and selection_bound are guaranteed to exist, but the
4018 * anchor only exists sometimes.
4019 */
4020 GtkTextIter start;
4021 GtkTextIter end;
4022
4023 priv = buffer->priv;
4024
4025 gtk_text_buffer_get_copy_target_list (buffer);
4026
4027 if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
4028 {
4029 /* Let's try the anchor thing */
4030 GtkTextMark * anchor = gtk_text_buffer_get_mark (buffer, "anchor");
4031
4032 if (anchor == NULL)
4033 return;
4034 else
4035 {
4036 gtk_text_buffer_get_iter_at_mark (buffer, &end, anchor);
4037 gtk_text_iter_order (&start, &end);
4038 }
4039 }
4040
4041 if (!gtk_text_iter_equal (&start, &end))
4042 {
4043 GtkTextIter ins;
4044 GtkTextBuffer *contents;
4045
4046 contents = create_clipboard_contents_buffer (buffer);
4047
4048 gtk_text_buffer_get_iter_at_offset (contents, &ins, 0);
4049
4050 gtk_text_buffer_insert_range (contents, &ins, &start, &end);
4051
4052 if (!gtk_clipboard_set_with_data (clipboard,
4053 priv->copy_target_entries,
4054 priv->n_copy_target_entries,
4055 clipboard_get_contents_cb,
4056 clipboard_clear_contents_cb,
4057 contents))
4058 g_object_unref (contents);
4059 else
4060 gtk_clipboard_set_can_store (clipboard,
4061 priv->copy_target_entries + 1,
4062 priv->n_copy_target_entries - 1);
4063
4064 if (delete_region_after)
4065 {
4066 if (interactive)
4067 gtk_text_buffer_delete_interactive (buffer, &start, &end,
4068 default_editable);
4069 else
4070 gtk_text_buffer_delete (buffer, &start, &end);
4071 }
4072 }
4073 }
4074
4075 /**
4076 * gtk_text_buffer_cut_clipboard:
4077 * @buffer: a #GtkTextBuffer
4078 * @clipboard: the #GtkClipboard object to cut to
4079 * @default_editable: default editability of the buffer
4080 *
4081 * Copies the currently-selected text to a clipboard, then deletes
4082 * said text if it’s editable.
4083 **/
4084 void
gtk_text_buffer_cut_clipboard(GtkTextBuffer * buffer,GtkClipboard * clipboard,gboolean default_editable)4085 gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer,
4086 GtkClipboard *clipboard,
4087 gboolean default_editable)
4088 {
4089 gtk_text_buffer_begin_user_action (buffer);
4090 cut_or_copy (buffer, clipboard, TRUE, TRUE, default_editable);
4091 gtk_text_buffer_end_user_action (buffer);
4092 }
4093
4094 /**
4095 * gtk_text_buffer_copy_clipboard:
4096 * @buffer: a #GtkTextBuffer
4097 * @clipboard: the #GtkClipboard object to copy to
4098 *
4099 * Copies the currently-selected text to a clipboard.
4100 **/
4101 void
gtk_text_buffer_copy_clipboard(GtkTextBuffer * buffer,GtkClipboard * clipboard)4102 gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer,
4103 GtkClipboard *clipboard)
4104 {
4105 cut_or_copy (buffer, clipboard, FALSE, TRUE, TRUE);
4106 }
4107
4108 /**
4109 * gtk_text_buffer_get_selection_bounds:
4110 * @buffer: a #GtkTextBuffer a #GtkTextBuffer
4111 * @start: (out): iterator to initialize with selection start
4112 * @end: (out): iterator to initialize with selection end
4113 *
4114 * Returns %TRUE if some text is selected; places the bounds
4115 * of the selection in @start and @end (if the selection has length 0,
4116 * then @start and @end are filled in with the same value).
4117 * @start and @end will be in ascending order. If @start and @end are
4118 * NULL, then they are not filled in, but the return value still indicates
4119 * whether text is selected.
4120 *
4121 * Returns: whether the selection has nonzero length
4122 **/
4123 gboolean
gtk_text_buffer_get_selection_bounds(GtkTextBuffer * buffer,GtkTextIter * start,GtkTextIter * end)4124 gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer,
4125 GtkTextIter *start,
4126 GtkTextIter *end)
4127 {
4128 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
4129
4130 return _gtk_text_btree_get_selection_bounds (get_btree (buffer), start, end);
4131 }
4132
4133 /**
4134 * gtk_text_buffer_begin_user_action:
4135 * @buffer: a #GtkTextBuffer
4136 *
4137 * Called to indicate that the buffer operations between here and a
4138 * call to gtk_text_buffer_end_user_action() are part of a single
4139 * user-visible operation. The operations between
4140 * gtk_text_buffer_begin_user_action() and
4141 * gtk_text_buffer_end_user_action() can then be grouped when creating
4142 * an undo stack. #GtkTextBuffer maintains a count of calls to
4143 * gtk_text_buffer_begin_user_action() that have not been closed with
4144 * a call to gtk_text_buffer_end_user_action(), and emits the
4145 * “begin-user-action” and “end-user-action” signals only for the
4146 * outermost pair of calls. This allows you to build user actions
4147 * from other user actions.
4148 *
4149 * The “interactive” buffer mutation functions, such as
4150 * gtk_text_buffer_insert_interactive(), automatically call begin/end
4151 * user action around the buffer operations they perform, so there's
4152 * no need to add extra calls if you user action consists solely of a
4153 * single call to one of those functions.
4154 **/
4155 void
gtk_text_buffer_begin_user_action(GtkTextBuffer * buffer)4156 gtk_text_buffer_begin_user_action (GtkTextBuffer *buffer)
4157 {
4158 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
4159
4160 buffer->priv->user_action_count += 1;
4161
4162 if (buffer->priv->user_action_count == 1)
4163 {
4164 /* Outermost nested user action begin emits the signal */
4165 g_signal_emit (buffer, signals[BEGIN_USER_ACTION], 0);
4166 }
4167 }
4168
4169 /**
4170 * gtk_text_buffer_end_user_action:
4171 * @buffer: a #GtkTextBuffer
4172 *
4173 * Should be paired with a call to gtk_text_buffer_begin_user_action().
4174 * See that function for a full explanation.
4175 **/
4176 void
gtk_text_buffer_end_user_action(GtkTextBuffer * buffer)4177 gtk_text_buffer_end_user_action (GtkTextBuffer *buffer)
4178 {
4179 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
4180 g_return_if_fail (buffer->priv->user_action_count > 0);
4181
4182 buffer->priv->user_action_count -= 1;
4183
4184 if (buffer->priv->user_action_count == 0)
4185 {
4186 /* Ended the outermost-nested user action end, so emit the signal */
4187 g_signal_emit (buffer, signals[END_USER_ACTION], 0);
4188 }
4189 }
4190
4191 static void
gtk_text_buffer_free_target_lists(GtkTextBuffer * buffer)4192 gtk_text_buffer_free_target_lists (GtkTextBuffer *buffer)
4193 {
4194 GtkTextBufferPrivate *priv = buffer->priv;
4195
4196 if (priv->copy_target_list)
4197 {
4198 gtk_target_list_unref (priv->copy_target_list);
4199 priv->copy_target_list = NULL;
4200
4201 gtk_target_table_free (priv->copy_target_entries,
4202 priv->n_copy_target_entries);
4203 priv->copy_target_entries = NULL;
4204 priv->n_copy_target_entries = 0;
4205 }
4206
4207 if (priv->paste_target_list)
4208 {
4209 gtk_target_list_unref (priv->paste_target_list);
4210 priv->paste_target_list = NULL;
4211
4212 gtk_target_table_free (priv->paste_target_entries,
4213 priv->n_paste_target_entries);
4214 priv->paste_target_entries = NULL;
4215 priv->n_paste_target_entries = 0;
4216 }
4217 }
4218
4219 static GtkTargetList *
gtk_text_buffer_get_target_list(GtkTextBuffer * buffer,gboolean deserializable,GtkTargetEntry ** entries,gint * n_entries)4220 gtk_text_buffer_get_target_list (GtkTextBuffer *buffer,
4221 gboolean deserializable,
4222 GtkTargetEntry **entries,
4223 gint *n_entries)
4224 {
4225 GtkTargetList *target_list;
4226
4227 target_list = gtk_target_list_new (NULL, 0);
4228
4229 gtk_target_list_add (target_list,
4230 gdk_atom_intern_static_string ("GTK_TEXT_BUFFER_CONTENTS"),
4231 GTK_TARGET_SAME_APP,
4232 GTK_TEXT_BUFFER_TARGET_INFO_BUFFER_CONTENTS);
4233
4234 gtk_target_list_add_rich_text_targets (target_list,
4235 GTK_TEXT_BUFFER_TARGET_INFO_RICH_TEXT,
4236 deserializable,
4237 buffer);
4238
4239 gtk_target_list_add_text_targets (target_list,
4240 GTK_TEXT_BUFFER_TARGET_INFO_TEXT);
4241
4242 *entries = gtk_target_table_new_from_list (target_list, n_entries);
4243
4244 return target_list;
4245 }
4246
4247 /**
4248 * gtk_text_buffer_get_copy_target_list:
4249 * @buffer: a #GtkTextBuffer
4250 *
4251 * This function returns the list of targets this text buffer can
4252 * provide for copying and as DND source. The targets in the list are
4253 * added with @info values from the #GtkTextBufferTargetInfo enum,
4254 * using gtk_target_list_add_rich_text_targets() and
4255 * gtk_target_list_add_text_targets().
4256 *
4257 * Returns: (transfer none): the #GtkTargetList
4258 *
4259 * Since: 2.10
4260 **/
4261 GtkTargetList *
gtk_text_buffer_get_copy_target_list(GtkTextBuffer * buffer)4262 gtk_text_buffer_get_copy_target_list (GtkTextBuffer *buffer)
4263 {
4264 GtkTextBufferPrivate *priv;
4265
4266 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
4267
4268 priv = buffer->priv;
4269
4270 if (! priv->copy_target_list)
4271 priv->copy_target_list =
4272 gtk_text_buffer_get_target_list (buffer, FALSE,
4273 &priv->copy_target_entries,
4274 &priv->n_copy_target_entries);
4275
4276 return priv->copy_target_list;
4277 }
4278
4279 /**
4280 * gtk_text_buffer_get_paste_target_list:
4281 * @buffer: a #GtkTextBuffer
4282 *
4283 * This function returns the list of targets this text buffer supports
4284 * for pasting and as DND destination. The targets in the list are
4285 * added with @info values from the #GtkTextBufferTargetInfo enum,
4286 * using gtk_target_list_add_rich_text_targets() and
4287 * gtk_target_list_add_text_targets().
4288 *
4289 * Returns: (transfer none): the #GtkTargetList
4290 *
4291 * Since: 2.10
4292 **/
4293 GtkTargetList *
gtk_text_buffer_get_paste_target_list(GtkTextBuffer * buffer)4294 gtk_text_buffer_get_paste_target_list (GtkTextBuffer *buffer)
4295 {
4296 GtkTextBufferPrivate *priv;
4297
4298 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
4299
4300 priv = buffer->priv;
4301
4302 if (! priv->paste_target_list)
4303 priv->paste_target_list =
4304 gtk_text_buffer_get_target_list (buffer, TRUE,
4305 &priv->paste_target_entries,
4306 &priv->n_paste_target_entries);
4307
4308 return priv->paste_target_list;
4309 }
4310
4311 /*
4312 * Logical attribute cache
4313 */
4314
4315 #define ATTR_CACHE_SIZE 2
4316
4317 typedef struct _CacheEntry CacheEntry;
4318 struct _CacheEntry
4319 {
4320 gint line;
4321 gint char_len;
4322 PangoLogAttr *attrs;
4323 };
4324
4325 struct _GtkTextLogAttrCache
4326 {
4327 gint chars_changed_stamp;
4328 CacheEntry entries[ATTR_CACHE_SIZE];
4329 };
4330
4331 static void
free_log_attr_cache(GtkTextLogAttrCache * cache)4332 free_log_attr_cache (GtkTextLogAttrCache *cache)
4333 {
4334 gint i;
4335
4336 for (i = 0; i < ATTR_CACHE_SIZE; i++)
4337 g_free (cache->entries[i].attrs);
4338
4339 g_slice_free (GtkTextLogAttrCache, cache);
4340 }
4341
4342 static void
clear_log_attr_cache(GtkTextLogAttrCache * cache)4343 clear_log_attr_cache (GtkTextLogAttrCache *cache)
4344 {
4345 gint i;
4346
4347 for (i = 0; i < ATTR_CACHE_SIZE; i++)
4348 {
4349 g_free (cache->entries[i].attrs);
4350 cache->entries[i].attrs = NULL;
4351 }
4352 }
4353
4354 static PangoLogAttr*
compute_log_attrs(const GtkTextIter * iter,gint * char_lenp)4355 compute_log_attrs (const GtkTextIter *iter,
4356 gint *char_lenp)
4357 {
4358 GtkTextIter start;
4359 GtkTextIter end;
4360 gchar *paragraph;
4361 gint char_len, byte_len;
4362 PangoLogAttr *attrs = NULL;
4363
4364 start = *iter;
4365 end = *iter;
4366
4367 gtk_text_iter_set_line_offset (&start, 0);
4368 gtk_text_iter_forward_line (&end);
4369
4370 paragraph = gtk_text_iter_get_slice (&start, &end);
4371 char_len = g_utf8_strlen (paragraph, -1);
4372 byte_len = strlen (paragraph);
4373
4374 if (char_lenp != NULL)
4375 *char_lenp = char_len;
4376
4377 attrs = g_new (PangoLogAttr, char_len + 1);
4378
4379 /* FIXME we need to follow PangoLayout and allow different language
4380 * tags within the paragraph
4381 */
4382 pango_get_log_attrs (paragraph, byte_len, -1,
4383 gtk_text_iter_get_language (&start),
4384 attrs,
4385 char_len + 1);
4386
4387 g_free (paragraph);
4388
4389 return attrs;
4390 }
4391
4392 /* The return value from this is valid until you call this a second time.
4393 * Returns (char_len + 1) PangoLogAttr's, one for each text position.
4394 */
4395 const PangoLogAttr *
_gtk_text_buffer_get_line_log_attrs(GtkTextBuffer * buffer,const GtkTextIter * anywhere_in_line,gint * char_len)4396 _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer *buffer,
4397 const GtkTextIter *anywhere_in_line,
4398 gint *char_len)
4399 {
4400 GtkTextBufferPrivate *priv;
4401 gint line;
4402 GtkTextLogAttrCache *cache;
4403 gint i;
4404
4405 g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
4406 g_return_val_if_fail (anywhere_in_line != NULL, NULL);
4407
4408 priv = buffer->priv;
4409
4410 /* FIXME we also need to recompute log attrs if the language tag at
4411 * the start of a paragraph changes
4412 */
4413
4414 if (priv->log_attr_cache == NULL)
4415 {
4416 priv->log_attr_cache = g_slice_new0 (GtkTextLogAttrCache);
4417 priv->log_attr_cache->chars_changed_stamp =
4418 _gtk_text_btree_get_chars_changed_stamp (get_btree (buffer));
4419 }
4420 else if (priv->log_attr_cache->chars_changed_stamp !=
4421 _gtk_text_btree_get_chars_changed_stamp (get_btree (buffer)))
4422 {
4423 clear_log_attr_cache (priv->log_attr_cache);
4424 }
4425
4426 cache = priv->log_attr_cache;
4427 line = gtk_text_iter_get_line (anywhere_in_line);
4428
4429 for (i = 0; i < ATTR_CACHE_SIZE; i++)
4430 {
4431 if (cache->entries[i].attrs != NULL &&
4432 cache->entries[i].line == line)
4433 {
4434 if (char_len != NULL)
4435 *char_len = cache->entries[i].char_len;
4436 return cache->entries[i].attrs;
4437 }
4438 }
4439
4440 /* Not in cache; open up the first cache entry */
4441 g_free (cache->entries[ATTR_CACHE_SIZE-1].attrs);
4442
4443 memmove (cache->entries + 1, cache->entries,
4444 sizeof (CacheEntry) * (ATTR_CACHE_SIZE - 1));
4445
4446 cache->entries[0].line = line;
4447 cache->entries[0].attrs = compute_log_attrs (anywhere_in_line,
4448 &cache->entries[0].char_len);
4449
4450 if (char_len != NULL)
4451 *char_len = cache->entries[0].char_len;
4452
4453 return cache->entries[0].attrs;
4454 }
4455
4456 void
_gtk_text_buffer_notify_will_remove_tag(GtkTextBuffer * buffer,GtkTextTag * tag)4457 _gtk_text_buffer_notify_will_remove_tag (GtkTextBuffer *buffer,
4458 GtkTextTag *tag)
4459 {
4460 /* This removes tag from the buffer, but DOESN'T emit the
4461 * remove-tag signal, because we can't afford to have user
4462 * code messing things up at this point; the tag MUST be removed
4463 * entirely.
4464 */
4465 if (buffer->priv->btree)
4466 _gtk_text_btree_notify_will_remove_tag (buffer->priv->btree, tag);
4467 }
4468
4469 /*
4470 * Debug spew
4471 */
4472
4473 void
_gtk_text_buffer_spew(GtkTextBuffer * buffer)4474 _gtk_text_buffer_spew (GtkTextBuffer *buffer)
4475 {
4476 _gtk_text_btree_spew (get_btree (buffer));
4477 }
4478
4479 void
_gtk_text_buffer_get_text_before(GtkTextBuffer * buffer,AtkTextBoundary boundary_type,GtkTextIter * position,GtkTextIter * start,GtkTextIter * end)4480 _gtk_text_buffer_get_text_before (GtkTextBuffer *buffer,
4481 AtkTextBoundary boundary_type,
4482 GtkTextIter *position,
4483 GtkTextIter *start,
4484 GtkTextIter *end)
4485 {
4486 gint line_number;
4487
4488 *start = *position;
4489 *end = *start;
4490
4491 switch (boundary_type)
4492 {
4493 case ATK_TEXT_BOUNDARY_CHAR:
4494 gtk_text_iter_backward_char (start);
4495 break;
4496
4497 case ATK_TEXT_BOUNDARY_WORD_START:
4498 if (!gtk_text_iter_starts_word (start))
4499 gtk_text_iter_backward_word_start (start);
4500 *end = *start;
4501 gtk_text_iter_backward_word_start (start);
4502 break;
4503
4504 case ATK_TEXT_BOUNDARY_WORD_END:
4505 if (gtk_text_iter_inside_word (start) &&
4506 !gtk_text_iter_starts_word (start))
4507 gtk_text_iter_backward_word_start (start);
4508 while (!gtk_text_iter_ends_word (start))
4509 {
4510 if (!gtk_text_iter_backward_char (start))
4511 break;
4512 }
4513 *end = *start;
4514 gtk_text_iter_backward_word_start (start);
4515 while (!gtk_text_iter_ends_word (start))
4516 {
4517 if (!gtk_text_iter_backward_char (start))
4518 break;
4519 }
4520 break;
4521
4522 case ATK_TEXT_BOUNDARY_SENTENCE_START:
4523 if (!gtk_text_iter_starts_sentence (start))
4524 gtk_text_iter_backward_sentence_start (start);
4525 *end = *start;
4526 gtk_text_iter_backward_sentence_start (start);
4527 break;
4528
4529 case ATK_TEXT_BOUNDARY_SENTENCE_END:
4530 if (gtk_text_iter_inside_sentence (start) &&
4531 !gtk_text_iter_starts_sentence (start))
4532 gtk_text_iter_backward_sentence_start (start);
4533 while (!gtk_text_iter_ends_sentence (start))
4534 {
4535 if (!gtk_text_iter_backward_char (start))
4536 break;
4537 }
4538 *end = *start;
4539 gtk_text_iter_backward_sentence_start (start);
4540 while (!gtk_text_iter_ends_sentence (start))
4541 {
4542 if (!gtk_text_iter_backward_char (start))
4543 break;
4544 }
4545 break;
4546
4547 case ATK_TEXT_BOUNDARY_LINE_START:
4548 line_number = gtk_text_iter_get_line (start);
4549 if (line_number == 0)
4550 {
4551 gtk_text_buffer_get_iter_at_offset (buffer, start, 0);
4552 }
4553 else
4554 {
4555 gtk_text_iter_backward_line (start);
4556 gtk_text_iter_forward_line (start);
4557 }
4558 *end = *start;
4559 gtk_text_iter_backward_line (start);
4560 break;
4561
4562 case ATK_TEXT_BOUNDARY_LINE_END:
4563 line_number = gtk_text_iter_get_line (start);
4564 if (line_number == 0)
4565 {
4566 gtk_text_buffer_get_iter_at_offset (buffer, start, 0);
4567 *end = *start;
4568 }
4569 else
4570 {
4571 gtk_text_iter_backward_line (start);
4572 *end = *start;
4573 while (!gtk_text_iter_ends_line (start))
4574 {
4575 if (!gtk_text_iter_backward_char (start))
4576 break;
4577 }
4578 gtk_text_iter_forward_to_line_end (end);
4579 }
4580 break;
4581 }
4582 }
4583
4584 void
_gtk_text_buffer_get_text_at(GtkTextBuffer * buffer,AtkTextBoundary boundary_type,GtkTextIter * position,GtkTextIter * start,GtkTextIter * end)4585 _gtk_text_buffer_get_text_at (GtkTextBuffer *buffer,
4586 AtkTextBoundary boundary_type,
4587 GtkTextIter *position,
4588 GtkTextIter *start,
4589 GtkTextIter *end)
4590 {
4591 gint line_number;
4592
4593 *start = *position;
4594 *end = *start;
4595
4596 switch (boundary_type)
4597 {
4598 case ATK_TEXT_BOUNDARY_CHAR:
4599 gtk_text_iter_forward_char (end);
4600 break;
4601
4602 case ATK_TEXT_BOUNDARY_WORD_START:
4603 if (!gtk_text_iter_starts_word (start))
4604 gtk_text_iter_backward_word_start (start);
4605 if (gtk_text_iter_inside_word (end))
4606 gtk_text_iter_forward_word_end (end);
4607 while (!gtk_text_iter_starts_word (end))
4608 {
4609 if (!gtk_text_iter_forward_char (end))
4610 break;
4611 }
4612 break;
4613
4614 case ATK_TEXT_BOUNDARY_WORD_END:
4615 if (gtk_text_iter_inside_word (start) &&
4616 !gtk_text_iter_starts_word (start))
4617 gtk_text_iter_backward_word_start (start);
4618 while (!gtk_text_iter_ends_word (start))
4619 {
4620 if (!gtk_text_iter_backward_char (start))
4621 break;
4622 }
4623 gtk_text_iter_forward_word_end (end);
4624 break;
4625
4626 case ATK_TEXT_BOUNDARY_SENTENCE_START:
4627 if (!gtk_text_iter_starts_sentence (start))
4628 gtk_text_iter_backward_sentence_start (start);
4629 if (gtk_text_iter_inside_sentence (end))
4630 gtk_text_iter_forward_sentence_end (end);
4631 while (!gtk_text_iter_starts_sentence (end))
4632 {
4633 if (!gtk_text_iter_forward_char (end))
4634 break;
4635 }
4636 break;
4637
4638 case ATK_TEXT_BOUNDARY_SENTENCE_END:
4639 if (gtk_text_iter_inside_sentence (start) &&
4640 !gtk_text_iter_starts_sentence (start))
4641 gtk_text_iter_backward_sentence_start (start);
4642 while (!gtk_text_iter_ends_sentence (start))
4643 {
4644 if (!gtk_text_iter_backward_char (start))
4645 break;
4646 }
4647 gtk_text_iter_forward_sentence_end (end);
4648 break;
4649
4650 case ATK_TEXT_BOUNDARY_LINE_START:
4651 line_number = gtk_text_iter_get_line (start);
4652 if (line_number == 0)
4653 {
4654 gtk_text_buffer_get_iter_at_offset (buffer, start, 0);
4655 }
4656 else
4657 {
4658 gtk_text_iter_backward_line (start);
4659 gtk_text_iter_forward_line (start);
4660 }
4661 gtk_text_iter_forward_line (end);
4662 break;
4663
4664 case ATK_TEXT_BOUNDARY_LINE_END:
4665 line_number = gtk_text_iter_get_line (start);
4666 if (line_number == 0)
4667 {
4668 gtk_text_buffer_get_iter_at_offset (buffer, start, 0);
4669 }
4670 else
4671 {
4672 gtk_text_iter_backward_line (start);
4673 gtk_text_iter_forward_line (start);
4674 }
4675 while (!gtk_text_iter_ends_line (start))
4676 {
4677 if (!gtk_text_iter_backward_char (start))
4678 break;
4679 }
4680 gtk_text_iter_forward_to_line_end (end);
4681 break;
4682 }
4683 }
4684
4685 void
_gtk_text_buffer_get_text_after(GtkTextBuffer * buffer,AtkTextBoundary boundary_type,GtkTextIter * position,GtkTextIter * start,GtkTextIter * end)4686 _gtk_text_buffer_get_text_after (GtkTextBuffer *buffer,
4687 AtkTextBoundary boundary_type,
4688 GtkTextIter *position,
4689 GtkTextIter *start,
4690 GtkTextIter *end)
4691 {
4692 *start = *position;
4693 *end = *start;
4694
4695 switch (boundary_type)
4696 {
4697 case ATK_TEXT_BOUNDARY_CHAR:
4698 gtk_text_iter_forward_char (start);
4699 gtk_text_iter_forward_chars (end, 2);
4700 break;
4701
4702 case ATK_TEXT_BOUNDARY_WORD_START:
4703 if (gtk_text_iter_inside_word (end))
4704 gtk_text_iter_forward_word_end (end);
4705 while (!gtk_text_iter_starts_word (end))
4706 {
4707 if (!gtk_text_iter_forward_char (end))
4708 break;
4709 }
4710 *start = *end;
4711 if (!gtk_text_iter_is_end (end))
4712 {
4713 gtk_text_iter_forward_word_end (end);
4714 while (!gtk_text_iter_starts_word (end))
4715 {
4716 if (!gtk_text_iter_forward_char (end))
4717 break;
4718 }
4719 }
4720 break;
4721
4722 case ATK_TEXT_BOUNDARY_WORD_END:
4723 gtk_text_iter_forward_word_end (end);
4724 *start = *end;
4725 if (!gtk_text_iter_is_end (end))
4726 gtk_text_iter_forward_word_end (end);
4727 break;
4728
4729 case ATK_TEXT_BOUNDARY_SENTENCE_START:
4730 if (gtk_text_iter_inside_sentence (end))
4731 gtk_text_iter_forward_sentence_end (end);
4732 while (!gtk_text_iter_starts_sentence (end))
4733 {
4734 if (!gtk_text_iter_forward_char (end))
4735 break;
4736 }
4737 *start = *end;
4738 if (!gtk_text_iter_is_end (end))
4739 {
4740 gtk_text_iter_forward_sentence_end (end);
4741 while (!gtk_text_iter_starts_sentence (end))
4742 {
4743 if (!gtk_text_iter_forward_char (end))
4744 break;
4745 }
4746 }
4747 break;
4748
4749 case ATK_TEXT_BOUNDARY_SENTENCE_END:
4750 gtk_text_iter_forward_sentence_end (end);
4751 *start = *end;
4752 if (!gtk_text_iter_is_end (end))
4753 gtk_text_iter_forward_sentence_end (end);
4754 break;
4755
4756 case ATK_TEXT_BOUNDARY_LINE_START:
4757 gtk_text_iter_forward_line (end);
4758 *start = *end;
4759 gtk_text_iter_forward_line (end);
4760 break;
4761
4762 case ATK_TEXT_BOUNDARY_LINE_END:
4763 gtk_text_iter_forward_line (start);
4764 *end = *start;
4765 if (!gtk_text_iter_is_end (start))
4766 {
4767 while (!gtk_text_iter_ends_line (start))
4768 {
4769 if (!gtk_text_iter_backward_char (start))
4770 break;
4771 }
4772 gtk_text_iter_forward_to_line_end (end);
4773 }
4774 break;
4775 }
4776 }
4777
4778 static GtkTextTag *
get_tag_for_attributes(PangoAttrIterator * iter)4779 get_tag_for_attributes (PangoAttrIterator *iter)
4780 {
4781 PangoAttribute *attr;
4782 GtkTextTag *tag;
4783
4784 tag = gtk_text_tag_new (NULL);
4785
4786 attr = pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
4787 if (attr)
4788 g_object_set (tag, "language", pango_language_to_string (((PangoAttrLanguage*)attr)->value), NULL);
4789
4790 attr = pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
4791 if (attr)
4792 g_object_set (tag, "family", ((PangoAttrString*)attr)->value, NULL);
4793
4794 attr = pango_attr_iterator_get (iter, PANGO_ATTR_STYLE);
4795 if (attr)
4796 g_object_set (tag, "style", ((PangoAttrInt*)attr)->value, NULL);
4797
4798 attr = pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT);
4799 if (attr)
4800 g_object_set (tag, "weight", ((PangoAttrInt*)attr)->value, NULL);
4801
4802 attr = pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT);
4803 if (attr)
4804 g_object_set (tag, "variant", ((PangoAttrInt*)attr)->value, NULL);
4805
4806 attr = pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH);
4807 if (attr)
4808 g_object_set (tag, "stretch", ((PangoAttrInt*)attr)->value, NULL);
4809
4810 attr = pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
4811 if (attr)
4812 g_object_set (tag, "size", ((PangoAttrInt*)attr)->value, NULL);
4813
4814 attr = pango_attr_iterator_get (iter, PANGO_ATTR_FONT_DESC);
4815 if (attr)
4816 g_object_set (tag, "font-desc", ((PangoAttrFontDesc*)attr)->desc, NULL);
4817
4818 attr = pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
4819 if (attr)
4820 {
4821 PangoColor *color;
4822 GdkRGBA rgba;
4823
4824 color = &((PangoAttrColor*)attr)->color;
4825 rgba.red = color->red / 65535.;
4826 rgba.green = color->green / 65535.;
4827 rgba.blue = color->blue / 65535.;
4828 rgba.alpha = 1.;
4829 g_object_set (tag, "foreground-rgba", &rgba, NULL);
4830 };
4831
4832 attr = pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND);
4833 if (attr)
4834 {
4835 PangoColor *color;
4836 GdkRGBA rgba;
4837
4838 color = &((PangoAttrColor*)attr)->color;
4839 rgba.red = color->red / 65535.;
4840 rgba.green = color->green / 65535.;
4841 rgba.blue = color->blue / 65535.;
4842 rgba.alpha = 1.;
4843 g_object_set (tag, "background-rgba", &rgba, NULL);
4844 };
4845
4846 attr = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
4847 if (attr)
4848 g_object_set (tag, "underline", ((PangoAttrInt*)attr)->value, NULL);
4849
4850 attr = pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE_COLOR);
4851 if (attr)
4852 {
4853 PangoColor *color;
4854 GdkRGBA rgba;
4855
4856 color = &((PangoAttrColor*)attr)->color;
4857 rgba.red = color->red / 65535.;
4858 rgba.green = color->green / 65535.;
4859 rgba.blue = color->blue / 65535.;
4860 rgba.alpha = 1.;
4861 g_object_set (tag, "underline-rgba", &rgba, NULL);
4862 }
4863
4864 attr = pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH);
4865 if (attr)
4866 g_object_set (tag, "strikethrough", (gboolean) (((PangoAttrInt*)attr)->value != 0), NULL);
4867
4868 attr = pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH_COLOR);
4869 if (attr)
4870 {
4871 PangoColor *color;
4872 GdkRGBA rgba;
4873
4874 color = &((PangoAttrColor*)attr)->color;
4875 rgba.red = color->red / 65535.;
4876 rgba.green = color->green / 65535.;
4877 rgba.blue = color->blue / 65535.;
4878 rgba.alpha = 1.;
4879 g_object_set (tag, "strikethrough-rgba", &rgba, NULL);
4880 }
4881
4882 attr = pango_attr_iterator_get (iter, PANGO_ATTR_RISE);
4883 if (attr)
4884 g_object_set (tag, "rise", ((PangoAttrInt*)attr)->value, NULL);
4885
4886 attr = pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
4887 if (attr)
4888 g_object_set (tag, "scale", ((PangoAttrFloat*)attr)->value, NULL);
4889
4890 attr = pango_attr_iterator_get (iter, PANGO_ATTR_FALLBACK);
4891 if (attr)
4892 g_object_set (tag, "fallback", (gboolean) (((PangoAttrInt*)attr)->value != 0), NULL);
4893
4894 attr = pango_attr_iterator_get (iter, PANGO_ATTR_LETTER_SPACING);
4895 if (attr)
4896 g_object_set (tag, "letter-spacing", ((PangoAttrInt*)attr)->value, NULL);
4897
4898 attr = pango_attr_iterator_get (iter, PANGO_ATTR_FONT_FEATURES);
4899 if (attr)
4900 g_object_set (tag, "font-features", ((PangoAttrString*)attr)->value, NULL);
4901
4902 return tag;
4903 }
4904
4905 static void
gtk_text_buffer_insert_with_attributes(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,PangoAttrList * attributes)4906 gtk_text_buffer_insert_with_attributes (GtkTextBuffer *buffer,
4907 GtkTextIter *iter,
4908 const gchar *text,
4909 PangoAttrList *attributes)
4910 {
4911 GtkTextMark *mark;
4912 PangoAttrIterator *attr;
4913 GtkTextTagTable *tags;
4914
4915 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
4916
4917 if (!attributes)
4918 {
4919 gtk_text_buffer_insert (buffer, iter, text, -1);
4920 return;
4921 }
4922
4923 /* create mark with right gravity */
4924 mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
4925 attr = pango_attr_list_get_iterator (attributes);
4926 tags = gtk_text_buffer_get_tag_table (buffer);
4927
4928 do
4929 {
4930 GtkTextTag *tag;
4931 gint start, end;
4932
4933 pango_attr_iterator_range (attr, &start, &end);
4934
4935 if (end == G_MAXINT) /* last chunk */
4936 end = start - 1; /* resulting in -1 to be passed to _insert */
4937
4938 tag = get_tag_for_attributes (attr);
4939 gtk_text_tag_table_add (tags, tag);
4940
4941 gtk_text_buffer_insert_with_tags (buffer, iter, text + start, end - start, tag, NULL);
4942
4943 gtk_text_buffer_get_iter_at_mark (buffer, iter, mark);
4944 }
4945 while (pango_attr_iterator_next (attr));
4946
4947 gtk_text_buffer_delete_mark (buffer, mark);
4948 pango_attr_iterator_destroy (attr);
4949 }
4950
4951 /**
4952 * gtk_text_buffer_insert_markup:
4953 * @buffer: a #GtkTextBuffer
4954 * @iter: location to insert the markup
4955 * @markup: a nul-terminated UTF-8 string containing [Pango markup][PangoMarkupFormat]
4956 * @len: length of @markup in bytes, or -1
4957 *
4958 * Inserts the text in @markup at position @iter. @markup will be inserted
4959 * in its entirety and must be nul-terminated and valid UTF-8. Emits the
4960 * #GtkTextBuffer::insert-text signal, possibly multiple times; insertion
4961 * actually occurs in the default handler for the signal. @iter will point
4962 * to the end of the inserted text on return.
4963 *
4964 * Since: 3.16
4965 */
4966 void
gtk_text_buffer_insert_markup(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * markup,gint len)4967 gtk_text_buffer_insert_markup (GtkTextBuffer *buffer,
4968 GtkTextIter *iter,
4969 const gchar *markup,
4970 gint len)
4971 {
4972 PangoAttrList *attributes;
4973 gchar *text;
4974 GError *error = NULL;
4975
4976 if (!pango_parse_markup (markup, len, 0, &attributes, &text, NULL, &error))
4977 {
4978 g_warning ("Invalid markup string: %s", error->message);
4979 g_error_free (error);
4980 return;
4981 }
4982
4983 gtk_text_buffer_insert_with_attributes (buffer, iter, text, attributes);
4984
4985 pango_attr_list_unref (attributes);
4986 g_free (text);
4987 }
4988