Update Spanish translation
[gnumeric.git] / src / gnm-plugin.c
blob3f830b5785121c72945ab6f7e291a1090c3a3a63
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 *func = gnm_func_lookup_or_add_placeholder (fname);
212 gnm_func_set_stub (func);
213 gnm_func_set_translation_domain (func, sfg->tdomain);
214 gnm_func_set_function_group (func, sfg->func_group);
215 // Clear localized_name so we can deduce the proper name.
216 //gnm_func_set_localized_name (func, NULL);
218 g_signal_connect
219 (func, "notify::in-use",
220 G_CALLBACK (plugin_service_function_group_func_ref_notify),
221 plugin);
223 g_signal_connect
224 (func, "load-stub",
225 G_CALLBACK (plugin_service_function_group_func_load_stub),
226 service);
228 if (gnm_func_get_in_use (func))
229 g_signal_connect (plugin,
230 "state_changed",
231 G_CALLBACK (delayed_ref_notify),
232 func);
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 GOPlugin *plugin = go_plugin_service_get_plugin (service);
242 GSList *l;
244 if (gnm_debug_flag ("plugin-func"))
245 g_printerr ("Deactivating group %s\n", sfg->category_name);
247 GO_INIT_RET_ERROR_INFO (ret_error);
249 for (l = sfg->function_name_list; l; l = l->next) {
250 const char *fname = l->data;
251 GnmFunc *func = gnm_func_lookup (fname, NULL);
253 // This should not happen, but if it were to, having a handler
254 // of some other object is not going to be good.
255 if (gnm_func_get_in_use (func))
256 g_signal_handlers_disconnect_by_func
257 (plugin, G_CALLBACK (delayed_ref_notify), func);
259 // Someone else might hold a ref so make sure the object
260 // becomes inaccessible via gnm_func_lookup
261 gnm_func_dispose (func);
263 g_object_unref (func);
265 service->is_active = FALSE;
268 static char *
269 plugin_service_function_group_get_description (GOPluginService *service)
271 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
272 int n_functions;
273 char const *category_name;
275 n_functions = g_slist_length (sfg->function_name_list);
276 category_name = sfg->translated_category_name != NULL
277 ? sfg->translated_category_name
278 : sfg->category_name;
280 return g_strdup_printf (ngettext (
281 "%d function in category \"%s\"",
282 "Group of %d functions in category \"%s\"",
283 n_functions),
284 n_functions, category_name);
287 static void
288 plugin_service_function_group_init (GnmPluginServiceFunctionGroup *s)
290 GO_PLUGIN_SERVICE (s)->cbs_ptr = &s->cbs;
291 s->category_name = NULL;
292 s->translated_category_name = NULL;
293 s->function_name_list = NULL;
294 s->func_group = NULL;
295 s->tdomain = NULL;
298 static void
299 plugin_service_function_group_class_init (GObjectClass *gobject_class)
301 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
303 gobject_class->finalize = plugin_service_function_group_finalize;
304 plugin_service_class->read_xml = plugin_service_function_group_read_xml;
305 plugin_service_class->activate = plugin_service_function_group_activate;
306 plugin_service_class->deactivate = plugin_service_function_group_deactivate;
307 plugin_service_class->get_description = plugin_service_function_group_get_description;
310 GSF_CLASS (GnmPluginServiceFunctionGroup, gnm_plugin_service_function_group,
311 plugin_service_function_group_class_init, plugin_service_function_group_init,
312 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
314 /****************************************************************************/
317 * PluginServiceUI
319 typedef GOPluginServiceSimpleClass PluginServiceUIClass;
320 struct GnmPluginServiceUI_ {
321 GOPluginServiceSimple base;
323 char *file_name;
324 GSList *actions;
326 gpointer layout_id;
327 GnmPluginServiceUICallbacks cbs;
330 static void
331 plugin_service_ui_init (PluginServiceUI *s)
333 GO_PLUGIN_SERVICE (s)->cbs_ptr = &s->cbs;
334 s->file_name = NULL;
335 s->actions = NULL;
336 s->layout_id = NULL;
337 s->cbs.plugin_func_exec_action = NULL;
340 static void
341 plugin_service_ui_finalize (GObject *obj)
343 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (obj);
344 GObjectClass *parent_class;
346 g_free (service_ui->file_name);
347 service_ui->file_name = NULL;
348 g_slist_free_full (service_ui->actions, (GDestroyNotify)gnm_action_unref);
349 service_ui->actions = NULL;
351 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
352 parent_class->finalize (obj);
355 static void
356 cb_ui_service_activate (GnmAction const *action, WorkbookControl *wbc, GOPluginService *service)
358 GOErrorInfo *load_error = NULL;
360 go_plugin_service_load (service, &load_error);
361 if (load_error == NULL) {
362 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
363 GOErrorInfo *ignored_error = NULL;
365 g_return_if_fail (service_ui->cbs.plugin_func_exec_action != NULL);
366 service_ui->cbs.plugin_func_exec_action (
367 service, action, wbc, &ignored_error);
368 if (ignored_error != NULL) {
369 go_error_info_print (ignored_error);
370 go_error_info_free (ignored_error);
372 } else {
373 go_error_info_print (load_error);
374 go_error_info_free (load_error);
378 static void
379 plugin_service_ui_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
381 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
382 xmlChar *file_name;
383 xmlNode *verbs_node;
384 GSList *actions = NULL;
386 GO_INIT_RET_ERROR_INFO (ret_error);
387 file_name = xml2c (go_xml_node_get_cstr (tree, "file"));
388 if (file_name == NULL) {
389 *ret_error = go_error_info_new_str (
390 _("Missing file name."));
391 return;
393 verbs_node = go_xml_get_child_by_name (tree, "actions");
394 if (verbs_node != NULL) {
395 xmlNode *ptr, *label_node;
396 xmlChar *name, *icon;
397 gchar *label;
398 gboolean always_available;
399 GnmAction *action;
401 for (ptr = verbs_node->xmlChildrenNode; ptr != NULL; ptr = ptr->next) {
402 if (xmlIsBlankNode (ptr) || ptr->name == NULL ||
403 strcmp (CXML2C (ptr->name), "action"))
404 continue;
405 name = go_xml_node_get_cstr (ptr, "name");
406 /* label = go_xml_node_get_cstr (ptr, "label");*/
407 /*****************************************************************************************/
408 label_node = go_xml_get_child_by_name_no_lang (ptr, "label");
409 label = label_node
410 ? xml2c (xmlNodeGetContent (label_node))
411 : NULL;
413 label_node = go_xml_get_child_by_name_by_lang (ptr, "label");
414 if (label_node != NULL) {
415 gchar *lang;
417 lang = go_xml_node_get_cstr (label_node, "lang");
418 if (lang != NULL) {
419 label = xml2c (xmlNodeGetContent (label_node));
420 xmlFree (lang);
423 /*****************************************************************************************/
424 icon = go_xml_node_get_cstr (ptr, "icon");
425 if (!go_xml_node_get_bool (ptr, "always_available", &always_available))
426 always_available = FALSE;
427 action = gnm_action_new (name, label, icon, always_available,
428 (GnmActionHandler) cb_ui_service_activate,
429 service, NULL);
430 if (NULL != name) xmlFree (name);
431 g_free (label);
432 if (NULL != icon) xmlFree (icon);
433 if (NULL != action)
434 GO_SLIST_PREPEND (actions, action);
437 GO_SLIST_REVERSE (actions);
439 service_ui->file_name = file_name;
440 service_ui->actions = actions;
443 static void
444 plugin_service_ui_activate (GOPluginService *service, GOErrorInfo **ret_error)
446 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
447 const char *uifile = service_ui->file_name;
448 char *xml_ui, *group_name;
449 char const *tdomain;
450 GError *error = NULL;
451 GsfInput *src;
452 size_t len;
454 GO_INIT_RET_ERROR_INFO (ret_error);
456 if (strncmp (uifile, "res:", 4) == 0) {
457 size_t len;
458 gconstpointer data = go_rsm_lookup (uifile + 4, &len);
459 src = data
460 ? gsf_input_memory_new (data, len, FALSE)
461 : NULL;
462 } else if (strncmp (uifile, "data:", 5) == 0) {
463 const char *data = uifile + 5;
464 src = gsf_input_memory_new (data, strlen (data), FALSE);
465 } else {
466 char *full_file_name = g_path_is_absolute (uifile)
467 ? g_strdup (uifile)
468 : g_build_filename
469 (go_plugin_get_dir_name (service->plugin),
470 uifile,
471 NULL);
472 src = gsf_input_stdio_new (full_file_name, &error);
473 g_free (full_file_name);
475 if (!src)
476 goto err;
478 src = gsf_input_uncompress (src);
479 len = gsf_input_size (src);
480 xml_ui = g_strndup (gsf_input_read (src, len, NULL), len);
481 if (!xml_ui)
482 goto err;
484 tdomain = go_plugin_get_textdomain (service->plugin);
485 group_name = g_strconcat (go_plugin_get_id (service->plugin), service->id, NULL);
486 service_ui->layout_id = gnm_app_add_extra_ui (group_name,
487 service_ui->actions,
488 xml_ui, tdomain);
489 g_free (group_name);
490 g_free (xml_ui);
491 g_object_unref (src);
492 service->is_active = TRUE;
493 return;
495 err:
496 *ret_error = go_error_info_new_printf
497 (_("Cannot read UI description from %s: %s"),
498 uifile,
499 error ? error->message : "?");
500 g_clear_error (&error);
501 if (src)
502 g_object_unref (src);
505 static void
506 plugin_service_ui_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
508 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
510 GO_INIT_RET_ERROR_INFO (ret_error);
511 gnm_app_remove_extra_ui (service_ui->layout_id);
512 service_ui->layout_id = NULL;
513 service->is_active = FALSE;
516 static char *
517 plugin_service_ui_get_description (GOPluginService *service)
519 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
520 int n_actions;
522 n_actions = g_slist_length (service_ui->actions);
523 return g_strdup_printf (
524 ngettext (
525 /* xgettext : %d gives the number of actions. This is input to ngettext. */
526 "User interface with %d action",
527 "User interface with %d actions",
528 n_actions),
529 n_actions);
532 static void
533 plugin_service_ui_class_init (GObjectClass *gobject_class)
535 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
537 gobject_class->finalize = plugin_service_ui_finalize;
538 plugin_service_class->read_xml = plugin_service_ui_read_xml;
539 plugin_service_class->activate = plugin_service_ui_activate;
540 plugin_service_class->deactivate = plugin_service_ui_deactivate;
541 plugin_service_class->get_description = plugin_service_ui_get_description;
544 GSF_CLASS (PluginServiceUI, gnm_plugin_service_ui,
545 plugin_service_ui_class_init, plugin_service_ui_init,
546 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
548 /****************************************************************************/
551 * PluginServiceSolver
553 typedef GOPluginServiceClass PluginServiceSolverClass;
554 struct GnmPluginServiceSolver_ {
555 GOPluginService base;
557 GnmSolverFactory *factory;
559 GnmPluginServiceSolverCallbacks cbs;
562 static GnmSolver *
563 cb_load_and_create (GnmSolverFactory *factory, GnmSolverParameters *param,
564 gpointer data)
566 PluginServiceSolver *ssol =
567 g_object_get_data (G_OBJECT (factory), "ssol");
568 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
569 GOErrorInfo *ignored_error = NULL;
570 GnmSolver *res;
572 go_plugin_service_load (service, &ignored_error);
573 if (ignored_error != NULL) {
574 go_error_info_print (ignored_error);
575 go_error_info_free (ignored_error);
576 return NULL;
579 res = ssol->cbs.creator (factory, param, data);
580 if (res) {
581 go_plugin_use_ref (service->plugin);
582 g_object_set_data_full (G_OBJECT (res),
583 "plugin-use", service->plugin,
584 (GDestroyNotify)go_plugin_use_unref);
587 return res;
590 static gboolean
591 cb_load_and_functional (GnmSolverFactory *factory,
592 WBCGtk *wbcg,
593 gpointer data)
595 PluginServiceSolver *ssol =
596 g_object_get_data (G_OBJECT (factory), "ssol");
597 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
598 GOErrorInfo *ignored_error = NULL;
599 GnmSolverFactoryFunctional functional;
601 go_plugin_service_load (service, &ignored_error);
602 if (ignored_error != NULL) {
603 go_error_info_print (ignored_error);
604 go_error_info_free (ignored_error);
605 return FALSE;
608 functional = ssol->cbs.functional;
609 return (functional == NULL || functional (factory, wbcg, data));
612 static void
613 plugin_service_solver_init (PluginServiceSolver *ssol)
615 GO_PLUGIN_SERVICE (ssol)->cbs_ptr = &ssol->cbs;
616 ssol->factory = NULL;
617 ssol->cbs.creator = NULL;
620 static void
621 plugin_service_solver_finalize (GObject *obj)
623 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (obj);
624 GObjectClass *parent_class;
626 if (ssol->factory)
627 g_object_unref (ssol->factory);
629 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
630 parent_class->finalize (obj);
633 static void
634 plugin_service_solver_read_xml (GOPluginService *service, xmlNode *tree,
635 GOErrorInfo **ret_error)
637 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
638 xmlChar *s_id, *s_name, *s_type;
639 GnmSolverModelType type = GNM_SOLVER_LP;
640 xmlNode *information_node;
642 GO_INIT_RET_ERROR_INFO (ret_error);
644 s_type = go_xml_node_get_cstr (tree, "model_type");
645 if (s_type && strcmp (CXML2C (s_type), "mip") == 0)
646 type = GNM_SOLVER_LP;
647 else if (s_type && strcmp (CXML2C (s_type), "qp") == 0)
648 type = GNM_SOLVER_QP;
649 else if (s_type && strcmp (CXML2C (s_type), "nlp") == 0)
650 type = GNM_SOLVER_NLP;
651 else {
652 *ret_error = go_error_info_new_str (_("Invalid solver model type."));
653 return;
655 xmlFree (s_type);
657 s_id = go_xml_node_get_cstr (tree, "id");
659 s_name = NULL;
660 information_node = go_xml_get_child_by_name (tree, "information");
661 if (information_node != NULL) {
662 xmlNode *node =
663 go_xml_get_child_by_name_by_lang (information_node,
664 "description");
665 if (node != NULL) {
666 s_name = xmlNodeGetContent (node);
670 if (!s_id || !s_name) {
671 *ret_error = go_error_info_new_str (_("Missing fields in plugin file"));
672 } else {
673 ssol->factory = gnm_solver_factory_new (CXML2C (s_id),
674 CXML2C (s_name),
675 type,
676 cb_load_and_create,
677 cb_load_and_functional,
678 NULL,
679 NULL);
680 g_object_set_data (G_OBJECT (ssol->factory), "ssol", ssol);
682 xmlFree (s_id);
683 xmlFree (s_name);
684 if (*ret_error)
685 return;
687 /* More? */
690 static void
691 plugin_service_solver_activate (GOPluginService *service, GOErrorInfo **ret_error)
693 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
695 GO_INIT_RET_ERROR_INFO (ret_error);
696 gnm_solver_db_register (ssol->factory);
697 service->is_active = TRUE;
700 static void
701 plugin_service_solver_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
703 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
705 GO_INIT_RET_ERROR_INFO (ret_error);
706 gnm_solver_db_unregister (ssol->factory);
707 service->is_active = FALSE;
710 static char *
711 plugin_service_solver_get_description (GOPluginService *service)
713 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
714 return g_strdup_printf (_("Solver Algorithm %s"),
715 ssol->factory->name);
718 static void
719 plugin_service_solver_class_init (GObjectClass *gobject_class)
721 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
723 gobject_class->finalize = plugin_service_solver_finalize;
724 plugin_service_class->read_xml = plugin_service_solver_read_xml;
725 plugin_service_class->activate = plugin_service_solver_activate;
726 plugin_service_class->deactivate = plugin_service_solver_deactivate;
727 plugin_service_class->get_description = plugin_service_solver_get_description;
730 GSF_CLASS (PluginServiceSolver, gnm_plugin_service_solver,
731 plugin_service_solver_class_init, plugin_service_solver_init,
732 GO_TYPE_PLUGIN_SERVICE)
734 /****************************************************************************/
737 typedef GOPluginLoaderModule GnmPluginLoaderModule;
738 typedef GOPluginLoaderModuleClass GnmPluginLoaderModuleClass;
741 * Service - function_group
743 typedef struct {
744 GnmFuncDescriptor *module_fn_info_array;
745 GHashTable *function_indices;
746 } ServiceLoaderDataFunctionGroup;
748 static void
749 function_group_loader_data_free (gpointer data)
751 ServiceLoaderDataFunctionGroup *ld = data;
753 g_hash_table_destroy (ld->function_indices);
754 g_free (ld);
757 static void
758 gnm_plugin_loader_module_func_load_stub (GOPluginService *service,
759 GnmFunc *func)
761 ServiceLoaderDataFunctionGroup *loader_data;
762 gpointer index_ptr;
763 GnmFuncDescriptor *desc;
764 const char *name;
766 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
767 g_return_if_fail (GNM_IS_FUNC (func));
769 name = gnm_func_get_name (func, FALSE);
770 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
771 if (!g_hash_table_lookup_extended (loader_data->function_indices,
772 (gpointer)name,
773 NULL, &index_ptr))
774 return; // Failed
776 desc = loader_data->module_fn_info_array + GPOINTER_TO_INT (index_ptr);
777 gnm_func_set_from_desc (func, desc);
780 static void
781 gnm_plugin_loader_module_load_service_function_group (GOPluginLoader *loader,
782 GOPluginService *service,
783 GOErrorInfo **ret_error)
785 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
786 gchar *fn_info_array_name;
787 GnmFuncDescriptor *module_fn_info_array = NULL;
789 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
791 GO_INIT_RET_ERROR_INFO (ret_error);
792 fn_info_array_name = g_strconcat (
793 go_plugin_service_get_id (service), "_functions", NULL);
794 g_module_symbol (loader_module->handle, fn_info_array_name, (gpointer) &module_fn_info_array);
795 if (module_fn_info_array != NULL) {
796 GnmPluginServiceFunctionGroupCallbacks *cbs;
797 ServiceLoaderDataFunctionGroup *loader_data;
798 gint i;
800 cbs = go_plugin_service_get_cbs (service);
801 cbs->load_stub = &gnm_plugin_loader_module_func_load_stub;
803 loader_data = g_new (ServiceLoaderDataFunctionGroup, 1);
804 loader_data->module_fn_info_array = module_fn_info_array;
805 loader_data->function_indices = g_hash_table_new (&g_str_hash, &g_str_equal);
806 for (i = 0; module_fn_info_array[i].name != NULL; i++) {
807 g_hash_table_insert (loader_data->function_indices,
808 (gpointer) module_fn_info_array[i].name,
809 GINT_TO_POINTER (i));
811 g_object_set_data_full (
812 G_OBJECT (service), "loader_data", loader_data, function_group_loader_data_free);
813 } else {
814 *ret_error = go_error_info_new_printf (
815 _("Module file \"%s\" has invalid format."),
816 loader_module->module_file_name);
817 go_error_info_add_details (*ret_error,
818 go_error_info_new_printf (
819 _("File doesn't contain \"%s\" array."),
820 fn_info_array_name));
822 g_free (fn_info_array_name);
826 * Service - ui
829 typedef struct {
830 GnmModulePluginUIActions *module_ui_actions_array;
831 GHashTable *ui_actions_hash;
832 } ServiceLoaderDataUI;
834 static void
835 ui_loader_data_free (gpointer data)
837 ServiceLoaderDataUI *ld = data;
839 g_hash_table_destroy (ld->ui_actions_hash);
840 g_free (ld);
843 static void
844 gnm_plugin_loader_module_func_exec_action (GOPluginService *service,
845 GnmAction const *action,
846 WorkbookControl *wbc,
847 GOErrorInfo **ret_error)
849 ServiceLoaderDataUI *loader_data;
850 gpointer action_index_ptr;
851 int action_index;
853 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
855 GO_INIT_RET_ERROR_INFO (ret_error);
856 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
857 if (!g_hash_table_lookup_extended (loader_data->ui_actions_hash, action->id,
858 NULL, &action_index_ptr)) {
859 *ret_error = go_error_info_new_printf (_("Unknown action: %s"), action->id);
860 return;
862 action_index = GPOINTER_TO_INT (action_index_ptr);
863 if (NULL != loader_data->module_ui_actions_array [action_index].handler)
864 (*loader_data->module_ui_actions_array [action_index].handler) (action, wbc);
867 static void
868 gnm_plugin_loader_module_load_service_ui (GOPluginLoader *loader,
869 GOPluginService *service,
870 GOErrorInfo **ret_error)
872 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
873 char *ui_actions_array_name;
874 GnmModulePluginUIActions *module_ui_actions_array = NULL;
875 GnmPluginServiceUICallbacks *cbs;
876 ServiceLoaderDataUI *loader_data;
877 gint i;
879 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
881 GO_INIT_RET_ERROR_INFO (ret_error);
882 ui_actions_array_name = g_strconcat (
883 go_plugin_service_get_id (service), "_ui_actions", NULL);
884 g_module_symbol (loader_module->handle, ui_actions_array_name, (gpointer) &module_ui_actions_array);
885 if (module_ui_actions_array == NULL) {
886 *ret_error = go_error_info_new_printf (
887 _("Module file \"%s\" has invalid format."),
888 loader_module->module_file_name);
889 go_error_info_add_details (*ret_error, go_error_info_new_printf (
890 _("File doesn't contain \"%s\" array."), ui_actions_array_name));
891 g_free (ui_actions_array_name);
892 return;
894 g_free (ui_actions_array_name);
896 cbs = go_plugin_service_get_cbs (service);
897 cbs->plugin_func_exec_action = gnm_plugin_loader_module_func_exec_action;
899 loader_data = g_new (ServiceLoaderDataUI, 1);
900 loader_data->module_ui_actions_array = module_ui_actions_array;
901 loader_data->ui_actions_hash = g_hash_table_new (g_str_hash, g_str_equal);
902 for (i = 0; module_ui_actions_array[i].name != NULL; i++)
903 g_hash_table_insert (loader_data->ui_actions_hash,
904 (gpointer) module_ui_actions_array[i].name,
905 GINT_TO_POINTER (i));
906 g_object_set_data_full (G_OBJECT (service),
907 "loader_data", loader_data, ui_loader_data_free);
910 static void
911 gnm_plugin_loader_module_load_service_solver (GOPluginLoader *loader,
912 GOPluginService *service,
913 GOErrorInfo **ret_error)
915 GnmPluginLoaderModule *loader_module =
916 GNM_PLUGIN_LOADER_MODULE (loader);
917 GnmPluginServiceSolverCallbacks *cbs;
918 char *symname;
919 GnmSolverCreator creator;
920 GnmSolverFactoryFunctional functional;
922 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_SOLVER (service));
924 GO_INIT_RET_ERROR_INFO (ret_error);
926 symname = g_strconcat (go_plugin_service_get_id (service),
927 "_solver_factory",
928 NULL);
929 g_module_symbol (loader_module->handle, symname, (gpointer)&creator);
930 g_free (symname);
931 if (!creator) {
932 *ret_error = go_error_info_new_printf (
933 _("Module file \"%s\" has invalid format."),
934 loader_module->module_file_name);
935 return;
938 symname = g_strconcat (go_plugin_service_get_id (service),
939 "_solver_factory_functional",
940 NULL);
941 g_module_symbol (loader_module->handle, symname, (gpointer)&functional);
942 g_free (symname);
944 cbs = go_plugin_service_get_cbs (service);
945 cbs->creator = creator;
946 cbs->functional = functional;
949 static gboolean
950 gplm_service_load (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
952 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s))
953 gnm_plugin_loader_module_load_service_function_group (l, s, err);
954 else if (GNM_IS_PLUGIN_SERVICE_UI (s))
955 gnm_plugin_loader_module_load_service_ui (l, s, err);
956 else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s))
957 gnm_plugin_loader_module_load_service_solver (l, s, err);
958 else
959 return FALSE;
960 return TRUE;
963 static gboolean
964 gplm_service_unload (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
966 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s)) {
967 GnmPluginServiceFunctionGroupCallbacks *cbs = go_plugin_service_get_cbs (s);
968 cbs->load_stub = NULL;
969 } else if (GNM_IS_PLUGIN_SERVICE_UI (s)) {
970 GnmPluginServiceUICallbacks *cbs = go_plugin_service_get_cbs (s);
971 cbs->plugin_func_exec_action = NULL;
972 } else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s)) {
973 GnmPluginServiceSolverCallbacks *cbs =
974 go_plugin_service_get_cbs (s);
975 cbs->creator = NULL;
976 cbs->functional = NULL;
977 } else
978 return FALSE;
979 return TRUE;
982 static void
983 go_plugin_loader_module_iface_init (GOPluginLoaderClass *iface)
985 iface->service_load = gplm_service_load;
986 iface->service_unload = gplm_service_unload;
989 GSF_CLASS_FULL (GnmPluginLoaderModule, gnm_plugin_loader_module,
990 NULL, NULL, NULL, NULL,
991 NULL, GO_TYPE_PLUGIN_LOADER_MODULE, 0,
992 GSF_INTERFACE (go_plugin_loader_module_iface_init, GO_TYPE_PLUGIN_LOADER))
994 /****************************************************************************/
997 * gnm_plugins_service_init: (skip)
999 void
1000 gnm_plugins_service_init (void)
1002 go_plugin_service_define ("function_group",
1003 &gnm_plugin_service_function_group_get_type);
1004 go_plugin_service_define ("ui",
1005 &gnm_plugin_service_ui_get_type);
1006 go_plugin_service_define ("solver",
1007 &gnm_plugin_service_solver_get_type);
1008 go_plugin_loader_module_register_version ("gnumeric", GNM_VERSION_FULL);
1012 void
1013 gnm_plugins_init (GOCmdContext *context)
1015 char const *env_var;
1016 GSList *dir_list = go_slist_create (
1017 g_build_filename (gnm_sys_lib_dir (), PLUGIN_SUBDIR, NULL),
1018 g_strdup (gnm_sys_extern_plugin_dir ()),
1019 (gnm_usr_dir (TRUE) == NULL ? NULL :
1020 g_build_filename (gnm_usr_dir (TRUE), PLUGIN_SUBDIR, NULL)),
1021 NULL);
1022 dir_list = g_slist_concat (dir_list,
1023 go_string_slist_copy (gnm_conf_get_plugins_extra_dirs ()));
1025 env_var = g_getenv ("GNUMERIC_PLUGIN_PATH");
1026 if (env_var != NULL)
1027 GO_SLIST_CONCAT (dir_list, go_strsplit_to_slist (env_var, G_SEARCHPATH_SEPARATOR));
1029 go_plugins_init (GO_CMD_CONTEXT (context),
1030 gnm_conf_get_plugins_file_states (),
1031 gnm_conf_get_plugins_active (),
1032 dir_list,
1033 gnm_conf_get_plugins_activate_newplugins (),
1034 gnm_plugin_loader_module_get_type ());