GnmFunc: make this a GObject.
[gnumeric.git] / src / gnm-plugin.c
blobfac15210edafcf4a4c7315864c8151a5107d584b
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 void
147 plugin_service_function_group_func_ref_notify (GnmFunc *fn_def,
148 G_GNUC_UNUSED GParamSpec *pspec,
149 GOPlugin *plugin)
151 if (gnm_func_get_in_use (fn_def))
152 go_plugin_use_ref (plugin);
153 else
154 go_plugin_use_unref (plugin);
157 static void
158 plugin_service_function_group_func_load_stub (GnmFunc *fn_def,
159 GOPluginService *service)
161 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
162 GOErrorInfo *error = NULL;
164 g_return_if_fail (fn_def != NULL);
166 go_plugin_service_load (service, &error);
167 if (error != NULL) {
168 go_error_info_print (error);
169 go_error_info_free (error);
170 return;
173 if (!sfg->cbs.load_stub) {
174 error = go_error_info_new_printf (_("No load_stub method.\n"));
175 go_error_info_print (error);
176 go_error_info_free (error);
177 return;
180 sfg->cbs.load_stub (service, fn_def);
183 static void
184 delayed_ref_notify (GOPlugin *plugin, GnmFunc *fd)
186 g_signal_handlers_disconnect_by_func (plugin,
187 G_CALLBACK (delayed_ref_notify),
188 fd);
190 /* We cannot do this until after the plugin has been activated. */
191 plugin_service_function_group_func_ref_notify (fd, NULL, plugin);
194 static void
195 plugin_service_function_group_activate (GOPluginService *service, GOErrorInfo **ret_error)
197 GnmPluginServiceFunctionGroup *sfg =
198 GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
199 GOPlugin *plugin = go_plugin_service_get_plugin (service);
200 GSList *l;
202 GO_INIT_RET_ERROR_INFO (ret_error);
203 sfg->func_group = gnm_func_group_fetch (sfg->category_name,
204 sfg->translated_category_name);
205 if (gnm_debug_flag ("plugin-func"))
206 g_printerr ("Activating group %s\n", sfg->category_name);
208 for (l = sfg->function_name_list; l; l = l->next) {
209 const char *fname = l->data;
210 GnmFunc *fd = gnm_func_lookup_or_add_placeholder (fname);
212 gnm_func_set_function_type (fd, GNM_FUNC_TYPE_STUB);
213 gnm_func_set_translation_domain (fd, sfg->tdomain);
214 gnm_func_set_function_group (fd, sfg->func_group);
215 // Clear localized_name so we can deduce the proper name.
216 //gnm_func_set_localized_name (fd, NULL);
218 g_signal_connect
219 (G_OBJECT (fd), "notify::in-use",
220 G_CALLBACK (plugin_service_function_group_func_ref_notify),
221 plugin);
223 g_signal_connect
224 (G_OBJECT (fd), "load-stub",
225 G_CALLBACK (plugin_service_function_group_func_load_stub),
226 service);
228 if (fd->usage_count > 0)
229 g_signal_connect (plugin,
230 "state_changed",
231 G_CALLBACK (delayed_ref_notify),
232 fd);
234 service->is_active = TRUE;
237 static void
238 plugin_service_function_group_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
240 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
241 GSList *l;
243 if (gnm_debug_flag ("plugin-func"))
244 g_printerr ("Deactivating group %s\n", sfg->category_name);
246 GO_INIT_RET_ERROR_INFO (ret_error);
248 for (l = sfg->function_name_list; l; l = l->next) {
249 const char *fname = l->data;
250 GnmFunc *func = gnm_func_lookup (fname, NULL);
251 g_object_unref (func);
253 service->is_active = FALSE;
256 static char *
257 plugin_service_function_group_get_description (GOPluginService *service)
259 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
260 int n_functions;
261 char const *category_name;
263 n_functions = g_slist_length (sfg->function_name_list);
264 category_name = sfg->translated_category_name != NULL
265 ? sfg->translated_category_name
266 : sfg->category_name;
268 return g_strdup_printf (ngettext (
269 "%d function in category \"%s\"",
270 "Group of %d functions in category \"%s\"",
271 n_functions),
272 n_functions, category_name);
275 static void
276 plugin_service_function_group_init (GnmPluginServiceFunctionGroup *s)
278 GO_PLUGIN_SERVICE (s)->cbs_ptr = &s->cbs;
279 s->category_name = NULL;
280 s->translated_category_name = NULL;
281 s->function_name_list = NULL;
282 s->func_group = NULL;
283 s->tdomain = NULL;
286 static void
287 plugin_service_function_group_class_init (GObjectClass *gobject_class)
289 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
291 gobject_class->finalize = plugin_service_function_group_finalize;
292 plugin_service_class->read_xml = plugin_service_function_group_read_xml;
293 plugin_service_class->activate = plugin_service_function_group_activate;
294 plugin_service_class->deactivate = plugin_service_function_group_deactivate;
295 plugin_service_class->get_description = plugin_service_function_group_get_description;
298 GSF_CLASS (GnmPluginServiceFunctionGroup, gnm_plugin_service_function_group,
299 plugin_service_function_group_class_init, plugin_service_function_group_init,
300 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
302 /****************************************************************************/
305 * PluginServiceUI
307 typedef GOPluginServiceSimpleClass PluginServiceUIClass;
308 struct GnmPluginServiceUI_ {
309 GOPluginServiceSimple base;
311 char *file_name;
312 GSList *actions;
314 gpointer layout_id;
315 GnmPluginServiceUICallbacks cbs;
318 static void
319 plugin_service_ui_init (PluginServiceUI *s)
321 GO_PLUGIN_SERVICE (s)->cbs_ptr = &s->cbs;
322 s->file_name = NULL;
323 s->actions = NULL;
324 s->layout_id = NULL;
325 s->cbs.plugin_func_exec_action = NULL;
328 static void
329 plugin_service_ui_finalize (GObject *obj)
331 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (obj);
332 GObjectClass *parent_class;
334 g_free (service_ui->file_name);
335 service_ui->file_name = NULL;
336 g_slist_free_full (service_ui->actions, (GDestroyNotify)gnm_action_unref);
337 service_ui->actions = NULL;
339 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
340 parent_class->finalize (obj);
343 static void
344 cb_ui_service_activate (GnmAction const *action, WorkbookControl *wbc, GOPluginService *service)
346 GOErrorInfo *load_error = NULL;
348 go_plugin_service_load (service, &load_error);
349 if (load_error == NULL) {
350 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
351 GOErrorInfo *ignored_error = NULL;
353 g_return_if_fail (service_ui->cbs.plugin_func_exec_action != NULL);
354 service_ui->cbs.plugin_func_exec_action (
355 service, action, wbc, &ignored_error);
356 if (ignored_error != NULL) {
357 go_error_info_print (ignored_error);
358 go_error_info_free (ignored_error);
360 } else {
361 go_error_info_print (load_error);
362 go_error_info_free (load_error);
366 static void
367 plugin_service_ui_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
369 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
370 xmlChar *file_name;
371 xmlNode *verbs_node;
372 GSList *actions = NULL;
374 GO_INIT_RET_ERROR_INFO (ret_error);
375 file_name = xml2c (go_xml_node_get_cstr (tree, "file"));
376 if (file_name == NULL) {
377 *ret_error = go_error_info_new_str (
378 _("Missing file name."));
379 return;
381 verbs_node = go_xml_get_child_by_name (tree, "actions");
382 if (verbs_node != NULL) {
383 xmlNode *ptr, *label_node;
384 xmlChar *name, *icon;
385 gchar *label;
386 gboolean always_available;
387 GnmAction *action;
389 for (ptr = verbs_node->xmlChildrenNode; ptr != NULL; ptr = ptr->next) {
390 if (xmlIsBlankNode (ptr) || ptr->name == NULL ||
391 strcmp (CXML2C (ptr->name), "action"))
392 continue;
393 name = go_xml_node_get_cstr (ptr, "name");
394 /* label = go_xml_node_get_cstr (ptr, "label");*/
395 /*****************************************************************************************/
396 label_node = go_xml_get_child_by_name_no_lang (ptr, "label");
397 label = label_node
398 ? xml2c (xmlNodeGetContent (label_node))
399 : NULL;
401 label_node = go_xml_get_child_by_name_by_lang (ptr, "label");
402 if (label_node != NULL) {
403 gchar *lang;
405 lang = go_xml_node_get_cstr (label_node, "lang");
406 if (lang != NULL) {
407 label = xml2c (xmlNodeGetContent (label_node));
408 xmlFree (lang);
411 /*****************************************************************************************/
412 icon = go_xml_node_get_cstr (ptr, "icon");
413 if (!go_xml_node_get_bool (ptr, "always_available", &always_available))
414 always_available = FALSE;
415 action = gnm_action_new (name, label, icon, always_available,
416 (GnmActionHandler) cb_ui_service_activate,
417 service, NULL);
418 if (NULL != name) xmlFree (name);
419 g_free (label);
420 if (NULL != icon) xmlFree (icon);
421 if (NULL != action)
422 GO_SLIST_PREPEND (actions, action);
425 GO_SLIST_REVERSE (actions);
427 service_ui->file_name = file_name;
428 service_ui->actions = actions;
431 static void
432 plugin_service_ui_activate (GOPluginService *service, GOErrorInfo **ret_error)
434 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
435 const char *uifile = service_ui->file_name;
436 char *xml_ui, *group_name;
437 char const *tdomain;
438 GError *error = NULL;
439 GsfInput *src;
440 size_t len;
442 GO_INIT_RET_ERROR_INFO (ret_error);
444 if (strncmp (uifile, "res:", 4) == 0) {
445 size_t len;
446 gconstpointer data = go_rsm_lookup (uifile + 4, &len);
447 src = data
448 ? gsf_input_memory_new (data, len, FALSE)
449 : NULL;
450 } else if (strncmp (uifile, "data:", 5) == 0) {
451 const char *data = uifile + 5;
452 src = gsf_input_memory_new (data, strlen (data), FALSE);
453 } else {
454 char *full_file_name = g_path_is_absolute (uifile)
455 ? g_strdup (uifile)
456 : g_build_filename
457 (go_plugin_get_dir_name (service->plugin),
458 uifile,
459 NULL);
460 src = gsf_input_stdio_new (full_file_name, &error);
461 g_free (full_file_name);
463 if (!src)
464 goto err;
466 src = gsf_input_uncompress (src);
467 len = gsf_input_size (src);
468 xml_ui = g_strndup (gsf_input_read (src, len, NULL), len);
469 if (!xml_ui)
470 goto err;
472 tdomain = go_plugin_get_textdomain (service->plugin);
473 group_name = g_strconcat (go_plugin_get_id (service->plugin), service->id, NULL);
474 service_ui->layout_id = gnm_app_add_extra_ui (group_name,
475 service_ui->actions,
476 xml_ui, tdomain);
477 g_free (group_name);
478 g_free (xml_ui);
479 g_object_unref (src);
480 service->is_active = TRUE;
481 return;
483 err:
484 *ret_error = go_error_info_new_printf
485 (_("Cannot read UI description from %s: %s"),
486 uifile,
487 error ? error->message : "?");
488 g_clear_error (&error);
489 if (src)
490 g_object_unref (src);
493 static void
494 plugin_service_ui_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
496 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
498 GO_INIT_RET_ERROR_INFO (ret_error);
499 gnm_app_remove_extra_ui (service_ui->layout_id);
500 service_ui->layout_id = NULL;
501 service->is_active = FALSE;
504 static char *
505 plugin_service_ui_get_description (GOPluginService *service)
507 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
508 int n_actions;
510 n_actions = g_slist_length (service_ui->actions);
511 return g_strdup_printf (
512 ngettext (
513 /* xgettext : %d gives the number of actions. This is input to ngettext. */
514 "User interface with %d action",
515 "User interface with %d actions",
516 n_actions),
517 n_actions);
520 static void
521 plugin_service_ui_class_init (GObjectClass *gobject_class)
523 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
525 gobject_class->finalize = plugin_service_ui_finalize;
526 plugin_service_class->read_xml = plugin_service_ui_read_xml;
527 plugin_service_class->activate = plugin_service_ui_activate;
528 plugin_service_class->deactivate = plugin_service_ui_deactivate;
529 plugin_service_class->get_description = plugin_service_ui_get_description;
532 GSF_CLASS (PluginServiceUI, gnm_plugin_service_ui,
533 plugin_service_ui_class_init, plugin_service_ui_init,
534 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
536 /****************************************************************************/
539 * PluginServiceSolver
541 typedef GOPluginServiceClass PluginServiceSolverClass;
542 struct GnmPluginServiceSolver_ {
543 GOPluginService base;
545 GnmSolverFactory *factory;
547 GnmPluginServiceSolverCallbacks cbs;
550 static GnmSolver *
551 cb_load_and_create (GnmSolverFactory *factory, GnmSolverParameters *param,
552 gpointer data)
554 PluginServiceSolver *ssol =
555 g_object_get_data (G_OBJECT (factory), "ssol");
556 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
557 GOErrorInfo *ignored_error = NULL;
558 GnmSolver *res;
560 go_plugin_service_load (service, &ignored_error);
561 if (ignored_error != NULL) {
562 go_error_info_print (ignored_error);
563 go_error_info_free (ignored_error);
564 return NULL;
567 res = ssol->cbs.creator (factory, param, data);
568 if (res) {
569 go_plugin_use_ref (service->plugin);
570 g_object_set_data_full (G_OBJECT (res),
571 "plugin-use", service->plugin,
572 (GDestroyNotify)go_plugin_use_unref);
575 return res;
578 static gboolean
579 cb_load_and_functional (GnmSolverFactory *factory,
580 WBCGtk *wbcg,
581 gpointer data)
583 PluginServiceSolver *ssol =
584 g_object_get_data (G_OBJECT (factory), "ssol");
585 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
586 GOErrorInfo *ignored_error = NULL;
587 GnmSolverFactoryFunctional functional;
589 go_plugin_service_load (service, &ignored_error);
590 if (ignored_error != NULL) {
591 go_error_info_print (ignored_error);
592 go_error_info_free (ignored_error);
593 return FALSE;
596 functional = ssol->cbs.functional;
597 return (functional == NULL || functional (factory, wbcg, data));
600 static void
601 plugin_service_solver_init (PluginServiceSolver *ssol)
603 GO_PLUGIN_SERVICE (ssol)->cbs_ptr = &ssol->cbs;
604 ssol->factory = NULL;
605 ssol->cbs.creator = NULL;
608 static void
609 plugin_service_solver_finalize (GObject *obj)
611 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (obj);
612 GObjectClass *parent_class;
614 if (ssol->factory)
615 g_object_unref (ssol->factory);
617 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
618 parent_class->finalize (obj);
621 static void
622 plugin_service_solver_read_xml (GOPluginService *service, xmlNode *tree,
623 GOErrorInfo **ret_error)
625 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
626 xmlChar *s_id, *s_name, *s_type;
627 GnmSolverModelType type = GNM_SOLVER_LP;
628 xmlNode *information_node;
630 GO_INIT_RET_ERROR_INFO (ret_error);
632 s_type = go_xml_node_get_cstr (tree, "model_type");
633 if (s_type && strcmp (CXML2C (s_type), "mip") == 0)
634 type = GNM_SOLVER_LP;
635 else if (s_type && strcmp (CXML2C (s_type), "qp") == 0)
636 type = GNM_SOLVER_QP;
637 else if (s_type && strcmp (CXML2C (s_type), "nlp") == 0)
638 type = GNM_SOLVER_NLP;
639 else {
640 *ret_error = go_error_info_new_str (_("Invalid solver model type."));
641 return;
643 xmlFree (s_type);
645 s_id = go_xml_node_get_cstr (tree, "id");
647 s_name = NULL;
648 information_node = go_xml_get_child_by_name (tree, "information");
649 if (information_node != NULL) {
650 xmlNode *node =
651 go_xml_get_child_by_name_by_lang (information_node,
652 "description");
653 if (node != NULL) {
654 s_name = xmlNodeGetContent (node);
658 if (!s_id || !s_name) {
659 *ret_error = go_error_info_new_str (_("Missing fields in plugin file"));
660 } else {
661 ssol->factory = gnm_solver_factory_new (CXML2C (s_id),
662 CXML2C (s_name),
663 type,
664 cb_load_and_create,
665 cb_load_and_functional,
666 NULL,
667 NULL);
668 g_object_set_data (G_OBJECT (ssol->factory), "ssol", ssol);
670 xmlFree (s_id);
671 xmlFree (s_name);
672 if (*ret_error)
673 return;
675 /* More? */
678 static void
679 plugin_service_solver_activate (GOPluginService *service, GOErrorInfo **ret_error)
681 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
683 GO_INIT_RET_ERROR_INFO (ret_error);
684 gnm_solver_db_register (ssol->factory);
685 service->is_active = TRUE;
688 static void
689 plugin_service_solver_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
691 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
693 GO_INIT_RET_ERROR_INFO (ret_error);
694 gnm_solver_db_unregister (ssol->factory);
695 service->is_active = FALSE;
698 static char *
699 plugin_service_solver_get_description (GOPluginService *service)
701 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
702 return g_strdup_printf (_("Solver Algorithm %s"),
703 ssol->factory->name);
706 static void
707 plugin_service_solver_class_init (GObjectClass *gobject_class)
709 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
711 gobject_class->finalize = plugin_service_solver_finalize;
712 plugin_service_class->read_xml = plugin_service_solver_read_xml;
713 plugin_service_class->activate = plugin_service_solver_activate;
714 plugin_service_class->deactivate = plugin_service_solver_deactivate;
715 plugin_service_class->get_description = plugin_service_solver_get_description;
718 GSF_CLASS (PluginServiceSolver, gnm_plugin_service_solver,
719 plugin_service_solver_class_init, plugin_service_solver_init,
720 GO_TYPE_PLUGIN_SERVICE)
722 /****************************************************************************/
725 typedef GOPluginLoaderModule GnmPluginLoaderModule;
726 typedef GOPluginLoaderModuleClass GnmPluginLoaderModuleClass;
729 * Service - function_group
731 typedef struct {
732 GnmFuncDescriptor *module_fn_info_array;
733 GHashTable *function_indices;
734 } ServiceLoaderDataFunctionGroup;
736 static void
737 function_group_loader_data_free (gpointer data)
739 ServiceLoaderDataFunctionGroup *ld = data;
741 g_hash_table_destroy (ld->function_indices);
742 g_free (ld);
745 static void
746 gnm_plugin_loader_module_func_load_stub (GOPluginService *service,
747 GnmFunc *func)
749 ServiceLoaderDataFunctionGroup *loader_data;
750 gpointer index_ptr;
751 GnmFuncDescriptor *desc;
752 const char *name;
754 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
755 g_return_if_fail (GNM_IS_FUNC (func));
757 name = gnm_func_get_name (func, FALSE);
758 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
759 if (!g_hash_table_lookup_extended (loader_data->function_indices,
760 (gpointer)name,
761 NULL, &index_ptr))
762 return; // Failed
764 desc = loader_data->module_fn_info_array + GPOINTER_TO_INT (index_ptr);
766 func->help = desc->help ? desc->help : NULL;
767 func->impl_status = desc->impl_status;
768 func->test_status = desc->test_status;
769 func->flags = desc->flags;
770 if (desc->fn_args != NULL) {
771 func->fn.args.func = desc->fn_args;
772 func->fn.args.arg_spec = desc->arg_spec;
773 gnm_func_set_function_type (func, GNM_FUNC_TYPE_ARGS);
774 } else if (desc->fn_nodes != NULL) {
775 func->fn.nodes = desc->fn_nodes;
776 gnm_func_set_function_type (func, GNM_FUNC_TYPE_NODES);
777 } else {
778 g_warning ("Invalid function descriptor with no function");
782 static void
783 gnm_plugin_loader_module_load_service_function_group (GOPluginLoader *loader,
784 GOPluginService *service,
785 GOErrorInfo **ret_error)
787 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
788 gchar *fn_info_array_name;
789 GnmFuncDescriptor *module_fn_info_array = NULL;
791 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
793 GO_INIT_RET_ERROR_INFO (ret_error);
794 fn_info_array_name = g_strconcat (
795 go_plugin_service_get_id (service), "_functions", NULL);
796 g_module_symbol (loader_module->handle, fn_info_array_name, (gpointer) &module_fn_info_array);
797 if (module_fn_info_array != NULL) {
798 GnmPluginServiceFunctionGroupCallbacks *cbs;
799 ServiceLoaderDataFunctionGroup *loader_data;
800 gint i;
802 cbs = go_plugin_service_get_cbs (service);
803 cbs->load_stub = &gnm_plugin_loader_module_func_load_stub;
805 loader_data = g_new (ServiceLoaderDataFunctionGroup, 1);
806 loader_data->module_fn_info_array = module_fn_info_array;
807 loader_data->function_indices = g_hash_table_new (&g_str_hash, &g_str_equal);
808 for (i = 0; module_fn_info_array[i].name != NULL; i++) {
809 g_hash_table_insert (loader_data->function_indices,
810 (gpointer) module_fn_info_array[i].name,
811 GINT_TO_POINTER (i));
813 g_object_set_data_full (
814 G_OBJECT (service), "loader_data", loader_data, function_group_loader_data_free);
815 } else {
816 *ret_error = go_error_info_new_printf (
817 _("Module file \"%s\" has invalid format."),
818 loader_module->module_file_name);
819 go_error_info_add_details (*ret_error,
820 go_error_info_new_printf (
821 _("File doesn't contain \"%s\" array."),
822 fn_info_array_name));
824 g_free (fn_info_array_name);
828 * Service - ui
831 typedef struct {
832 GnmModulePluginUIActions *module_ui_actions_array;
833 GHashTable *ui_actions_hash;
834 } ServiceLoaderDataUI;
836 static void
837 ui_loader_data_free (gpointer data)
839 ServiceLoaderDataUI *ld = data;
841 g_hash_table_destroy (ld->ui_actions_hash);
842 g_free (ld);
845 static void
846 gnm_plugin_loader_module_func_exec_action (GOPluginService *service,
847 GnmAction const *action,
848 WorkbookControl *wbc,
849 GOErrorInfo **ret_error)
851 ServiceLoaderDataUI *loader_data;
852 gpointer action_index_ptr;
853 int action_index;
855 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
857 GO_INIT_RET_ERROR_INFO (ret_error);
858 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
859 if (!g_hash_table_lookup_extended (loader_data->ui_actions_hash, action->id,
860 NULL, &action_index_ptr)) {
861 *ret_error = go_error_info_new_printf (_("Unknown action: %s"), action->id);
862 return;
864 action_index = GPOINTER_TO_INT (action_index_ptr);
865 if (NULL != loader_data->module_ui_actions_array [action_index].handler)
866 (*loader_data->module_ui_actions_array [action_index].handler) (action, wbc);
869 static void
870 gnm_plugin_loader_module_load_service_ui (GOPluginLoader *loader,
871 GOPluginService *service,
872 GOErrorInfo **ret_error)
874 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
875 char *ui_actions_array_name;
876 GnmModulePluginUIActions *module_ui_actions_array = NULL;
877 GnmPluginServiceUICallbacks *cbs;
878 ServiceLoaderDataUI *loader_data;
879 gint i;
881 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
883 GO_INIT_RET_ERROR_INFO (ret_error);
884 ui_actions_array_name = g_strconcat (
885 go_plugin_service_get_id (service), "_ui_actions", NULL);
886 g_module_symbol (loader_module->handle, ui_actions_array_name, (gpointer) &module_ui_actions_array);
887 if (module_ui_actions_array == NULL) {
888 *ret_error = go_error_info_new_printf (
889 _("Module file \"%s\" has invalid format."),
890 loader_module->module_file_name);
891 go_error_info_add_details (*ret_error, go_error_info_new_printf (
892 _("File doesn't contain \"%s\" array."), ui_actions_array_name));
893 g_free (ui_actions_array_name);
894 return;
896 g_free (ui_actions_array_name);
898 cbs = go_plugin_service_get_cbs (service);
899 cbs->plugin_func_exec_action = gnm_plugin_loader_module_func_exec_action;
901 loader_data = g_new (ServiceLoaderDataUI, 1);
902 loader_data->module_ui_actions_array = module_ui_actions_array;
903 loader_data->ui_actions_hash = g_hash_table_new (g_str_hash, g_str_equal);
904 for (i = 0; module_ui_actions_array[i].name != NULL; i++)
905 g_hash_table_insert (loader_data->ui_actions_hash,
906 (gpointer) module_ui_actions_array[i].name,
907 GINT_TO_POINTER (i));
908 g_object_set_data_full (G_OBJECT (service),
909 "loader_data", loader_data, ui_loader_data_free);
912 static void
913 gnm_plugin_loader_module_load_service_solver (GOPluginLoader *loader,
914 GOPluginService *service,
915 GOErrorInfo **ret_error)
917 GnmPluginLoaderModule *loader_module =
918 GNM_PLUGIN_LOADER_MODULE (loader);
919 GnmPluginServiceSolverCallbacks *cbs;
920 char *symname;
921 GnmSolverCreator creator;
922 GnmSolverFactoryFunctional functional;
924 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_SOLVER (service));
926 GO_INIT_RET_ERROR_INFO (ret_error);
928 symname = g_strconcat (go_plugin_service_get_id (service),
929 "_solver_factory",
930 NULL);
931 g_module_symbol (loader_module->handle, symname, (gpointer)&creator);
932 g_free (symname);
933 if (!creator) {
934 *ret_error = go_error_info_new_printf (
935 _("Module file \"%s\" has invalid format."),
936 loader_module->module_file_name);
937 return;
940 symname = g_strconcat (go_plugin_service_get_id (service),
941 "_solver_factory_functional",
942 NULL);
943 g_module_symbol (loader_module->handle, symname, (gpointer)&functional);
944 g_free (symname);
946 cbs = go_plugin_service_get_cbs (service);
947 cbs->creator = creator;
948 cbs->functional = functional;
951 static gboolean
952 gplm_service_load (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
954 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s))
955 gnm_plugin_loader_module_load_service_function_group (l, s, err);
956 else if (GNM_IS_PLUGIN_SERVICE_UI (s))
957 gnm_plugin_loader_module_load_service_ui (l, s, err);
958 else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s))
959 gnm_plugin_loader_module_load_service_solver (l, s, err);
960 else
961 return FALSE;
962 return TRUE;
965 static gboolean
966 gplm_service_unload (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
968 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s)) {
969 GnmPluginServiceFunctionGroupCallbacks *cbs = go_plugin_service_get_cbs (s);
970 cbs->load_stub = NULL;
971 } else if (GNM_IS_PLUGIN_SERVICE_UI (s)) {
972 GnmPluginServiceUICallbacks *cbs = go_plugin_service_get_cbs (s);
973 cbs->plugin_func_exec_action = NULL;
974 } else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s)) {
975 GnmPluginServiceSolverCallbacks *cbs =
976 go_plugin_service_get_cbs (s);
977 cbs->creator = NULL;
978 cbs->functional = NULL;
979 } else
980 return FALSE;
981 return TRUE;
984 static void
985 go_plugin_loader_module_iface_init (GOPluginLoaderClass *iface)
987 iface->service_load = gplm_service_load;
988 iface->service_unload = gplm_service_unload;
991 GSF_CLASS_FULL (GnmPluginLoaderModule, gnm_plugin_loader_module,
992 NULL, NULL, NULL, NULL,
993 NULL, GO_TYPE_PLUGIN_LOADER_MODULE, 0,
994 GSF_INTERFACE (go_plugin_loader_module_iface_init, GO_TYPE_PLUGIN_LOADER))
996 /****************************************************************************/
999 * gnm_plugins_service_init: (skip)
1001 void
1002 gnm_plugins_service_init (void)
1004 go_plugin_service_define ("function_group",
1005 &gnm_plugin_service_function_group_get_type);
1006 go_plugin_service_define ("ui",
1007 &gnm_plugin_service_ui_get_type);
1008 go_plugin_service_define ("solver",
1009 &gnm_plugin_service_solver_get_type);
1010 go_plugin_loader_module_register_version ("gnumeric", GNM_VERSION_FULL);
1014 void
1015 gnm_plugins_init (GOCmdContext *context)
1017 char const *env_var;
1018 GSList *dir_list = go_slist_create (
1019 g_build_filename (gnm_sys_lib_dir (), PLUGIN_SUBDIR, NULL),
1020 g_strdup (gnm_sys_extern_plugin_dir ()),
1021 (gnm_usr_dir (TRUE) == NULL ? NULL :
1022 g_build_filename (gnm_usr_dir (TRUE), PLUGIN_SUBDIR, NULL)),
1023 NULL);
1024 dir_list = g_slist_concat (dir_list,
1025 go_string_slist_copy (gnm_conf_get_plugins_extra_dirs ()));
1027 env_var = g_getenv ("GNUMERIC_PLUGIN_PATH");
1028 if (env_var != NULL)
1029 GO_SLIST_CONCAT (dir_list, go_strsplit_to_slist (env_var, G_SEARCHPATH_SEPARATOR));
1031 go_plugins_init (GO_CMD_CONTEXT (context),
1032 gnm_conf_get_plugins_file_states (),
1033 gnm_conf_get_plugins_active (),
1034 dir_list,
1035 gnm_conf_get_plugins_activate_newplugins (),
1036 gnm_plugin_loader_module_get_type ());