1 /*
2  * e-extensible.c
3  *
4  * This library is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library. If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 /**
19  * SECTION: e-extensible
20  * @include: libedataserver/libedataserver.h
21  * @short_description: An interface for extending objects
22  *
23  * #EExtension objects can be tacked on to any #GObject instance that
24  * implements the #EExtensible interface.  A #GObject type can be made
25  * extensible in two steps:
26  *
27  * 1. Add the #EExtensible interface when registering the #GType.
28  *    There are no methods to implement.
29  *
30  * |[
31  * #include <libedataserver/libedataserver.h>
32  *
33  * G_DEFINE_TYPE_WITH_CODE (
34  *         ECustomWidget, e_custom_widget, GTK_TYPE_WIDGET,
35  *         G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
36  * ]|
37  *
38  * 2. Load extensions for the class at some point during #GObject
39  *    initialization.  Generally this should be done toward the end of
40  *    the initialization code, so extensions get a fully initialized
41  *    object to work with.
42  *
43  * |[
44  * static void
45  * e_custom_widget_constructed (ECustomWidget *widget)
46  * {
47  *         Construction code goes here, same as call to parent's 'constructed'...
48  *
49  *         e_extensible_load_extensions (E_EXTENSIBLE (widget));
50  * }
51  * ]|
52  **/
53 
54 #include "evolution-data-server-config.h"
55 
56 #include "e-extension.h"
57 #include "e-data-server-util.h"
58 
59 #include "e-extensible.h"
60 
61 #define IS_AN_EXTENSION_TYPE(type) \
62 	(g_type_is_a ((type), E_TYPE_EXTENSION))
63 
64 static GQuark extensible_quark;
65 
G_DEFINE_INTERFACE(EExtensible,e_extensible,G_TYPE_OBJECT)66 G_DEFINE_INTERFACE (
67 	EExtensible,
68 	e_extensible,
69 	G_TYPE_OBJECT)
70 
71 static GPtrArray *
72 extensible_get_extensions (EExtensible *extensible)
73 {
74 	return g_object_get_qdata (G_OBJECT (extensible), extensible_quark);
75 }
76 
77 static void
extensible_load_extension(GType extension_type,EExtensible * extensible)78 extensible_load_extension (GType extension_type,
79                            EExtensible *extensible)
80 {
81 	EExtensionClass *extension_class;
82 	GType extensible_type;
83 	GPtrArray *extensions;
84 	EExtension *extension;
85 
86 	extensible_type = G_OBJECT_TYPE (extensible);
87 	extension_class = g_type_class_ref (extension_type);
88 
89 	/* Only load extensions that extend the given extensible object. */
90 	if (!g_type_is_a (extensible_type, extension_class->extensible_type))
91 		goto exit;
92 
93 	extension = g_object_new (
94 		extension_type, "extensible", extensible, NULL);
95 
96 	extensions = extensible_get_extensions (extensible);
97 	g_ptr_array_add (extensions, extension);
98 
99 exit:
100 	g_type_class_unref (extension_class);
101 }
102 
103 static void
e_extensible_default_init(EExtensibleInterface * iface)104 e_extensible_default_init (EExtensibleInterface *iface)
105 {
106 	extensible_quark = g_quark_from_static_string ("e-extensible-quark");
107 }
108 
109 /**
110  * e_extensible_load_extensions:
111  * @extensible: an #EExtensible
112  *
113  * Creates an instance of all instantiable subtypes of #EExtension which
114  * target the class of @extensible.  The lifetimes of these newly created
115  * #EExtension objects are bound to @extensible such that they are finalized
116  * when @extensible is finalized.
117  *
118  * Since: 3.4
119  **/
120 void
e_extensible_load_extensions(EExtensible * extensible)121 e_extensible_load_extensions (EExtensible *extensible)
122 {
123 	GPtrArray *extensions;
124 
125 	g_return_if_fail (E_IS_EXTENSIBLE (extensible));
126 
127 	if (extensible_get_extensions (extensible) != NULL)
128 		return;
129 
130 	extensions = g_ptr_array_new_with_free_func (
131 		(GDestroyNotify) g_object_unref);
132 
133 	g_object_set_qdata_full (
134 		G_OBJECT (extensible), extensible_quark,
135 		g_ptr_array_ref (extensions),
136 		(GDestroyNotify) g_ptr_array_unref);
137 
138 	e_type_traverse (
139 		E_TYPE_EXTENSION, (ETypeFunc)
140 		extensible_load_extension, extensible);
141 
142 	/* If the extension array is still empty, remove it from the
143 	 * extensible object.  It may be that no extension types have
144 	 * been registered yet, so this allows for trying again later. */
145 	if (extensions->len == 0)
146 		g_object_set_qdata (
147 			G_OBJECT (extensible),
148 			extensible_quark, NULL);
149 
150 	g_ptr_array_unref (extensions);
151 }
152 
153 /**
154  * e_extensible_list_extensions:
155  * @extensible: an #EExtensible
156  * @extension_type: the type of extensions to list
157  *
158  * Returns a list of #EExtension objects bound to @extensible whose
159  * types are ancestors of @extension_type.  For a complete list of
160  * extension objects bound to @extensible, pass %E_TYPE_EXTENSION.
161  *
162  * The list itself should be freed with g_list_free().  The extension
163  * objects are owned by @extensible and should not be unreferenced.
164  *
165  * Returns: (element-type EExtension) (transfer container): a list of extension objects derived from @extension_type
166  *
167  * Since: 3.4
168  **/
169 GList *
e_extensible_list_extensions(EExtensible * extensible,GType extension_type)170 e_extensible_list_extensions (EExtensible *extensible,
171                               GType extension_type)
172 {
173 	GPtrArray *extensions;
174 	GList *list = NULL;
175 	guint ii;
176 
177 	g_return_val_if_fail (E_IS_EXTENSIBLE (extensible), NULL);
178 	g_return_val_if_fail (IS_AN_EXTENSION_TYPE (extension_type), NULL);
179 
180 	e_extensible_load_extensions (extensible);
181 
182 	extensions = extensible_get_extensions (extensible);
183 
184 	/* This will be NULL if no extensions are present. */
185 	if (extensions == NULL)
186 		return NULL;
187 
188 	for (ii = 0; ii < extensions->len; ii++) {
189 		GObject *object;
190 
191 		object = g_ptr_array_index (extensions, ii);
192 		if (g_type_is_a (G_OBJECT_TYPE (object), extension_type))
193 			list = g_list_prepend (list, object);
194 	}
195 
196 	return g_list_reverse (list);
197 }
198