1 /* Copyright 1998-2002 The gtkmm Development Team
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <glibmm/object.h>
18 #include <glibmm/private/object_p.h>
19 #include <glibmm/property.h>
20
21 #include <glib-object.h>
22 #include <gobject/gvaluecollector.h>
23
24 #include <cstdarg>
25 #include <cstring>
26
27 #include <string.h>
28
29 // Weak references:
30 // I'm not sure what the point of these are apart from being a hacky way out of circular references,
31 // but maybe we could make it easier to use them by making a Java Reference Object -style class like
32 // so:
33 // Glib::WeakRef<SomeDerivedObject> weakrefSomeObject(object1);
34 // ...
35 // if(weakrefSomeObject->isStillAlive())
36 // {
37 // weakrefSomeObject->some_method();
38 // }
39 // else
40 // {
41 // //Deal with it, maybe recreating the object.
42 // }
43 //
44 // Without this, the coder has to define his own signal handler which sets his own isStillAlive
45 // boolean.
46 // weakrefSomeObject<> could still have its own signal_destroyed signal so that coders can choose to
47 // deal
48 // with the destruction as soon as it happens instead of just checking later before they try to use
49 // it.
50
51 namespace Glib
52 {
53
ConstructParams(const Glib::Class & glibmm_class_)54 ConstructParams::ConstructParams(const Glib::Class& glibmm_class_)
55 : glibmm_class(glibmm_class_), n_parameters(0), parameters(nullptr)
56 {
57 }
58
59 /*
60 * The implementation is mostly copied from gobject.c, with some minor tweaks.
61 * Basically, it looks up each property name to get its GType, and then uses
62 * G_VALUE_COLLECT() to store the varargs argument in a GValue of the correct
63 * type.
64 *
65 * Note that the property name arguments are assumed to be static string
66 * literals. No attempt is made to copy the string content. This is no
67 * different from g_object_new().
68 */
ConstructParams(const Glib::Class & glibmm_class_,const char * first_property_name,...)69 ConstructParams::ConstructParams(
70 const Glib::Class& glibmm_class_, const char* first_property_name, ...)
71 : glibmm_class(glibmm_class_), n_parameters(0), parameters(nullptr)
72 {
73 va_list var_args;
74 va_start(var_args, first_property_name);
75
76 GObjectClass* const g_class =
77 static_cast<GObjectClass*>(g_type_class_ref(glibmm_class.get_type()));
78
79 unsigned int n_alloced_params = 0;
80 char* collect_error = nullptr; // output argument of G_VALUE_COLLECT()
81
82 for (const char* name = first_property_name; name != nullptr; name = va_arg(var_args, char*))
83 {
84 GParamSpec* const pspec = g_object_class_find_property(g_class, name);
85
86 if (!pspec)
87 {
88 g_warning("Glib::ConstructParams::ConstructParams(): "
89 "object class \"%s\" has no property named \"%s\"",
90 g_type_name(glibmm_class.get_type()), name);
91 break;
92 }
93
94 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
95 if (n_parameters >= n_alloced_params)
96 parameters = g_renew(GParameter, parameters, n_alloced_params += 8);
97
98 GParameter& param = parameters[n_parameters];
99 G_GNUC_END_IGNORE_DEPRECATIONS
100
101 param.name = name;
102 param.value.g_type = 0;
103
104 // Fill the GValue with the current vararg, and move on to the next one.
105 g_value_init(¶m.value, G_PARAM_SPEC_VALUE_TYPE(pspec));
106 G_VALUE_COLLECT(¶m.value, var_args, 0, &collect_error);
107
108 if (collect_error)
109 {
110 g_warning("Glib::ConstructParams::ConstructParams(): %s", collect_error);
111 g_free(collect_error);
112 g_value_unset(¶m.value);
113 break;
114 }
115
116 ++n_parameters;
117 }
118
119 g_type_class_unref(g_class);
120
121 va_end(var_args);
122 }
123
~ConstructParams()124 ConstructParams::~ConstructParams() noexcept
125 {
126 while (n_parameters > 0)
127 g_value_unset(¶meters[--n_parameters].value);
128
129 g_free(parameters);
130 }
131
132 /*
133 * Some compilers require the existence of a copy constructor in certain
134 * usage contexts. This implementation is fully functional, but unlikely
135 * to be ever actually called due to optimization.
136 */
137 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
ConstructParams(const ConstructParams & other)138 ConstructParams::ConstructParams(const ConstructParams& other)
139 : glibmm_class(other.glibmm_class),
140 n_parameters(other.n_parameters),
141 parameters(g_new(GParameter, n_parameters))
142 {
143 for (unsigned int i = 0; i < n_parameters; ++i)
144 {
145 parameters[i].name = other.parameters[i].name;
146 parameters[i].value.g_type = 0;
147
148 g_value_init(¶meters[i].value, G_VALUE_TYPE(&other.parameters[i].value));
149 g_value_copy(&other.parameters[i].value, ¶meters[i].value);
150 }
151 }
152 G_GNUC_END_IGNORE_DEPRECATIONS
153
154 /**** Glib::Object_Class ***************************************************/
155
156 const Glib::Class&
init()157 Object_Class::init()
158 {
159 if (!gtype_)
160 {
161 class_init_func_ = &Object_Class::class_init_function;
162 register_derived_type(G_TYPE_OBJECT);
163 }
164
165 return *this;
166 }
167
168 void
class_init_function(void *,void *)169 Object_Class::class_init_function(void*, void*)
170 {
171 }
172
173 Object*
wrap_new(GObject * object)174 Object_Class::wrap_new(GObject* object)
175 {
176 return new Object(object);
177 }
178
179 /**** Glib::Object *********************************************************/
180
181 // static data
182 Object::CppClassType Object::object_class_;
183
Object()184 Object::Object()
185 {
186 // This constructor is ONLY for derived classes that are NOT wrappers of
187 // derived C objects. For instance, Gtk::Object should NOT use this
188 // constructor.
189
190 // g_warning("Object::Object(): Did you really mean to call this?");
191
192 // If Glib::ObjectBase has been constructed with a custom typeid, we derive
193 // a new GType on the fly. This works because ObjectBase is a virtual base
194 // class, therefore its constructor is always executed first.
195
196 GType object_type = G_TYPE_OBJECT; // the default -- not very useful
197
198 if (custom_type_name_ && !is_anonymous_custom_())
199 {
200 object_class_.init();
201 // This creates a type that is derived (indirectly) from GObject.
202 object_type = object_class_.clone_custom_type(custom_type_name_,
203 get_custom_interface_classes(), get_custom_class_init_functions(),
204 get_custom_instance_init_function());
205 custom_class_init_finished();
206 }
207
208 void* const new_object = g_object_new(object_type, nullptr);
209
210 // Connect the GObject and Glib::Object instances.
211 ObjectBase::initialize(static_cast<GObject*>(new_object));
212 }
213
Object(const Glib::ConstructParams & construct_params)214 Object::Object(const Glib::ConstructParams& construct_params)
215 {
216 GType object_type = construct_params.glibmm_class.get_type();
217
218 // If Glib::ObjectBase has been constructed with a custom typeid, we derive
219 // a new GType on the fly. This works because ObjectBase is a virtual base
220 // class, therefore its constructor is always executed first.
221
222 if (custom_type_name_ && !is_anonymous_custom_())
223 {
224 object_type =
225 construct_params.glibmm_class.clone_custom_type(custom_type_name_,
226 get_custom_interface_classes(), get_custom_class_init_functions(),
227 get_custom_instance_init_function());
228 custom_class_init_finished();
229 }
230
231 // Create a new GObject with the specified array of construct properties.
232 // This works with custom types too, since those inherit the properties of
233 // their base class.
234
235 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
236 //TODO: Replace g_object_newv() by g_object_new_with_properties() when we can
237 // require glib 2.54. GParameter is also deprecated.
238 // Don't use it in ConstructParams when we can break ABI.
239 void* const new_object =
240 g_object_newv(object_type, construct_params.n_parameters, construct_params.parameters);
241 G_GNUC_END_IGNORE_DEPRECATIONS
242
243 // Connect the GObject and Glib::Object instances.
244 ObjectBase::initialize(static_cast<GObject*>(new_object));
245 }
246
Object(GObject * castitem)247 Object::Object(GObject* castitem)
248 {
249 // I disabled this check because libglademm really does need to do this.
250 //(actually it tells libglade to instantiate "gtkmm_" types.
251 // The 2nd instance bug will be caught elsewhere anyway.
252 /*
253 static const char gtkmm_prefix[] = "gtkmm__";
254 const char *const type_name = G_OBJECT_TYPE_NAME(castitem);
255
256 if(strncmp(type_name, gtkmm_prefix, sizeof(gtkmm_prefix) - 1) == 0)
257 {
258 g_warning("Glib::Object::Object(GObject*): "
259 "An object of type '%s' was created directly via g_object_new(). "
260 "The Object::Object(const Glib::ConstructParams&) constructor "
261 "should be used instead.\n"
262 "This could happen if the C instance lived longer than the C++ instance, so that "
263 "a second C++ instance was created automatically to wrap it. That would be a gtkmm
264 bug that you should report.",
265 type_name);
266 }
267 */
268
269 // Connect the GObject and Glib::Object instances.
270 ObjectBase::initialize(castitem);
271 }
272
Object(Object && src)273 Object::Object(Object&& src) noexcept
274 : sigc::trackable(std::move(src)), // not actually called because it's a virtual base
275 ObjectBase(std::move(src)) // not actually called because it's a virtual base
276 {
277 // Perhaps trackable's move constructor has not been called. Do its job here.
278 // (No harm is done if notify_callbacks() is called twice. The second call
279 // won't do anything.)
280 src.notify_callbacks();
281 ObjectBase::initialize_move(src.gobject_, &src);
282 }
283
284 Object&
operator =(Object && src)285 Object::operator=(Object&& src) noexcept
286 {
287 ObjectBase::operator=(std::move(src));
288 return *this;
289 }
290
~Object()291 Object::~Object() noexcept
292 {
293 cpp_destruction_in_progress_ = true;
294 }
295
296 /*
297 RefPtr<Object> Object::create()
298 {
299 // Derived classes will actually return RefPtr<>s that contain useful instances.
300 return RefPtr<Object>();
301 }
302 */
303
304 GType
get_type()305 Object::get_type()
306 {
307 return object_class_.init().get_type();
308 }
309
310 GType
get_base_type()311 Object::get_base_type()
312 {
313 return G_TYPE_OBJECT;
314 }
315
316 // Data services
317 void*
get_data(const QueryQuark & id)318 Object::get_data(const QueryQuark& id)
319 {
320 return g_object_get_qdata(gobj(), id);
321 }
322
323 void
set_data(const Quark & id,void * data)324 Object::set_data(const Quark& id, void* data)
325 {
326 g_object_set_qdata(gobj(), id, data);
327 }
328
329 void
set_data(const Quark & id,void * data,DestroyNotify destroy)330 Object::set_data(const Quark& id, void* data, DestroyNotify destroy)
331 {
332 g_object_set_qdata_full(gobj(), id, data, destroy);
333 }
334
335 void
remove_data(const QueryQuark & id)336 Object::remove_data(const QueryQuark& id)
337 {
338 // missing in glib??
339 g_return_if_fail(id.id() > 0);
340 g_datalist_id_remove_data(&gobj()->qdata, id);
341 }
342
343 void*
steal_data(const QueryQuark & id)344 Object::steal_data(const QueryQuark& id)
345 {
346 return g_object_steal_qdata(gobj(), id);
347 }
348
349 } // namespace Glib
350