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