1 /* ide-object.c
2  *
3  * Copyright 2014-2019 Christian Hergert <chergert@redhat.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #define G_LOG_DOMAIN "ide-object"
22 
23 #include "config.h"
24 
25 #include "ide-context.h"
26 #include "ide-object.h"
27 #include "ide-macros.h"
28 
29 /**
30  * SECTION:ide-object
31  * @title: IdeObject
32  * @short_description: Base object with support for object trees
33  *
34  * #IdeObject is a specialized #GObject for use in Builder. It provides a
35  * hierarchy of objects using a specialized tree similar to a DOM. You can
36  * insert/append/prepend objects to a parent node, and track their lifetime
37  * as part of the tree.
38  *
39  * When an object is removed from the tree, it can automatically be destroyed
40  * via the #IdeObject::destroy signal. This is useful as it may cause the
41  * children of that object to be removed, recursively destroying the objects
42  * descendants. This behavior is ideal when you want a large amount of objects
43  * to be reclaimed once an ancestor is no longer necessary.
44  *
45  * #IdeObject's may also have a #GCancellable associated with them. The
46  * cancellable is created on demand when ide_object_ref_cancellable() is
47  * called. When the object is destroyed, the #GCancellable::cancel signal
48  * is emitted. This allows automatic cleanup of asynchronous operations
49  * when used properly.
50  *
51  * Since: 3.32
52  */
53 
54 typedef struct
55 {
56   GRecMutex     mutex;
57   GCancellable *cancellable;
58   IdeObject    *parent;
59   GQueue        children;
60   GList         link;
61   guint         in_destruction : 1;
62   guint         destroyed : 1;
63 } IdeObjectPrivate;
64 
65 typedef struct
66 {
67   GType      type;
68   IdeObject *child;
69 } GetChildTyped;
70 
71 typedef struct
72 {
73   GType      type;
74   GPtrArray *array;
75 } GetChildrenTyped;
76 
77 enum {
78   PROP_0,
79   PROP_CANCELLABLE,
80   PROP_PARENT,
81   N_PROPS
82 };
83 
84 enum {
85   DESTROY,
86   N_SIGNALS
87 };
88 
G_DEFINE_TYPE_WITH_PRIVATE(IdeObject,ide_object,G_TYPE_OBJECT)89 G_DEFINE_TYPE_WITH_PRIVATE (IdeObject, ide_object, G_TYPE_OBJECT)
90 
91 static GParamSpec *properties [N_PROPS];
92 static guint signals [N_SIGNALS];
93 
94 static inline void
95 ide_object_private_lock (IdeObjectPrivate *priv)
96 {
97   g_rec_mutex_lock (&priv->mutex);
98 }
99 
100 static inline void
ide_object_private_unlock(IdeObjectPrivate * priv)101 ide_object_private_unlock (IdeObjectPrivate *priv)
102 {
103   g_rec_mutex_unlock (&priv->mutex);
104 }
105 
106 static gboolean
check_disposition(IdeObject * child,IdeObject * parent,IdeObjectPrivate * sibling_priv)107 check_disposition (IdeObject        *child,
108                    IdeObject        *parent,
109                    IdeObjectPrivate *sibling_priv)
110 {
111   IdeObjectPrivate *priv = ide_object_get_instance_private (child);
112 
113   if (priv->parent != NULL)
114     {
115       g_critical ("Attempt to add %s to %s, but it already has a parent",
116                   G_OBJECT_TYPE_NAME (child),
117                   G_OBJECT_TYPE_NAME (parent));
118       return FALSE;
119     }
120 
121   if (sibling_priv && sibling_priv->parent != parent)
122     {
123       g_critical ("Attempt to add child relative to sibling of another parent");
124       return FALSE;
125     }
126 
127   return TRUE;
128 }
129 
130 static gchar *
ide_object_real_repr(IdeObject * self)131 ide_object_real_repr (IdeObject *self)
132 {
133   return g_strdup (G_OBJECT_TYPE_NAME (self));
134 }
135 
136 static void
ide_object_real_add(IdeObject * self,IdeObject * sibling,IdeObject * child,IdeObjectLocation location)137 ide_object_real_add (IdeObject         *self,
138                      IdeObject         *sibling,
139                      IdeObject         *child,
140                      IdeObjectLocation  location)
141 {
142   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
143   IdeObjectPrivate *child_priv = ide_object_get_instance_private (child);
144   IdeObjectPrivate *sibling_priv = ide_object_get_instance_private (sibling);
145 
146   g_assert (IDE_IS_OBJECT (self));
147   g_assert (IDE_IS_OBJECT (child));
148   g_assert (!sibling || IDE_IS_OBJECT (sibling));
149 
150   if (location == IDE_OBJECT_BEFORE_SIBLING ||
151       location == IDE_OBJECT_AFTER_SIBLING)
152     g_return_if_fail (IDE_IS_OBJECT (sibling));
153 
154   ide_object_private_lock (priv);
155   ide_object_private_lock (child_priv);
156 
157   if (sibling)
158     ide_object_private_lock (sibling_priv);
159 
160   if (!check_disposition (child, self, NULL))
161     goto unlock;
162 
163   switch (location)
164     {
165     case IDE_OBJECT_START:
166       g_queue_push_head_link (&priv->children, &child_priv->link);
167       break;
168 
169     case IDE_OBJECT_END:
170       g_queue_push_tail_link (&priv->children, &child_priv->link);
171       break;
172 
173     case IDE_OBJECT_BEFORE_SIBLING:
174       _g_queue_insert_before_link (&priv->children, &sibling_priv->link, &child_priv->link);
175       break;
176 
177     case IDE_OBJECT_AFTER_SIBLING:
178       _g_queue_insert_after_link (&priv->children, &sibling_priv->link, &child_priv->link);
179       break;
180 
181     default:
182       g_critical ("Invalid location to add object child");
183       goto unlock;
184     }
185 
186   child_priv->parent = self;
187   g_object_ref (child);
188 
189   if (IDE_OBJECT_GET_CLASS (child)->parent_set)
190     IDE_OBJECT_GET_CLASS (child)->parent_set (child, self);
191 
192 unlock:
193   if (sibling)
194     ide_object_private_unlock (sibling_priv);
195   ide_object_private_unlock (child_priv);
196   ide_object_private_unlock (priv);
197 }
198 
199 static void
ide_object_real_remove(IdeObject * self,IdeObject * child)200 ide_object_real_remove (IdeObject *self,
201                         IdeObject *child)
202 {
203   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
204   IdeObjectPrivate *child_priv = ide_object_get_instance_private (child);
205 
206   g_assert (IDE_IS_OBJECT (self));
207   g_assert (IDE_IS_OBJECT (child));
208 
209   ide_object_private_lock (priv);
210   ide_object_private_lock (child_priv);
211 
212   g_assert (child_priv->parent == self);
213 
214   if (child_priv->parent != self)
215     {
216       g_critical ("Attempt to remove child object from incorrect parent");
217       ide_object_private_unlock (child_priv);
218       ide_object_private_unlock (priv);
219       return;
220     }
221 
222   g_queue_unlink (&priv->children, &child_priv->link);
223   child_priv->parent = NULL;
224 
225   if (IDE_OBJECT_GET_CLASS (child)->parent_set)
226     IDE_OBJECT_GET_CLASS (child)->parent_set (child, NULL);
227 
228   ide_object_private_unlock (child_priv);
229   ide_object_private_unlock (priv);
230 
231   g_object_unref (child);
232 }
233 
234 static void
ide_object_real_destroy(IdeObject * self)235 ide_object_real_destroy (IdeObject *self)
236 {
237   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
238   IdeObject *hold = NULL;
239 
240   g_assert (IDE_IS_OBJECT (self));
241 
242   /* We already hold the instance lock, for destroy */
243 
244   g_cancellable_cancel (priv->cancellable);
245 
246   if (priv->parent != NULL)
247     {
248       hold = g_object_ref (self);
249       ide_object_remove (priv->parent, self);
250     }
251 
252   g_assert (priv->parent == NULL);
253   g_assert (priv->link.prev == NULL);
254   g_assert (priv->link.next == NULL);
255 
256   while (priv->children.head != NULL)
257     {
258       IdeObject *child = priv->children.head->data;
259 
260       ide_object_destroy (child);
261     }
262 
263   g_assert (priv->children.tail == NULL);
264   g_assert (priv->children.head == NULL);
265   g_assert (priv->children.length == 0);
266 
267   g_assert (priv->parent == NULL);
268   g_assert (priv->link.prev == NULL);
269   g_assert (priv->link.next == NULL);
270 
271   priv->destroyed = TRUE;
272 
273   if (hold != NULL)
274     g_object_unref (hold);
275 }
276 
277 static gboolean
ide_object_destroy_in_main_cb(IdeObject * object)278 ide_object_destroy_in_main_cb (IdeObject *object)
279 {
280   g_assert (IDE_IS_MAIN_THREAD ());
281   g_assert (IDE_IS_OBJECT (object));
282 
283   ide_object_destroy (object);
284 
285   return G_SOURCE_REMOVE;
286 }
287 
288 void
ide_object_destroy(IdeObject * self)289 ide_object_destroy (IdeObject *self)
290 {
291   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
292 
293   g_return_if_fail (IDE_IS_OBJECT (self));
294 
295   g_object_ref (self);
296   ide_object_private_lock (priv);
297 
298   /* If we are not on the main thread, we want to detach from the
299    * object tree and then dispatch the rest of the destroy to the
300    * main thread (so no threaded cleanup can occur).
301    */
302 
303   if (IDE_IS_MAIN_THREAD ())
304     {
305       g_cancellable_cancel (priv->cancellable);
306       if (!priv->in_destruction && !priv->destroyed)
307         g_object_run_dispose (G_OBJECT (self));
308     }
309   else
310     {
311       g_autoptr(IdeObject) parent = NULL;
312 
313       if ((parent = ide_object_ref_parent (self)))
314         ide_object_remove (parent, self);
315 
316       g_idle_add_full (G_PRIORITY_LOW + 1000,
317                        (GSourceFunc)ide_object_destroy_in_main_cb,
318                        g_object_ref (self),
319                        g_object_unref);
320     }
321 
322   ide_object_private_unlock (priv);
323   g_object_unref (self);
324 }
325 
326 static gboolean
ide_object_dispose_from_main_cb(gpointer user_data)327 ide_object_dispose_from_main_cb (gpointer user_data)
328 {
329   IdeObject *self = user_data;
330   g_object_run_dispose (G_OBJECT (self));
331   return G_SOURCE_REMOVE;
332 }
333 
334 static void
ide_object_dispose(GObject * object)335 ide_object_dispose (GObject *object)
336 {
337   IdeObject *self = (IdeObject *)object;
338   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
339 
340   if (!IDE_IS_MAIN_THREAD ())
341     {
342       /* We are not on the main thread and might lose our last reference count.
343        * Pass this object to the main thread for disposal. This usually only
344        * happens when an object was temporarily created/destroyed on a thread.
345        */
346       g_idle_add_full (G_PRIORITY_LOW + 1000,
347                        ide_object_dispose_from_main_cb,
348                        g_object_ref (self),
349                        g_object_unref);
350       return;
351     }
352 
353   g_assert (IDE_IS_OBJECT (object));
354 
355   ide_object_private_lock (priv);
356 
357   if (!priv->in_destruction)
358     {
359       priv->in_destruction = TRUE;
360       g_signal_emit (self, signals [DESTROY], 0);
361       priv->in_destruction = FALSE;
362     }
363 
364   ide_object_private_unlock (priv);
365 
366   G_OBJECT_CLASS (ide_object_parent_class)->dispose (object);
367 }
368 
369 static void
ide_object_finalize(GObject * object)370 ide_object_finalize (GObject *object)
371 {
372   IdeObject *self = (IdeObject *)object;
373   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
374 
375   if (!IDE_IS_MAIN_THREAD ())
376     {
377       g_critical ("Attempt to finalize %s on a thread which is not allowed. Leaking instead.",
378                   G_OBJECT_TYPE_NAME (object));
379       return;
380     }
381 
382   g_assert (priv->parent == NULL);
383   g_assert (priv->children.length == 0);
384   g_assert (priv->children.head == NULL);
385   g_assert (priv->children.tail == NULL);
386   g_assert (priv->link.prev == NULL);
387   g_assert (priv->link.next == NULL);
388 
389   g_clear_object (&priv->cancellable);
390   g_rec_mutex_clear (&priv->mutex);
391 
392   G_OBJECT_CLASS (ide_object_parent_class)->finalize (object);
393 }
394 
395 static void
ide_object_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)396 ide_object_get_property (GObject    *object,
397                          guint       prop_id,
398                          GValue     *value,
399                          GParamSpec *pspec)
400 {
401   IdeObject *self = IDE_OBJECT (object);
402 
403   switch (prop_id)
404     {
405     case PROP_PARENT:
406       g_value_take_object (value, ide_object_ref_parent (self));
407       break;
408 
409     case PROP_CANCELLABLE:
410       g_value_take_object (value, ide_object_ref_cancellable (self));
411       break;
412 
413     default:
414       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
415     }
416 }
417 
418 static void
ide_object_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)419 ide_object_set_property (GObject      *object,
420                          guint         prop_id,
421                          const GValue *value,
422                          GParamSpec   *pspec)
423 {
424   IdeObject *self = IDE_OBJECT (object);
425   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
426 
427   switch (prop_id)
428     {
429     case PROP_CANCELLABLE:
430       priv->cancellable = g_value_dup_object (value);
431       break;
432 
433     case PROP_PARENT:
434       {
435         IdeObject *parent = g_value_get_object (value);
436         if (parent != NULL)
437           ide_object_append (parent, IDE_OBJECT (self));
438       }
439       break;
440 
441     default:
442       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
443     }
444 }
445 
446 static void
ide_object_class_init(IdeObjectClass * klass)447 ide_object_class_init (IdeObjectClass *klass)
448 {
449   GObjectClass *object_class = G_OBJECT_CLASS (klass);
450 
451   object_class->dispose = ide_object_dispose;
452   object_class->finalize = ide_object_finalize;
453   object_class->get_property = ide_object_get_property;
454   object_class->set_property = ide_object_set_property;
455 
456   klass->add = ide_object_real_add;
457   klass->remove = ide_object_real_remove;
458   klass->destroy = ide_object_real_destroy;
459   klass->repr = ide_object_real_repr;
460 
461   /**
462    * IdeObject:parent:
463    *
464    * The parent #IdeObject, if any.
465    *
466    * Since: 3.32
467    */
468   properties [PROP_PARENT] =
469     g_param_spec_object ("parent",
470                          "Parent",
471                          "The parent IdeObject",
472                          IDE_TYPE_OBJECT,
473                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
474 
475   /**
476    * IdeObject:cancellable:
477    *
478    * The "cancellable" property is a #GCancellable that can be used by operations
479    * that will be cancelled when the #IdeObject::destroy signal is emitted on @self.
480    *
481    * This is convenient when you want operations to automatically be cancelled when
482    * part of teh object tree is segmented.
483    *
484    * Since: 3.32
485    */
486   properties [PROP_CANCELLABLE] =
487     g_param_spec_object ("cancellable",
488                          "Cancellable",
489                          "A GCancellable for the object to use in operations",
490                          G_TYPE_CANCELLABLE,
491                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
492 
493   g_object_class_install_properties (object_class, N_PROPS, properties);
494 
495   /**
496    * IdeObject::destroy:
497    *
498    * The "destroy" signal is emitted when the object should destroy itself
499    * and cleanup any state that is no longer necessary. This happens when
500    * the object has been removed from the because it was requested to be
501    * destroyed, or because a parent object is being destroyed.
502    *
503    * If you do not want to receive the "destroy" signal, then you must
504    * manually remove the object from the tree using ide_object_remove()
505    * while holding a reference to the object.
506    *
507    * Since: 3.32
508    */
509   signals [DESTROY] =
510     g_signal_new ("destroy",
511                   G_TYPE_FROM_CLASS (klass),
512                   (G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS),
513                   G_STRUCT_OFFSET (IdeObjectClass, destroy),
514                   NULL, NULL,
515                   g_cclosure_marshal_VOID__VOID,
516                   G_TYPE_NONE, 0);
517   g_signal_set_va_marshaller (signals [DESTROY],
518                               G_TYPE_FROM_CLASS (klass),
519                               g_cclosure_marshal_VOID__VOIDv);
520 }
521 
522 static void
ide_object_init(IdeObject * self)523 ide_object_init (IdeObject *self)
524 {
525   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
526 
527   priv->link.data = self;
528 
529   g_rec_mutex_init (&priv->mutex);
530 }
531 
532 /**
533  * ide_object_new:
534  * @type: a #GType of an #IdeObject derived object
535  * @parent: (nullable): an optional #IdeObject parent
536  *
537  * This is a convenience function for creating an #IdeObject and appending it
538  * to a parent.
539  *
540  * This function may only be called from the main-thread, as calling from any
541  * other thread would potentially risk being disposed before returning.
542  *
543  * Returns: (transfer full) (type IdeObject): a new #IdeObject
544  *
545  * Since: 3.32
546  */
547 gpointer
ide_object_new(GType type,IdeObject * parent)548 ide_object_new (GType      type,
549                 IdeObject *parent)
550 {
551   IdeObject *ret;
552 
553   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
554   g_return_val_if_fail (g_type_is_a (type, IDE_TYPE_OBJECT), NULL);
555   g_return_val_if_fail (!parent || IDE_IS_OBJECT (parent), NULL);
556 
557   ret = g_object_new (type, NULL);
558   if (parent != NULL)
559     ide_object_append (parent, ret);
560 
561   return g_steal_pointer (&ret);
562 }
563 
564 /**
565  * ide_object_get_n_children:
566  * @self: a #IdeObject
567  *
568  * Gets the number of children for an object.
569  *
570  * Returns: the number of children
571  *
572  * Since: 3.32
573  */
574 guint
ide_object_get_n_children(IdeObject * self)575 ide_object_get_n_children (IdeObject *self)
576 {
577   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
578   guint ret;
579 
580   g_return_val_if_fail (IDE_IS_OBJECT (self), 0);
581 
582   ide_object_private_lock (priv);
583   ret = priv->children.length;
584   ide_object_private_unlock (priv);
585 
586   return ret;
587 }
588 
589 /**
590  * ide_object_get_nth_child:
591  * @self: a #IdeObject
592  * @nth: position of child to fetch
593  *
594  * Gets the @nth child of @self.
595  *
596  * A full reference to the child is returned.
597  *
598  * Returns: (transfer full) (nullable): an #IdeObject or %NULL
599  *
600  * Since: 3.32
601  */
602 IdeObject *
ide_object_get_nth_child(IdeObject * self,guint nth)603 ide_object_get_nth_child (IdeObject *self,
604                           guint      nth)
605 {
606   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
607   IdeObject *ret;
608 
609   g_return_val_if_fail (IDE_IS_OBJECT (self), 0);
610 
611   ide_object_private_lock (priv);
612   ret = g_list_nth_data (priv->children.head, nth);
613   if (ret != NULL)
614     g_object_ref (ret);
615   ide_object_private_unlock (priv);
616 
617   g_return_val_if_fail (!ret || IDE_IS_OBJECT (ret), NULL);
618 
619   return g_steal_pointer (&ret);
620 }
621 
622 /**
623  * ide_object_get_position:
624  * @self: a #IdeObject
625  *
626  * Gets the position of @self within the parent node.
627  *
628  * Returns: the position, starting from 0
629  *
630  * Since: 3.32
631  */
632 guint
ide_object_get_position(IdeObject * self)633 ide_object_get_position (IdeObject *self)
634 {
635   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
636   guint ret = 0;
637 
638   g_return_val_if_fail (IDE_IS_OBJECT (self), 0);
639 
640   ide_object_private_lock (priv);
641 
642   if (priv->parent != NULL)
643     {
644       IdeObjectPrivate *parent_priv = ide_object_get_instance_private (priv->parent);
645       ret = g_list_position (parent_priv->children.head, &priv->link);
646     }
647 
648   ide_object_private_unlock (priv);
649 
650   return ret;
651 }
652 
653 /**
654  * ide_object_lock:
655  * @self: a #IdeObject
656  *
657  * Acquires the lock for @self. This can be useful when you need to do
658  * multi-threaded work with @self and want to ensure exclusivity.
659  *
660  * Call ide_object_unlock() to release the lock.
661  *
662  * The synchronization used is a #GRecMutex.
663  *
664  * Since: 3.32
665  */
666 void
ide_object_lock(IdeObject * self)667 ide_object_lock (IdeObject *self)
668 {
669   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
670 
671   g_return_if_fail (IDE_IS_OBJECT (self));
672 
673   ide_object_private_lock (priv);
674 }
675 
676 /**
677  * ide_object_unlock:
678  * @self: a #IdeObject
679  *
680  * Releases a previously acuiqred lock from ide_object_lock().
681  *
682  * The synchronization used is a #GRecMutex.
683  *
684  * Since: 3.32
685  */
686 void
ide_object_unlock(IdeObject * self)687 ide_object_unlock (IdeObject *self)
688 {
689   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
690 
691   g_return_if_fail (IDE_IS_OBJECT (self));
692 
693   ide_object_private_unlock (priv);
694 }
695 
696 /**
697  * ide_object_ref_cancellable:
698  * @self: a #IdeObject
699  *
700  * Gets a #GCancellable for the object.
701  *
702  * Returns: (transfer none) (not nullable): a #GCancellable
703  *
704  * Since: 3.32
705  */
706 GCancellable *
ide_object_ref_cancellable(IdeObject * self)707 ide_object_ref_cancellable (IdeObject *self)
708 {
709   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
710   GCancellable *ret;
711 
712   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
713 
714   ide_object_private_lock (priv);
715   if (priv->cancellable == NULL)
716     priv->cancellable = g_cancellable_new ();
717   ret = g_object_ref (priv->cancellable);
718   ide_object_private_unlock (priv);
719 
720   return g_steal_pointer (&ret);
721 }
722 
723 /**
724  * ide_object_get_parent:
725  * @self: a #IdeObject
726  *
727  * Gets the parent #IdeObject, if any.
728  *
729  * This function may only be called from the main thread.
730  *
731  * Returns: (transfer none) (nullable): an #IdeObject or %NULL
732  *
733  * Since: 3.32
734  */
735 IdeObject *
ide_object_get_parent(IdeObject * self)736 ide_object_get_parent (IdeObject *self)
737 {
738   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
739   IdeObject *ret;
740 
741   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
742   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
743 
744   ide_object_private_lock (priv);
745   ret = priv->parent;
746   ide_object_private_unlock (priv);
747 
748   return g_steal_pointer (&ret);
749 }
750 
751 /**
752  * ide_object_ref_parent:
753  * @self: a #IdeObject
754  *
755  * Gets the parent #IdeObject, if any.
756  *
757  * Returns: (transfer full) (nullable): an #IdeObject or %NULL
758  *
759  * Since: 3.32
760  */
761 IdeObject *
ide_object_ref_parent(IdeObject * self)762 ide_object_ref_parent (IdeObject *self)
763 {
764   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
765   IdeObject *ret;
766 
767   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
768 
769   ide_object_private_lock (priv);
770   ret = priv->parent ? g_object_ref (priv->parent) : NULL;
771   ide_object_private_unlock (priv);
772 
773   return g_steal_pointer (&ret);
774 }
775 
776 /**
777  * ide_object_is_root:
778  * @self: a #IdeObject
779  *
780  * Checks if @self is root, meaning it has no parent.
781  *
782  * Returns: %TRUE if @self has no parent
783  *
784  * Since: 3.32
785  */
786 gboolean
ide_object_is_root(IdeObject * self)787 ide_object_is_root (IdeObject *self)
788 {
789   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
790   gboolean ret;
791 
792   g_return_val_if_fail (IDE_IS_OBJECT (self), FALSE);
793 
794   ide_object_private_lock (priv);
795   ret = priv->parent == NULL;
796   ide_object_private_unlock (priv);
797 
798   return ret;
799 }
800 
801 /**
802  * ide_object_add:
803  * @self: an #IdeObject
804  * @sibling: (nullable): an #IdeObject or %NULL
805  * @child: an #IdeObject
806  * @location: location for child
807  *
808  * Adds @child to @self, with location dependent on @location.
809  *
810  * Generally, it is simpler to use the helper functions such as
811  * ide_object_append(), ide_object_prepend(), ide_object_insert_before(),
812  * or ide_object_insert_after().
813  *
814  * This function is primarily meant for consumers that don't know the
815  * relative position they need until runtime.
816  *
817  * Since: 3.32
818  */
819 void
ide_object_add(IdeObject * self,IdeObject * sibling,IdeObject * child,IdeObjectLocation location)820 ide_object_add (IdeObject         *self,
821                 IdeObject         *sibling,
822                 IdeObject         *child,
823                 IdeObjectLocation  location)
824 {
825   g_return_if_fail (IDE_IS_OBJECT (self));
826   g_return_if_fail (IDE_IS_OBJECT (child));
827 
828   if (location == IDE_OBJECT_BEFORE_SIBLING ||
829       location == IDE_OBJECT_AFTER_SIBLING)
830     g_return_if_fail (IDE_IS_OBJECT (sibling));
831   else
832     g_return_if_fail (sibling == NULL);
833 
834   IDE_OBJECT_GET_CLASS (self)->add (self, sibling, child, location);
835 }
836 
837 /**
838  * ide_object_remove:
839  * @self: an #IdeObject
840  * @child: an #IdeObject
841  *
842  * Removes @child from @self.
843  *
844  * If @child is a borrowed reference, it may be finalized before this
845  * function returns.
846  *
847  * Since: 3.32
848  */
849 void
ide_object_remove(IdeObject * self,IdeObject * child)850 ide_object_remove (IdeObject *self,
851                    IdeObject *child)
852 {
853   g_return_if_fail (IDE_IS_OBJECT (self));
854   g_return_if_fail (IDE_IS_OBJECT (child));
855 
856   IDE_OBJECT_GET_CLASS (self)->remove (self, child);
857 }
858 
859 /**
860  * ide_object_append:
861  * @self: an #IdeObject
862  * @child: an #IdeObject
863  *
864  * Inserts @child as the last child of @self.
865  *
866  * Since: 3.32
867  */
868 void
ide_object_append(IdeObject * self,IdeObject * child)869 ide_object_append (IdeObject *self,
870                    IdeObject *child)
871 {
872   ide_object_add (self, NULL, child, IDE_OBJECT_END);
873 }
874 
875 /**
876  * ide_object_prepend:
877  * @self: an #IdeObject
878  * @child: an #IdeObject
879  *
880  * Inserts @child as the first child of @self.
881  *
882  * Since: 3.32
883  */
884 void
ide_object_prepend(IdeObject * self,IdeObject * child)885 ide_object_prepend (IdeObject *self,
886                     IdeObject *child)
887 {
888   ide_object_add (self, NULL, child, IDE_OBJECT_START);
889 }
890 
891 /**
892  * ide_object_insert_before:
893  * @self: an #IdeObject
894  * @sibling: an #IdeObject
895  * @child: an #IdeObject
896  *
897  * Inserts @child into @self's children, directly before @sibling.
898  *
899  * @sibling MUST BE a child of @self.
900  *
901  * Since: 3.32
902  */
903 void
ide_object_insert_before(IdeObject * self,IdeObject * sibling,IdeObject * child)904 ide_object_insert_before (IdeObject *self,
905                           IdeObject *sibling,
906                           IdeObject *child)
907 {
908   ide_object_add (self, sibling, child, IDE_OBJECT_BEFORE_SIBLING);
909 }
910 
911 /**
912  * ide_object_insert_after:
913  * @self: an #IdeObject
914  * @sibling: an #IdeObject
915  * @child: an #IdeObject
916  *
917  * Inserts @child into @self's children, directly after @sibling.
918  *
919  * @sibling MUST BE a child of @self.
920  *
921  * Since: 3.32
922  */
923 void
ide_object_insert_after(IdeObject * self,IdeObject * sibling,IdeObject * child)924 ide_object_insert_after (IdeObject *self,
925                          IdeObject *sibling,
926                          IdeObject *child)
927 {
928   ide_object_add (self, sibling, child, IDE_OBJECT_AFTER_SIBLING);
929 }
930 
931 /**
932  * ide_object_insert_sorted:
933  * @self: a #IdeObject
934  * @child: an #IdeObject
935  * @func: (scope call): a #GCompareDataFunc that can be used to locate the
936  *    proper sibling
937  * @user_data: user data for @func
938  *
939  * Locates the proper sibling for @child by using @func amongst @self's
940  * children #IdeObject. Those objects must already be sorted.
941  *
942  * Since: 3.32
943  */
944 void
ide_object_insert_sorted(IdeObject * self,IdeObject * child,GCompareDataFunc func,gpointer user_data)945 ide_object_insert_sorted (IdeObject        *self,
946                           IdeObject        *child,
947                           GCompareDataFunc  func,
948                           gpointer          user_data)
949 {
950   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
951 
952   g_return_if_fail (IDE_IS_OBJECT (self));
953   g_return_if_fail (IDE_IS_OBJECT (child));
954   g_return_if_fail (func != NULL);
955 
956   ide_object_lock (self);
957 
958   if (priv->children.length == 0)
959     {
960       ide_object_prepend (self, child);
961       goto unlock;
962     }
963 
964   g_assert (priv->children.head != NULL);
965   g_assert (priv->children.tail != NULL);
966 
967   for (GList *iter = priv->children.head; iter; iter = iter->next)
968     {
969       IdeObject *other = iter->data;
970 
971       g_assert (IDE_IS_OBJECT (other));
972 
973       if (func (child, other, user_data) <= 0)
974         {
975           ide_object_insert_before (self, other, child);
976           goto unlock;
977         }
978     }
979 
980   ide_object_append (self, child);
981 
982 unlock:
983   ide_object_unlock (self);
984 }
985 
986 /**
987  * ide_object_foreach:
988  * @self: a #IdeObject
989  * @callback: (scope call): a #GFunc to call for each child
990  * @user_data: closure data for @callback
991  *
992  * Calls @callback for each child of @self.
993  *
994  * @callback is allowed to remove children from @self, but only as long as they are
995  * the child passed to callback (or child itself). See g_queue_foreach() for more
996  * details about what is allowed.
997  *
998  * Since: 3.32
999  */
1000 void
ide_object_foreach(IdeObject * self,GFunc callback,gpointer user_data)1001 ide_object_foreach (IdeObject *self,
1002                     GFunc      callback,
1003                     gpointer   user_data)
1004 {
1005   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
1006 
1007   g_return_if_fail (IDE_IS_OBJECT (self));
1008   g_return_if_fail (callback != NULL);
1009 
1010   ide_object_private_lock (priv);
1011   g_queue_foreach (&priv->children, callback, user_data);
1012   ide_object_private_unlock (priv);
1013 }
1014 
1015 static void
get_child_typed_cb(gpointer data,gpointer user_data)1016 get_child_typed_cb (gpointer data,
1017                     gpointer user_data)
1018 {
1019   IdeObject *child = data;
1020   GetChildTyped *q = user_data;
1021 
1022   if (q->child != NULL)
1023     return;
1024 
1025   if (G_TYPE_CHECK_INSTANCE_TYPE (child, q->type))
1026     q->child = g_object_ref (child);
1027 }
1028 
1029 /**
1030  * ide_object_get_child_typed:
1031  * @self: a #IdeObject
1032  * @type: the #GType of the child to match
1033  *
1034  * Finds the first child of @self that is of @type.
1035  *
1036  * Returns: (transfer full) (type IdeObject) (nullable): an #IdeObject or %NULL
1037  *
1038  * Since: 3.32
1039  */
1040 gpointer
ide_object_get_child_typed(IdeObject * self,GType type)1041 ide_object_get_child_typed (IdeObject *self,
1042                             GType      type)
1043 {
1044   GetChildTyped q = { type, NULL };
1045 
1046   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
1047   g_return_val_if_fail (g_type_is_a (type, IDE_TYPE_OBJECT), NULL);
1048 
1049   ide_object_foreach (self, get_child_typed_cb, &q);
1050 
1051   return g_steal_pointer (&q.child);
1052 }
1053 
1054 static void
get_children_typed_cb(gpointer data,gpointer user_data)1055 get_children_typed_cb (gpointer data,
1056                        gpointer user_data)
1057 {
1058   IdeObject *child = data;
1059   GetChildrenTyped *q = user_data;
1060 
1061   if (G_TYPE_CHECK_INSTANCE_TYPE (child, q->type))
1062     g_ptr_array_add (q->array, g_object_ref (child));
1063 }
1064 
1065 /**
1066  * ide_object_get_children_typed:
1067  * @self: a #IdeObject
1068  * @type: a #GType
1069  *
1070  * Gets all children matching @type.
1071  *
1072  * Returns: (transfer full) (element-type IdeObject): a #GPtrArray of
1073  *   #IdeObject matching @type.
1074  *
1075  * Since: 3.32
1076  */
1077 GPtrArray *
ide_object_get_children_typed(IdeObject * self,GType type)1078 ide_object_get_children_typed (IdeObject *self,
1079                                GType      type)
1080 {
1081   g_autoptr(GPtrArray) ar = NULL;
1082   GetChildrenTyped q;
1083 
1084   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
1085   g_return_val_if_fail (g_type_is_a (type, IDE_TYPE_OBJECT), NULL);
1086 
1087   ar = g_ptr_array_new ();
1088 
1089   q.type = type;
1090   q.array = ar;
1091 
1092   ide_object_foreach (self, get_children_typed_cb, &q);
1093 
1094   return g_steal_pointer (&ar);
1095 }
1096 
1097 /**
1098  * ide_object_ref_root:
1099  * @self: a #IdeObject
1100  *
1101  * Finds and returns the toplevel object in the tree.
1102  *
1103  * Returns: (transfer full): an #IdeObject
1104  *
1105  * Since: 3.32
1106  */
1107 IdeObject *
ide_object_ref_root(IdeObject * self)1108 ide_object_ref_root (IdeObject *self)
1109 {
1110   IdeObject *cur;
1111 
1112   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
1113 
1114   cur = g_object_ref (self);
1115 
1116   while (!ide_object_is_root (cur))
1117     {
1118       IdeObject *tmp = cur;
1119       cur = ide_object_ref_parent (tmp);
1120       g_object_unref (tmp);
1121     }
1122 
1123   return g_steal_pointer (&cur);
1124 }
1125 
1126 static void
ide_object_async_init_cb(GObject * object,GAsyncResult * result,gpointer user_data)1127 ide_object_async_init_cb (GObject      *object,
1128                           GAsyncResult *result,
1129                           gpointer      user_data)
1130 {
1131   GAsyncInitable *initable = (GAsyncInitable *)object;
1132   g_autoptr(IdeObject) self = user_data;
1133   g_autoptr(GError) error = NULL;
1134 
1135   g_assert (G_IS_ASYNC_INITABLE (initable));
1136   g_assert (G_IS_ASYNC_RESULT (result));
1137   g_assert (IDE_IS_OBJECT (self));
1138 
1139   if (!g_async_initable_init_finish (initable, result, &error))
1140     {
1141       g_warning ("Failed to initialize %s: %s",
1142                  G_OBJECT_TYPE_NAME (initable),
1143                  error->message);
1144       ide_object_destroy (IDE_OBJECT (initable));
1145     }
1146 }
1147 
1148 /**
1149  * ide_object_ensure_child_typed:
1150  * @self: a #IdeObject
1151  * @type: the #GType of the child
1152  *
1153  * Like ide_object_get_child_typed() except that it creates an object of
1154  * @type if it is missing.
1155  *
1156  * Returns: (transfer full) (nullable) (type IdeObject): an #IdeObject or %NULL
1157  *
1158  * Since: 3.32
1159  */
1160 gpointer
ide_object_ensure_child_typed(IdeObject * self,GType type)1161 ide_object_ensure_child_typed (IdeObject *self,
1162                                GType      type)
1163 {
1164   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
1165   g_autoptr(IdeObject) ret = NULL;
1166 
1167   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
1168   g_return_val_if_fail (g_type_is_a (type, IDE_TYPE_OBJECT), NULL);
1169   g_return_val_if_fail (!ide_object_in_destruction (self), NULL);
1170 
1171   ide_object_private_lock (priv);
1172   if (!(ret = ide_object_get_child_typed (self, type)))
1173     {
1174       g_autoptr(GError) error = NULL;
1175 
1176       ret = ide_object_new (type, self);
1177 
1178       if (G_IS_INITABLE (ret))
1179         {
1180           if (!g_initable_init (G_INITABLE (ret), NULL, &error))
1181             g_warning ("Failed to initialize %s: %s",
1182                        G_OBJECT_TYPE_NAME (ret), error->message);
1183         }
1184       else if (G_IS_ASYNC_INITABLE (ret))
1185         {
1186           g_async_initable_init_async (G_ASYNC_INITABLE (ret),
1187                                        G_PRIORITY_DEFAULT,
1188                                        priv->cancellable,
1189                                        ide_object_async_init_cb,
1190                                        g_object_ref (self));
1191         }
1192     }
1193   ide_object_private_unlock (priv);
1194 
1195   return g_steal_pointer (&ret);
1196 }
1197 
1198 /**
1199  * ide_object_destroyed:
1200  * @self: a #IdeObject
1201  *
1202  * This function sets *object_pointer to NULL if object_pointer != NULL. It's
1203  * intended to be used as a callback connected to the "destroy" signal of a
1204  * object. You connect ide_object_destroyed() as a signal handler, and pass the
1205  * address of your object variable as user data. Then when the object is
1206  * destroyed, the variable will be set to NULL. Useful for example to avoid
1207  * multiple copies of the same dialog.
1208  *
1209  * Since: 3.32
1210  */
1211 void
ide_object_destroyed(IdeObject ** object_pointer)1212 ide_object_destroyed (IdeObject **object_pointer)
1213 {
1214   if (object_pointer != NULL)
1215     *object_pointer = NULL;
1216 }
1217 
1218 /* compat for now to ease porting */
1219 void
ide_object_set_context(IdeObject * object,IdeContext * context)1220 ide_object_set_context (IdeObject  *object,
1221                         IdeContext *context)
1222 {
1223   ide_object_append (IDE_OBJECT (context), object);
1224 }
1225 
dummy(gpointer p)1226 static gboolean dummy (gpointer p) { return G_SOURCE_REMOVE; }
1227 
1228 /**
1229  * ide_object_get_context:
1230  * @object: a #IdeObject
1231  *
1232  * Gets the #IdeContext for the object.
1233  *
1234  * Returns: (transfer none) (nullable): an #IdeContext
1235  *
1236  * Since: 3.32
1237  */
1238 IdeContext *
ide_object_get_context(IdeObject * object)1239 ide_object_get_context (IdeObject *object)
1240 {
1241   g_autoptr(IdeObject) root = ide_object_ref_root (object);
1242   IdeContext *ret = NULL;
1243   GSource *source;
1244 
1245   if (IDE_IS_CONTEXT (root))
1246     ret = IDE_CONTEXT (root);
1247 
1248   /* We can just return a borrowed instance if in main thread,
1249    * otherwise we need to queue the object to the main loop.
1250    */
1251   if (IDE_IS_MAIN_THREAD ())
1252     return ret;
1253 
1254   source = g_idle_source_new ();
1255   g_source_set_name (source, "context-release");
1256   g_source_set_callback (source, dummy, g_steal_pointer (&root), g_object_unref);
1257   g_source_attach (source, g_main_context_get_thread_default ());
1258   g_source_unref (source);
1259 
1260   return ret;
1261 }
1262 
1263 /**
1264  * ide_object_ref_context:
1265  * @self: a #IdeContext
1266  *
1267  * Gets the root #IdeContext for the object, if any.
1268  *
1269  * Returns: (transfer full) (nullable): an #IdeContext or %NULL
1270  *
1271  * Since: 3.32
1272  */
1273 IdeContext *
ide_object_ref_context(IdeObject * self)1274 ide_object_ref_context (IdeObject *self)
1275 {
1276   g_autoptr(IdeObject) root = NULL;
1277 
1278   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
1279 
1280   if ((root = ide_object_ref_root (self)) && IDE_IS_CONTEXT (root))
1281     return IDE_CONTEXT (g_steal_pointer (&root));
1282 
1283   return NULL;
1284 }
1285 
1286 gboolean
ide_object_in_destruction(IdeObject * self)1287 ide_object_in_destruction (IdeObject *self)
1288 {
1289   IdeObjectPrivate *priv = ide_object_get_instance_private (self);
1290   gboolean ret;
1291 
1292   g_return_val_if_fail (IDE_IS_OBJECT (self), FALSE);
1293 
1294   ide_object_lock (self);
1295   ret = priv->in_destruction || priv->destroyed;
1296   ide_object_unlock (self);
1297 
1298   return ret;
1299 }
1300 
1301 /**
1302  * ide_object_repr:
1303  * @self: a #IdeObject
1304  *
1305  * This function is similar to Python's `repr()` which gives a string
1306  * representation for the object. It is useful when debugging Builder
1307  * or when writing plugins.
1308  *
1309  * Returns: (transfer full): a string containing the string representation
1310  *   of the #IdeObject
1311  *
1312  * Since: 3.32
1313  */
1314 gchar *
ide_object_repr(IdeObject * self)1315 ide_object_repr (IdeObject *self)
1316 {
1317   g_autofree gchar *str = NULL;
1318 
1319   g_return_val_if_fail (IDE_IS_OBJECT (self), NULL);
1320 
1321   str = IDE_OBJECT_GET_CLASS (self)->repr (self);
1322 
1323   return g_strdup_printf ("<%s at %p>", str, self);
1324 }
1325 
1326 gboolean
ide_object_set_error_if_destroyed(IdeObject * self,GError ** error)1327 ide_object_set_error_if_destroyed (IdeObject  *self,
1328                                    GError    **error)
1329 {
1330   g_return_val_if_fail (IDE_IS_OBJECT (self), FALSE);
1331 
1332   if (ide_object_in_destruction (self))
1333     {
1334       g_set_error (error,
1335                    G_IO_ERROR,
1336                    G_IO_ERROR_CANCELLED,
1337                    "The object was destroyed");
1338       return TRUE;
1339     }
1340 
1341   return FALSE;
1342 }
1343 
1344 void
ide_object_log(gpointer instance,GLogLevelFlags level,const gchar * domain,const gchar * format,...)1345 ide_object_log (gpointer        instance,
1346                 GLogLevelFlags  level,
1347                 const gchar    *domain,
1348                 const gchar    *format,
1349                 ...)
1350 {
1351   g_autoptr(IdeObject) root = NULL;
1352   va_list args;
1353 
1354   g_assert (!instance || IDE_IS_OBJECT (instance));
1355 
1356   if G_UNLIKELY (instance == NULL || !(root = ide_object_ref_root (instance)))
1357     {
1358       va_start (args, format);
1359       g_logv (domain, level, format, args);
1360       va_end (args);
1361     }
1362 
1363   if (IDE_IS_CONTEXT (root))
1364     {
1365       g_autofree gchar *message = NULL;
1366 
1367       va_start (args, format);
1368       message = g_strdup_vprintf (format, args);
1369       ide_context_log (IDE_CONTEXT (root), level, domain, message);
1370       va_end (args);
1371     }
1372 }
1373