1 /* Copyright (C) 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/class.h>
18 #include <glibmm/property.h>
19 #include <glibmm/ustring.h>
20 #include <glibmm/utility.h>
21 #include <glibmm/interface.h>
22 #include <glibmm/private/interface_p.h>
23 
24 namespace Glib
25 {
26 
27 void
register_derived_type(GType base_type)28 Class::register_derived_type(GType base_type)
29 {
30   return register_derived_type(base_type, nullptr);
31 }
32 
33 void
register_derived_type(GType base_type,GTypeModule * module)34 Class::register_derived_type(GType base_type, GTypeModule* module)
35 {
36   if (gtype_)
37     return; // already initialized
38 
39   // 0 is not a valid GType.
40   // It would lead to a crash later.
41   // We allow this, failing silently, to make life easier for gstreamermm.
42   if (base_type == 0)
43     return; // already initialized
44 
45   GTypeQuery base_query = {
46     0, nullptr, 0, 0,
47   };
48   g_type_query(base_type, &base_query);
49 
50   // GTypeQuery::class_size is guint but GTypeInfo::class_size is guint16.
51   const guint16 class_size = (guint16)base_query.class_size;
52 
53   // GTypeQuery::instance_size is guint but GTypeInfo::instance_size is guint16.
54   const guint16 instance_size = (guint16)base_query.instance_size;
55 
56   const GTypeInfo derived_info = {
57     class_size,
58     nullptr, // base_init
59     nullptr, // base_finalize
60     class_init_func_, // Set by the caller ( *_Class::init() ).
61     nullptr, // class_finalize
62     nullptr, // class_data
63     instance_size,
64     0, // n_preallocs
65     nullptr, // instance_init
66     nullptr, // value_table
67   };
68 
69   if (!(base_query.type_name))
70   {
71     g_critical("Class::register_derived_type(): base_query.type_name is NULL.");
72     return;
73   }
74 
75   gchar* derived_name = g_strconcat("gtkmm__", base_query.type_name, nullptr);
76 
77   if (module)
78     gtype_ =
79       g_type_module_register_type(module, base_type, derived_name, &derived_info, GTypeFlags(0));
80   else
81     gtype_ = g_type_register_static(base_type, derived_name, &derived_info, GTypeFlags(0));
82 
83   g_free(derived_name);
84 }
85 
86 GType
clone_custom_type(const char * custom_type_name) const87 Class::clone_custom_type(const char* custom_type_name) const
88 {
89   return clone_custom_type(custom_type_name, nullptr, nullptr, nullptr);
90 }
91 
92 GType
clone_custom_type(const char * custom_type_name,const interface_class_vector_type & interface_classes) const93 Class::clone_custom_type(
94   const char* custom_type_name, const interface_class_vector_type& interface_classes) const
95 {
96   return clone_custom_type(custom_type_name, &interface_classes, nullptr, nullptr);
97 }
98 
99 GType
clone_custom_type(const char * custom_type_name,const interface_class_vector_type * interface_classes,const class_init_funcs_type * class_init_funcs,GInstanceInitFunc instance_init_func) const100 Class::clone_custom_type(
101   const char* custom_type_name, const interface_class_vector_type* interface_classes,
102   const class_init_funcs_type* class_init_funcs, GInstanceInitFunc instance_init_func) const
103 {
104   std::string full_name("gtkmm__CustomObject_");
105   Glib::append_canonical_typename(full_name, custom_type_name);
106 
107   GType custom_type = g_type_from_name(full_name.c_str());
108 
109   if (!custom_type)
110   {
111     g_return_val_if_fail(gtype_ != 0, 0);
112 
113     // Cloned custom types derive from the wrapper's parent type,
114     // so that g_type_class_peek_parent() works correctly.
115     const GType base_type = g_type_parent(gtype_);
116 
117     GTypeQuery base_query = {
118       0, nullptr, 0, 0,
119     };
120     g_type_query(base_type, &base_query);
121 
122     // GTypeQuery::class_size is guint but GTypeInfo::class_size is guint16.
123     const guint16 class_size = (guint16)base_query.class_size;
124 
125     // GTypeQuery::instance_size is guint but GTypeInfo::instance_size is guint16.
126     const guint16 instance_size = (guint16)base_query.instance_size;
127 
128     // Let the wrapper's class_init_function() be the first one to call.
129     auto all_class_init_funcs = new class_init_funcs_type(
130       1, std::tuple<GClassInitFunc, void*>(class_init_func_, nullptr));
131     if (class_init_funcs)
132       all_class_init_funcs->insert(all_class_init_funcs->end(),
133         class_init_funcs->begin(), class_init_funcs->end());
134 
135     const GTypeInfo derived_info = {
136       class_size,
137       nullptr, // base_init
138       &Class::custom_class_base_finalize_function, // base_finalize
139       &Class::custom_class_init_function,
140       nullptr, // class_finalize
141       all_class_init_funcs, // class_data
142       instance_size,
143       0, // n_preallocs
144       instance_init_func, // instance_init
145       nullptr, // value_table
146     };
147 
148     // custom_class_init_function() is called when the first object of the custom
149     // class is created, which is after clone_custom_type() has returned.
150     // Let custom_class_init_function() delete all_class_init_funcs.
151 
152     custom_type =
153       g_type_register_static(base_type, full_name.c_str(), &derived_info, GTypeFlags(0));
154 
155     // Add derived versions of interfaces, if the C type implements any interfaces.
156     // For instance, TreeModel_Class::add_interface().
157     if (interface_classes)
158     {
159       for (auto interface_class : *interface_classes)
160       {
161         if (interface_class)
162         {
163           interface_class->add_interface(custom_type);
164         }
165       }
166     }
167   }
168 
169   return custom_type;
170 }
171 
172 // Initialize the static quark to store/get custom type properties.
173 GQuark Class::iface_properties_quark = g_quark_from_string("gtkmm_CustomObject_iface_properties");
174 
175 // static
176 void
custom_class_base_finalize_function(void * g_class)177 Class::custom_class_base_finalize_function(void* g_class)
178 {
179   const GType gtype = G_TYPE_FROM_CLASS(g_class);
180 
181   // Free the data related to the interface properties for the custom type, if any.
182   iface_properties_type* props =
183     static_cast<iface_properties_type*>(g_type_get_qdata(gtype, iface_properties_quark));
184 
185   if (props)
186   {
187     for (iface_properties_type::size_type i = 0; i < props->size(); i++)
188     {
189       g_value_unset((*props)[i]);
190       g_free((*props)[i]);
191     }
192     delete props;
193   }
194 }
195 
196 // static
197 void
custom_class_init_function(void * g_class,void * class_data)198 Class::custom_class_init_function(void* g_class, void* class_data)
199 {
200   // clone_custom_type() sets the class data pointer to a pointer to a vector
201   // of pointers to functions to be called.
202   const class_init_funcs_type& class_init_funcs =
203     *static_cast<class_init_funcs_type*>(class_data);
204 
205   g_return_if_fail(!class_init_funcs.empty() && std::get<0>(class_init_funcs[0]) != nullptr);
206 
207   // Call the wrapper's class_init_function() to redirect
208   // the vfunc and default signal handler callbacks.
209   GClassInitFunc init_func = std::get<0>(class_init_funcs[0]);
210   (*init_func)(g_class, nullptr);
211 
212   GObjectClass* const gobject_class = static_cast<GObjectClass*>(g_class);
213   gobject_class->get_property = &Glib::custom_get_property_callback;
214   gobject_class->set_property = &Glib::custom_set_property_callback;
215 
216   // Call extra class init functions, if any.
217   for (std::size_t i = 1; i < class_init_funcs.size(); ++i)
218   {
219     if (GClassInitFunc extra_init_func = std::get<0>(class_init_funcs[i]))
220     {
221       void* extra_class_data = std::get<1>(class_init_funcs[i]);
222       (*extra_init_func)(g_class, extra_class_data);
223     }
224   }
225 
226   // Assume that this function is called exactly once for each type.
227   // Delete the class_init_funcs_type that was created in clone_custom_type().
228   delete static_cast<class_init_funcs_type*>(class_data);
229 
230   // Override the properties of implemented interfaces, if any.
231   const GType object_type = G_TYPE_FROM_CLASS(g_class);
232 
233   Class::iface_properties_type* props = static_cast<Class::iface_properties_type*>(
234     g_type_get_qdata(object_type, Class::iface_properties_quark));
235   if (!props)
236   {
237     props = new Class::iface_properties_type();
238     g_type_set_qdata(object_type, Class::iface_properties_quark, props);
239   }
240 
241   guint n_interfaces = 0;
242   GType* iface_types = g_type_interfaces(object_type, &n_interfaces);
243 
244   for (guint i = 0; i < n_interfaces; ++i)
245   {
246     void* const g_iface = g_type_default_interface_ref(iface_types[i]);
247 
248     guint n_iface_props = 0;
249     GParamSpec** iface_props = g_object_interface_list_properties(g_iface, &n_iface_props);
250 
251     for (guint p = 0; p < n_iface_props; p++)
252     {
253       const gchar* prop_name = g_param_spec_get_name(iface_props[p]);
254 
255       // Override only properties which have not been overridden in a base class.
256       // Store the default values belonging to the class.
257       // They are copied to an object in custom_set_property_callback() in property.cc.
258       if (!g_object_class_find_property(gobject_class, prop_name))
259       {
260         GValue* g_value = g_new0(GValue, 1);
261         g_value_init(g_value, iface_props[p]->value_type);
262         g_param_value_set_default(iface_props[p], g_value);
263         props->emplace_back(g_value);
264 
265         g_object_class_override_property(gobject_class, props->size(), prop_name);
266       }
267     } // end for p
268 
269     g_type_default_interface_unref(g_iface);
270     g_free(iface_props);
271 
272   } // end for i
273 
274   g_free(iface_types);
275 }
276 
277 } // namespace Glib
278