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(&param.value, G_PARAM_SPEC_VALUE_TYPE(pspec));
106     G_VALUE_COLLECT(&param.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(&param.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(&parameters[--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(&parameters[i].value, G_VALUE_TYPE(&other.parameters[i].value));
149     g_value_copy(&other.parameters[i].value, &parameters[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