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, &parameters,
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 (&parameters[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