Move plugin init code
[gnumeric.git] / src / gnm-plugin.c
blobb78aa52a1e6e77c6132cda05676f32554918a8ff
1 /*
2 * go-plugin-service.c: Plugin services - reading XML info, activating, etc.
3 * (everything independent of plugin loading method)
5 * Author: Zbigniew Chyla (cyba@gnome.pl)
6 */
8 #include <gnumeric-config.h>
9 #include <gutils.h>
10 #include <tools/gnm-solver.h>
11 #include <func.h>
12 #include <gnm-plugin.h>
13 #include <gnumeric-conf.h>
14 #include <application.h>
16 #include <goffice/goffice.h>
17 #include <gsf/gsf-impl-utils.h>
18 #include <gsf/gsf-input-stdio.h>
19 #include <gsf/gsf-input-memory.h>
20 #include <glib/gi18n-lib.h>
21 #include <string.h>
23 #define CXML2C(s) ((char const *)(s))
24 #define CC2XML(s) ((xmlChar const *)(s))
26 static char *
27 xml2c (xmlChar *src)
29 char *dst = g_strdup (CXML2C (src));
30 xmlFree (src);
31 return dst;
34 typedef GOPluginServiceSimpleClass GnmPluginServiceFunctionGroupClass;
35 struct GnmPluginServiceFunctionGroup_ {
36 GOPluginServiceSimple base;
38 gchar *category_name, *translated_category_name;
39 GSList *function_name_list;
41 GnmFuncGroup *func_group;
42 GnmPluginServiceFunctionGroupCallbacks cbs;
43 char *tdomain;
46 static void
47 plugin_service_function_group_finalize (GObject *obj)
49 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (obj);
50 GObjectClass *parent_class;
52 g_free (sfg->category_name);
53 sfg->category_name = NULL;
55 g_free (sfg->translated_category_name);
56 sfg->translated_category_name = NULL;
58 g_slist_free_full (sfg->function_name_list, g_free);
59 sfg->function_name_list = NULL;
61 g_free (sfg->tdomain);
62 sfg->tdomain = NULL;
64 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
65 parent_class->finalize (obj);
68 static void
69 plugin_service_function_group_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
71 xmlNode *category_node, *translated_category_node, *functions_node;
72 gchar *category_name, *translated_category_name;
73 GSList *function_name_list = NULL;
74 gchar *tdomain = NULL;
76 GO_INIT_RET_ERROR_INFO (ret_error);
77 category_node = go_xml_get_child_by_name_no_lang (tree, "category");
78 category_name = category_node
79 ? xml2c (xmlNodeGetContent (category_node))
80 : NULL;
82 translated_category_node = go_xml_get_child_by_name_by_lang (tree, "category");
83 if (translated_category_node != NULL) {
84 xmlChar *lang;
86 lang = go_xml_node_get_cstr (translated_category_node, "lang");
87 if (lang != NULL) {
88 translated_category_name =
89 xml2c (xmlNodeGetContent (translated_category_node));
90 xmlFree (lang);
91 } else {
92 translated_category_name = NULL;
94 } else {
95 translated_category_name = NULL;
97 functions_node = go_xml_get_child_by_name (tree, CC2XML ("functions"));
98 if (functions_node != NULL) {
99 xmlNode *node;
101 tdomain = xml2c (go_xml_node_get_cstr (functions_node, "textdomain"));
103 for (node = functions_node->xmlChildrenNode; node != NULL; node = node->next) {
104 gchar *func_name;
106 if (strcmp (CXML2C (node->name), "function") != 0)
107 continue;
109 func_name = xml2c (go_xml_node_get_cstr (node, "name"));
110 if (!func_name)
111 continue;
113 GO_SLIST_PREPEND (function_name_list, func_name);
115 GO_SLIST_REVERSE (function_name_list);
117 if (category_name != NULL && function_name_list != NULL) {
118 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
120 sfg->category_name = category_name;
121 sfg->translated_category_name = translated_category_name;
122 sfg->function_name_list = function_name_list;
123 sfg->tdomain = tdomain;
124 } else {
125 GSList *error_list = NULL;
127 if (category_name == NULL) {
128 GO_SLIST_PREPEND (error_list, go_error_info_new_str (
129 _("Missing function category name.")));
131 if (function_name_list == NULL) {
132 GO_SLIST_PREPEND (error_list, go_error_info_new_str (
133 _("Function group is empty.")));
135 GO_SLIST_REVERSE (error_list);
136 *ret_error = go_error_info_new_from_error_list (error_list);
138 g_free (category_name);
139 g_free (translated_category_name);
140 g_slist_free_full (function_name_list, g_free);
142 g_free (tdomain);
146 static gboolean
147 plugin_service_function_group_func_desc_load (GnmFunc const *fn_def,
148 GnmFuncDescriptor *res)
150 GOPluginService *service = gnm_func_get_user_data (fn_def);
151 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
152 GOErrorInfo *error = NULL;
154 g_return_val_if_fail (fn_def != NULL, FALSE);
156 go_plugin_service_load (service, &error);
157 if (error != NULL) {
158 go_error_info_print (error);
159 go_error_info_free (error);
160 return FALSE;
162 if (NULL == sfg->cbs.func_desc_load) {
163 error = go_error_info_new_printf (_("No func_desc_load method.\n"));
164 go_error_info_print (error);
165 go_error_info_free (error);
166 return FALSE;
168 return sfg->cbs.func_desc_load (service,
169 gnm_func_get_name (fn_def, FALSE),
170 res);
173 static void
174 plugin_service_function_group_func_ref_notify (GnmFunc *fn_def, int refcount)
176 GOPluginService *service;
178 service = gnm_func_get_user_data (fn_def);
179 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
180 if (refcount == 0) {
181 go_plugin_use_unref (service->plugin);
182 } else {
183 go_plugin_use_ref (service->plugin);
187 static void
188 delayed_ref_notify (GOPlugin *plugin, GnmFunc *fd)
190 g_signal_handlers_disconnect_by_func (plugin,
191 G_CALLBACK (delayed_ref_notify),
192 fd);
194 /* We cannot do this until after the plugin has been activated. */
195 plugin_service_function_group_func_ref_notify (fd, 1);
198 static void
199 plugin_service_function_group_activate (GOPluginService *service, GOErrorInfo **ret_error)
201 GnmPluginServiceFunctionGroup *sfg =
202 GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
204 GO_INIT_RET_ERROR_INFO (ret_error);
205 sfg->func_group = gnm_func_group_fetch (sfg->category_name,
206 sfg->translated_category_name);
207 if (gnm_debug_flag ("plugin-func"))
208 g_printerr ("Activating group %s\n", sfg->category_name);
209 GO_SLIST_FOREACH
210 (sfg->function_name_list, char, fname,
211 GnmFunc *fd;
213 fd = gnm_func_lookup (fname, NULL);
214 if (fd) {
215 #if 0
216 g_printerr ("Reusing placeholder for %s\n", fname);
217 #endif
218 } else {
219 fd = gnm_func_add_placeholder (NULL, fname, "?");
221 if (fd->flags & GNM_FUNC_IS_PLACEHOLDER) {
222 gnm_func_set_user_data (fd, service);
223 gnm_func_upgrade_placeholder
224 (fd, sfg->func_group,
225 sfg->tdomain,
226 plugin_service_function_group_func_desc_load,
227 plugin_service_function_group_func_ref_notify);
228 if (fd->usage_count > 0)
229 g_signal_connect (go_plugin_service_get_plugin (service),
230 "state_changed",
231 G_CALLBACK (delayed_ref_notify),
232 fd);
233 } else {
234 g_warning ("Multiple definitions of function %s -- this cannot be good!", fname);
237 service->is_active = TRUE;
240 static void
241 plugin_service_function_group_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
243 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
245 if (gnm_debug_flag ("plugin-func"))
246 g_printerr ("Deactivating group %s\n", sfg->category_name);
248 GO_INIT_RET_ERROR_INFO (ret_error);
249 GO_SLIST_FOREACH (sfg->function_name_list, char, fname,
250 gnm_func_free (gnm_func_lookup (fname, NULL));
252 service->is_active = FALSE;
255 static char *
256 plugin_service_function_group_get_description (GOPluginService *service)
258 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
259 int n_functions;
260 char const *category_name;
262 n_functions = g_slist_length (sfg->function_name_list);
263 category_name = sfg->translated_category_name != NULL
264 ? sfg->translated_category_name
265 : sfg->category_name;
267 return g_strdup_printf (ngettext (
268 "%d function in category \"%s\"",
269 "Group of %d functions in category \"%s\"",
270 n_functions),
271 n_functions, category_name);
274 static void
275 plugin_service_function_group_init (GnmPluginServiceFunctionGroup *s)
277 GO_PLUGIN_SERVICE (s)->cbs_ptr = &s->cbs;
278 s->category_name = NULL;
279 s->translated_category_name = NULL;
280 s->function_name_list = NULL;
281 s->func_group = NULL;
282 s->tdomain = NULL;
285 static void
286 plugin_service_function_group_class_init (GObjectClass *gobject_class)
288 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
290 gobject_class->finalize = plugin_service_function_group_finalize;
291 plugin_service_class->read_xml = plugin_service_function_group_read_xml;
292 plugin_service_class->activate = plugin_service_function_group_activate;
293 plugin_service_class->deactivate = plugin_service_function_group_deactivate;
294 plugin_service_class->get_description = plugin_service_function_group_get_description;
297 GSF_CLASS (GnmPluginServiceFunctionGroup, gnm_plugin_service_function_group,
298 plugin_service_function_group_class_init, plugin_service_function_group_init,
299 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
301 /****************************************************************************/
304 * PluginServiceUI
306 typedef GOPluginServiceSimpleClass PluginServiceUIClass;
307 struct GnmPluginServiceUI_ {
308 GOPluginServiceSimple base;
310 char *file_name;
311 GSList *actions;
313 gpointer layout_id;
314 GnmPluginServiceUICallbacks cbs;
317 static void
318 plugin_service_ui_init (PluginServiceUI *s)
320 GO_PLUGIN_SERVICE (s)->cbs_ptr = &s->cbs;
321 s->file_name = NULL;
322 s->actions = NULL;
323 s->layout_id = NULL;
324 s->cbs.plugin_func_exec_action = NULL;
327 static void
328 plugin_service_ui_finalize (GObject *obj)
330 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (obj);
331 GObjectClass *parent_class;
333 g_free (service_ui->file_name);
334 service_ui->file_name = NULL;
335 g_slist_free_full (service_ui->actions, (GDestroyNotify)gnm_action_free);
336 service_ui->actions = NULL;
338 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
339 parent_class->finalize (obj);
342 static void
343 cb_ui_service_activate (GnmAction const *action, WorkbookControl *wbc, GOPluginService *service)
345 GOErrorInfo *load_error = NULL;
347 go_plugin_service_load (service, &load_error);
348 if (load_error == NULL) {
349 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
350 GOErrorInfo *ignored_error = NULL;
352 g_return_if_fail (service_ui->cbs.plugin_func_exec_action != NULL);
353 service_ui->cbs.plugin_func_exec_action (
354 service, action, wbc, &ignored_error);
355 if (ignored_error != NULL) {
356 go_error_info_print (ignored_error);
357 go_error_info_free (ignored_error);
359 } else {
360 go_error_info_print (load_error);
361 go_error_info_free (load_error);
365 static void
366 plugin_service_ui_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
368 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
369 xmlChar *file_name;
370 xmlNode *verbs_node;
371 GSList *actions = NULL;
373 GO_INIT_RET_ERROR_INFO (ret_error);
374 file_name = xml2c (go_xml_node_get_cstr (tree, "file"));
375 if (file_name == NULL) {
376 *ret_error = go_error_info_new_str (
377 _("Missing file name."));
378 return;
380 verbs_node = go_xml_get_child_by_name (tree, "actions");
381 if (verbs_node != NULL) {
382 xmlNode *ptr, *label_node;
383 xmlChar *name, *icon;
384 gchar *label;
385 gboolean always_available;
386 GnmAction *action;
388 for (ptr = verbs_node->xmlChildrenNode; ptr != NULL; ptr = ptr->next) {
389 if (xmlIsBlankNode (ptr) || ptr->name == NULL ||
390 strcmp (CXML2C (ptr->name), "action"))
391 continue;
392 name = go_xml_node_get_cstr (ptr, "name");
393 /* label = go_xml_node_get_cstr (ptr, "label");*/
394 /*****************************************************************************************/
395 label_node = go_xml_get_child_by_name_no_lang (ptr, "label");
396 label = label_node
397 ? xml2c (xmlNodeGetContent (label_node))
398 : NULL;
400 label_node = go_xml_get_child_by_name_by_lang (ptr, "label");
401 if (label_node != NULL) {
402 gchar *lang;
404 lang = go_xml_node_get_cstr (label_node, "lang");
405 if (lang != NULL) {
406 label = xml2c (xmlNodeGetContent (label_node));
407 xmlFree (lang);
410 /*****************************************************************************************/
411 icon = go_xml_node_get_cstr (ptr, "icon");
412 if (!go_xml_node_get_bool (ptr, "always_available", &always_available))
413 always_available = FALSE;
414 action = gnm_action_new (name, label, icon, always_available,
415 (GnmActionHandler) cb_ui_service_activate);
416 if (NULL != name) xmlFree (name);
417 g_free (label);
418 if (NULL != icon) xmlFree (icon);
419 if (NULL != action)
420 GO_SLIST_PREPEND (actions, action);
423 GO_SLIST_REVERSE (actions);
425 service_ui->file_name = file_name;
426 service_ui->actions = actions;
429 static void
430 plugin_service_ui_activate (GOPluginService *service, GOErrorInfo **ret_error)
432 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
433 const char *uifile = service_ui->file_name;
434 char *xml_ui, *group_name;
435 char const *tdomain;
436 GError *error = NULL;
437 GsfInput *src;
438 size_t len;
440 GO_INIT_RET_ERROR_INFO (ret_error);
442 if (strncmp (uifile, "res:", 4) == 0) {
443 size_t len;
444 gconstpointer data = go_rsm_lookup (uifile + 4, &len);
445 src = data
446 ? gsf_input_memory_new (data, len, FALSE)
447 : NULL;
448 } else if (strncmp (uifile, "data:", 5) == 0) {
449 const char *data = uifile + 5;
450 src = gsf_input_memory_new (data, strlen (data), FALSE);
451 } else {
452 char *full_file_name = g_path_is_absolute (uifile)
453 ? g_strdup (uifile)
454 : g_build_filename
455 (go_plugin_get_dir_name (service->plugin),
456 uifile,
457 NULL);
458 src = gsf_input_stdio_new (full_file_name, &error);
459 g_free (full_file_name);
461 if (!src)
462 goto err;
464 src = gsf_input_uncompress (src);
465 len = gsf_input_size (src);
466 xml_ui = g_strndup (gsf_input_read (src, len, NULL), len);
467 if (!xml_ui)
468 goto err;
470 tdomain = go_plugin_get_textdomain (service->plugin);
471 group_name = g_strconcat (go_plugin_get_id (service->plugin), service->id, NULL);
472 service_ui->layout_id = gnm_app_add_extra_ui (group_name,
473 service_ui->actions,
474 xml_ui, tdomain, service);
475 g_free (group_name);
476 g_free (xml_ui);
477 g_object_unref (src);
478 service->is_active = TRUE;
479 return;
481 err:
482 *ret_error = go_error_info_new_printf
483 (_("Cannot read UI description from %s: %s"),
484 uifile,
485 error ? error->message : "?");
486 g_clear_error (&error);
487 if (src)
488 g_object_unref (src);
491 static void
492 plugin_service_ui_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
494 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
496 GO_INIT_RET_ERROR_INFO (ret_error);
497 gnm_app_remove_extra_ui (service_ui->layout_id);
498 service_ui->layout_id = NULL;
499 service->is_active = FALSE;
502 static char *
503 plugin_service_ui_get_description (GOPluginService *service)
505 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
506 int n_actions;
508 n_actions = g_slist_length (service_ui->actions);
509 return g_strdup_printf (
510 ngettext (
511 /* xgettext : %d gives the number of actions. This is input to ngettext. */
512 "User interface with %d action",
513 "User interface with %d actions",
514 n_actions),
515 n_actions);
518 static void
519 plugin_service_ui_class_init (GObjectClass *gobject_class)
521 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
523 gobject_class->finalize = plugin_service_ui_finalize;
524 plugin_service_class->read_xml = plugin_service_ui_read_xml;
525 plugin_service_class->activate = plugin_service_ui_activate;
526 plugin_service_class->deactivate = plugin_service_ui_deactivate;
527 plugin_service_class->get_description = plugin_service_ui_get_description;
530 GSF_CLASS (PluginServiceUI, gnm_plugin_service_ui,
531 plugin_service_ui_class_init, plugin_service_ui_init,
532 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
534 /****************************************************************************/
537 * PluginServiceSolver
539 typedef GOPluginServiceClass PluginServiceSolverClass;
540 struct GnmPluginServiceSolver_ {
541 GOPluginService base;
543 GnmSolverFactory *factory;
545 GnmPluginServiceSolverCallbacks cbs;
548 static GnmSolver *
549 cb_load_and_create (GnmSolverFactory *factory, GnmSolverParameters *param,
550 gpointer data)
552 PluginServiceSolver *ssol =
553 g_object_get_data (G_OBJECT (factory), "ssol");
554 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
555 GOErrorInfo *ignored_error = NULL;
556 GnmSolver *res;
558 go_plugin_service_load (service, &ignored_error);
559 if (ignored_error != NULL) {
560 go_error_info_print (ignored_error);
561 go_error_info_free (ignored_error);
562 return NULL;
565 res = ssol->cbs.creator (factory, param, data);
566 if (res) {
567 go_plugin_use_ref (service->plugin);
568 g_object_set_data_full (G_OBJECT (res),
569 "plugin-use", service->plugin,
570 (GDestroyNotify)go_plugin_use_unref);
573 return res;
576 static gboolean
577 cb_load_and_functional (GnmSolverFactory *factory,
578 WBCGtk *wbcg,
579 gpointer data)
581 PluginServiceSolver *ssol =
582 g_object_get_data (G_OBJECT (factory), "ssol");
583 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
584 GOErrorInfo *ignored_error = NULL;
585 GnmSolverFactoryFunctional functional;
587 go_plugin_service_load (service, &ignored_error);
588 if (ignored_error != NULL) {
589 go_error_info_print (ignored_error);
590 go_error_info_free (ignored_error);
591 return FALSE;
594 functional = ssol->cbs.functional;
595 return (functional == NULL || functional (factory, wbcg, data));
598 static void
599 plugin_service_solver_init (PluginServiceSolver *ssol)
601 GO_PLUGIN_SERVICE (ssol)->cbs_ptr = &ssol->cbs;
602 ssol->factory = NULL;
603 ssol->cbs.creator = NULL;
606 static void
607 plugin_service_solver_finalize (GObject *obj)
609 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (obj);
610 GObjectClass *parent_class;
612 if (ssol->factory)
613 g_object_unref (ssol->factory);
615 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
616 parent_class->finalize (obj);
619 static void
620 plugin_service_solver_read_xml (GOPluginService *service, xmlNode *tree,
621 GOErrorInfo **ret_error)
623 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
624 xmlChar *s_id, *s_name, *s_type;
625 GnmSolverModelType type = GNM_SOLVER_LP;
626 xmlNode *information_node;
628 GO_INIT_RET_ERROR_INFO (ret_error);
630 s_type = go_xml_node_get_cstr (tree, "model_type");
631 if (s_type && strcmp (CXML2C (s_type), "mip") == 0)
632 type = GNM_SOLVER_LP;
633 else if (s_type && strcmp (CXML2C (s_type), "qp") == 0)
634 type = GNM_SOLVER_QP;
635 else if (s_type && strcmp (CXML2C (s_type), "nlp") == 0)
636 type = GNM_SOLVER_NLP;
637 else {
638 *ret_error = go_error_info_new_str (_("Invalid solver model type."));
639 return;
641 xmlFree (s_type);
643 s_id = go_xml_node_get_cstr (tree, "id");
645 s_name = NULL;
646 information_node = go_xml_get_child_by_name (tree, "information");
647 if (information_node != NULL) {
648 xmlNode *node =
649 go_xml_get_child_by_name_by_lang (information_node,
650 "description");
651 if (node != NULL) {
652 s_name = xmlNodeGetContent (node);
656 if (!s_id || !s_name) {
657 *ret_error = go_error_info_new_str (_("Missing fields in plugin file"));
658 } else {
659 ssol->factory = gnm_solver_factory_new (CXML2C (s_id),
660 CXML2C (s_name),
661 type,
662 cb_load_and_create,
663 cb_load_and_functional,
664 NULL,
665 NULL);
666 g_object_set_data (G_OBJECT (ssol->factory), "ssol", ssol);
668 xmlFree (s_id);
669 xmlFree (s_name);
670 if (*ret_error)
671 return;
673 /* More? */
676 static void
677 plugin_service_solver_activate (GOPluginService *service, GOErrorInfo **ret_error)
679 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
681 GO_INIT_RET_ERROR_INFO (ret_error);
682 gnm_solver_db_register (ssol->factory);
683 service->is_active = TRUE;
686 static void
687 plugin_service_solver_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
689 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
691 GO_INIT_RET_ERROR_INFO (ret_error);
692 gnm_solver_db_unregister (ssol->factory);
693 service->is_active = FALSE;
696 static char *
697 plugin_service_solver_get_description (GOPluginService *service)
699 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
700 return g_strdup_printf (_("Solver Algorithm %s"),
701 ssol->factory->name);
704 static void
705 plugin_service_solver_class_init (GObjectClass *gobject_class)
707 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
709 gobject_class->finalize = plugin_service_solver_finalize;
710 plugin_service_class->read_xml = plugin_service_solver_read_xml;
711 plugin_service_class->activate = plugin_service_solver_activate;
712 plugin_service_class->deactivate = plugin_service_solver_deactivate;
713 plugin_service_class->get_description = plugin_service_solver_get_description;
716 GSF_CLASS (PluginServiceSolver, gnm_plugin_service_solver,
717 plugin_service_solver_class_init, plugin_service_solver_init,
718 GO_TYPE_PLUGIN_SERVICE)
720 /****************************************************************************/
723 typedef GOPluginLoaderModule GnmPluginLoaderModule;
724 typedef GOPluginLoaderModuleClass GnmPluginLoaderModuleClass;
727 * Service - function_group
729 typedef struct {
730 GnmFuncDescriptor *module_fn_info_array;
731 GHashTable *function_indices;
732 } ServiceLoaderDataFunctionGroup;
734 static void
735 function_group_loader_data_free (gpointer data)
737 ServiceLoaderDataFunctionGroup *ld = data;
739 g_hash_table_destroy (ld->function_indices);
740 g_free (ld);
743 static gboolean
744 gnm_plugin_loader_module_func_desc_load (GOPluginService *service,
745 char const *name,
746 GnmFuncDescriptor *res)
748 ServiceLoaderDataFunctionGroup *loader_data;
749 gpointer func_index_ptr;
751 g_return_val_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service), FALSE);
752 g_return_val_if_fail (name != NULL, FALSE);
754 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
755 if (g_hash_table_lookup_extended (loader_data->function_indices, (gpointer) name,
756 NULL, &func_index_ptr)) {
757 int i = GPOINTER_TO_INT (func_index_ptr);
758 *res = loader_data->module_fn_info_array[i];
759 return TRUE;
761 return FALSE;
763 static void
764 gnm_plugin_loader_module_load_service_function_group (GOPluginLoader *loader,
765 GOPluginService *service,
766 GOErrorInfo **ret_error)
768 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
769 gchar *fn_info_array_name;
770 GnmFuncDescriptor *module_fn_info_array = NULL;
772 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
774 GO_INIT_RET_ERROR_INFO (ret_error);
775 fn_info_array_name = g_strconcat (
776 go_plugin_service_get_id (service), "_functions", NULL);
777 g_module_symbol (loader_module->handle, fn_info_array_name, (gpointer) &module_fn_info_array);
778 if (module_fn_info_array != NULL) {
779 GnmPluginServiceFunctionGroupCallbacks *cbs;
780 ServiceLoaderDataFunctionGroup *loader_data;
781 gint i;
783 cbs = go_plugin_service_get_cbs (service);
784 cbs->func_desc_load = &gnm_plugin_loader_module_func_desc_load;
786 loader_data = g_new (ServiceLoaderDataFunctionGroup, 1);
787 loader_data->module_fn_info_array = module_fn_info_array;
788 loader_data->function_indices = g_hash_table_new (&g_str_hash, &g_str_equal);
789 for (i = 0; module_fn_info_array[i].name != NULL; i++) {
790 g_hash_table_insert (loader_data->function_indices,
791 (gpointer) module_fn_info_array[i].name,
792 GINT_TO_POINTER (i));
794 g_object_set_data_full (
795 G_OBJECT (service), "loader_data", loader_data, function_group_loader_data_free);
796 } else {
797 *ret_error = go_error_info_new_printf (
798 _("Module file \"%s\" has invalid format."),
799 loader_module->module_file_name);
800 go_error_info_add_details (*ret_error,
801 go_error_info_new_printf (
802 _("File doesn't contain \"%s\" array."),
803 fn_info_array_name));
805 g_free (fn_info_array_name);
809 * Service - ui
812 typedef struct {
813 GnmModulePluginUIActions *module_ui_actions_array;
814 GHashTable *ui_actions_hash;
815 } ServiceLoaderDataUI;
817 static void
818 ui_loader_data_free (gpointer data)
820 ServiceLoaderDataUI *ld = data;
822 g_hash_table_destroy (ld->ui_actions_hash);
823 g_free (ld);
826 static void
827 gnm_plugin_loader_module_func_exec_action (GOPluginService *service,
828 GnmAction const *action,
829 WorkbookControl *wbc,
830 GOErrorInfo **ret_error)
832 ServiceLoaderDataUI *loader_data;
833 gpointer action_index_ptr;
834 int action_index;
836 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
838 GO_INIT_RET_ERROR_INFO (ret_error);
839 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
840 if (!g_hash_table_lookup_extended (loader_data->ui_actions_hash, action->id,
841 NULL, &action_index_ptr)) {
842 *ret_error = go_error_info_new_printf (_("Unknown action: %s"), action->id);
843 return;
845 action_index = GPOINTER_TO_INT (action_index_ptr);
846 if (NULL != loader_data->module_ui_actions_array [action_index].handler)
847 (*loader_data->module_ui_actions_array [action_index].handler) (action, wbc);
850 static void
851 gnm_plugin_loader_module_load_service_ui (GOPluginLoader *loader,
852 GOPluginService *service,
853 GOErrorInfo **ret_error)
855 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
856 char *ui_actions_array_name;
857 GnmModulePluginUIActions *module_ui_actions_array = NULL;
858 GnmPluginServiceUICallbacks *cbs;
859 ServiceLoaderDataUI *loader_data;
860 gint i;
862 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
864 GO_INIT_RET_ERROR_INFO (ret_error);
865 ui_actions_array_name = g_strconcat (
866 go_plugin_service_get_id (service), "_ui_actions", NULL);
867 g_module_symbol (loader_module->handle, ui_actions_array_name, (gpointer) &module_ui_actions_array);
868 if (module_ui_actions_array == NULL) {
869 *ret_error = go_error_info_new_printf (
870 _("Module file \"%s\" has invalid format."),
871 loader_module->module_file_name);
872 go_error_info_add_details (*ret_error, go_error_info_new_printf (
873 _("File doesn't contain \"%s\" array."), ui_actions_array_name));
874 g_free (ui_actions_array_name);
875 return;
877 g_free (ui_actions_array_name);
879 cbs = go_plugin_service_get_cbs (service);
880 cbs->plugin_func_exec_action = gnm_plugin_loader_module_func_exec_action;
882 loader_data = g_new (ServiceLoaderDataUI, 1);
883 loader_data->module_ui_actions_array = module_ui_actions_array;
884 loader_data->ui_actions_hash = g_hash_table_new (g_str_hash, g_str_equal);
885 for (i = 0; module_ui_actions_array[i].name != NULL; i++)
886 g_hash_table_insert (loader_data->ui_actions_hash,
887 (gpointer) module_ui_actions_array[i].name,
888 GINT_TO_POINTER (i));
889 g_object_set_data_full (G_OBJECT (service),
890 "loader_data", loader_data, ui_loader_data_free);
893 static void
894 gnm_plugin_loader_module_load_service_solver (GOPluginLoader *loader,
895 GOPluginService *service,
896 GOErrorInfo **ret_error)
898 GnmPluginLoaderModule *loader_module =
899 GNM_PLUGIN_LOADER_MODULE (loader);
900 GnmPluginServiceSolverCallbacks *cbs;
901 char *symname;
902 GnmSolverCreator creator;
903 GnmSolverFactoryFunctional functional;
905 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_SOLVER (service));
907 GO_INIT_RET_ERROR_INFO (ret_error);
909 symname = g_strconcat (go_plugin_service_get_id (service),
910 "_solver_factory",
911 NULL);
912 g_module_symbol (loader_module->handle, symname, (gpointer)&creator);
913 g_free (symname);
914 if (!creator) {
915 *ret_error = go_error_info_new_printf (
916 _("Module file \"%s\" has invalid format."),
917 loader_module->module_file_name);
918 return;
921 symname = g_strconcat (go_plugin_service_get_id (service),
922 "_solver_factory_functional",
923 NULL);
924 g_module_symbol (loader_module->handle, symname, (gpointer)&functional);
925 g_free (symname);
927 cbs = go_plugin_service_get_cbs (service);
928 cbs->creator = creator;
929 cbs->functional = functional;
932 static gboolean
933 gplm_service_load (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
935 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s))
936 gnm_plugin_loader_module_load_service_function_group (l, s, err);
937 else if (GNM_IS_PLUGIN_SERVICE_UI (s))
938 gnm_plugin_loader_module_load_service_ui (l, s, err);
939 else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s))
940 gnm_plugin_loader_module_load_service_solver (l, s, err);
941 else
942 return FALSE;
943 return TRUE;
946 static gboolean
947 gplm_service_unload (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
949 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s)) {
950 GnmPluginServiceFunctionGroupCallbacks *cbs = go_plugin_service_get_cbs (s);
951 cbs->func_desc_load = NULL;
952 } else if (GNM_IS_PLUGIN_SERVICE_UI (s)) {
953 GnmPluginServiceUICallbacks *cbs = go_plugin_service_get_cbs (s);
954 cbs->plugin_func_exec_action = NULL;
955 } else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s)) {
956 GnmPluginServiceSolverCallbacks *cbs =
957 go_plugin_service_get_cbs (s);
958 cbs->creator = NULL;
959 cbs->functional = NULL;
960 } else
961 return FALSE;
962 return TRUE;
965 static void
966 go_plugin_loader_module_iface_init (GOPluginLoaderClass *iface)
968 iface->service_load = gplm_service_load;
969 iface->service_unload = gplm_service_unload;
972 GSF_CLASS_FULL (GnmPluginLoaderModule, gnm_plugin_loader_module,
973 NULL, NULL, NULL, NULL,
974 NULL, GO_TYPE_PLUGIN_LOADER_MODULE, 0,
975 GSF_INTERFACE (go_plugin_loader_module_iface_init, GO_TYPE_PLUGIN_LOADER))
977 /****************************************************************************/
980 * gnm_plugins_service_init: (skip)
982 void
983 gnm_plugins_service_init (void)
985 go_plugin_service_define ("function_group",
986 &gnm_plugin_service_function_group_get_type);
987 go_plugin_service_define ("ui",
988 &gnm_plugin_service_ui_get_type);
989 go_plugin_service_define ("solver",
990 &gnm_plugin_service_solver_get_type);
991 go_plugin_loader_module_register_version ("gnumeric", GNM_VERSION_FULL);
995 void
996 gnm_plugins_init (GOCmdContext *context)
998 char const *env_var;
999 GSList *dir_list = go_slist_create (
1000 g_build_filename (gnm_sys_lib_dir (), PLUGIN_SUBDIR, NULL),
1001 g_strdup (gnm_sys_extern_plugin_dir ()),
1002 (gnm_usr_dir (TRUE) == NULL ? NULL :
1003 g_build_filename (gnm_usr_dir (TRUE), PLUGIN_SUBDIR, NULL)),
1004 NULL);
1005 dir_list = g_slist_concat (dir_list,
1006 go_string_slist_copy (gnm_conf_get_plugins_extra_dirs ()));
1008 env_var = g_getenv ("GNUMERIC_PLUGIN_PATH");
1009 if (env_var != NULL)
1010 GO_SLIST_CONCAT (dir_list, go_strsplit_to_slist (env_var, G_SEARCHPATH_SEPARATOR));
1012 go_plugins_init (GO_CMD_CONTEXT (context),
1013 gnm_conf_get_plugins_file_states (),
1014 gnm_conf_get_plugins_active (),
1015 dir_list,
1016 gnm_conf_get_plugins_activate_newplugins (),
1017 gnm_plugin_loader_module_get_type ());