Bug Summary

File:ctk/ctktextbuffer.c
Warning:line 3482, column 57
The code calls sizeof() on a pointer type. This can produce an unexpected result

Annotated Source Code

Press '?' to see keyboard shortcuts

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