1.12.39
[gnumeric.git] / src / gnm-plugin.c
blob127795b597de0488522f73ff63184e22a753c171
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * go-plugin-service.c: Plugin services - reading XML info, activating, etc.
4 * (everything independent of plugin loading method)
6 * Author: Zbigniew Chyla (cyba@gnome.pl)
7 */
9 #include <gnumeric-config.h>
10 #include "gutils.h"
11 #include <tools/gnm-solver.h>
12 #include "func.h"
13 #include "gnm-plugin.h"
14 #include "gnumeric-conf.h"
15 #include "application.h"
17 #include <goffice/goffice.h>
18 #include <gsf/gsf-impl-utils.h>
19 #include <gsf/gsf-input-stdio.h>
20 #include <gsf/gsf-input-memory.h>
21 #include <glib/gi18n-lib.h>
22 #include <string.h>
24 #define CXML2C(s) ((char const *)(s))
25 #define CC2XML(s) ((xmlChar const *)(s))
27 static char *
28 xml2c (xmlChar *src)
30 char *dst = g_strdup (CXML2C (src));
31 xmlFree (src);
32 return dst;
35 typedef GOPluginServiceSimpleClass GnmPluginServiceFunctionGroupClass;
36 struct GnmPluginServiceFunctionGroup_ {
37 GOPluginServiceSimple base;
39 gchar *category_name, *translated_category_name;
40 GSList *function_name_list;
42 GnmFuncGroup *func_group;
43 GnmPluginServiceFunctionGroupCallbacks cbs;
44 char *tdomain;
47 static void
48 plugin_service_function_group_finalize (GObject *obj)
50 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (obj);
51 GObjectClass *parent_class;
53 g_free (sfg->category_name);
54 sfg->category_name = NULL;
56 g_free (sfg->translated_category_name);
57 sfg->translated_category_name = NULL;
59 g_slist_free_full (sfg->function_name_list, g_free);
60 sfg->function_name_list = NULL;
62 g_free (sfg->tdomain);
63 sfg->tdomain = NULL;
65 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
66 parent_class->finalize (obj);
69 static void
70 plugin_service_function_group_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
72 xmlNode *category_node, *translated_category_node, *functions_node;
73 gchar *category_name, *translated_category_name;
74 GSList *function_name_list = NULL;
75 gchar *tdomain = NULL;
77 GO_INIT_RET_ERROR_INFO (ret_error);
78 category_node = go_xml_get_child_by_name_no_lang (tree, "category");
79 category_name = category_node
80 ? xml2c (xmlNodeGetContent (category_node))
81 : NULL;
83 translated_category_node = go_xml_get_child_by_name_by_lang (tree, "category");
84 if (translated_category_node != NULL) {
85 xmlChar *lang;
87 lang = go_xml_node_get_cstr (translated_category_node, "lang");
88 if (lang != NULL) {
89 translated_category_name =
90 xml2c (xmlNodeGetContent (translated_category_node));
91 xmlFree (lang);
92 } else {
93 translated_category_name = NULL;
95 } else {
96 translated_category_name = NULL;
98 functions_node = go_xml_get_child_by_name (tree, CC2XML ("functions"));
99 if (functions_node != NULL) {
100 xmlNode *node;
102 tdomain = xml2c (go_xml_node_get_cstr (functions_node, "textdomain"));
104 for (node = functions_node->xmlChildrenNode; node != NULL; node = node->next) {
105 gchar *func_name;
107 if (strcmp (CXML2C (node->name), "function") != 0)
108 continue;
110 func_name = xml2c (go_xml_node_get_cstr (node, "name"));
111 if (!func_name)
112 continue;
114 GO_SLIST_PREPEND (function_name_list, func_name);
116 GO_SLIST_REVERSE (function_name_list);
118 if (category_name != NULL && function_name_list != NULL) {
119 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
121 sfg->category_name = category_name;
122 sfg->translated_category_name = translated_category_name;
123 sfg->function_name_list = function_name_list;
124 sfg->tdomain = tdomain;
125 } else {
126 GSList *error_list = NULL;
128 if (category_name == NULL) {
129 GO_SLIST_PREPEND (error_list, go_error_info_new_str (
130 _("Missing function category name.")));
132 if (function_name_list == NULL) {
133 GO_SLIST_PREPEND (error_list, go_error_info_new_str (
134 _("Function group is empty.")));
136 GO_SLIST_REVERSE (error_list);
137 *ret_error = go_error_info_new_from_error_list (error_list);
139 g_free (category_name);
140 g_free (translated_category_name);
141 g_slist_free_full (function_name_list, g_free);
143 g_free (tdomain);
147 static gboolean
148 plugin_service_function_group_func_desc_load (GnmFunc const *fn_def,
149 GnmFuncDescriptor *res)
151 GOPluginService *service = gnm_func_get_user_data (fn_def);
152 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
153 GOErrorInfo *error = NULL;
155 g_return_val_if_fail (fn_def != NULL, FALSE);
157 go_plugin_service_load (service, &error);
158 if (error != NULL) {
159 go_error_info_print (error);
160 go_error_info_free (error);
161 return FALSE;
163 if (NULL == sfg->cbs.func_desc_load) {
164 error = go_error_info_new_printf (_("No func_desc_load method.\n"));
165 go_error_info_print (error);
166 go_error_info_free (error);
167 return FALSE;
169 return sfg->cbs.func_desc_load (service,
170 gnm_func_get_name (fn_def, FALSE),
171 res);
174 static void
175 plugin_service_function_group_func_ref_notify (GnmFunc *fn_def, int refcount)
177 GOPluginService *service;
179 service = gnm_func_get_user_data (fn_def);
180 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
181 if (refcount == 0) {
182 go_plugin_use_unref (service->plugin);
183 } else {
184 go_plugin_use_ref (service->plugin);
188 static void
189 delayed_ref_notify (GOPlugin *plugin, GnmFunc *fd)
191 g_signal_handlers_disconnect_by_func (plugin,
192 G_CALLBACK (delayed_ref_notify),
193 fd);
195 /* We cannot do this until after the plugin has been activated. */
196 plugin_service_function_group_func_ref_notify (fd, 1);
199 static void
200 plugin_service_function_group_activate (GOPluginService *service, GOErrorInfo **ret_error)
202 GnmPluginServiceFunctionGroup *sfg =
203 GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
205 GO_INIT_RET_ERROR_INFO (ret_error);
206 sfg->func_group = gnm_func_group_fetch (sfg->category_name,
207 sfg->translated_category_name);
208 if (gnm_debug_flag ("plugin-func"))
209 g_printerr ("Activating group %s\n", sfg->category_name);
210 GO_SLIST_FOREACH
211 (sfg->function_name_list, char, fname,
212 GnmFunc *fd;
214 fd = gnm_func_lookup (fname, NULL);
215 if (fd) {
216 #if 0
217 g_printerr ("Reusing placeholder for %s\n", fname);
218 #endif
219 } else {
220 fd = gnm_func_add_placeholder (NULL, fname, "?");
222 if (fd->flags & GNM_FUNC_IS_PLACEHOLDER) {
223 gnm_func_set_user_data (fd, service);
224 gnm_func_upgrade_placeholder
225 (fd, sfg->func_group,
226 sfg->tdomain,
227 plugin_service_function_group_func_desc_load,
228 plugin_service_function_group_func_ref_notify);
229 if (fd->usage_count > 0)
230 g_signal_connect (go_plugin_service_get_plugin (service),
231 "state_changed",
232 G_CALLBACK (delayed_ref_notify),
233 fd);
234 } else {
235 g_warning ("Multiple definitions of function %s -- this cannot be good!", fname);
238 service->is_active = TRUE;
241 static void
242 plugin_service_function_group_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
244 GnmPluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
246 if (gnm_debug_flag ("plugin-func"))
247 g_printerr ("Deactivating group %s\n", sfg->category_name);
249 GO_INIT_RET_ERROR_INFO (ret_error);
250 GO_SLIST_FOREACH (sfg->function_name_list, char, fname,
251 gnm_func_free (gnm_func_lookup (fname, NULL));
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_free);
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 if (NULL != name) xmlFree (name);
418 g_free (label);
419 if (NULL != icon) xmlFree (icon);
420 if (NULL != action)
421 GO_SLIST_PREPEND (actions, action);
424 GO_SLIST_REVERSE (actions);
426 service_ui->file_name = file_name;
427 service_ui->actions = actions;
430 static void
431 plugin_service_ui_activate (GOPluginService *service, GOErrorInfo **ret_error)
433 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
434 const char *uifile = service_ui->file_name;
435 char *xml_ui, *group_name;
436 char const *tdomain;
437 GError *error = NULL;
438 GsfInput *src;
439 size_t len;
441 GO_INIT_RET_ERROR_INFO (ret_error);
443 if (strncmp (uifile, "res:", 4) == 0) {
444 size_t len;
445 gconstpointer data = go_rsm_lookup (uifile + 4, &len);
446 src = data
447 ? gsf_input_memory_new (data, len, FALSE)
448 : NULL;
449 } else if (strncmp (uifile, "data:", 5) == 0) {
450 const char *data = uifile + 5;
451 src = gsf_input_memory_new (data, strlen (data), FALSE);
452 } else {
453 char *full_file_name = g_path_is_absolute (uifile)
454 ? g_strdup (uifile)
455 : g_build_filename
456 (go_plugin_get_dir_name (service->plugin),
457 uifile,
458 NULL);
459 src = gsf_input_stdio_new (full_file_name, &error);
460 g_free (full_file_name);
462 if (!src)
463 goto err;
465 src = gsf_input_uncompress (src);
466 len = gsf_input_size (src);
467 xml_ui = g_strndup (gsf_input_read (src, len, NULL), len);
468 if (!xml_ui)
469 goto err;
471 tdomain = go_plugin_get_textdomain (service->plugin);
472 group_name = g_strconcat (go_plugin_get_id (service->plugin), service->id, NULL);
473 service_ui->layout_id = gnm_app_add_extra_ui (group_name,
474 service_ui->actions,
475 xml_ui, tdomain, service);
476 g_free (group_name);
477 g_free (xml_ui);
478 g_object_unref (src);
479 service->is_active = TRUE;
480 return;
482 err:
483 *ret_error = go_error_info_new_printf
484 (_("Cannot read UI description from %s: %s"),
485 uifile,
486 error ? error->message : "?");
487 g_clear_error (&error);
488 if (src)
489 g_object_unref (src);
492 static void
493 plugin_service_ui_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
495 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
497 GO_INIT_RET_ERROR_INFO (ret_error);
498 gnm_app_remove_extra_ui (service_ui->layout_id);
499 service_ui->layout_id = NULL;
500 service->is_active = FALSE;
503 static char *
504 plugin_service_ui_get_description (GOPluginService *service)
506 PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
507 int n_actions;
509 n_actions = g_slist_length (service_ui->actions);
510 return g_strdup_printf (
511 ngettext (
512 /* xgettext : %d gives the number of actions. This is input to ngettext. */
513 "User interface with %d action",
514 "User interface with %d actions",
515 n_actions),
516 n_actions);
519 static void
520 plugin_service_ui_class_init (GObjectClass *gobject_class)
522 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
524 gobject_class->finalize = plugin_service_ui_finalize;
525 plugin_service_class->read_xml = plugin_service_ui_read_xml;
526 plugin_service_class->activate = plugin_service_ui_activate;
527 plugin_service_class->deactivate = plugin_service_ui_deactivate;
528 plugin_service_class->get_description = plugin_service_ui_get_description;
531 GSF_CLASS (PluginServiceUI, gnm_plugin_service_ui,
532 plugin_service_ui_class_init, plugin_service_ui_init,
533 GO_TYPE_PLUGIN_SERVICE_SIMPLE)
535 /****************************************************************************/
538 * PluginServiceSolver
540 typedef GOPluginServiceClass PluginServiceSolverClass;
541 struct GnmPluginServiceSolver_ {
542 GOPluginService base;
544 GnmSolverFactory *factory;
546 GnmPluginServiceSolverCallbacks cbs;
549 static GnmSolver *
550 cb_load_and_create (GnmSolverFactory *factory, GnmSolverParameters *param)
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);
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)
580 PluginServiceSolver *ssol =
581 g_object_get_data (G_OBJECT (factory), "ssol");
582 GOPluginService *service = GO_PLUGIN_SERVICE (ssol);
583 GOErrorInfo *ignored_error = NULL;
584 GnmSolverFactoryFunctional functional;
586 go_plugin_service_load (service, &ignored_error);
587 if (ignored_error != NULL) {
588 go_error_info_print (ignored_error);
589 go_error_info_free (ignored_error);
590 return FALSE;
593 functional = ssol->cbs.functional;
594 return (functional == NULL || functional (factory, wbcg));
597 static void
598 plugin_service_solver_init (PluginServiceSolver *ssol)
600 GO_PLUGIN_SERVICE (ssol)->cbs_ptr = &ssol->cbs;
601 ssol->factory = NULL;
602 ssol->cbs.creator = NULL;
605 static void
606 plugin_service_solver_finalize (GObject *obj)
608 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (obj);
609 GObjectClass *parent_class;
611 if (ssol->factory)
612 g_object_unref (ssol->factory);
614 parent_class = g_type_class_peek (GO_TYPE_PLUGIN_SERVICE);
615 parent_class->finalize (obj);
618 static void
619 plugin_service_solver_read_xml (GOPluginService *service, xmlNode *tree,
620 GOErrorInfo **ret_error)
622 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
623 xmlChar *s_id, *s_name, *s_type;
624 GnmSolverModelType type = GNM_SOLVER_LP;
625 xmlNode *information_node;
627 GO_INIT_RET_ERROR_INFO (ret_error);
629 s_type = go_xml_node_get_cstr (tree, "model_type");
630 if (s_type && strcmp (CXML2C (s_type), "mip") == 0)
631 type = GNM_SOLVER_LP;
632 else if (s_type && strcmp (CXML2C (s_type), "qp") == 0)
633 type = GNM_SOLVER_QP;
634 else if (s_type && strcmp (CXML2C (s_type), "nlp") == 0)
635 type = GNM_SOLVER_NLP;
636 else {
637 *ret_error = go_error_info_new_str (_("Invalid solver model type."));
638 return;
640 xmlFree (s_type);
642 s_id = go_xml_node_get_cstr (tree, "id");
644 s_name = NULL;
645 information_node = go_xml_get_child_by_name (tree, "information");
646 if (information_node != NULL) {
647 xmlNode *node =
648 go_xml_get_child_by_name_by_lang (information_node,
649 "description");
650 if (node != NULL) {
651 s_name = xmlNodeGetContent (node);
655 if (!s_id || !s_name) {
656 *ret_error = go_error_info_new_str (_("Missing fields in plugin file"));
657 } else {
658 ssol->factory = gnm_solver_factory_new (CXML2C (s_id),
659 CXML2C (s_name),
660 type,
661 cb_load_and_create,
662 cb_load_and_functional);
663 g_object_set_data (G_OBJECT (ssol->factory), "ssol", ssol);
665 xmlFree (s_id);
666 xmlFree (s_name);
667 if (*ret_error)
668 return;
670 /* More? */
673 static void
674 plugin_service_solver_activate (GOPluginService *service, GOErrorInfo **ret_error)
676 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
678 GO_INIT_RET_ERROR_INFO (ret_error);
679 gnm_solver_db_register (ssol->factory);
680 service->is_active = TRUE;
683 static void
684 plugin_service_solver_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
686 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
688 GO_INIT_RET_ERROR_INFO (ret_error);
689 gnm_solver_db_unregister (ssol->factory);
690 service->is_active = FALSE;
693 static char *
694 plugin_service_solver_get_description (GOPluginService *service)
696 PluginServiceSolver *ssol = GNM_PLUGIN_SERVICE_SOLVER (service);
697 return g_strdup_printf (_("Solver Algorithm %s"),
698 ssol->factory->name);
701 static void
702 plugin_service_solver_class_init (GObjectClass *gobject_class)
704 GOPluginServiceClass *plugin_service_class = GO_PLUGIN_SERVICE_CLASS (gobject_class);
706 gobject_class->finalize = plugin_service_solver_finalize;
707 plugin_service_class->read_xml = plugin_service_solver_read_xml;
708 plugin_service_class->activate = plugin_service_solver_activate;
709 plugin_service_class->deactivate = plugin_service_solver_deactivate;
710 plugin_service_class->get_description = plugin_service_solver_get_description;
713 GSF_CLASS (PluginServiceSolver, gnm_plugin_service_solver,
714 plugin_service_solver_class_init, plugin_service_solver_init,
715 GO_TYPE_PLUGIN_SERVICE)
717 /****************************************************************************/
720 typedef GOPluginLoaderModule GnmPluginLoaderModule;
721 typedef GOPluginLoaderModuleClass GnmPluginLoaderModuleClass;
724 * Service - function_group
726 typedef struct {
727 GnmFuncDescriptor *module_fn_info_array;
728 GHashTable *function_indices;
729 } ServiceLoaderDataFunctionGroup;
731 static void
732 function_group_loader_data_free (gpointer data)
734 ServiceLoaderDataFunctionGroup *ld = data;
736 g_hash_table_destroy (ld->function_indices);
737 g_free (ld);
740 static gboolean
741 gnm_plugin_loader_module_func_desc_load (GOPluginService *service,
742 char const *name,
743 GnmFuncDescriptor *res)
745 ServiceLoaderDataFunctionGroup *loader_data;
746 gpointer func_index_ptr;
748 g_return_val_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service), FALSE);
749 g_return_val_if_fail (name != NULL, FALSE);
751 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
752 if (g_hash_table_lookup_extended (loader_data->function_indices, (gpointer) name,
753 NULL, &func_index_ptr)) {
754 int i = GPOINTER_TO_INT (func_index_ptr);
755 *res = loader_data->module_fn_info_array[i];
756 return TRUE;
758 return FALSE;
760 static void
761 gnm_plugin_loader_module_load_service_function_group (GOPluginLoader *loader,
762 GOPluginService *service,
763 GOErrorInfo **ret_error)
765 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
766 gchar *fn_info_array_name;
767 GnmFuncDescriptor *module_fn_info_array = NULL;
769 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (service));
771 GO_INIT_RET_ERROR_INFO (ret_error);
772 fn_info_array_name = g_strconcat (
773 go_plugin_service_get_id (service), "_functions", NULL);
774 g_module_symbol (loader_module->handle, fn_info_array_name, (gpointer) &module_fn_info_array);
775 if (module_fn_info_array != NULL) {
776 GnmPluginServiceFunctionGroupCallbacks *cbs;
777 ServiceLoaderDataFunctionGroup *loader_data;
778 gint i;
780 cbs = go_plugin_service_get_cbs (service);
781 cbs->func_desc_load = &gnm_plugin_loader_module_func_desc_load;
783 loader_data = g_new (ServiceLoaderDataFunctionGroup, 1);
784 loader_data->module_fn_info_array = module_fn_info_array;
785 loader_data->function_indices = g_hash_table_new (&g_str_hash, &g_str_equal);
786 for (i = 0; module_fn_info_array[i].name != NULL; i++) {
787 g_hash_table_insert (loader_data->function_indices,
788 (gpointer) module_fn_info_array[i].name,
789 GINT_TO_POINTER (i));
791 g_object_set_data_full (
792 G_OBJECT (service), "loader_data", loader_data, function_group_loader_data_free);
793 } else {
794 *ret_error = go_error_info_new_printf (
795 _("Module file \"%s\" has invalid format."),
796 loader_module->module_file_name);
797 go_error_info_add_details (*ret_error,
798 go_error_info_new_printf (
799 _("File doesn't contain \"%s\" array."),
800 fn_info_array_name));
802 g_free (fn_info_array_name);
806 * Service - ui
809 typedef struct {
810 GnmModulePluginUIActions *module_ui_actions_array;
811 GHashTable *ui_actions_hash;
812 } ServiceLoaderDataUI;
814 static void
815 ui_loader_data_free (gpointer data)
817 ServiceLoaderDataUI *ld = data;
819 g_hash_table_destroy (ld->ui_actions_hash);
820 g_free (ld);
823 static void
824 gnm_plugin_loader_module_func_exec_action (GOPluginService *service,
825 GnmAction const *action,
826 WorkbookControl *wbc,
827 GOErrorInfo **ret_error)
829 ServiceLoaderDataUI *loader_data;
830 gpointer action_index_ptr;
831 int action_index;
833 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
835 GO_INIT_RET_ERROR_INFO (ret_error);
836 loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
837 if (!g_hash_table_lookup_extended (loader_data->ui_actions_hash, action->id,
838 NULL, &action_index_ptr)) {
839 *ret_error = go_error_info_new_printf (_("Unknown action: %s"), action->id);
840 return;
842 action_index = GPOINTER_TO_INT (action_index_ptr);
843 if (NULL != loader_data->module_ui_actions_array [action_index].handler)
844 (*loader_data->module_ui_actions_array [action_index].handler) (action, wbc);
847 static void
848 gnm_plugin_loader_module_load_service_ui (GOPluginLoader *loader,
849 GOPluginService *service,
850 GOErrorInfo **ret_error)
852 GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
853 char *ui_actions_array_name;
854 GnmModulePluginUIActions *module_ui_actions_array = NULL;
855 GnmPluginServiceUICallbacks *cbs;
856 ServiceLoaderDataUI *loader_data;
857 gint i;
859 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_UI (service));
861 GO_INIT_RET_ERROR_INFO (ret_error);
862 ui_actions_array_name = g_strconcat (
863 go_plugin_service_get_id (service), "_ui_actions", NULL);
864 g_module_symbol (loader_module->handle, ui_actions_array_name, (gpointer) &module_ui_actions_array);
865 if (module_ui_actions_array == NULL) {
866 *ret_error = go_error_info_new_printf (
867 _("Module file \"%s\" has invalid format."),
868 loader_module->module_file_name);
869 go_error_info_add_details (*ret_error, go_error_info_new_printf (
870 _("File doesn't contain \"%s\" array."), ui_actions_array_name));
871 g_free (ui_actions_array_name);
872 return;
874 g_free (ui_actions_array_name);
876 cbs = go_plugin_service_get_cbs (service);
877 cbs->plugin_func_exec_action = gnm_plugin_loader_module_func_exec_action;
879 loader_data = g_new (ServiceLoaderDataUI, 1);
880 loader_data->module_ui_actions_array = module_ui_actions_array;
881 loader_data->ui_actions_hash = g_hash_table_new (g_str_hash, g_str_equal);
882 for (i = 0; module_ui_actions_array[i].name != NULL; i++)
883 g_hash_table_insert (loader_data->ui_actions_hash,
884 (gpointer) module_ui_actions_array[i].name,
885 GINT_TO_POINTER (i));
886 g_object_set_data_full (G_OBJECT (service),
887 "loader_data", loader_data, ui_loader_data_free);
890 static void
891 gnm_plugin_loader_module_load_service_solver (GOPluginLoader *loader,
892 GOPluginService *service,
893 GOErrorInfo **ret_error)
895 GnmPluginLoaderModule *loader_module =
896 GNM_PLUGIN_LOADER_MODULE (loader);
897 GnmPluginServiceSolverCallbacks *cbs;
898 char *symname;
899 GnmSolverCreator creator;
900 GnmSolverFactoryFunctional functional;
902 g_return_if_fail (GNM_IS_PLUGIN_SERVICE_SOLVER (service));
904 GO_INIT_RET_ERROR_INFO (ret_error);
906 symname = g_strconcat (go_plugin_service_get_id (service),
907 "_solver_factory",
908 NULL);
909 g_module_symbol (loader_module->handle, symname, (gpointer)&creator);
910 g_free (symname);
911 if (!creator) {
912 *ret_error = go_error_info_new_printf (
913 _("Module file \"%s\" has invalid format."),
914 loader_module->module_file_name);
915 return;
918 symname = g_strconcat (go_plugin_service_get_id (service),
919 "_solver_factory_functional",
920 NULL);
921 g_module_symbol (loader_module->handle, symname, (gpointer)&functional);
922 g_free (symname);
924 cbs = go_plugin_service_get_cbs (service);
925 cbs->creator = creator;
926 cbs->functional = functional;
929 static gboolean
930 gplm_service_load (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
932 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s))
933 gnm_plugin_loader_module_load_service_function_group (l, s, err);
934 else if (GNM_IS_PLUGIN_SERVICE_UI (s))
935 gnm_plugin_loader_module_load_service_ui (l, s, err);
936 else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s))
937 gnm_plugin_loader_module_load_service_solver (l, s, err);
938 else
939 return FALSE;
940 return TRUE;
943 static gboolean
944 gplm_service_unload (GOPluginLoader *l, GOPluginService *s, GOErrorInfo **err)
946 if (GNM_IS_PLUGIN_SERVICE_FUNCTION_GROUP (s)) {
947 GnmPluginServiceFunctionGroupCallbacks *cbs = go_plugin_service_get_cbs (s);
948 cbs->func_desc_load = NULL;
949 } else if (GNM_IS_PLUGIN_SERVICE_UI (s)) {
950 GnmPluginServiceUICallbacks *cbs = go_plugin_service_get_cbs (s);
951 cbs->plugin_func_exec_action = NULL;
952 } else if (GNM_IS_PLUGIN_SERVICE_SOLVER (s)) {
953 GnmPluginServiceSolverCallbacks *cbs =
954 go_plugin_service_get_cbs (s);
955 cbs->creator = NULL;
956 cbs->functional = NULL;
957 } else
958 return FALSE;
959 return TRUE;
962 static void
963 go_plugin_loader_module_iface_init (GOPluginLoaderClass *iface)
965 iface->service_load = gplm_service_load;
966 iface->service_unload = gplm_service_unload;
969 GSF_CLASS_FULL (GnmPluginLoaderModule, gnm_plugin_loader_module,
970 NULL, NULL, NULL, NULL,
971 NULL, GO_TYPE_PLUGIN_LOADER_MODULE, 0,
972 GSF_INTERFACE (go_plugin_loader_module_iface_init, GO_TYPE_PLUGIN_LOADER))
974 /****************************************************************************/
976 void
977 gnm_plugins_init (GOCmdContext *context)
979 char const *env_var;
980 GSList *dir_list = go_slist_create (
981 g_build_filename (gnm_sys_lib_dir (), PLUGIN_SUBDIR, NULL),
982 g_strdup (gnm_sys_extern_plugin_dir ()),
983 (gnm_usr_dir (TRUE) == NULL ? NULL :
984 g_build_filename (gnm_usr_dir (TRUE), PLUGIN_SUBDIR, NULL)),
985 NULL);
986 dir_list = g_slist_concat (dir_list,
987 go_string_slist_copy (gnm_conf_get_plugins_extra_dirs ()));
989 env_var = g_getenv ("GNUMERIC_PLUGIN_PATH");
990 if (env_var != NULL)
991 GO_SLIST_CONCAT (dir_list, go_strsplit_to_slist (env_var, G_SEARCHPATH_SEPARATOR));
993 go_plugins_init (GO_CMD_CONTEXT (context),
994 gnm_conf_get_plugins_file_states (),
995 gnm_conf_get_plugins_active (),
996 dir_list,
997 gnm_conf_get_plugins_activate_newplugins (),
998 gnm_plugin_loader_module_get_type ());