1/*
2 * Copyright © 2019 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include "gtkexpression.h"
23
24#include "gtkprivate.h"
25
26#include <gobject/gvaluecollector.h>
27
28/**
29 * GtkExpression: (ref-func gtk_expression_ref) (unref-func gtk_expression_unref) (set-value-func gtk_value_set_expression) (get-value-func gtk_value_get_expression)
30 *
31 * `GtkExpression` provides a way to describe references to values.
32 *
33 * An important aspect of expressions is that the value can be obtained
34 * from a source that is several steps away. For example, an expression
35 * may describe ‘the value of property A of `object1`, which is itself the
36 * value of a property of `object2`’. And `object1` may not even exist yet
37 * at the time that the expression is created. This is contrast to `GObject`
38 * property bindings, which can only create direct connections between
39 * the properties of two objects that must both exist for the duration
40 * of the binding.
41 *
42 * An expression needs to be "evaluated" to obtain the value that it currently
43 * refers to. An evaluation always happens in the context of a current object
44 * called `this` (it mirrors the behavior of object-oriented languages),
45 * which may or may not influence the result of the evaluation. Use
46 * [method@Gtk.Expression.evaluate] for evaluating an expression.
47 *
48 * Various methods for defining expressions exist, from simple constants via
49 * [ctor@Gtk.ConstantExpression.new] to looking up properties in a `GObject`
50 * (even recursively) via [ctor@Gtk.PropertyExpression.new] or providing
51 * custom functions to transform and combine expressions via
52 * [ctor@Gtk.ClosureExpression.new].
53 *
54 * Here is an example of a complex expression:
55 *
56 * ```c
57 * color_expr = gtk_property_expression_new (GTK_TYPE_LIST_ITEM,
58 * NULL, "item");
59 * expression = gtk_property_expression_new (GTK_TYPE_COLOR,
60 * color_expr, "name");
61 * ```
62 *
63 * when evaluated with `this` being a `GtkListItem`, it will obtain the
64 * "item" property from the `GtkListItem`, and then obtain the "name" property
65 * from the resulting object (which is assumed to be of type `GTK_TYPE_COLOR`).
66 *
67 * A more concise way to describe this would be
68 *
69 * ```
70 * this->item->name
71 * ```
72 *
73 * The most likely place where you will encounter expressions is in the context
74 * of list models and list widgets using them. For example, `GtkDropDown` is
75 * evaluating a `GtkExpression` to obtain strings from the items in its model
76 * that it can then use to match against the contents of its search entry.
77 * `GtkStringFilter` is using a `GtkExpression` for similar reasons.
78 *
79 * By default, expressions are not paying attention to changes and evaluation is
80 * just a snapshot of the current state at a given time. To get informed about
81 * changes, an expression needs to be "watched" via a [struct@Gtk.ExpressionWatch],
82 * which will cause a callback to be called whenever the value of the expression may
83 * have changed; [method@Gtk.Expression.watch] starts watching an expression, and
84 * [method@Gtk.ExpressionWatch.unwatch] stops.
85 *
86 * Watches can be created for automatically updating the property of an object,
87 * similar to GObject's `GBinding` mechanism, by using [method@Gtk.Expression.bind].
88 *
89 * ## GtkExpression in GObject properties
90 *
91 * In order to use a `GtkExpression` as a `GObject` property, you must use the
92 * [id@gtk_param_spec_expression] when creating a `GParamSpec` to install in the
93 * `GObject` class being defined; for instance:
94 *
95 * ```c
96 * obj_props[PROP_EXPRESSION] =
97 * gtk_param_spec_expression ("expression",
98 * "Expression",
99 * "The expression used by the widget",
100 * G_PARAM_READWRITE |
101 * G_PARAM_STATIC_STRINGS |
102 * G_PARAM_EXPLICIT_NOTIFY);
103 * ```
104 *
105 * When implementing the `GObjectClass.set_property` and `GObjectClass.get_property`
106 * virtual functions, you must use [id@gtk_value_get_expression], to retrieve the
107 * stored `GtkExpression` from the `GValue` container, and [id@gtk_value_set_expression],
108 * to store the `GtkExpression` into the `GValue`; for instance:
109 *
110 * ```c
111 * // in set_property()...
112 * case PROP_EXPRESSION:
113 * foo_widget_set_expression (foo, gtk_value_get_expression (value));
114 * break;
115 *
116 * // in get_property()...
117 * case PROP_EXPRESSION:
118 * gtk_value_set_expression (value, foo->expression);
119 * break;
120 * ```
121 *
122 * ## GtkExpression in .ui files
123 *
124 * `GtkBuilder` has support for creating expressions. The syntax here can be used where
125 * a `GtkExpression` object is needed like in a `<property>` tag for an expression
126 * property, or in a `<binding name="property">` tag to bind a property to an expression.
127 *
128 * To create a property expression, use the `<lookup>` element. It can have a `type`
129 * attribute to specify the object type, and a `name` attribute to specify the property
130 * to look up. The content of `<lookup>` can either be an element specfiying the expression
131 * to use the object, or a string that specifies the name of the object to use.
132 *
133 * Example:
134 *
135 * ```xml
136 * <lookup name='search'>string_filter</lookup>
137 * ```
138 *
139 * To create a constant expression, use the `<constant>` element. If the type attribute
140 * is specified, the element content is interpreted as a value of that type. Otherwise,
141 * it is assumed to be an object. For instance:
142 *
143 * ```xml
144 * <constant>string_filter</constant>
145 * <constant type='gchararray'>Hello, world</constant>
146 * ```
147 *
148 * To create a closure expression, use the `<closure>` element. The `type` and `function`
149 * attributes specify what function to use for the closure, the content of the element
150 * contains the expressions for the parameters. For instance:
151 *
152 * ```xml
153 * <closure type='gchararray' function='combine_args_somehow'>
154 * <constant type='gchararray'>File size:</constant>
155 * <lookup type='GFile' name='size'>myfile</lookup>
156 * </closure>
157 * ```
158 */
159
160
161typedef struct _GtkExpressionClass GtkExpressionClass;
162typedef struct _GtkExpressionSubWatch GtkExpressionSubWatch;
163typedef struct _GtkExpressionTypeInfo GtkExpressionTypeInfo;
164
165struct _GtkExpression
166{
167 GTypeInstance parent_instance;
168
169 gatomicrefcount ref_count;
170
171 GType value_type;
172
173 GtkExpression *owner;
174};
175
176struct _GtkExpressionClass
177{
178 GTypeClass parent_class;
179
180 void (* finalize) (GtkExpression *expr);
181 gboolean (* is_static) (GtkExpression *expr);
182 gboolean (* evaluate) (GtkExpression *expr,
183 gpointer this,
184 GValue *value);
185
186 gsize (* watch_size) (GtkExpression *expr);
187 void (* watch) (GtkExpression *self,
188 GtkExpressionSubWatch *watch,
189 gpointer this_,
190 GtkExpressionNotify notify,
191 gpointer user_data);
192 void (* unwatch) (GtkExpression *self,
193 GtkExpressionSubWatch *watch);
194};
195
196struct _GtkExpressionTypeInfo
197{
198 gsize instance_size;
199
200 void (* instance_init) (GtkExpression *expr);
201 void (* finalize) (GtkExpression *expr);
202 gboolean (* is_static) (GtkExpression *expr);
203 gboolean (* evaluate) (GtkExpression *expr,
204 gpointer this,
205 GValue *value);
206
207 gsize (* watch_size) (GtkExpression *expr);
208 void (* watch) (GtkExpression *self,
209 GtkExpressionSubWatch *watch,
210 gpointer this_,
211 GtkExpressionNotify notify,
212 gpointer user_data);
213 void (* unwatch) (GtkExpression *self,
214 GtkExpressionSubWatch *watch);
215};
216
217/**
218 * GtkExpressionWatch:
219 *
220 * An opaque structure representing a watched `GtkExpression`.
221 *
222 * The contents of `GtkExpressionWatch` should only be accessed through the
223 * provided API.
224 */
225struct _GtkExpressionWatch
226{
227 GtkExpression *expression;
228 GObject *this;
229 GDestroyNotify user_destroy;
230 GtkExpressionNotify notify;
231 gpointer user_data;
232 guchar sub[0];
233};
234
235G_DEFINE_BOXED_TYPE (GtkExpressionWatch, gtk_expression_watch,
236 gtk_expression_watch_ref,
237 gtk_expression_watch_unref)
238
239#define GTK_EXPRESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_EXPRESSION, GtkExpressionClass))
240
241/*< private >
242 * GTK_DEFINE_EXPRESSION_TYPE:
243 * @TypeName: the type name, in camel case
244 * @type_name: the type name, in snake case
245 * @type_info: the address of the `GtkExpressionTypeInfo` for the expression type
246 *
247 * Registers a new `GtkExpression` subclass with the given @TypeName and @type_info.
248 *
249 * Similarly to %G_DEFINE_TYPE, this macro will generate a `get_type()`
250 * function that registers the event type.
251 *
252 * You can specify code to be run after the type registration; the `GType` of
253 * the event is available in the `gtk_define_expression_type_id` variable.
254 */
255#define GTK_DEFINE_EXPRESSION_TYPE(TypeName, type_name, type_info) \
256GType \
257type_name ## _get_type (void) \
258{ \
259 static gsize gtk_define_expression_type_id__volatile; \
260 if (g_once_init_enter (&gtk_define_expression_type_id__volatile)) \
261 { \
262 GType gtk_define_expression_type_id = \
263 gtk_expression_type_register_static (g_intern_static_string (#TypeName), type_info); \
264 g_once_init_leave (&gtk_define_expression_type_id__volatile, gtk_define_expression_type_id); \
265 } \
266 return gtk_define_expression_type_id__volatile; \
267}
268
269#define GTK_EXPRESSION_SUPER(expr) \
270 ((GtkExpressionClass *) g_type_class_peek (g_type_parent (G_TYPE_FROM_INSTANCE (expr))))
271
272/* {{{ GtkExpression internals */
273
274static void
275value_expression_init (GValue *value)
276{
277 value->data[0].v_pointer = NULL;
278}
279
280static void
281value_expression_free_value (GValue *value)
282{
283 if (value->data[0].v_pointer != NULL)
284 gtk_expression_unref (self: value->data[0].v_pointer);
285}
286
287static void
288value_expression_copy_value (const GValue *src,
289 GValue *dst)
290{
291 if (src->data[0].v_pointer != NULL)
292 dst->data[0].v_pointer = gtk_expression_ref (self: src->data[0].v_pointer);
293 else
294 dst->data[0].v_pointer = NULL;
295}
296
297static gpointer
298value_expression_peek_pointer (const GValue *value)
299{
300 return value->data[0].v_pointer;
301}
302
303static char *
304value_expression_collect_value (GValue *value,
305 guint n_collect_values,
306 GTypeCValue *collect_values,
307 guint collect_flags)
308{
309 GtkExpression *expression = collect_values[0].v_pointer;
310
311 if (expression == NULL)
312 {
313 value->data[0].v_pointer = NULL;
314 return NULL;
315 }
316
317 if (expression->parent_instance.g_class == NULL)
318 return g_strconcat (string1: "invalid unclassed GtkExpression pointer for "
319 "value type '",
320 G_VALUE_TYPE_NAME (value),
321 "'",
322 NULL);
323
324 value->data[0].v_pointer = gtk_expression_ref (self: expression);
325
326 return NULL;
327}
328
329static char *
330value_expression_lcopy_value (const GValue *value,
331 guint n_collect_values,
332 GTypeCValue *collect_values,
333 guint collect_flags)
334{
335 GtkExpression **expression_p = collect_values[0].v_pointer;
336
337 if (expression_p == NULL)
338 return g_strconcat (string1: "value location for '",
339 G_VALUE_TYPE_NAME (value),
340 "' passed as NULL",
341 NULL);
342
343 if (value->data[0].v_pointer == NULL)
344 *expression_p = NULL;
345 else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
346 *expression_p = value->data[0].v_pointer;
347 else
348 *expression_p = gtk_expression_ref (self: value->data[0].v_pointer);
349
350 return NULL;
351}
352
353/**
354 * gtk_value_set_expression:
355 * @value: a `GValue` initialized with type `GTK_TYPE_EXPRESSION`
356 * @expression: a `GtkExpression`
357 *
358 * Stores the given `GtkExpression` inside `value`.
359 *
360 * The `GValue` will acquire a reference to the `expression`.
361 */
362void
363gtk_value_set_expression (GValue *value,
364 GtkExpression *expression)
365{
366 g_return_if_fail (G_VALUE_HOLDS (value, GTK_TYPE_EXPRESSION));
367
368 GtkExpression *old_expression = value->data[0].v_pointer;
369
370 if (expression != NULL)
371 {
372 g_return_if_fail (GTK_IS_EXPRESSION (expression));
373
374 value->data[0].v_pointer = gtk_expression_ref (self: expression);
375 }
376 else
377 {
378 value->data[0].v_pointer = NULL;
379 }
380
381 if (old_expression != NULL)
382 gtk_expression_unref (self: old_expression);
383}
384
385/**
386 * gtk_value_take_expression:
387 * @value: a `GValue` initialized with type `GTK_TYPE_EXPRESSION`
388 * @expression: (transfer full) (nullable): a `GtkExpression`
389 *
390 * Stores the given `GtkExpression` inside `value`.
391 *
392 * This function transfers the ownership of the `expression` to the `GValue`.
393 */
394void
395gtk_value_take_expression (GValue *value,
396 GtkExpression *expression)
397{
398 g_return_if_fail (G_VALUE_HOLDS (value, GTK_TYPE_EXPRESSION));
399
400 GtkExpression *old_expression = value->data[0].v_pointer;
401
402 if (expression != NULL)
403 {
404 g_return_if_fail (GTK_IS_EXPRESSION (expression));
405
406 value->data[0].v_pointer = expression;
407 }
408 else
409 {
410 value->data[0].v_pointer = NULL;
411 }
412
413 if (old_expression != NULL)
414 gtk_expression_unref (self: old_expression);
415}
416
417/**
418 * gtk_value_get_expression:
419 * @value: a `GValue` initialized with type `GTK_TYPE_EXPRESSION`
420 *
421 * Retrieves the `GtkExpression` stored inside the given `value`.
422 *
423 * Returns: (transfer none) (nullable): a `GtkExpression`
424 */
425GtkExpression *
426gtk_value_get_expression (const GValue *value)
427{
428 g_return_val_if_fail (G_VALUE_HOLDS (value, GTK_TYPE_EXPRESSION), NULL);
429
430 return value->data[0].v_pointer;
431}
432
433/**
434 * gtk_value_dup_expression:
435 * @value: a `GValue` initialized with type `GTK_TYPE_EXPRESSION`
436 *
437 * Retrieves the `GtkExpression` stored inside the given `value`, and acquires
438 * a reference to it.
439 *
440 * Returns: (transfer full) (nullable): a `GtkExpression`
441 */
442GtkExpression *
443gtk_value_dup_expression (const GValue *value)
444{
445 g_return_val_if_fail (G_VALUE_HOLDS (value, GTK_TYPE_EXPRESSION), NULL);
446
447 if (value->data[0].v_pointer == NULL)
448 return NULL;
449
450 GtkExpression *expression = value->data[0].v_pointer;
451
452 return gtk_expression_ref (self: expression);
453}
454
455static void
456param_expression_init (GParamSpec *pspec)
457{
458}
459
460static void
461param_expression_set_default (GParamSpec *pspec,
462 GValue *value)
463{
464 value->data[0].v_pointer = NULL;
465}
466
467static gboolean
468param_expression_validate (GParamSpec *pspec,
469 GValue *value)
470{
471 GtkParamSpecExpression *espec = GTK_PARAM_SPEC_EXPRESSION (pspec);
472 GtkExpression *expr = value->data[0].v_pointer;
473 guint changed = 0;
474
475 if (expr != NULL &&
476 !g_value_type_compatible (G_TYPE_FROM_INSTANCE (expr), G_PARAM_SPEC_VALUE_TYPE (espec)))
477 {
478 gtk_expression_unref (self: expr);
479 value->data[0].v_pointer = NULL;
480 changed++;
481 }
482
483 return changed;
484}
485
486static int
487param_expression_values_cmp (GParamSpec *pspec,
488 const GValue *value1,
489 const GValue *value2)
490{
491 guint8 *p1 = value1->data[0].v_pointer;
492 guint8 *p2 = value2->data[0].v_pointer;
493
494 return p1 < p2 ? -1 : p1 > p2;
495}
496
497GType
498gtk_param_expression_get_type (void)
499{
500 static gsize param_expression_type__volatile;
501
502 if (g_once_init_enter (&param_expression_type__volatile))
503 {
504 const GParamSpecTypeInfo pspec_info = {
505 sizeof (GtkParamSpecExpression),
506 16,
507 param_expression_init,
508 GTK_TYPE_EXPRESSION,
509 NULL,
510 param_expression_set_default,
511 param_expression_validate,
512 param_expression_values_cmp,
513 };
514
515 GType param_expression_type =
516 g_param_type_register_static (name: g_intern_static_string (string: "GtkParamSpecExpression"),
517 pspec_info: &pspec_info);
518
519 g_once_init_leave (&param_expression_type__volatile, param_expression_type);
520 }
521
522 return param_expression_type__volatile;
523}
524
525/**
526 * gtk_param_spec_expression:
527 * @name: canonical name of the property
528 * @nick: a user-readable name for the property
529 * @blurb: a user-readable description of the property
530 * @flags: flags for the property
531 *
532 * Creates a new `GParamSpec` instance for a property holding a `GtkExpression`.
533 *
534 * See `g_param_spec_internal()` for details on the property strings.
535 *
536 * Returns: (transfer full): a newly created property specification
537 */
538GParamSpec *
539gtk_param_spec_expression (const char *name,
540 const char *nick,
541 const char *blurb,
542 GParamFlags flags)
543{
544 GParamSpec *pspec = g_param_spec_internal (GTK_TYPE_PARAM_SPEC_EXPRESSION,
545 name, nick, blurb,
546 flags);
547
548 pspec->value_type = GTK_TYPE_EXPRESSION;
549
550 return pspec;
551}
552
553/* }}} */
554
555/* {{{ GtkExpression internals */
556
557static void
558gtk_expression_real_finalize (GtkExpression *self)
559{
560 g_type_free_instance (instance: (GTypeInstance *) self);
561}
562
563static gsize
564gtk_expression_real_watch_size (GtkExpression *self)
565{
566 return 0;
567}
568
569static void
570gtk_expression_real_watch (GtkExpression *self,
571 GtkExpressionSubWatch *watch,
572 gpointer this_,
573 GtkExpressionNotify notify,
574 gpointer user_data)
575{
576}
577
578static void
579gtk_expression_real_unwatch (GtkExpression *self,
580 GtkExpressionSubWatch *watch)
581{
582}
583
584static gsize
585gtk_expression_watch_size (GtkExpression *self)
586{
587 return GTK_EXPRESSION_GET_CLASS (self)->watch_size (self);
588}
589
590static void
591gtk_expression_class_init (GtkExpressionClass *klass)
592{
593 klass->finalize = gtk_expression_real_finalize;
594 klass->watch_size = gtk_expression_real_watch_size;
595 klass->watch = gtk_expression_real_watch;
596 klass->unwatch = gtk_expression_real_unwatch;
597}
598
599static void
600gtk_expression_init (GtkExpression *self)
601{
602 g_atomic_ref_count_init (arc: &self->ref_count);
603}
604
605GType
606gtk_expression_get_type (void)
607{
608 static gsize expression_type__volatile;
609
610 if (g_once_init_enter (&expression_type__volatile))
611 {
612 static const GTypeFundamentalInfo finfo = {
613 (G_TYPE_FLAG_CLASSED |
614 G_TYPE_FLAG_INSTANTIATABLE |
615 G_TYPE_FLAG_DERIVABLE |
616 G_TYPE_FLAG_DEEP_DERIVABLE),
617 };
618
619 static const GTypeValueTable value_table = {
620 value_expression_init,
621 value_expression_free_value,
622 value_expression_copy_value,
623 value_expression_peek_pointer,
624 "p",
625 value_expression_collect_value,
626 "p",
627 value_expression_lcopy_value,
628 };
629
630 const GTypeInfo event_info = {
631 /* Class */
632 sizeof (GtkExpressionClass),
633 (GBaseInitFunc) NULL,
634 (GBaseFinalizeFunc) NULL,
635 (GClassInitFunc) gtk_expression_class_init,
636 (GClassFinalizeFunc) NULL,
637 NULL,
638
639 /* Instance */
640 sizeof (GtkExpression),
641 0,
642 (GInstanceInitFunc) gtk_expression_init,
643
644 /* GValue */
645 &value_table,
646 };
647
648 GType expression_type =
649 g_type_register_fundamental (type_id: g_type_fundamental_next (),
650 type_name: g_intern_static_string (string: "GtkExpression"),
651 info: &event_info, finfo: &finfo,
652 flags: G_TYPE_FLAG_ABSTRACT);
653
654 g_once_init_leave (&expression_type__volatile, expression_type);
655 }
656
657 return expression_type__volatile;
658}
659
660static void
661gtk_expression_generic_class_init (gpointer g_class,
662 gpointer class_data)
663{
664 GtkExpressionTypeInfo *info = class_data;
665 GtkExpressionClass *expression_class = g_class;
666
667 /* Mandatory */
668 expression_class->is_static = info->is_static;
669 expression_class->evaluate = info->evaluate;
670
671 /* Optional */
672 if (info->finalize != NULL)
673 expression_class->finalize = info->finalize;
674 if (info->watch_size != NULL)
675 expression_class->watch_size = info->watch_size;
676 if (info->watch != NULL)
677 expression_class->watch = info->watch;
678 if (info->unwatch != NULL)
679 expression_class->unwatch = info->unwatch;
680
681 g_free (mem: info);
682}
683
684static GType
685gtk_expression_type_register_static (const char *type_name,
686 const GtkExpressionTypeInfo *type_info)
687{
688 GTypeInfo info;
689
690 info.class_size = sizeof (GtkExpressionClass);
691 info.base_init = NULL;
692 info.base_finalize = NULL;
693 info.class_init = gtk_expression_generic_class_init;
694 info.class_finalize = NULL;
695 info.class_data = g_memdup2 (mem: type_info, byte_size: sizeof (GtkExpressionTypeInfo));
696
697 info.instance_size = type_info->instance_size;
698 info.n_preallocs = 0;
699 info.instance_init = (GInstanceInitFunc) type_info->instance_init;
700 info.value_table = NULL;
701
702 return g_type_register_static (GTK_TYPE_EXPRESSION, type_name, info: &info, flags: 0);
703}
704
705/*< private >
706 * gtk_expression_alloc:
707 * @expression_type: the type of expression to create
708 * @value_type: the type of the value returned by this expression
709 *
710 * Returns: (transfer full): the newly created `GtkExpression`
711 */
712static gpointer
713gtk_expression_alloc (GType expression_type,
714 GType value_type)
715{
716 GtkExpression *self;
717
718 self = (GtkExpression *) g_type_create_instance (type: expression_type);
719
720 self->value_type = value_type;
721
722 return self;
723}
724
725static void
726gtk_expression_subwatch_init (GtkExpression *self,
727 GtkExpressionSubWatch *watch,
728 gpointer this,
729 GtkExpressionNotify notify,
730 gpointer user_data)
731{
732 GTK_EXPRESSION_GET_CLASS (self)->watch (self, watch, this, notify, user_data);
733}
734
735static void
736gtk_expression_subwatch_finish (GtkExpression *self,
737 GtkExpressionSubWatch *watch)
738{
739 GTK_EXPRESSION_GET_CLASS (self)->unwatch (self, watch);
740}
741
742/* }}} */
743
744/* {{{ GtkConstantExpression */
745
746/**
747 * GtkConstantExpression:
748 *
749 * A constant value in a `GtkExpression`.
750 */
751struct _GtkConstantExpression
752{
753 GtkExpression parent;
754
755 GValue value;
756};
757
758static void
759gtk_constant_expression_finalize (GtkExpression *expr)
760{
761 GtkConstantExpression *self = (GtkConstantExpression *) expr;
762
763 g_value_unset (value: &self->value);
764
765 GTK_EXPRESSION_SUPER (expr)->finalize (expr);
766}
767
768static gboolean
769gtk_constant_expression_is_static (GtkExpression *expr)
770{
771 return TRUE;
772}
773
774static gboolean
775gtk_constant_expression_evaluate (GtkExpression *expr,
776 gpointer this,
777 GValue *value)
778{
779 GtkConstantExpression *self = (GtkConstantExpression *) expr;
780
781 g_value_init (value, G_VALUE_TYPE (&self->value));
782 g_value_copy (src_value: &self->value, dest_value: value);
783 return TRUE;
784}
785
786static const GtkExpressionTypeInfo gtk_constant_expression_info =
787{
788 sizeof (GtkConstantExpression),
789 NULL,
790 gtk_constant_expression_finalize,
791 gtk_constant_expression_is_static,
792 gtk_constant_expression_evaluate,
793 NULL,
794 NULL,
795 NULL,
796};
797
798GTK_DEFINE_EXPRESSION_TYPE (GtkConstantExpression,
799 gtk_constant_expression,
800 &gtk_constant_expression_info)
801
802/**
803 * gtk_constant_expression_new:
804 * @value_type: The type of the object
805 * @...: arguments to create the object from
806 *
807 * Creates a `GtkExpression` that evaluates to the
808 * object given by the arguments.
809 *
810 * Returns: (transfer full) (type GtkConstantExpression): a new `GtkExpression`
811 */
812GtkExpression *
813gtk_constant_expression_new (GType value_type,
814 ...)
815{
816 GValue value = G_VALUE_INIT;
817 GtkExpression *result;
818 va_list args;
819 char *error;
820
821 va_start (args, value_type);
822 G_VALUE_COLLECT_INIT (&value, value_type,
823 args, G_VALUE_NOCOPY_CONTENTS,
824 &error);
825 if (error)
826 {
827 g_critical ("%s: %s", G_STRLOC, error);
828 g_free (mem: error);
829 /* we purposely leak the value here, it might not be
830 * in a sane state if an error condition occurred
831 */
832 return NULL;
833 }
834
835 result = gtk_constant_expression_new_for_value (value: &value);
836
837 g_value_unset (value: &value);
838 va_end (args);
839
840 return result;
841}
842
843/**
844 * gtk_constant_expression_new_for_value: (constructor)
845 * @value: a `GValue`
846 *
847 * Creates an expression that always evaluates to the given `value`.
848 *
849 * Returns: (transfer full) (type GtkConstantExpression): a new `GtkExpression`
850 **/
851GtkExpression *
852gtk_constant_expression_new_for_value (const GValue *value)
853{
854 GtkExpression *result;
855 GtkConstantExpression *self;
856
857 g_return_val_if_fail (G_IS_VALUE (value), NULL);
858
859 result = gtk_expression_alloc (GTK_TYPE_CONSTANT_EXPRESSION, G_VALUE_TYPE (value));
860 self = (GtkConstantExpression *) result;
861
862 g_value_init (value: &self->value, G_VALUE_TYPE (value));
863 g_value_copy (src_value: value, dest_value: &self->value);
864
865 return result;
866}
867
868/**
869 * gtk_constant_expression_get_value:
870 * @expression: (type GtkConstantExpression): a constant `GtkExpression`
871 *
872 * Gets the value that a constant expression evaluates to.
873 *
874 * Returns: (transfer none): the value
875 */
876const GValue *
877gtk_constant_expression_get_value (GtkExpression *expression)
878{
879 GtkConstantExpression *self = (GtkConstantExpression *) expression;
880
881 g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (expression, GTK_TYPE_CONSTANT_EXPRESSION), NULL);
882
883 return &self->value;
884}
885
886/* }}} */
887
888/* {{{ GtkObjectExpression */
889
890typedef struct _GtkObjectExpressionWatch GtkObjectExpressionWatch;
891
892/**
893 * GtkObjectExpression:
894 *
895 * A `GObject` value in a `GtkExpression`.
896 */
897struct _GtkObjectExpression
898{
899 GtkExpression parent;
900
901 GObject *object;
902 GSList *watches;
903};
904
905struct _GtkObjectExpressionWatch
906{
907 GtkExpressionNotify notify;
908 gpointer user_data;
909};
910
911static void
912gtk_object_expression_weak_ref_cb (gpointer data,
913 GObject *object)
914{
915 GtkObjectExpression *self = (GtkObjectExpression *) data;
916 GSList *l;
917
918 self->object = NULL;
919
920 for (l = self->watches; l; l = l->next)
921 {
922 GtkObjectExpressionWatch *owatch = l->data;
923
924 owatch->notify (owatch->user_data);
925 }
926}
927
928static void
929gtk_object_expression_finalize (GtkExpression *expr)
930{
931 GtkObjectExpression *self = (GtkObjectExpression *) expr;
932
933 if (self->object)
934 g_object_weak_unref (object: self->object, notify: gtk_object_expression_weak_ref_cb, data: self);
935
936 g_assert (self->watches == NULL);
937
938 GTK_EXPRESSION_SUPER (expr)->finalize (expr);
939}
940
941static gboolean
942gtk_object_expression_is_static (GtkExpression *expr)
943{
944 return FALSE;
945}
946
947static gboolean
948gtk_object_expression_evaluate (GtkExpression *expr,
949 gpointer this,
950 GValue *value)
951{
952 GtkObjectExpression *self = (GtkObjectExpression *) expr;
953
954 if (self->object == NULL)
955 return FALSE;
956
957 g_value_init (value, g_type: gtk_expression_get_value_type (self: expr));
958 g_value_set_object (value, v_object: self->object);
959 return TRUE;
960}
961
962static gsize
963gtk_object_expression_watch_size (GtkExpression *expr)
964{
965 return sizeof (GtkObjectExpressionWatch);
966}
967
968static void
969gtk_object_expression_watch (GtkExpression *expr,
970 GtkExpressionSubWatch *watch,
971 gpointer this_,
972 GtkExpressionNotify notify,
973 gpointer user_data)
974{
975 GtkObjectExpression *self = (GtkObjectExpression *) expr;
976 GtkObjectExpressionWatch *owatch = (GtkObjectExpressionWatch *) watch;
977
978 owatch->notify = notify;
979 owatch->user_data = user_data;
980 self->watches = g_slist_prepend (list: self->watches, data: owatch);
981}
982
983static void
984gtk_object_expression_unwatch (GtkExpression *expr,
985 GtkExpressionSubWatch *watch)
986{
987 GtkObjectExpression *self = (GtkObjectExpression *) expr;
988
989 self->watches = g_slist_remove (list: self->watches, data: watch);
990}
991
992static const GtkExpressionTypeInfo gtk_object_expression_info =
993{
994 sizeof (GtkObjectExpression),
995 NULL,
996 gtk_object_expression_finalize,
997 gtk_object_expression_is_static,
998 gtk_object_expression_evaluate,
999 gtk_object_expression_watch_size,
1000 gtk_object_expression_watch,
1001 gtk_object_expression_unwatch
1002};
1003
1004GTK_DEFINE_EXPRESSION_TYPE (GtkObjectExpression,
1005 gtk_object_expression,
1006 &gtk_object_expression_info)
1007
1008/**
1009 * gtk_object_expression_new: (constructor)
1010 * @object: (transfer none): object to watch
1011 *
1012 * Creates an expression evaluating to the given `object` with a weak reference.
1013 *
1014 * Once the `object` is disposed, it will fail to evaluate.
1015 *
1016 * This expression is meant to break reference cycles.
1017 *
1018 * If you want to keep a reference to `object`, use [ctor@Gtk.ConstantExpression.new].
1019 *
1020 * Returns: (type GtkObjectExpression) (transfer full): a new `GtkExpression`
1021 **/
1022GtkExpression *
1023gtk_object_expression_new (GObject *object)
1024{
1025 GtkExpression *result;
1026 GtkObjectExpression *self;
1027
1028 g_return_val_if_fail (G_IS_OBJECT (object), NULL);
1029
1030 result = gtk_expression_alloc (GTK_TYPE_OBJECT_EXPRESSION, G_OBJECT_TYPE (object));
1031 self = (GtkObjectExpression *) result;
1032
1033 self->object = object;
1034 g_object_weak_ref (object, notify: gtk_object_expression_weak_ref_cb, data: self);
1035
1036 return result;
1037}
1038
1039/**
1040 * gtk_object_expression_get_object:
1041 * @expression: (type GtkObjectExpression): an object `GtkExpression`
1042 *
1043 * Gets the object that the expression evaluates to.
1044 *
1045 * Returns: (transfer none) (nullable): the object, or `NULL`
1046 */
1047GObject *
1048gtk_object_expression_get_object (GtkExpression *expression)
1049{
1050 GtkObjectExpression *self = (GtkObjectExpression *) expression;
1051
1052 g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (expression, GTK_TYPE_OBJECT_EXPRESSION), NULL);
1053
1054 return self->object;
1055}
1056
1057/* }}} */
1058
1059/* {{{ GtkPropertyExpression */
1060
1061/**
1062 * GtkPropertyExpression:
1063 *
1064 * A `GObject` property value in a `GtkExpression`.
1065 */
1066struct _GtkPropertyExpression
1067{
1068 GtkExpression parent;
1069
1070 GtkExpression *expr;
1071
1072 GParamSpec *pspec;
1073};
1074
1075static void
1076gtk_property_expression_finalize (GtkExpression *expr)
1077{
1078 GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
1079
1080 g_clear_pointer (&self->expr, gtk_expression_unref);
1081
1082 GTK_EXPRESSION_SUPER (expr)->finalize (expr);
1083}
1084
1085static gboolean
1086gtk_property_expression_is_static (GtkExpression *expr)
1087{
1088 return FALSE;
1089}
1090
1091static GObject *
1092gtk_property_expression_get_object (GtkPropertyExpression *self,
1093 gpointer this)
1094{
1095 GValue expr_value = G_VALUE_INIT;
1096 GObject *object;
1097
1098 if (self->expr == NULL)
1099 {
1100 if (this)
1101 return g_object_ref (this);
1102 else
1103 return NULL;
1104 }
1105
1106 if (!gtk_expression_evaluate (self: self->expr, this_: this, value: &expr_value))
1107 return NULL;
1108
1109 if (!G_VALUE_HOLDS_OBJECT (&expr_value))
1110 {
1111 g_value_unset (value: &expr_value);
1112 return NULL;
1113 }
1114
1115 object = g_value_dup_object (value: &expr_value);
1116 g_value_unset (value: &expr_value);
1117 if (object == NULL)
1118 return NULL;
1119
1120 if (!G_TYPE_CHECK_INSTANCE_TYPE (object, self->pspec->owner_type))
1121 {
1122 g_object_unref (object);
1123 return NULL;
1124 }
1125
1126 return object;
1127}
1128
1129static gboolean
1130gtk_property_expression_evaluate (GtkExpression *expr,
1131 gpointer this,
1132 GValue *value)
1133{
1134 GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
1135 GObject *object;
1136
1137 object = gtk_property_expression_get_object (self, this);
1138 if (object == NULL)
1139 return FALSE;
1140
1141 g_object_get_property (object, property_name: self->pspec->name, value);
1142 g_object_unref (object);
1143 return TRUE;
1144}
1145
1146typedef struct _GtkPropertyExpressionWatch GtkPropertyExpressionWatch;
1147
1148struct _GtkPropertyExpressionWatch
1149{
1150 GtkExpressionNotify notify;
1151 gpointer user_data;
1152
1153 GtkPropertyExpression *expr;
1154 gpointer this;
1155 GClosure *closure;
1156 guchar sub[0];
1157};
1158
1159static void
1160gtk_property_expression_watch_destroy_closure (GtkPropertyExpressionWatch *pwatch)
1161{
1162 if (pwatch->closure == NULL)
1163 return;
1164
1165 g_closure_invalidate (closure: pwatch->closure);
1166 g_closure_unref (closure: pwatch->closure);
1167 pwatch->closure = NULL;
1168}
1169
1170static void
1171gtk_property_expression_watch_notify_cb (GObject *object,
1172 GParamSpec *pspec,
1173 GtkPropertyExpressionWatch *pwatch)
1174{
1175 pwatch->notify (pwatch->user_data);
1176}
1177
1178static void
1179gtk_property_expression_watch_create_closure (GtkPropertyExpressionWatch *pwatch)
1180{
1181 GObject *object;
1182
1183 object = gtk_property_expression_get_object (self: pwatch->expr, this: pwatch->this);
1184 if (object == NULL)
1185 return;
1186
1187 pwatch->closure = g_cclosure_new (G_CALLBACK (gtk_property_expression_watch_notify_cb), user_data: pwatch, NULL);
1188 if (!g_signal_connect_closure_by_id (instance: object,
1189 signal_id: g_signal_lookup (name: "notify", G_OBJECT_TYPE (object)),
1190 detail: g_quark_from_string (string: pwatch->expr->pspec->name),
1191 closure: g_closure_ref (closure: pwatch->closure),
1192 FALSE))
1193 {
1194 g_assert_not_reached ();
1195 }
1196
1197 g_object_unref (object);
1198}
1199
1200static void
1201gtk_property_expression_watch_expr_notify_cb (gpointer data)
1202{
1203 GtkPropertyExpressionWatch *pwatch = data;
1204
1205 gtk_property_expression_watch_destroy_closure (pwatch);
1206 gtk_property_expression_watch_create_closure (pwatch);
1207 pwatch->notify (pwatch->user_data);
1208}
1209
1210static gsize
1211gtk_property_expression_watch_size (GtkExpression *expr)
1212{
1213 GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
1214 gsize result;
1215
1216 result = sizeof (GtkPropertyExpressionWatch);
1217 if (self->expr)
1218 result += gtk_expression_watch_size (self: self->expr);
1219
1220 return result;
1221}
1222
1223static void
1224gtk_property_expression_watch (GtkExpression *expr,
1225 GtkExpressionSubWatch *watch,
1226 gpointer this_,
1227 GtkExpressionNotify notify,
1228 gpointer user_data)
1229{
1230 GtkPropertyExpressionWatch *pwatch = (GtkPropertyExpressionWatch *) watch;
1231 GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
1232
1233 pwatch->notify = notify;
1234 pwatch->user_data = user_data;
1235 pwatch->expr = self;
1236 pwatch->this = this_;
1237 if (self->expr && !gtk_expression_is_static (self: self->expr))
1238 {
1239 gtk_expression_subwatch_init (self: self->expr,
1240 watch: (GtkExpressionSubWatch *) pwatch->sub,
1241 this: this_,
1242 notify: gtk_property_expression_watch_expr_notify_cb,
1243 user_data: pwatch);
1244 }
1245
1246 gtk_property_expression_watch_create_closure (pwatch);
1247}
1248
1249static void
1250gtk_property_expression_unwatch (GtkExpression *expr,
1251 GtkExpressionSubWatch *watch)
1252{
1253 GtkPropertyExpressionWatch *pwatch = (GtkPropertyExpressionWatch *) watch;
1254 GtkPropertyExpression *self = (GtkPropertyExpression *) expr;
1255
1256 gtk_property_expression_watch_destroy_closure (pwatch);
1257
1258 if (self->expr && !gtk_expression_is_static (self: self->expr))
1259 gtk_expression_subwatch_finish (self: self->expr, watch: (GtkExpressionSubWatch *) pwatch->sub);
1260}
1261
1262static const GtkExpressionTypeInfo gtk_property_expression_info =
1263{
1264 sizeof (GtkPropertyExpression),
1265 NULL,
1266 gtk_property_expression_finalize,
1267 gtk_property_expression_is_static,
1268 gtk_property_expression_evaluate,
1269 gtk_property_expression_watch_size,
1270 gtk_property_expression_watch,
1271 gtk_property_expression_unwatch
1272};
1273
1274GTK_DEFINE_EXPRESSION_TYPE (GtkPropertyExpression,
1275 gtk_property_expression,
1276 &gtk_property_expression_info)
1277
1278/**
1279 * gtk_property_expression_new: (constructor)
1280 * @this_type: The type to expect for the this type
1281 * @expression: (nullable) (transfer full): Expression to
1282 * evaluate to get the object to query or `NULL` to
1283 * query the `this` object
1284 * @property_name: name of the property
1285 *
1286 * Creates an expression that looks up a property.
1287 *
1288 * The object to use is found by evaluating the `expression`,
1289 * or using the `this` argument when `expression` is `NULL`.
1290 *
1291 * If the resulting object conforms to `this_type`, its property named
1292 * `property_name` will be queried. Otherwise, this expression's
1293 * evaluation will fail.
1294 *
1295 * The given `this_type` must have a property with `property_name`.
1296 *
1297 * Returns: (type GtkPropertyExpression) (transfer full): a new `GtkExpression`
1298 **/
1299GtkExpression *
1300gtk_property_expression_new (GType this_type,
1301 GtkExpression *expression,
1302 const char *property_name)
1303{
1304 GParamSpec *pspec;
1305
1306 if (g_type_fundamental (type_id: this_type) == G_TYPE_OBJECT)
1307 {
1308 GObjectClass *class = g_type_class_ref (type: this_type);
1309 pspec = g_object_class_find_property (oclass: class, property_name);
1310 g_type_class_unref (g_class: class);
1311 }
1312 else if (g_type_fundamental (type_id: this_type) == G_TYPE_INTERFACE)
1313 {
1314 GTypeInterface *iface = g_type_default_interface_ref (g_type: this_type);
1315 pspec = g_object_interface_find_property (g_iface: iface, property_name);
1316 g_type_default_interface_unref (g_iface: iface);
1317 }
1318 else
1319 {
1320 g_critical ("Type `%s` does not support properties", g_type_name (this_type));
1321 return NULL;
1322 }
1323
1324 if (pspec == NULL)
1325 {
1326 g_critical ("Type `%s` does not have a property named `%s`", g_type_name (this_type), property_name);
1327 return NULL;
1328 }
1329
1330 return gtk_property_expression_new_for_pspec (expression, pspec);
1331}
1332
1333/**
1334 * gtk_property_expression_new_for_pspec: (constructor)
1335 * @expression: (nullable) (transfer full): Expression to
1336 * evaluate to get the object to query or `NULL` to
1337 * query the `this` object
1338 * @pspec: the `GParamSpec` for the property to query
1339 *
1340 * Creates an expression that looks up a property.
1341 *
1342 * The object to use is found by evaluating the `expression`,
1343 * or using the `this` argument when `expression` is `NULL`.
1344 *
1345 * If the resulting object conforms to `this_type`, its
1346 * property specified by `pspec` will be queried.
1347 * Otherwise, this expression's evaluation will fail.
1348 *
1349 * Returns: (type GtkPropertyExpression) (transfer full): a new `GtkExpression`
1350 **/
1351GtkExpression *
1352gtk_property_expression_new_for_pspec (GtkExpression *expression,
1353 GParamSpec *pspec)
1354{
1355 GtkExpression *result;
1356 GtkPropertyExpression *self;
1357
1358 result = gtk_expression_alloc (GTK_TYPE_PROPERTY_EXPRESSION, value_type: pspec->value_type);
1359 self = (GtkPropertyExpression *) result;
1360
1361 self->pspec = pspec;
1362 self->expr = expression;
1363
1364 return result;
1365}
1366
1367/**
1368 * gtk_property_expression_get_expression:
1369 * @expression: (type GtkPropertyExpression): a property `GtkExpression`
1370 *
1371 * Gets the expression specifying the object of
1372 * a property expression.
1373 *
1374 * Returns: (transfer none) (nullable): the object expression
1375 */
1376GtkExpression *
1377gtk_property_expression_get_expression (GtkExpression *expression)
1378{
1379 GtkPropertyExpression *self = (GtkPropertyExpression *) expression;
1380
1381 g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (expression, GTK_TYPE_PROPERTY_EXPRESSION), NULL);
1382
1383 return self->expr;
1384}
1385
1386/**
1387 * gtk_property_expression_get_pspec:
1388 * @expression: (type GtkPropertyExpression): a property `GtkExpression`
1389 *
1390 * Gets the `GParamSpec` specifying the property of
1391 * a property expression.
1392 *
1393 * Returns: (transfer none): the `GParamSpec` for the property
1394 */
1395GParamSpec *
1396gtk_property_expression_get_pspec (GtkExpression *expression)
1397{
1398 GtkPropertyExpression *self = (GtkPropertyExpression *) expression;
1399
1400 g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (expression, GTK_TYPE_PROPERTY_EXPRESSION), NULL);
1401
1402 return self->pspec;
1403}
1404
1405/* }}} */
1406
1407/* {{{ GtkClosureExpression */
1408
1409/**
1410 * GtkClosureExpression:
1411 *
1412 * An expression using a custom `GClosure` to compute the value from
1413 * its parameters.
1414 */
1415struct _GtkClosureExpression
1416{
1417 GtkExpression parent;
1418
1419 GClosure *closure;
1420 guint n_params;
1421 GtkExpression **params;
1422};
1423
1424static void
1425gtk_closure_expression_finalize (GtkExpression *expr)
1426{
1427 GtkClosureExpression *self = (GtkClosureExpression *) expr;
1428 guint i;
1429
1430 for (i = 0; i < self->n_params; i++)
1431 {
1432 gtk_expression_unref (self: self->params[i]);
1433 }
1434 g_free (mem: self->params);
1435
1436 g_closure_unref (closure: self->closure);
1437
1438 GTK_EXPRESSION_SUPER (expr)->finalize (expr);
1439}
1440
1441static gboolean
1442gtk_closure_expression_is_static (GtkExpression *expr)
1443{
1444 GtkClosureExpression *self = (GtkClosureExpression *) expr;
1445 guint i;
1446
1447 for (i = 0; i < self->n_params; i++)
1448 {
1449 if (!gtk_expression_is_static (self: self->params[i]))
1450 return FALSE;
1451 }
1452
1453 return TRUE;
1454}
1455
1456static gboolean
1457gtk_closure_expression_evaluate (GtkExpression *expr,
1458 gpointer this,
1459 GValue *value)
1460{
1461 GtkClosureExpression *self = (GtkClosureExpression *) expr;
1462 GValue *instance_and_params;
1463 gboolean result = TRUE;
1464 guint i;
1465
1466 instance_and_params = g_alloca (sizeof (GValue) * (self->n_params + 1));
1467 memset (s: instance_and_params, c: 0, n: sizeof (GValue) * (self->n_params + 1));
1468
1469 for (i = 0; i < self->n_params; i++)
1470 {
1471 if (!gtk_expression_evaluate (self: self->params[i], this_: this, value: &instance_and_params[i + 1]))
1472 {
1473 result = FALSE;
1474 goto out;
1475 }
1476 }
1477 if (this)
1478 g_value_init_from_instance (value: instance_and_params, instance: this);
1479 else
1480 g_value_init (value: instance_and_params, G_TYPE_OBJECT);
1481
1482 g_value_init (value, g_type: expr->value_type);
1483 g_closure_invoke (closure: self->closure,
1484 return_value: value,
1485 n_param_values: self->n_params + 1,
1486 param_values: instance_and_params,
1487 NULL);
1488
1489out:
1490 for (i = 0; i < self->n_params + 1; i++)
1491 g_value_unset (value: &instance_and_params[i]);
1492
1493 return result;
1494}
1495
1496typedef struct _GtkClosureExpressionWatch GtkClosureExpressionWatch;
1497struct _GtkClosureExpressionWatch
1498{
1499 GtkExpressionNotify notify;
1500 gpointer user_data;
1501
1502 guchar sub[0];
1503};
1504
1505static void
1506gtk_closure_expression_watch_notify_cb (gpointer data)
1507{
1508 GtkClosureExpressionWatch *cwatch = data;
1509
1510 cwatch->notify (cwatch->user_data);
1511}
1512
1513static gsize
1514gtk_closure_expression_watch_size (GtkExpression *expr)
1515{
1516 GtkClosureExpression *self = (GtkClosureExpression *) expr;
1517 gsize size;
1518 guint i;
1519
1520 size = sizeof (GtkClosureExpressionWatch);
1521
1522 for (i = 0; i < self->n_params; i++)
1523 {
1524 if (gtk_expression_is_static (self: self->params[i]))
1525 continue;
1526
1527 size += gtk_expression_watch_size (self: self->params[i]);
1528 }
1529
1530 return size;
1531}
1532
1533static void
1534gtk_closure_expression_watch (GtkExpression *expr,
1535 GtkExpressionSubWatch *watch,
1536 gpointer this_,
1537 GtkExpressionNotify notify,
1538 gpointer user_data)
1539{
1540 GtkClosureExpressionWatch *cwatch = (GtkClosureExpressionWatch *) watch;
1541 GtkClosureExpression *self = (GtkClosureExpression *) expr;
1542 guchar *sub;
1543 guint i;
1544
1545 cwatch->notify = notify;
1546 cwatch->user_data = user_data;
1547
1548 sub = cwatch->sub;
1549 for (i = 0; i < self->n_params; i++)
1550 {
1551 if (gtk_expression_is_static (self: self->params[i]))
1552 continue;
1553
1554 gtk_expression_subwatch_init (self: self->params[i],
1555 watch: (GtkExpressionSubWatch *) sub,
1556 this: this_,
1557 notify: gtk_closure_expression_watch_notify_cb,
1558 user_data: watch);
1559 sub += gtk_expression_watch_size (self: self->params[i]);
1560 }
1561}
1562
1563static void
1564gtk_closure_expression_unwatch (GtkExpression *expr,
1565 GtkExpressionSubWatch *watch)
1566{
1567 GtkClosureExpressionWatch *cwatch = (GtkClosureExpressionWatch *) watch;
1568 GtkClosureExpression *self = (GtkClosureExpression *) expr;
1569 guchar *sub;
1570 guint i;
1571
1572 sub = cwatch->sub;
1573 for (i = 0; i < self->n_params; i++)
1574 {
1575 if (gtk_expression_is_static (self: self->params[i]))
1576 continue;
1577
1578 gtk_expression_subwatch_finish (self: self->params[i],
1579 watch: (GtkExpressionSubWatch *) sub);
1580 sub += gtk_expression_watch_size (self: self->params[i]);
1581 }
1582}
1583
1584static const GtkExpressionTypeInfo gtk_closure_expression_info =
1585{
1586 sizeof (GtkClosureExpression),
1587 NULL,
1588 gtk_closure_expression_finalize,
1589 gtk_closure_expression_is_static,
1590 gtk_closure_expression_evaluate,
1591 gtk_closure_expression_watch_size,
1592 gtk_closure_expression_watch,
1593 gtk_closure_expression_unwatch
1594};
1595
1596GTK_DEFINE_EXPRESSION_TYPE (GtkClosureExpression,
1597 gtk_closure_expression,
1598 &gtk_closure_expression_info)
1599
1600/**
1601 * gtk_closure_expression_new: (constructor)
1602 * @value_type: the type of the value that this expression evaluates to
1603 * @closure: closure to call when evaluating this expression. If closure is floating, it is adopted
1604 * @n_params: the number of params needed for evaluating `closure`
1605 * @params: (nullable) (array length=n_params) (transfer full): expressions for each parameter
1606 *
1607 * Creates a `GtkExpression` that calls `closure` when it is evaluated.
1608 *
1609 * `closure` is called with the `this` object and the results of evaluating
1610 * the `params` expressions.
1611 *
1612 * Returns: (transfer full) (type GtkClosureExpression): a new `GtkExpression`
1613 */
1614GtkExpression *
1615gtk_closure_expression_new (GType value_type,
1616 GClosure *closure,
1617 guint n_params,
1618 GtkExpression **params)
1619{
1620 GtkExpression *result;
1621 GtkClosureExpression *self;
1622 guint i;
1623
1624 g_return_val_if_fail (closure != NULL, NULL);
1625 g_return_val_if_fail (n_params == 0 || params != NULL, NULL);
1626
1627 result = gtk_expression_alloc (GTK_TYPE_CLOSURE_EXPRESSION, value_type);
1628 self = (GtkClosureExpression *) result;
1629
1630 self->closure = g_closure_ref (closure);
1631 g_closure_sink (closure);
1632 if (G_CLOSURE_NEEDS_MARSHAL (closure))
1633 g_closure_set_marshal (closure, marshal: g_cclosure_marshal_generic);
1634
1635 self->n_params = n_params;
1636 self->params = g_new (GtkExpression *, n_params);
1637 for (i = 0; i < n_params; i++)
1638 self->params[i] = params[i];
1639
1640 return result;
1641}
1642
1643/* }}} */
1644
1645/* {{{ GtkCClosureExpression */
1646
1647/**
1648 * GtkCClosureExpression:
1649 *
1650 * A variant of `GtkClosureExpression` using a C closure.
1651 */
1652struct _GtkCClosureExpression
1653{
1654 GtkClosureExpression parent;
1655};
1656
1657static const GtkExpressionTypeInfo gtk_cclosure_expression_info =
1658{
1659 sizeof (GtkClosureExpression),
1660 NULL,
1661 gtk_closure_expression_finalize,
1662 gtk_closure_expression_is_static,
1663 gtk_closure_expression_evaluate,
1664 gtk_closure_expression_watch_size,
1665 gtk_closure_expression_watch,
1666 gtk_closure_expression_unwatch
1667};
1668
1669GTK_DEFINE_EXPRESSION_TYPE (GtkCClosureExpression,
1670 gtk_cclosure_expression,
1671 &gtk_cclosure_expression_info)
1672
1673/**
1674 * gtk_cclosure_expression_new: (constructor)
1675 * @value_type: the type of the value that this expression evaluates to
1676 * @marshal: (scope call) (nullable): marshaller used for creating a closure
1677 * @n_params: the number of params needed for evaluating @closure
1678 * @params: (array length=n_params) (transfer full): expressions for each parameter
1679 * @callback_func: (scope notified) (closure user_data) (destroy user_destroy): callback used for creating a closure
1680 * @user_data: (nullable): user data used for creating a closure
1681 * @user_destroy: (nullable): destroy notify for @user_data
1682 *
1683 * Creates a `GtkExpression` that calls `callback_func` when it is evaluated.
1684 *
1685 * This function is a variant of [ctor@Gtk.ClosureExpression.new] that
1686 * creates a `GClosure` by calling g_cclosure_new() with the given
1687 * `callback_func`, `user_data` and `user_destroy`.
1688 *
1689 * Returns: (transfer full) (type GtkCClosureExpression): a new `GtkExpression`
1690 */
1691GtkExpression *
1692gtk_cclosure_expression_new (GType value_type,
1693 GClosureMarshal marshal,
1694 guint n_params,
1695 GtkExpression **params,
1696 GCallback callback_func,
1697 gpointer user_data,
1698 GClosureNotify user_destroy)
1699{
1700 GtkExpression *result;
1701 GtkClosureExpression *self;
1702 GClosure *closure;
1703 guint i;
1704
1705 g_return_val_if_fail (callback_func != NULL, NULL);
1706 g_return_val_if_fail (n_params == 0 || params != NULL, NULL);
1707
1708 result = gtk_expression_alloc (GTK_TYPE_CCLOSURE_EXPRESSION, value_type);
1709 self = (GtkClosureExpression *) result;
1710
1711 closure = g_cclosure_new (callback_func, user_data, destroy_data: user_destroy);
1712 if (marshal)
1713 g_closure_set_marshal (closure, marshal);
1714
1715 self->closure = g_closure_ref (closure);
1716 g_closure_sink (closure);
1717 if (G_CLOSURE_NEEDS_MARSHAL (closure))
1718 g_closure_set_marshal (closure, marshal: g_cclosure_marshal_generic);
1719
1720 self->n_params = n_params;
1721 self->params = g_new (GtkExpression *, n_params);
1722 for (i = 0; i < n_params; i++)
1723 self->params[i] = params[i];
1724
1725 return result;
1726}
1727
1728/* }}} */
1729
1730/* {{{ GtkExpression public API */
1731
1732/**
1733 * gtk_expression_ref:
1734 * @self: a `GtkExpression`
1735 *
1736 * Acquires a reference on the given `GtkExpression`.
1737 *
1738 * Returns: (transfer full): the `GtkExpression` with an additional reference
1739 */
1740GtkExpression *
1741gtk_expression_ref (GtkExpression *self)
1742{
1743 g_return_val_if_fail (GTK_IS_EXPRESSION (self), NULL);
1744
1745 g_atomic_ref_count_inc (arc: &self->ref_count);
1746
1747 return self;
1748}
1749
1750/**
1751 * gtk_expression_unref:
1752 * @self: (transfer full): a `GtkExpression`
1753 *
1754 * Releases a reference on the given `GtkExpression`.
1755 *
1756 * If the reference was the last, the resources associated to the `self` are
1757 * freed.
1758 */
1759void
1760gtk_expression_unref (GtkExpression *self)
1761{
1762 g_return_if_fail (GTK_IS_EXPRESSION (self));
1763
1764 if (g_atomic_ref_count_dec (arc: &self->ref_count))
1765 GTK_EXPRESSION_GET_CLASS (self)->finalize (self);
1766}
1767
1768/**
1769 * gtk_expression_get_value_type:
1770 * @self: a `GtkExpression`
1771 *
1772 * Gets the `GType` that this expression evaluates to.
1773 *
1774 * This type is constant and will not change over the lifetime
1775 * of this expression.
1776 *
1777 * Returns: The type returned from [method@Gtk.Expression.evaluate]
1778 */
1779GType
1780gtk_expression_get_value_type (GtkExpression *self)
1781{
1782 g_return_val_if_fail (GTK_IS_EXPRESSION (self), G_TYPE_INVALID);
1783
1784 return self->value_type;
1785}
1786
1787/**
1788 * gtk_expression_evaluate:
1789 * @self: a `GtkExpression`
1790 * @this_: (transfer none) (type GObject) (nullable): the this argument for the evaluation
1791 * @value: an empty `GValue`
1792 *
1793 * Evaluates the given expression and on success stores the result
1794 * in @value.
1795 *
1796 * The `GType` of `value` will be the type given by
1797 * [method@Gtk.Expression.get_value_type].
1798 *
1799 * It is possible that expressions cannot be evaluated - for example
1800 * when the expression references objects that have been destroyed or
1801 * set to `NULL`. In that case `value` will remain empty and `FALSE`
1802 * will be returned.
1803 *
1804 * Returns: `TRUE` if the expression could be evaluated
1805 **/
1806gboolean
1807gtk_expression_evaluate (GtkExpression *self,
1808 gpointer this_,
1809 GValue *value)
1810{
1811 g_return_val_if_fail (GTK_IS_EXPRESSION (self), FALSE);
1812 g_return_val_if_fail (this_ == NULL || G_IS_OBJECT (this_), FALSE);
1813 g_return_val_if_fail (value != NULL, FALSE);
1814
1815 return GTK_EXPRESSION_GET_CLASS (self)->evaluate (self, this_, value);
1816}
1817
1818/**
1819 * gtk_expression_is_static:
1820 * @self: a `GtkExpression`
1821 *
1822 * Checks if the expression is static.
1823 *
1824 * A static expression will never change its result when
1825 * [method@Gtk.Expression.evaluate] is called on it with the same arguments.
1826 *
1827 * That means a call to [method@Gtk.Expression.watch] is not necessary because
1828 * it will never trigger a notify.
1829 *
1830 * Returns: `TRUE` if the expression is static
1831 **/
1832gboolean
1833gtk_expression_is_static (GtkExpression *self)
1834{
1835 g_return_val_if_fail (GTK_IS_EXPRESSION (self), FALSE);
1836
1837 return GTK_EXPRESSION_GET_CLASS (self)->is_static (self);
1838}
1839
1840static gboolean
1841gtk_expression_watch_is_watching (GtkExpressionWatch *watch)
1842{
1843 return watch->expression != NULL;
1844}
1845
1846static void
1847gtk_expression_watch_this_cb (gpointer data,
1848 GObject *this)
1849{
1850 GtkExpressionWatch *watch = data;
1851
1852 watch->this = NULL;
1853
1854 watch->notify (watch->user_data);
1855 gtk_expression_watch_unwatch (watch);
1856}
1857
1858static void
1859gtk_expression_watch_cb (gpointer data)
1860{
1861 GtkExpressionWatch *watch = data;
1862
1863 if (!gtk_expression_watch_is_watching (watch))
1864 return;
1865
1866 watch->notify (watch->user_data);
1867}
1868
1869/**
1870 * gtk_expression_watch:
1871 * @self: a `GtkExpression`
1872 * @this_: (transfer none) (type GObject) (nullable): the `this` argument to
1873 * watch
1874 * @notify: (closure user_data): callback to invoke when the expression changes
1875 * @user_data: user data to pass to the `notify` callback
1876 * @user_destroy: destroy notify for `user_data`
1877 *
1878 * Watch the given `expression` for changes.
1879 *
1880 * The @notify function will be called whenever the evaluation of `self`
1881 * may have changed.
1882 *
1883 * GTK cannot guarantee that the evaluation did indeed change when the @notify
1884 * gets invoked, but it guarantees the opposite: When it did in fact change,
1885 * the @notify will be invoked.
1886 *
1887 * Returns: (transfer none): The newly installed watch. Note that the only
1888 * reference held to the watch will be released when the watch is unwatched
1889 * which can happen automatically, and not just via
1890 * [method@Gtk.ExpressionWatch.unwatch]. You should call [method@Gtk.ExpressionWatch.ref]
1891 * if you want to keep the watch around.
1892 **/
1893GtkExpressionWatch *
1894gtk_expression_watch (GtkExpression *self,
1895 gpointer this_,
1896 GtkExpressionNotify notify,
1897 gpointer user_data,
1898 GDestroyNotify user_destroy)
1899{
1900 GtkExpressionWatch *watch;
1901
1902 g_return_val_if_fail (self != NULL, NULL);
1903 g_return_val_if_fail (this_ == NULL || G_IS_OBJECT (this_), NULL);
1904 g_return_val_if_fail (notify != NULL, NULL);
1905
1906 watch = g_atomic_rc_box_alloc0 (block_size: sizeof (GtkExpressionWatch) + gtk_expression_watch_size (self));
1907
1908 watch->expression = gtk_expression_ref (self);
1909 watch->this = this_;
1910 if (this_)
1911 g_object_weak_ref (object: this_, notify: gtk_expression_watch_this_cb, data: watch);
1912 watch->notify = notify;
1913 watch->user_data = user_data;
1914 watch->user_destroy = user_destroy;
1915
1916 gtk_expression_subwatch_init (self,
1917 watch: (GtkExpressionSubWatch *) watch->sub,
1918 this: this_,
1919 notify: gtk_expression_watch_cb,
1920 user_data: watch);
1921
1922 return watch;
1923}
1924
1925/**
1926 * gtk_expression_watch_ref:
1927 * @watch: a `GtkExpressionWatch`
1928 *
1929 * Acquires a reference on the given `GtkExpressionWatch`.
1930 *
1931 * Returns: (transfer full): the `GtkExpressionWatch` with an additional reference
1932 */
1933GtkExpressionWatch *
1934gtk_expression_watch_ref (GtkExpressionWatch *watch)
1935{
1936 return g_atomic_rc_box_acquire (watch);
1937}
1938
1939static void
1940gtk_expression_watch_finalize (gpointer data)
1941{
1942 GtkExpressionWatch *watch G_GNUC_UNUSED = data;
1943
1944 g_assert (!gtk_expression_watch_is_watching (data));
1945}
1946
1947/**
1948 * gtk_expression_watch_unref:
1949 * @watch: (transfer full): a `GtkExpressionWatch`
1950 *
1951 * Releases a reference on the given `GtkExpressionWatch`.
1952 *
1953 * If the reference was the last, the resources associated to `self` are
1954 * freed.
1955 */
1956void
1957gtk_expression_watch_unref (GtkExpressionWatch *watch)
1958{
1959 g_atomic_rc_box_release_full (mem_block: watch, clear_func: gtk_expression_watch_finalize);
1960}
1961
1962/**
1963 * gtk_expression_watch_unwatch:
1964 * @watch: (transfer none): watch to release
1965 *
1966 * Stops watching an expression.
1967 *
1968 * See [method@Gtk.Expression.watch] for how the watch
1969 * was established.
1970 */
1971void
1972gtk_expression_watch_unwatch (GtkExpressionWatch *watch)
1973{
1974 if (!gtk_expression_watch_is_watching (watch))
1975 return;
1976
1977 gtk_expression_subwatch_finish (self: watch->expression, watch: (GtkExpressionSubWatch *) watch->sub);
1978
1979 if (watch->this)
1980 g_object_weak_unref (object: watch->this, notify: gtk_expression_watch_this_cb, data: watch);
1981
1982 if (watch->user_destroy)
1983 watch->user_destroy (watch->user_data);
1984
1985 g_clear_pointer (&watch->expression, gtk_expression_unref);
1986
1987 gtk_expression_watch_unref (watch);
1988}
1989
1990/**
1991 * gtk_expression_watch_evaluate:
1992 * @watch: a `GtkExpressionWatch`
1993 * @value: an empty `GValue` to be set
1994 *
1995 * Evaluates the watched expression and on success stores the result
1996 * in `value`.
1997 *
1998 * This is equivalent to calling [method@Gtk.Expression.evaluate] with the
1999 * expression and this pointer originally used to create `watch`.
2000 *
2001 * Returns: `TRUE` if the expression could be evaluated and `value` was set
2002 **/
2003gboolean
2004gtk_expression_watch_evaluate (GtkExpressionWatch *watch,
2005 GValue *value)
2006{
2007 g_return_val_if_fail (watch != NULL, FALSE);
2008
2009 if (!gtk_expression_watch_is_watching (watch))
2010 return FALSE;
2011
2012 return gtk_expression_evaluate (self: watch->expression, this_: watch->this, value);
2013}
2014
2015typedef struct {
2016 GtkExpressionWatch *watch;
2017 GObject *target;
2018 GParamSpec *pspec;
2019} GtkExpressionBind;
2020
2021static void
2022invalidate_binds (gpointer unused,
2023 GObject *object)
2024{
2025 GSList *l, *binds;
2026
2027 binds = g_object_get_data (object, key: "gtk-expression-binds");
2028 for (l = binds; l; l = l->next)
2029 {
2030 GtkExpressionBind *bind = l->data;
2031
2032 /* This guarantees we neither try to update bindings
2033 * (which would wreck havoc because the object is
2034 * dispose()ing itself) nor try to destroy bindings
2035 * anymore, so destruction can be done in free_binds().
2036 */
2037 bind->target = NULL;
2038 }
2039}
2040
2041static void
2042free_binds (gpointer data)
2043{
2044 GSList *l;
2045
2046 for (l = data; l; l = l->next)
2047 {
2048 GtkExpressionBind *bind = l->data;
2049
2050 g_assert (bind->target == NULL);
2051 if (bind->watch)
2052 gtk_expression_watch_unwatch (watch: bind->watch);
2053 g_slice_free (GtkExpressionBind, bind);
2054 }
2055 g_slist_free (list: data);
2056}
2057
2058static void
2059gtk_expression_bind_free (gpointer data)
2060{
2061 GtkExpressionBind *bind = data;
2062
2063 if (bind->target)
2064 {
2065 GSList *binds;
2066 binds = g_object_steal_data (object: bind->target, key: "gtk-expression-binds");
2067 binds = g_slist_remove (list: binds, data: bind);
2068 if (binds)
2069 g_object_set_data_full (object: bind->target, key: "gtk-expression-binds", data: binds, destroy: free_binds);
2070 else
2071 g_object_weak_unref (object: bind->target, notify: invalidate_binds, NULL);
2072
2073 g_slice_free (GtkExpressionBind, bind);
2074 }
2075 else
2076 {
2077 /* If a bind gets unwatched after invalidate_binds() but
2078 * before free_binds(), we end up here. This can happen if
2079 * the bind was watching itself or if the target's dispose()
2080 * function freed the object that was watched.
2081 * We make sure we don't destroy the binding or free_binds() will do
2082 * bad stuff, but we clear the watch, so free_binds() won't try to
2083 * unwatch() it.
2084 */
2085 bind->watch = NULL;
2086 }
2087}
2088
2089static void
2090gtk_expression_bind_notify (gpointer data)
2091{
2092 GValue value = G_VALUE_INIT;
2093 GtkExpressionBind *bind = data;
2094
2095 if (bind->target == NULL)
2096 return;
2097
2098 if (!gtk_expression_watch_evaluate (watch: bind->watch, value: &value))
2099 return;
2100
2101 g_object_set_property (object: bind->target, property_name: bind->pspec->name, value: &value);
2102 g_value_unset (value: &value);
2103}
2104
2105/**
2106 * gtk_expression_bind:
2107 * @self: (transfer full): a `GtkExpression`
2108 * @target: (transfer none) (type GObject): the target object to bind to
2109 * @property: name of the property on `target` to bind to
2110 * @this_: (transfer none) (type GObject) (nullable): the this argument for
2111 * the evaluation of `self`
2112 *
2113 * Bind `target`'s property named `property` to `self`.
2114 *
2115 * The value that `self` evaluates to is set via `g_object_set()` on
2116 * `target`. This is repeated whenever `self` changes to ensure that
2117 * the object's property stays synchronized with `self`.
2118 *
2119 * If `self`'s evaluation fails, `target`'s `property` is not updated.
2120 * You can ensure that this doesn't happen by using a fallback
2121 * expression.
2122 *
2123 * Note that this function takes ownership of `self`. If you want
2124 * to keep it around, you should [method@Gtk.Expression.ref] it beforehand.
2125 *
2126 * Returns: (transfer none): a `GtkExpressionWatch`
2127 **/
2128GtkExpressionWatch *
2129gtk_expression_bind (GtkExpression *self,
2130 gpointer target,
2131 const char *property,
2132 gpointer this_)
2133{
2134 GtkExpressionBind *bind;
2135 GParamSpec *pspec;
2136 GSList *binds;
2137
2138 g_return_val_if_fail (GTK_IS_EXPRESSION (self), NULL);
2139 g_return_val_if_fail (G_IS_OBJECT (target), NULL);
2140 g_return_val_if_fail (property != NULL, NULL);
2141 g_return_val_if_fail (this_ == NULL || G_IS_OBJECT (this_), NULL);
2142
2143 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (target), property_name: property);
2144 if (G_UNLIKELY (pspec == NULL))
2145 {
2146 g_critical ("%s: Class '%s' has no property named '%s'",
2147 G_STRFUNC, G_OBJECT_TYPE_NAME (target), property);
2148 return NULL;
2149 }
2150 if (G_UNLIKELY ((pspec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE))
2151 {
2152 g_critical ("%s: property '%s' of class '%s' is not writable",
2153 G_STRFUNC, pspec->name, G_OBJECT_TYPE_NAME (target));
2154 return NULL;
2155 }
2156
2157 bind = g_slice_new0 (GtkExpressionBind);
2158 binds = g_object_steal_data (object: target, key: "gtk-expression-binds");
2159 if (binds == NULL)
2160 g_object_weak_ref (object: target, notify: invalidate_binds, NULL);
2161 bind->target = target;
2162 bind->pspec = pspec;
2163 bind->watch = gtk_expression_watch (self,
2164 this_,
2165 notify: gtk_expression_bind_notify,
2166 user_data: bind,
2167 user_destroy: gtk_expression_bind_free);
2168 binds = g_slist_prepend (list: binds, data: bind);
2169 g_object_set_data_full (object: target, key: "gtk-expression-binds", data: binds, destroy: free_binds);
2170
2171 gtk_expression_unref (self);
2172
2173 gtk_expression_bind_notify (data: bind);
2174
2175 return bind->watch;
2176}
2177
2178/* }}} */
2179

source code of gtk/gtk/gtkexpression.c