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

source code of gtk/gtk/gtktextbuffer.c