1 /* ide-object-box.c
2  *
3  * Copyright 2018 Christian Hergert <unknown@domain.org>
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-box"
22 
23 #include "config.h"
24 
25 #include "ide-object-box.h"
26 #include "ide-macros.h"
27 
28 struct _IdeObjectBox
29 {
30   IdeObject  parent_instance;
31   GObject   *object;
32   guint      propagate_disposal : 1;
33 };
34 
35 G_DEFINE_FINAL_TYPE (IdeObjectBox, ide_object_box, IDE_TYPE_OBJECT)
36 
37 enum {
38   PROP_0,
39   PROP_OBJECT,
40   PROP_PROPAGATE_DISPOSAL,
41   N_PROPS
42 };
43 
44 static GParamSpec *properties [N_PROPS];
45 
46 static void
ide_object_box_set_object(IdeObjectBox * self,GObject * object)47 ide_object_box_set_object (IdeObjectBox *self,
48                            GObject      *object)
49 {
50   g_return_if_fail (IDE_IS_OBJECT_BOX (self));
51   g_return_if_fail (G_IS_OBJECT (object));
52   g_return_if_fail (g_object_get_data (object, "IDE_OBJECT_BOX") == NULL);
53 
54   self->object = g_object_ref (object);
55   g_object_set_data (self->object, "IDE_OBJECT_BOX", self);
56 }
57 
58 /**
59  * ide_object_box_new:
60  *
61  * Create a new #IdeObjectBox.
62  *
63  * Returns: (transfer full): a newly created #IdeObjectBox
64  *
65  * Since: 3.32
66  */
67 IdeObjectBox *
ide_object_box_new(GObject * object)68 ide_object_box_new (GObject *object)
69 {
70   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
71 
72   return g_object_new (IDE_TYPE_OBJECT_BOX,
73                        "object", object,
74                        NULL);
75 }
76 
77 static gchar *
ide_object_box_repr(IdeObject * object)78 ide_object_box_repr (IdeObject *object)
79 {
80   g_autoptr(GObject) obj = ide_object_box_ref_object (IDE_OBJECT_BOX (object));
81 
82   if (obj != NULL)
83     return g_strdup_printf ("%s object=\"%s\"",
84                             G_OBJECT_TYPE_NAME (object),
85                             G_OBJECT_TYPE_NAME (obj));
86   else
87     return IDE_OBJECT_CLASS (ide_object_box_parent_class)->repr (object);
88 }
89 
90 static void
ide_object_box_destroy(IdeObject * object)91 ide_object_box_destroy (IdeObject *object)
92 {
93   IdeObjectBox *self = (IdeObjectBox *)object;
94 
95   g_assert (IDE_IS_MAIN_THREAD ());
96   g_assert (IDE_IS_OBJECT (self));
97 
98   g_object_ref (self);
99 
100   /* Clear the backpointer before any disposal to the object, since that
101    * will possibly result in the object calling back into this peer object.
102    */
103   if (self->object)
104     {
105       g_object_set_data (G_OBJECT (self->object), "IDE_OBJECT_BOX", NULL);
106       if (self->propagate_disposal)
107         g_object_run_dispose (G_OBJECT (self->object));
108     }
109 
110   IDE_OBJECT_CLASS (ide_object_box_parent_class)->destroy (object);
111 
112   g_clear_object (&self->object);
113 
114   g_object_unref (self);
115 }
116 
117 static void
ide_object_box_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)118 ide_object_box_get_property (GObject    *object,
119                              guint       prop_id,
120                              GValue     *value,
121                              GParamSpec *pspec)
122 {
123   IdeObjectBox *self = IDE_OBJECT_BOX (object);
124 
125   switch (prop_id)
126     {
127     case PROP_OBJECT:
128       g_value_take_object (value, ide_object_box_ref_object (self));
129       break;
130 
131     case PROP_PROPAGATE_DISPOSAL:
132       g_value_set_boolean (value, self->propagate_disposal);
133       break;
134 
135     default:
136       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
137     }
138 }
139 
140 static void
ide_object_box_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)141 ide_object_box_set_property (GObject      *object,
142                              guint         prop_id,
143                              const GValue *value,
144                              GParamSpec   *pspec)
145 {
146   IdeObjectBox *self = IDE_OBJECT_BOX (object);
147 
148   switch (prop_id)
149     {
150     case PROP_OBJECT:
151       ide_object_box_set_object (self, g_value_get_object (value));
152       break;
153 
154     case PROP_PROPAGATE_DISPOSAL:
155       self->propagate_disposal = g_value_get_boolean (value);
156       break;
157 
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160     }
161 }
162 
163 static void
ide_object_box_class_init(IdeObjectBoxClass * klass)164 ide_object_box_class_init (IdeObjectBoxClass *klass)
165 {
166   GObjectClass *object_class = G_OBJECT_CLASS (klass);
167   IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
168 
169   object_class->get_property = ide_object_box_get_property;
170   object_class->set_property = ide_object_box_set_property;
171 
172   i_object_class->destroy = ide_object_box_destroy;
173   i_object_class->repr = ide_object_box_repr;
174 
175   /**
176    * IdeObjectBox:object:
177    *
178    * The "object" property contains the object that is boxed and
179    * placed onto the object graph using this box.
180    *
181    * Since: 3.32
182    */
183   properties [PROP_OBJECT] =
184     g_param_spec_object ("object",
185                          "Object",
186                          "The boxed object",
187                          G_TYPE_OBJECT,
188                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
189 
190   /**
191    * IdeObjectBox:propagate-disposal:
192    *
193    * The "propagate-disposal" property denotes if the #IdeObject:object
194    * property contents should have g_object_run_dispose() called when the
195    * #IdeObjectBox is destroyed.
196    *
197    * This is useful when you want to force disposal of an external object
198    * when @self is removed from the object tree.
199    *
200    * Since: 3.32
201    */
202   properties [PROP_PROPAGATE_DISPOSAL] =
203     g_param_spec_boolean ("propagate-disposal",
204                           "Propagate Disposal",
205                           "If the object should be disposed when the box is destroyed",
206                           TRUE,
207                           (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
208 
209   g_object_class_install_properties (object_class, N_PROPS, properties);
210 }
211 
212 static void
ide_object_box_init(IdeObjectBox * self)213 ide_object_box_init (IdeObjectBox *self)
214 {
215   self->propagate_disposal = TRUE;
216 }
217 
218 /**
219  * ide_object_box_ref_object:
220  * @self: an #IdeObjectBox
221  *
222  * Gets the boxed object.
223  *
224  * Returns: (transfer full) (nullable) (type GObject): a #GObject or %NULL
225  *
226  * Since: 3.32
227  */
228 gpointer
ide_object_box_ref_object(IdeObjectBox * self)229 ide_object_box_ref_object (IdeObjectBox *self)
230 {
231   GObject *ret;
232 
233   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
234   g_return_val_if_fail (IDE_IS_OBJECT_BOX (self), NULL);
235 
236   ide_object_lock (IDE_OBJECT (self));
237   ret = self->object ? g_object_ref (self->object) : NULL;
238   ide_object_unlock (IDE_OBJECT (self));
239 
240   return g_steal_pointer (&ret);
241 }
242 
243 /**
244  * ide_object_box_from_object:
245  * @object: a #GObject
246  *
247  * Gets the #IdeObjectBox that contains @object, if any.
248  *
249  * This function may only be called from the main thread.
250  *
251  * Returns: (transfer none): an #IdeObjectBox
252  *
253  * Since: 3.32
254  */
255 IdeObjectBox *
ide_object_box_from_object(GObject * object)256 ide_object_box_from_object (GObject *object)
257 {
258   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
259   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
260 
261   return g_object_get_data (G_OBJECT (object), "IDE_OBJECT_BOX");
262 }
263 
264 /**
265  * ide_object_box_contains:
266  * @self: a #IdeObjectBox
267  * @instance: (type GObject) (nullable): a #GObject or %NULL
268  *
269  * Checks if @self contains @instance.
270  *
271  * Returns: %TRUE if #IdeObjectBox:object matches @instance
272  *
273  * Since: 3.32
274  */
275 gboolean
ide_object_box_contains(IdeObjectBox * self,gpointer instance)276 ide_object_box_contains (IdeObjectBox *self,
277                          gpointer      instance)
278 {
279   gboolean ret;
280 
281   g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
282   g_return_val_if_fail (IDE_IS_OBJECT_BOX (self), FALSE);
283 
284   ide_object_lock (IDE_OBJECT (self));
285   ret = (instance == (gpointer)self->object);
286   ide_object_unlock (IDE_OBJECT (self));
287 
288   return ret;
289 }
290