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