1 /*
2 * peas-extension-set.c
3 * This file is part of libpeas
4 *
5 * Copyright (C) 2010 Steve Frécinaux
6 *
7 * libpeas is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * libpeas is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include "peas-extension-set.h"
27
28 #include "peas-i18n-priv.h"
29 #include "peas-introspection.h"
30 #include "peas-plugin-info.h"
31 #include "peas-marshal.h"
32 #include "peas-utils.h"
33
34 /**
35 * SECTION:peas-extension-set
36 * @short_description: Proxy for a set of extensions of the same type.
37 * @see_also: #PeasExtension
38 *
39 * A #PeasExtensionSet is an object which proxies method calls to a set
40 * of actual extensions. The application writer will use these objects
41 * in order to call methods on several instances of an actual extension
42 * exported by all the currently loaded plugins.
43 *
44 * #PeasExtensionSet will automatically track loading and unloading of
45 * the plugins, and signal appearance and disappearance of new
46 * extension instances. You should connect to those signals if you
47 * wish to call specific methods on loading or unloading time.
48 *
49 * Here is the code for a typical setup of #PeasExtensionSet with
50 * #PeasActivatable as the watched extension point, and #GtkWindow
51 * instances as the target objects:
52 * |[
53 * static void
54 * on_extension_added (PeasExtensionSet *set,
55 * PeasPluginInfo *info,
56 * PeasActivatable *activatable)
57 * {
58 * peas_activatable_activate (activatable);
59 * }
60 *
61 * static void
62 * on_extension_removed (PeasExtensionSet *set,
63 * PeasPluginInfo *info,
64 * PeasActivatable *activatable)
65 * {
66 * peas_activatable_deactivate (activatable);
67 * }
68 *
69 * PeasExtensionSet *
70 * setup_extension_set (PeasEngine *engine,
71 * GtkWindow *window)
72 * {
73 * PeasExtensionSet *set;
74 *
75 * set = peas_extension_set_new (engine, PEAS_TYPE_ACTIVATABLE,
76 * "object", window, NULL);
77 * peas_extension_set_foreach (set,
78 * (PeasExtensionSetForeachFunc) on_extension_added,
79 * NULL);
80 * g_signal_connect (set, "extension-added",
81 * G_CALLBACK (on_extension_added), NULL);
82 * g_signal_connect (set, "extension-removed",
83 * G_CALLBACK (on_extension_removed), NULL);
84 * return set;
85 * }
86 * ]|
87 **/
88
89 struct _PeasExtensionSetPrivate {
90 PeasEngine *engine;
91 GType exten_type;
92 guint n_parameters;
93
94 GParameter *parameters;
95
96 GQueue extensions;
97 };
98
99 typedef struct {
100 PeasPluginInfo *info;
101 PeasExtension *exten;
102 } ExtensionItem;
103
104 typedef struct {
105 guint n_parameters;
106
107 GParameter *parameters;
108 } PeasParameterArray;
109
110 /* Signals */
111 enum {
112 EXTENSION_ADDED,
113 EXTENSION_REMOVED,
114 LAST_SIGNAL
115 };
116
117 /* Properties */
118 enum {
119 PROP_0,
120 PROP_ENGINE,
121 PROP_EXTENSION_TYPE,
122 PROP_CONSTRUCT_PROPERTIES,
123 N_PROPERTIES
124 };
125
126 static guint signals[LAST_SIGNAL];
127 static GParamSpec *properties[N_PROPERTIES] = { NULL };
128
G_DEFINE_TYPE_WITH_PRIVATE(PeasExtensionSet,peas_extension_set,G_TYPE_OBJECT)129 G_DEFINE_TYPE_WITH_PRIVATE (PeasExtensionSet,
130 peas_extension_set,
131 G_TYPE_OBJECT)
132
133 #define GET_PRIV(o) \
134 (peas_extension_set_get_instance_private (o))
135
136 static void
137 set_construct_properties (PeasExtensionSet *set,
138 PeasParameterArray *array)
139 {
140 PeasExtensionSetPrivate *priv = GET_PRIV (set);
141 guint i;
142
143 priv->n_parameters = array->n_parameters;
144
145 priv->parameters = g_new0 (GParameter, array->n_parameters);
146
147 for (i = 0; i < array->n_parameters; i++)
148 {
149 priv->parameters[i].name = g_intern_string (array->parameters[i].name);
150 g_value_init (&priv->parameters[i].value, G_VALUE_TYPE (&array->parameters[i].value));
151 g_value_copy (&array->parameters[i].value, &priv->parameters[i].value);
152 }
153 }
154
155 static void
peas_extension_set_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)156 peas_extension_set_set_property (GObject *object,
157 guint prop_id,
158 const GValue *value,
159 GParamSpec *pspec)
160 {
161 PeasExtensionSet *set = PEAS_EXTENSION_SET (object);
162 PeasExtensionSetPrivate *priv = GET_PRIV (set);
163
164 switch (prop_id)
165 {
166 case PROP_ENGINE:
167 priv->engine = g_value_get_object (value);
168 break;
169 case PROP_EXTENSION_TYPE:
170 priv->exten_type = g_value_get_gtype (value);
171 break;
172 case PROP_CONSTRUCT_PROPERTIES:
173 set_construct_properties (set, g_value_get_pointer (value));
174 break;
175 default:
176 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177 }
178 }
179
180 static void
peas_extension_set_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)181 peas_extension_set_get_property (GObject *object,
182 guint prop_id,
183 GValue *value,
184 GParamSpec *pspec)
185 {
186 PeasExtensionSet *set = PEAS_EXTENSION_SET (object);
187 PeasExtensionSetPrivate *priv = GET_PRIV (set);
188
189 switch (prop_id)
190 {
191 case PROP_ENGINE:
192 g_value_set_object (value, priv->engine);
193 break;
194 case PROP_EXTENSION_TYPE:
195 g_value_set_gtype (value, priv->exten_type);
196 break;
197 default:
198 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199 }
200 }
201
202 static void
add_extension(PeasExtensionSet * set,PeasPluginInfo * info)203 add_extension (PeasExtensionSet *set,
204 PeasPluginInfo *info)
205 {
206 PeasExtensionSetPrivate *priv = GET_PRIV (set);
207 PeasExtension *exten;
208 ExtensionItem *item;
209
210 /* Let's just ignore unloaded plugins... */
211 if (!peas_plugin_info_is_loaded (info))
212 return;
213
214 if (!peas_engine_provides_extension (priv->engine, info,
215 priv->exten_type))
216 return;
217
218 exten = peas_engine_create_extensionv (priv->engine, info,
219 priv->exten_type,
220 priv->n_parameters,
221 priv->parameters);
222
223 item = g_slice_new (ExtensionItem);
224 item->info = info;
225 item->exten = exten;
226
227 g_queue_push_tail (&priv->extensions, item);
228 g_signal_emit (set, signals[EXTENSION_ADDED], 0, info, exten);
229 }
230
231 static void
remove_extension_item(PeasExtensionSet * set,ExtensionItem * item)232 remove_extension_item (PeasExtensionSet *set,
233 ExtensionItem *item)
234 {
235 g_signal_emit (set, signals[EXTENSION_REMOVED], 0, item->info, item->exten);
236
237 g_object_unref (item->exten);
238
239 g_slice_free (ExtensionItem, item);
240 }
241
242 static void
remove_extension(PeasExtensionSet * set,PeasPluginInfo * info)243 remove_extension (PeasExtensionSet *set,
244 PeasPluginInfo *info)
245 {
246 PeasExtensionSetPrivate *priv = GET_PRIV (set);
247 GList *l;
248
249 for (l = priv->extensions.head; l != NULL; l = l->next)
250 {
251 ExtensionItem *item = l->data;
252
253 if (item->info != info)
254 continue;
255
256 remove_extension_item (set, item);
257 g_queue_delete_link (&priv->extensions, l);
258 return;
259 }
260 }
261
262 static void
peas_extension_set_init(PeasExtensionSet * set)263 peas_extension_set_init (PeasExtensionSet *set)
264 {
265 PeasExtensionSetPrivate *priv = GET_PRIV (set);
266
267 g_queue_init (&priv->extensions);
268 }
269
270 static void
peas_extension_set_constructed(GObject * object)271 peas_extension_set_constructed (GObject *object)
272 {
273 PeasExtensionSet *set = PEAS_EXTENSION_SET (object);
274 PeasExtensionSetPrivate *priv = GET_PRIV (set);
275 GList *plugins, *l;
276
277 if (priv->engine == NULL)
278 priv->engine = peas_engine_get_default ();
279
280 g_object_ref (priv->engine);
281
282 plugins = (GList *) peas_engine_get_plugin_list (priv->engine);
283 for (l = plugins; l; l = l->next)
284 add_extension (set, (PeasPluginInfo *) l->data);
285
286 g_signal_connect_object (priv->engine, "load-plugin",
287 G_CALLBACK (add_extension), set,
288 G_CONNECT_AFTER | G_CONNECT_SWAPPED);
289 g_signal_connect_object (priv->engine, "unload-plugin",
290 G_CALLBACK (remove_extension), set,
291 G_CONNECT_SWAPPED);
292
293 G_OBJECT_CLASS (peas_extension_set_parent_class)->constructed (object);
294 }
295
296 static void
peas_extension_set_dispose(GObject * object)297 peas_extension_set_dispose (GObject *object)
298 {
299 PeasExtensionSet *set = PEAS_EXTENSION_SET (object);
300 PeasExtensionSetPrivate *priv = GET_PRIV (set);
301 GList *l;
302
303 if (priv->extensions.length > 0)
304 {
305 for (l = priv->extensions.tail; l != NULL; l = l->prev)
306 remove_extension_item (set, (ExtensionItem *) l->data);
307
308 g_queue_clear (&priv->extensions);
309 }
310
311 if (priv->parameters != NULL)
312 {
313 while (priv->n_parameters-- > 0)
314 g_value_unset (&priv->parameters[priv->n_parameters].value);
315
316 g_free (priv->parameters);
317 priv->parameters = NULL;
318 }
319
320 g_clear_object (&priv->engine);
321
322 G_OBJECT_CLASS (peas_extension_set_parent_class)->dispose (object);
323 }
324
325 static gboolean
peas_extension_set_call_real(PeasExtensionSet * set,const gchar * method_name,GIArgument * args)326 peas_extension_set_call_real (PeasExtensionSet *set,
327 const gchar *method_name,
328 GIArgument *args)
329 {
330 PeasExtensionSetPrivate *priv = GET_PRIV (set);
331 gboolean ret = TRUE;
332 GList *l;
333 GIArgument dummy;
334
335 for (l = priv->extensions.head; l != NULL; l = l->next)
336 {
337 ExtensionItem *item = (ExtensionItem *) l->data;
338 ret = peas_extension_callv (item->exten, method_name, args, &dummy) && ret;
339 }
340
341 return ret;
342 }
343
344 static void
peas_extension_set_class_init(PeasExtensionSetClass * klass)345 peas_extension_set_class_init (PeasExtensionSetClass *klass)
346 {
347 GType the_type = G_TYPE_FROM_CLASS (klass);
348 GObjectClass *object_class = G_OBJECT_CLASS (klass);
349
350 object_class->set_property = peas_extension_set_set_property;
351 object_class->get_property = peas_extension_set_get_property;
352 object_class->constructed = peas_extension_set_constructed;
353 object_class->dispose = peas_extension_set_dispose;
354
355 klass->call = peas_extension_set_call_real;
356
357 /**
358 * PeasExtensionSet::extension-added:
359 * @set: A #PeasExtensionSet.
360 * @info: A #PeasPluginInfo.
361 * @exten: A #PeasExtension.
362 *
363 * The extension-added signal is emitted when a new extension has been
364 * added to the #PeasExtensionSet. It happens when a new plugin implementing
365 * the extension set's extension type is loaded.
366 *
367 * You should connect to this signal in order to set up the extensions when
368 * they are loaded. Note that this signal is not fired for extensions coming
369 * from plugins that were already loaded when the #PeasExtensionSet instance
370 * was created. You should set those up by yourself.
371 */
372 signals[EXTENSION_ADDED] =
373 g_signal_new (I_("extension-added"),
374 the_type,
375 G_SIGNAL_RUN_LAST,
376 G_STRUCT_OFFSET (PeasExtensionSetClass, extension_added),
377 NULL, NULL,
378 peas_cclosure_marshal_VOID__BOXED_OBJECT,
379 G_TYPE_NONE,
380 2,
381 PEAS_TYPE_PLUGIN_INFO | G_SIGNAL_TYPE_STATIC_SCOPE,
382 PEAS_TYPE_EXTENSION);
383
384 /**
385 * PeasExtensionSet::extension-removed:
386 * @set: A #PeasExtensionSet.
387 * @info: A #PeasPluginInfo.
388 * @exten: A #PeasExtension.
389 *
390 * The extension-removed signal is emitted when a new extension is about to be
391 * removed from the #PeasExtensionSet. It happens when a plugin implementing
392 * the extension set's extension type is unloaded, or when the
393 * #PeasExtensionSet itself is destroyed.
394 *
395 * You should connect to this signal in order to clean up the extensions
396 * when their plugin is unload. Note that this signal is not fired for the
397 * #PeasExtension instances still available when the #PeasExtensionSet
398 * instance is destroyed. You should clean those up by yourself.
399 */
400 signals[EXTENSION_REMOVED] =
401 g_signal_new (I_("extension-removed"),
402 the_type,
403 G_SIGNAL_RUN_LAST,
404 G_STRUCT_OFFSET (PeasExtensionSetClass, extension_removed),
405 NULL, NULL,
406 peas_cclosure_marshal_VOID__BOXED_OBJECT,
407 G_TYPE_NONE,
408 2,
409 PEAS_TYPE_PLUGIN_INFO | G_SIGNAL_TYPE_STATIC_SCOPE,
410 PEAS_TYPE_EXTENSION);
411
412 properties[PROP_ENGINE] =
413 g_param_spec_object ("engine",
414 "Engine",
415 "The PeasEngine this set is attached to",
416 PEAS_TYPE_ENGINE,
417 G_PARAM_READWRITE |
418 G_PARAM_CONSTRUCT_ONLY |
419 G_PARAM_STATIC_STRINGS);
420
421 properties[PROP_EXTENSION_TYPE] =
422 g_param_spec_gtype ("extension-type",
423 "Extension Type",
424 "The extension GType managed by this set",
425 G_TYPE_NONE,
426 G_PARAM_READWRITE |
427 G_PARAM_CONSTRUCT_ONLY |
428 G_PARAM_STATIC_STRINGS);
429
430 properties[PROP_CONSTRUCT_PROPERTIES] =
431 g_param_spec_pointer ("construct-properties",
432 "Construct Properties",
433 "The properties to pass the extensions when creating them",
434 G_PARAM_WRITABLE |
435 G_PARAM_CONSTRUCT_ONLY |
436 G_PARAM_STATIC_STRINGS);
437
438 g_object_class_install_properties (object_class, N_PROPERTIES, properties);
439 }
440
441 /**
442 * peas_extension_set_get_extension:
443 * @set: A #PeasExtensionSet
444 * @info: a #PeasPluginInfo
445 *
446 * Returns the #PeasExtension object corresponding to @info, or %NULL
447 * if the plugin doesn't provide such an extension.
448 *
449 * Returns: (transfer none): a reference to a #PeasExtension or %NULL
450 */
451 PeasExtension *
peas_extension_set_get_extension(PeasExtensionSet * set,PeasPluginInfo * info)452 peas_extension_set_get_extension (PeasExtensionSet *set,
453 PeasPluginInfo *info)
454 {
455 PeasExtensionSetPrivate *priv = GET_PRIV (set);
456 GList *l;
457
458 g_return_val_if_fail (PEAS_IS_EXTENSION_SET (set), NULL);
459 g_return_val_if_fail (info != NULL, NULL);
460
461 for (l = priv->extensions.head; l != NULL; l = l->next)
462 {
463 ExtensionItem *item = l->data;
464
465 if (item->info == info)
466 return item->exten;
467 }
468
469 return NULL;
470 }
471
472 /**
473 * peas_extension_set_call:
474 * @set: A #PeasExtensionSet.
475 * @method_name: the name of the method that should be called.
476 * @...: arguments for the method.
477 *
478 * Call a method on all the #PeasExtension instances contained in @set.
479 *
480 * See peas_extension_call() for more information.
481 *
482 * Deprecated: 1.2: Use peas_extension_set_foreach() instead.
483 *
484 * Return value: %TRUE on successful call.
485 */
486 gboolean
peas_extension_set_call(PeasExtensionSet * set,const gchar * method_name,...)487 peas_extension_set_call (PeasExtensionSet *set,
488 const gchar *method_name,
489 ...)
490 {
491 va_list args;
492 gboolean result;
493
494 g_return_val_if_fail (PEAS_IS_EXTENSION_SET (set), FALSE);
495 g_return_val_if_fail (method_name != NULL, FALSE);
496
497 va_start (args, method_name);
498 result = peas_extension_set_call_valist (set, method_name, args);
499 va_end (args);
500
501 return result;
502 }
503
504 /**
505 * peas_extension_set_call_valist:
506 * @set: A #PeasExtensionSet.
507 * @method_name: the name of the method that should be called.
508 * @va_args: the arguments for the method.
509 *
510 * Call a method on all the #PeasExtension instances contained in @set.
511 *
512 * See peas_extension_call_valist() for more information.
513 *
514 * Deprecated: 1.2: Use peas_extension_set_foreach() instead.
515 *
516 * Return value: %TRUE on successful call.
517 */
518 gboolean
peas_extension_set_call_valist(PeasExtensionSet * set,const gchar * method_name,va_list va_args)519 peas_extension_set_call_valist (PeasExtensionSet *set,
520 const gchar *method_name,
521 va_list va_args)
522 {
523 PeasExtensionSetPrivate *priv = GET_PRIV (set);
524 GICallableInfo *callable_info;
525 GIArgument *args;
526 gint n_args;
527
528 g_return_val_if_fail (PEAS_IS_EXTENSION_SET (set), FALSE);
529 g_return_val_if_fail (method_name != NULL, FALSE);
530
531 callable_info = peas_gi_get_method_info (priv->exten_type, method_name);
532
533 if (callable_info == NULL)
534 {
535 g_warning ("Method '%s.%s' was not found",
536 g_type_name (priv->exten_type), method_name);
537 return FALSE;
538 }
539
540 n_args = g_callable_info_get_n_args (callable_info);
541 g_return_val_if_fail (n_args >= 0, FALSE);
542
543 args = g_newa (GIArgument, n_args);
544 peas_gi_valist_to_arguments (callable_info, va_args, args, NULL);
545
546 g_base_info_unref ((GIBaseInfo *) callable_info);
547
548 return peas_extension_set_callv (set, method_name, args);
549 }
550
551 /**
552 * peas_extension_set_callv:
553 * @set: A #PeasExtensionSet.
554 * @method_name: the name of the method that should be called.
555 * @args: the arguments for the method.
556 *
557 * Call a method on all the #PeasExtension instances contained in @set.
558 *
559 * See peas_extension_callv() for more information.
560 *
561 * Return value: %TRUE on successful call.
562 *
563 * Deprecated: 1.2: Use peas_extension_set_foreach() instead.
564 */
565 gboolean
peas_extension_set_callv(PeasExtensionSet * set,const gchar * method_name,GIArgument * args)566 peas_extension_set_callv (PeasExtensionSet *set,
567 const gchar *method_name,
568 GIArgument *args)
569 {
570 PeasExtensionSetClass *klass;
571
572 g_return_val_if_fail (PEAS_IS_EXTENSION_SET (set), FALSE);
573 g_return_val_if_fail (method_name != NULL, FALSE);
574
575 klass = PEAS_EXTENSION_SET_GET_CLASS (set);
576 return klass->call (set, method_name, args);
577 }
578
579 /**
580 * peas_extension_set_foreach:
581 * @set: A #PeasExtensionSet.
582 * @func: (scope call): A function call for each extension.
583 * @data: Optional data to be passed to the function or %NULL.
584 *
585 * Calls @func for each #PeasExtension.
586 *
587 * Since: 1.2
588 */
589 void
peas_extension_set_foreach(PeasExtensionSet * set,PeasExtensionSetForeachFunc func,gpointer data)590 peas_extension_set_foreach (PeasExtensionSet *set,
591 PeasExtensionSetForeachFunc func,
592 gpointer data)
593 {
594 PeasExtensionSetPrivate *priv = GET_PRIV (set);
595 GList *l;
596
597 g_return_if_fail (PEAS_IS_EXTENSION_SET (set));
598 g_return_if_fail (func != NULL);
599
600 for (l = priv->extensions.head; l != NULL; l = l->next)
601 {
602 ExtensionItem *item = (ExtensionItem *) l->data;
603
604 func (set, item->info, item->exten, data);
605 }
606 }
607
608 /**
609 * peas_extension_set_newv: (skip)
610 * @engine: (allow-none): A #PeasEngine, or %NULL.
611 * @exten_type: the extension #GType.
612 * @n_parameters: the length of the @parameters array.
613 * @parameters: (array length=n_parameters): an array of #GParameter.
614 *
615 * Create a new #PeasExtensionSet for the @exten_type extension type.
616 *
617 * If @engine is %NULL, then the default engine will be used.
618 *
619 * Since libpeas 1.22, @exten_type can be an Abstract #GType
620 * and not just an Interface #GType.
621 *
622 * See peas_extension_set_new() for more information.
623 *
624 * Returns: (transfer full): a new instance of #PeasExtensionSet.
625 */
626 PeasExtensionSet *
peas_extension_set_newv(PeasEngine * engine,GType exten_type,guint n_parameters,GParameter * parameters)627 peas_extension_set_newv (PeasEngine *engine,
628 GType exten_type,
629 guint n_parameters,
630 GParameter *parameters)
631 {
632 PeasParameterArray construct_properties = { n_parameters, parameters };
633
634 g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
635 g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
636 G_TYPE_IS_ABSTRACT (exten_type), NULL);
637
638 return PEAS_EXTENSION_SET (g_object_new (PEAS_TYPE_EXTENSION_SET,
639 "engine", engine,
640 "extension-type", exten_type,
641 "construct-properties", &construct_properties,
642 NULL));
643 }
644
645 /**
646 * peas_extension_set_new_with_properties: (rename-to peas_extension_set_new)
647 * @engine: (allow-none): A #PeasEngine, or %NULL.
648 * @exten_type: the extension #GType.
649 * @n_properties: the length of the @prop_names and @prop_values array.
650 * @prop_names: (array length=n_properties): an array of property names.
651 * @prop_values: (array length=n_properties): an array of property values.
652 *
653 * Create a new #PeasExtensionSet for the @exten_type extension type.
654 *
655 * If @engine is %NULL, then the default engine will be used.
656 *
657 * Since libpeas 1.22, @exten_type can be an Abstract #GType
658 * and not just an Interface #GType.
659 *
660 * See peas_extension_set_new() for more information.
661 *
662 * Returns: (transfer full): a new instance of #PeasExtensionSet.
663 *
664 * Since 1.24.0
665 */
666 PeasExtensionSet *
peas_extension_set_new_with_properties(PeasEngine * engine,GType exten_type,guint n_properties,const gchar ** prop_names,const GValue * prop_values)667 peas_extension_set_new_with_properties (PeasEngine *engine,
668 GType exten_type,
669 guint n_properties,
670 const gchar **prop_names,
671 const GValue *prop_values)
672 {
673 PeasExtensionSet *ret;
674 PeasParameterArray construct_properties;
675 GParameter *parameters = NULL;
676
677 g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
678 g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
679 G_TYPE_IS_ABSTRACT (exten_type), NULL);
680 g_return_val_if_fail (n_properties == 0 || prop_names != NULL, NULL);
681 g_return_val_if_fail (n_properties == 0 || prop_values != NULL, NULL);
682
683 if (n_properties > 0)
684 {
685 parameters = g_new0 (GParameter, n_properties);
686 if (!peas_utils_properties_array_to_parameter_list (exten_type,
687 n_properties,
688 prop_names,
689 prop_values,
690 parameters))
691 {
692 /* Already warned */
693 g_free (parameters);
694 return NULL;
695 }
696 }
697
698 construct_properties.n_parameters = n_properties;
699 construct_properties.parameters = parameters;
700
701 ret = g_object_new (PEAS_TYPE_EXTENSION_SET,
702 "engine", engine,
703 "extension-type", exten_type,
704 "construct-properties", &construct_properties,
705 NULL);
706
707 g_free (parameters);
708 return ret;
709 }
710
711 /**
712 * peas_extension_set_new_valist: (skip)
713 * @engine: (allow-none): A #PeasEngine, or %NULL.
714 * @exten_type: the extension #GType.
715 * @first_property: the name of the first property.
716 * @var_args: the value of the first property, followed optionally by more
717 * name/value pairs, followed by %NULL.
718 *
719 * Create a new #PeasExtensionSet for the @exten_type extension type.
720 *
721 * If @engine is %NULL, then the default engine will be used.
722 *
723 * Since libpeas 1.22, @exten_type can be an Abstract #GType
724 * and not just an Interface #GType.
725 *
726 * See peas_extension_set_new() for more information.
727 *
728 * Returns: a new instance of #PeasExtensionSet.
729 */
730 PeasExtensionSet *
peas_extension_set_new_valist(PeasEngine * engine,GType exten_type,const gchar * first_property,va_list var_args)731 peas_extension_set_new_valist (PeasEngine *engine,
732 GType exten_type,
733 const gchar *first_property,
734 va_list var_args)
735 {
736 GParameter *parameters;
737 guint n_parameters;
738 PeasExtensionSet *set;
739
740 g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
741 g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
742 G_TYPE_IS_ABSTRACT (exten_type), NULL);
743
744 if (!peas_utils_valist_to_parameter_list (exten_type, first_property,
745 var_args, ¶meters,
746 &n_parameters))
747 {
748 /* Already warned */
749 return NULL;
750 }
751
752 set = peas_extension_set_newv (engine, exten_type, n_parameters, parameters);
753
754 while (n_parameters-- > 0)
755 g_value_unset (¶meters[n_parameters].value);
756 g_free (parameters);
757
758 return set;
759 }
760
761 /**
762 * peas_extension_set_new: (skip)
763 * @engine: (allow-none): A #PeasEngine, or %NULL.
764 * @exten_type: the extension #GType.
765 * @first_property: the name of the first property.
766 * @...: the value of the first property, followed optionally by more
767 * name/value pairs, followed by %NULL.
768 *
769 * Create a new #PeasExtensionSet for the @exten_type extension type.
770 *
771 * At any moment, the #PeasExtensionSet will contain an extension instance for
772 * each loaded plugin which implements the @exten_type extension type. It does
773 * so by connecting to the relevant signals from #PeasEngine.
774 *
775 * The property values passed to peas_extension_set_new() will be used for the
776 * construction of new extension instances.
777 *
778 * If @engine is %NULL, then the default engine will be used.
779 *
780 * Since libpeas 1.22, @exten_type can be an Abstract #GType
781 * and not just an Interface #GType.
782 *
783 * See peas_engine_create_extension() for more information.
784 *
785 * Returns: a new instance of #PeasExtensionSet.
786 */
787 PeasExtensionSet *
peas_extension_set_new(PeasEngine * engine,GType exten_type,const gchar * first_property,...)788 peas_extension_set_new (PeasEngine *engine,
789 GType exten_type,
790 const gchar *first_property,
791 ...)
792 {
793 va_list var_args;
794 PeasExtensionSet *set;
795
796 g_return_val_if_fail (engine == NULL || PEAS_IS_ENGINE (engine), NULL);
797 g_return_val_if_fail (G_TYPE_IS_INTERFACE (exten_type) ||
798 G_TYPE_IS_ABSTRACT (exten_type), NULL);
799
800 va_start (var_args, first_property);
801 set = peas_extension_set_new_valist (engine, exten_type, first_property, var_args);
802 va_end (var_args);
803
804 return set;
805 }
806