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
56typedef struct {
57 char *replacement;
58} GtkTextChildAnchorPrivate;
59
60G_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
88static void
89paintable_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
104static void
105paintable_invalidate_contents (GdkPaintable *paintable,
106 GtkTextLineSegment *seg)
107{
108 /* These do the same anyway */
109 paintable_invalidate_size (paintable, seg);
110}
111
112static GtkTextLineSegment *
113paintable_segment_cleanup_func (GtkTextLineSegment *seg,
114 GtkTextLine *line)
115{
116 seg->body.paintable.line = line;
117
118 return seg;
119}
120
121static int
122paintable_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
150static void
151paintable_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
164const 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
175GtkTextLineSegment *
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 = &gtk_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
223static GtkTextLineSegment *
224child_segment_cleanup_func (GtkTextLineSegment *seg,
225 GtkTextLine *line)
226{
227 seg->body.child.line = line;
228
229 return seg;
230}
231
232static int
233child_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
269static void
270child_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
280const 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
290GtkTextLineSegment *
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 = &gtk_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
323void
324_gtk_widget_segment_add (GtkTextLineSegment *widget_segment,
325 GtkWidget *child)
326{
327 g_return_if_fail (widget_segment->type == &gtk_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
337void
338_gtk_widget_segment_remove (GtkTextLineSegment *widget_segment,
339 GtkWidget *child)
340{
341 g_return_if_fail (widget_segment->type == &gtk_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
350void
351_gtk_widget_segment_ref (GtkTextLineSegment *widget_segment)
352{
353 g_assert (widget_segment->type == &gtk_text_child_type);
354
355 g_object_ref (widget_segment->body.child.obj);
356}
357
358void
359_gtk_widget_segment_unref (GtkTextLineSegment *widget_segment)
360{
361 g_assert (widget_segment->type == &gtk_text_child_type);
362
363 g_object_unref (object: widget_segment->body.child.obj);
364}
365
366GtkTextLayout*
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
372static 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
381static void gtk_text_child_anchor_finalize (GObject *obj);
382
383static void
384gtk_text_child_anchor_init (GtkTextChildAnchor *child_anchor)
385{
386 child_anchor->segment = NULL;
387}
388
389static void
390gtk_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 **/
409GtkTextChildAnchor*
410gtk_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 **/
427GtkTextChildAnchor *
428gtk_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
445static void
446gtk_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 */
484GtkWidget **
485gtk_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 == &gtk_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 */
535gboolean
536gtk_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 == &gtk_text_child_type, TRUE);
543
544 return seg->body.child.tree == NULL;
545}
546
547void
548gtk_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
564void
565gtk_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
584void
585gtk_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
610void
611gtk_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
620const char *
621gtk_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

source code of gtk/gtk/gtktextchild.c