1 | /* gtktextchild.c - child pixmaps and widgets |
2 | * |
3 | * Copyright (c) 1994 The Regents of the University of California. |
4 | * Copyright (c) 1994-1997 Sun Microsystems, Inc. |
5 | * Copyright (c) 2000 Red Hat, Inc. |
6 | * Tk -> Gtk port by Havoc Pennington <hp@redhat.com> |
7 | * |
8 | * This software is copyrighted by the Regents of the University of |
9 | * California, Sun Microsystems, Inc., and other parties. The |
10 | * following terms apply to all files associated with the software |
11 | * unless explicitly disclaimed in individual files. |
12 | * |
13 | * The authors hereby grant permission to use, copy, modify, |
14 | * distribute, and license this software and its documentation for any |
15 | * purpose, provided that existing copyright notices are retained in |
16 | * all copies and that this notice is included verbatim in any |
17 | * distributions. No written agreement, license, or royalty fee is |
18 | * required for any of the authorized uses. Modifications to this |
19 | * software may be copyrighted by their authors and need not follow |
20 | * the licensing terms described here, provided that the new terms are |
21 | * clearly indicated on the first page of each file where they apply. |
22 | * |
23 | * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY |
24 | * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL |
25 | * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, |
26 | * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED |
27 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | * |
29 | * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, |
30 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
31 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND |
32 | * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, |
33 | * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE |
34 | * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
35 | * |
36 | * GOVERNMENT USE: If you are acquiring this software on behalf of the |
37 | * U.S. government, the Government shall have only "Restricted Rights" |
38 | * in the software and related documentation as defined in the Federal |
39 | * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you |
40 | * are acquiring the software on behalf of the Department of Defense, |
41 | * the software shall be classified as "Commercial Computer Software" |
42 | * and the Government shall have only "Restricted Rights" as defined |
43 | * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the |
44 | * foregoing, the authors grant the U.S. Government and others acting |
45 | * in its behalf permission to use and distribute the software in |
46 | * accordance with the terms specified in this license. |
47 | * |
48 | */ |
49 | |
50 | #include "config.h" |
51 | #include "gtktextchild.h" |
52 | #include "gtktextbtree.h" |
53 | #include "gtktextlayoutprivate.h" |
54 | #include "gtkintl.h" |
55 | |
56 | typedef struct { |
57 | char *replacement; |
58 | } GtkTextChildAnchorPrivate; |
59 | |
60 | G_DEFINE_TYPE_WITH_PRIVATE (GtkTextChildAnchor, gtk_text_child_anchor, G_TYPE_OBJECT) |
61 | |
62 | #define CHECK_IN_BUFFER(anchor) \ |
63 | G_STMT_START { \ |
64 | if ((anchor)->segment == NULL) \ |
65 | { \ |
66 | g_warning ("%s: GtkTextChildAnchor hasn't been in a buffer yet",\ |
67 | G_STRFUNC); \ |
68 | } \ |
69 | } G_STMT_END |
70 | |
71 | #define CHECK_IN_BUFFER_RETURN(anchor, val) \ |
72 | G_STMT_START { \ |
73 | if ((anchor)->segment == NULL) \ |
74 | { \ |
75 | g_warning ("%s: GtkTextChildAnchor hasn't been in a buffer yet",\ |
76 | G_STRFUNC); \ |
77 | return (val); \ |
78 | } \ |
79 | } G_STMT_END |
80 | |
81 | #define PAINTABLE_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \ |
82 | + sizeof (GtkTextPaintable))) |
83 | |
84 | #define WIDGET_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET (GtkTextLineSegment, body) \ |
85 | + sizeof (GtkTextChildBody))) |
86 | |
87 | |
88 | static void |
89 | paintable_invalidate_size (GdkPaintable *paintable, |
90 | GtkTextLineSegment *seg) |
91 | { |
92 | if (seg->body.paintable.tree) |
93 | { |
94 | GtkTextIter start, end; |
95 | |
96 | _gtk_text_btree_get_iter_at_paintable (tree: seg->body.paintable.tree, iter: &start, seg); |
97 | end = start; |
98 | gtk_text_iter_forward_char (iter: &end); |
99 | |
100 | _gtk_text_btree_invalidate_region (tree: seg->body.paintable.tree, start: &start, end: &end, FALSE); |
101 | } |
102 | } |
103 | |
104 | static void |
105 | paintable_invalidate_contents (GdkPaintable *paintable, |
106 | GtkTextLineSegment *seg) |
107 | { |
108 | /* These do the same anyway */ |
109 | paintable_invalidate_size (paintable, seg); |
110 | } |
111 | |
112 | static GtkTextLineSegment * |
113 | paintable_segment_cleanup_func (GtkTextLineSegment *seg, |
114 | GtkTextLine *line) |
115 | { |
116 | seg->body.paintable.line = line; |
117 | |
118 | return seg; |
119 | } |
120 | |
121 | static int |
122 | paintable_segment_delete_func (GtkTextLineSegment *seg, |
123 | GtkTextLine *line, |
124 | gboolean tree_gone) |
125 | { |
126 | GdkPaintable *paintable; |
127 | guint flags; |
128 | |
129 | seg->body.paintable.tree = NULL; |
130 | seg->body.paintable.line = NULL; |
131 | |
132 | paintable = seg->body.paintable.paintable; |
133 | if (paintable) |
134 | { |
135 | flags = gdk_paintable_get_flags (paintable); |
136 | if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0) |
137 | g_signal_handlers_disconnect_by_func (paintable, G_CALLBACK (paintable_invalidate_contents), seg); |
138 | |
139 | if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0) |
140 | g_signal_handlers_disconnect_by_func (paintable, G_CALLBACK (paintable_invalidate_size), seg); |
141 | |
142 | g_object_unref (object: paintable); |
143 | } |
144 | |
145 | g_slice_free1 (PAINTABLE_SEG_SIZE, mem_block: seg); |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static void |
151 | paintable_segment_check_func (GtkTextLineSegment *seg, |
152 | GtkTextLine *line) |
153 | { |
154 | if (seg->next == NULL) |
155 | g_error ("paintable segment is the last segment in a line" ); |
156 | |
157 | if (seg->byte_count != GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN) |
158 | g_error ("paintable segment has byte count of %d" , seg->byte_count); |
159 | |
160 | if (seg->char_count != 1) |
161 | g_error ("paintable segment has char count of %d" , seg->char_count); |
162 | } |
163 | |
164 | const GtkTextLineSegmentClass gtk_text_paintable_type = { |
165 | "paintable" , /* name */ |
166 | FALSE, /* leftGravity */ |
167 | NULL, /* splitFunc */ |
168 | paintable_segment_delete_func, /* deleteFunc */ |
169 | paintable_segment_cleanup_func, /* cleanupFunc */ |
170 | NULL, /* lineChangeFunc */ |
171 | paintable_segment_check_func /* checkFunc */ |
172 | |
173 | }; |
174 | |
175 | GtkTextLineSegment * |
176 | _gtk_paintable_segment_new (GdkPaintable *paintable) |
177 | { |
178 | /* gcc-11 issues a diagnostic here because the size allocated |
179 | for SEG does not cover the entire size of a GtkTextLineSegment |
180 | and gcc has no way to know that the union will only be used |
181 | for limited types and the additional space is not needed. */ |
182 | #pragma GCC diagnostic push |
183 | #pragma GCC diagnostic ignored "-Warray-bounds" |
184 | GtkTextLineSegment *seg; |
185 | guint flags; |
186 | |
187 | seg = g_slice_alloc (PAINTABLE_SEG_SIZE); |
188 | |
189 | seg->type = >k_text_paintable_type; |
190 | |
191 | seg->next = NULL; |
192 | |
193 | /* We convert to the 0xFFFC "unknown character", |
194 | * a 3-byte sequence in UTF-8. |
195 | */ |
196 | seg->byte_count = GTK_TEXT_UNKNOWN_CHAR_UTF8_LEN; |
197 | seg->char_count = 1; |
198 | |
199 | seg->body.paintable.paintable = paintable; |
200 | seg->body.paintable.tree = NULL; |
201 | seg->body.paintable.line = NULL; |
202 | |
203 | flags = gdk_paintable_get_flags (paintable); |
204 | if ((flags & GDK_PAINTABLE_STATIC_CONTENTS) == 0) |
205 | g_signal_connect (paintable, |
206 | "invalidate-contents" , |
207 | G_CALLBACK (paintable_invalidate_contents), |
208 | seg); |
209 | |
210 | if ((flags & GDK_PAINTABLE_STATIC_SIZE) == 0) |
211 | g_signal_connect (paintable, |
212 | "invalidate-size" , |
213 | G_CALLBACK (paintable_invalidate_size), |
214 | seg); |
215 | |
216 | g_object_ref (paintable); |
217 | |
218 | return seg; |
219 | #pragma GCC diagnostic pop |
220 | } |
221 | |
222 | |
223 | static GtkTextLineSegment * |
224 | child_segment_cleanup_func (GtkTextLineSegment *seg, |
225 | GtkTextLine *line) |
226 | { |
227 | seg->body.child.line = line; |
228 | |
229 | return seg; |
230 | } |
231 | |
232 | static int |
233 | child_segment_delete_func (GtkTextLineSegment *seg, |
234 | GtkTextLine *line, |
235 | gboolean tree_gone) |
236 | { |
237 | GSList *tmp_list; |
238 | GSList *copy; |
239 | |
240 | _gtk_text_btree_unregister_child_anchor (anchor: seg->body.child.obj); |
241 | |
242 | seg->body.child.tree = NULL; |
243 | seg->body.child.line = NULL; |
244 | |
245 | /* avoid removing widgets while walking the list */ |
246 | copy = g_slist_copy (list: seg->body.child.widgets); |
247 | tmp_list = copy; |
248 | while (tmp_list != NULL) |
249 | { |
250 | GtkWidget *child = tmp_list->data; |
251 | |
252 | gtk_text_view_remove (GTK_TEXT_VIEW (gtk_widget_get_parent (child)), child); |
253 | |
254 | tmp_list = tmp_list->next; |
255 | } |
256 | |
257 | /* On removal from the widget's parents (GtkTextView), |
258 | * the widget should have been removed from the anchor. |
259 | */ |
260 | g_assert (seg->body.child.widgets == NULL); |
261 | |
262 | g_slist_free (list: copy); |
263 | |
264 | _gtk_widget_segment_unref (widget_segment: seg); |
265 | |
266 | return 0; |
267 | } |
268 | |
269 | static void |
270 | child_segment_check_func (GtkTextLineSegment *seg, |
271 | GtkTextLine *line) |
272 | { |
273 | if (seg->next == NULL) |
274 | g_error ("child segment is the last segment in a line" ); |
275 | |
276 | if (seg->char_count != 1) |
277 | g_error ("child segment has char count of %d" , seg->char_count); |
278 | } |
279 | |
280 | const GtkTextLineSegmentClass gtk_text_child_type = { |
281 | "child-widget" , /* name */ |
282 | FALSE, /* leftGravity */ |
283 | NULL, /* splitFunc */ |
284 | child_segment_delete_func, /* deleteFunc */ |
285 | child_segment_cleanup_func, /* cleanupFunc */ |
286 | NULL, /* lineChangeFunc */ |
287 | child_segment_check_func /* checkFunc */ |
288 | }; |
289 | |
290 | GtkTextLineSegment * |
291 | _gtk_widget_segment_new (GtkTextChildAnchor *anchor) |
292 | { |
293 | /* gcc-11 issues a diagnostic here because the size allocated |
294 | for SEG does not cover the entire size of a GtkTextLineSegment |
295 | and gcc has no way to know that the union will only be used |
296 | for limited types and the additional space is not needed. */ |
297 | #pragma GCC diagnostic push |
298 | #pragma GCC diagnostic ignored "-Warray-bounds" |
299 | GtkTextLineSegment *seg; |
300 | GtkTextChildAnchorPrivate *priv = gtk_text_child_anchor_get_instance_private (self: anchor); |
301 | |
302 | seg = g_slice_alloc (WIDGET_SEG_SIZE); |
303 | |
304 | seg->type = >k_text_child_type; |
305 | |
306 | seg->next = NULL; |
307 | |
308 | seg->byte_count = strlen (s: priv->replacement); |
309 | seg->char_count = g_utf8_strlen (p: priv->replacement, max: seg->byte_count); |
310 | |
311 | seg->body.child.obj = anchor; |
312 | seg->body.child.obj->segment = seg; |
313 | seg->body.child.widgets = NULL; |
314 | seg->body.child.tree = NULL; |
315 | seg->body.child.line = NULL; |
316 | |
317 | g_object_ref (anchor); |
318 | |
319 | return seg; |
320 | #pragma GCC diagnostic pop |
321 | } |
322 | |
323 | void |
324 | _gtk_widget_segment_add (GtkTextLineSegment *widget_segment, |
325 | GtkWidget *child) |
326 | { |
327 | g_return_if_fail (widget_segment->type == >k_text_child_type); |
328 | g_return_if_fail (widget_segment->body.child.tree != NULL); |
329 | |
330 | g_object_ref (child); |
331 | |
332 | widget_segment->body.child.widgets = |
333 | g_slist_prepend (list: widget_segment->body.child.widgets, |
334 | data: child); |
335 | } |
336 | |
337 | void |
338 | _gtk_widget_segment_remove (GtkTextLineSegment *widget_segment, |
339 | GtkWidget *child) |
340 | { |
341 | g_return_if_fail (widget_segment->type == >k_text_child_type); |
342 | |
343 | widget_segment->body.child.widgets = |
344 | g_slist_remove (list: widget_segment->body.child.widgets, |
345 | data: child); |
346 | |
347 | g_object_unref (object: child); |
348 | } |
349 | |
350 | void |
351 | _gtk_widget_segment_ref (GtkTextLineSegment *widget_segment) |
352 | { |
353 | g_assert (widget_segment->type == >k_text_child_type); |
354 | |
355 | g_object_ref (widget_segment->body.child.obj); |
356 | } |
357 | |
358 | void |
359 | _gtk_widget_segment_unref (GtkTextLineSegment *widget_segment) |
360 | { |
361 | g_assert (widget_segment->type == >k_text_child_type); |
362 | |
363 | g_object_unref (object: widget_segment->body.child.obj); |
364 | } |
365 | |
366 | GtkTextLayout* |
367 | _gtk_anchored_child_get_layout (GtkWidget *child) |
368 | { |
369 | return g_object_get_data (G_OBJECT (child), key: "gtk-text-child-anchor-layout" ); |
370 | } |
371 | |
372 | static void |
373 | _gtk_anchored_child_set_layout (GtkWidget *child, |
374 | GtkTextLayout *layout) |
375 | { |
376 | g_object_set_data (G_OBJECT (child), |
377 | I_("gtk-text-child-anchor-layout" ), |
378 | data: layout); |
379 | } |
380 | |
381 | static void gtk_text_child_anchor_finalize (GObject *obj); |
382 | |
383 | static void |
384 | gtk_text_child_anchor_init (GtkTextChildAnchor *child_anchor) |
385 | { |
386 | child_anchor->segment = NULL; |
387 | } |
388 | |
389 | static void |
390 | gtk_text_child_anchor_class_init (GtkTextChildAnchorClass *klass) |
391 | { |
392 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
393 | |
394 | object_class->finalize = gtk_text_child_anchor_finalize; |
395 | } |
396 | |
397 | /** |
398 | * gtk_text_child_anchor_new: |
399 | * |
400 | * Creates a new `GtkTextChildAnchor`. |
401 | * |
402 | * Usually you would then insert it into a `GtkTextBuffer` with |
403 | * [method@Gtk.TextBuffer.insert_child_anchor]. To perform the |
404 | * creation and insertion in one step, use the convenience |
405 | * function [method@Gtk.TextBuffer.create_child_anchor]. |
406 | * |
407 | * Returns: a new `GtkTextChildAnchor` |
408 | **/ |
409 | GtkTextChildAnchor* |
410 | gtk_text_child_anchor_new (void) |
411 | { |
412 | return gtk_text_child_anchor_new_with_replacement (character: _gtk_text_unknown_char_utf8); |
413 | } |
414 | |
415 | /** |
416 | * gtk_text_child_anchor_new_with_replacement: |
417 | * |
418 | * Creates a new `GtkTextChildAnchor` with the given replacement character. |
419 | * |
420 | * Usually you would then insert it into a `GtkTextBuffer` with |
421 | * [method@Gtk.TextBuffer.insert_child_anchor]. |
422 | * |
423 | * Returns: a new `GtkTextChildAnchor` |
424 | * |
425 | * Since: 4.6 |
426 | **/ |
427 | GtkTextChildAnchor * |
428 | gtk_text_child_anchor_new_with_replacement (const char *replacement_character) |
429 | { |
430 | GtkTextChildAnchor *anchor; |
431 | GtkTextChildAnchorPrivate *priv; |
432 | |
433 | /* only a single character can be set as replacement */ |
434 | g_return_val_if_fail (g_utf8_strlen (replacement_character, -1) == 1, NULL); |
435 | |
436 | anchor = g_object_new (GTK_TYPE_TEXT_CHILD_ANCHOR, NULL); |
437 | |
438 | priv = gtk_text_child_anchor_get_instance_private (self: anchor); |
439 | |
440 | priv->replacement = g_strdup (str: replacement_character); |
441 | |
442 | return anchor; |
443 | } |
444 | |
445 | static void |
446 | gtk_text_child_anchor_finalize (GObject *obj) |
447 | { |
448 | GtkTextChildAnchor *anchor = GTK_TEXT_CHILD_ANCHOR (obj); |
449 | GtkTextChildAnchorPrivate *priv = gtk_text_child_anchor_get_instance_private (self: anchor); |
450 | GtkTextLineSegment *seg = anchor->segment; |
451 | |
452 | if (seg) |
453 | { |
454 | if (seg->body.child.tree != NULL) |
455 | { |
456 | g_warning ("Someone removed a reference to a GtkTextChildAnchor " |
457 | "they didn't own; the anchor is still in the text buffer " |
458 | "and the refcount is 0." ); |
459 | return; |
460 | } |
461 | |
462 | g_slist_free_full (list: seg->body.child.widgets, free_func: g_object_unref); |
463 | |
464 | g_slice_free1 (WIDGET_SEG_SIZE, mem_block: seg); |
465 | } |
466 | |
467 | g_free (mem: priv->replacement); |
468 | |
469 | G_OBJECT_CLASS (gtk_text_child_anchor_parent_class)->finalize (obj); |
470 | } |
471 | |
472 | /** |
473 | * gtk_text_child_anchor_get_widgets: |
474 | * @anchor: a `GtkTextChildAnchor` |
475 | * @out_len: (out): return location for the length of the array |
476 | * |
477 | * Gets a list of all widgets anchored at this child anchor. |
478 | * |
479 | * The order in which the widgets are returned is not defined. |
480 | * |
481 | * Returns: (array length=out_len) (transfer container): an |
482 | * array of widgets anchored at @anchor |
483 | */ |
484 | GtkWidget ** |
485 | gtk_text_child_anchor_get_widgets (GtkTextChildAnchor *anchor, |
486 | guint *out_len) |
487 | { |
488 | GtkTextLineSegment *seg = anchor->segment; |
489 | GPtrArray *arr; |
490 | GSList *iter; |
491 | |
492 | CHECK_IN_BUFFER_RETURN (anchor, NULL); |
493 | |
494 | g_return_val_if_fail (out_len != NULL, NULL); |
495 | g_return_val_if_fail (seg->type == >k_text_child_type, NULL); |
496 | |
497 | iter = seg->body.child.widgets; |
498 | |
499 | if (!iter) |
500 | { |
501 | *out_len = 0; |
502 | return NULL; |
503 | } |
504 | |
505 | arr = g_ptr_array_new (); |
506 | while (iter != NULL) |
507 | { |
508 | g_ptr_array_add (array: arr, data: iter->data); |
509 | |
510 | iter = iter->next; |
511 | } |
512 | |
513 | /* Order is not relevant, so we don't need to reverse the list |
514 | * again. |
515 | */ |
516 | *out_len = arr->len; |
517 | return (GtkWidget **)g_ptr_array_free (array: arr, FALSE); |
518 | } |
519 | |
520 | /** |
521 | * gtk_text_child_anchor_get_deleted: |
522 | * @anchor: a `GtkTextChildAnchor` |
523 | * |
524 | * Determines whether a child anchor has been deleted from |
525 | * the buffer. |
526 | * |
527 | * Keep in mind that the child anchor will be unreferenced |
528 | * when removed from the buffer, so you need to hold your own |
529 | * reference (with g_object_ref()) if you plan to use this |
530 | * function — otherwise all deleted child anchors will also |
531 | * be finalized. |
532 | * |
533 | * Returns: %TRUE if the child anchor has been deleted from its buffer |
534 | */ |
535 | gboolean |
536 | gtk_text_child_anchor_get_deleted (GtkTextChildAnchor *anchor) |
537 | { |
538 | GtkTextLineSegment *seg = anchor->segment; |
539 | |
540 | CHECK_IN_BUFFER_RETURN (anchor, TRUE); |
541 | |
542 | g_return_val_if_fail (seg->type == >k_text_child_type, TRUE); |
543 | |
544 | return seg->body.child.tree == NULL; |
545 | } |
546 | |
547 | void |
548 | gtk_text_child_anchor_register_child (GtkTextChildAnchor *anchor, |
549 | GtkWidget *child, |
550 | GtkTextLayout *layout) |
551 | { |
552 | g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor)); |
553 | g_return_if_fail (GTK_IS_WIDGET (child)); |
554 | |
555 | CHECK_IN_BUFFER (anchor); |
556 | |
557 | _gtk_anchored_child_set_layout (child, layout); |
558 | |
559 | _gtk_widget_segment_add (widget_segment: anchor->segment, child); |
560 | |
561 | gtk_text_child_anchor_queue_resize (anchor, layout); |
562 | } |
563 | |
564 | void |
565 | gtk_text_child_anchor_unregister_child (GtkTextChildAnchor *anchor, |
566 | GtkWidget *child) |
567 | { |
568 | g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor)); |
569 | g_return_if_fail (GTK_IS_WIDGET (child)); |
570 | |
571 | CHECK_IN_BUFFER (anchor); |
572 | |
573 | if (_gtk_anchored_child_get_layout (child)) |
574 | { |
575 | gtk_text_child_anchor_queue_resize (anchor, |
576 | layout: _gtk_anchored_child_get_layout (child)); |
577 | } |
578 | |
579 | _gtk_anchored_child_set_layout (child, NULL); |
580 | |
581 | _gtk_widget_segment_remove (widget_segment: anchor->segment, child); |
582 | } |
583 | |
584 | void |
585 | gtk_text_child_anchor_queue_resize (GtkTextChildAnchor *anchor, |
586 | GtkTextLayout *layout) |
587 | { |
588 | GtkTextIter start; |
589 | GtkTextIter end; |
590 | GtkTextLineSegment *seg; |
591 | |
592 | g_return_if_fail (GTK_IS_TEXT_CHILD_ANCHOR (anchor)); |
593 | g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout)); |
594 | |
595 | CHECK_IN_BUFFER (anchor); |
596 | |
597 | seg = anchor->segment; |
598 | |
599 | if (seg->body.child.tree == NULL) |
600 | return; |
601 | |
602 | gtk_text_buffer_get_iter_at_child_anchor (buffer: layout->buffer, |
603 | iter: &start, anchor); |
604 | end = start; |
605 | gtk_text_iter_forward_char (iter: &end); |
606 | |
607 | gtk_text_layout_invalidate (layout, start: &start, end: &end); |
608 | } |
609 | |
610 | void |
611 | gtk_text_anchored_child_set_layout (GtkWidget *child, |
612 | GtkTextLayout *layout) |
613 | { |
614 | g_return_if_fail (GTK_IS_WIDGET (child)); |
615 | g_return_if_fail (layout == NULL || GTK_IS_TEXT_LAYOUT (layout)); |
616 | |
617 | _gtk_anchored_child_set_layout (child, layout); |
618 | } |
619 | |
620 | const char * |
621 | gtk_text_child_anchor_get_replacement (GtkTextChildAnchor *anchor) |
622 | { |
623 | GtkTextChildAnchorPrivate *priv = gtk_text_child_anchor_get_instance_private (self: anchor); |
624 | |
625 | return priv->replacement; |
626 | } |
627 | |