1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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)
9 #include <gnumeric-config.h>
11 #include <tools/gnm-solver.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>
24 #define CXML2C(s) ((char const *)(s))
25 #define CC2XML(s) ((xmlChar const *)(s))
30 char *dst
= g_strdup (CXML2C (src
));
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
;
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
);
65 parent_class
= g_type_class_peek (GO_TYPE_PLUGIN_SERVICE
);
66 parent_class
->finalize (obj
);
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
))
83 translated_category_node
= go_xml_get_child_by_name_by_lang (tree
, "category");
84 if (translated_category_node
!= NULL
) {
87 lang
= go_xml_node_get_cstr (translated_category_node
, "lang");
89 translated_category_name
=
90 xml2c (xmlNodeGetContent (translated_category_node
));
93 translated_category_name
= NULL
;
96 translated_category_name
= NULL
;
98 functions_node
= go_xml_get_child_by_name (tree
, CC2XML ("functions"));
99 if (functions_node
!= NULL
) {
102 tdomain
= xml2c (go_xml_node_get_cstr (functions_node
, "textdomain"));
104 for (node
= functions_node
->xmlChildrenNode
; node
!= NULL
; node
= node
->next
) {
107 if (strcmp (CXML2C (node
->name
), "function") != 0)
110 func_name
= xml2c (go_xml_node_get_cstr (node
, "name"));
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
;
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
);
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
);
159 go_error_info_print (error
);
160 go_error_info_free (error
);
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
);
169 return sfg
->cbs
.func_desc_load (service
,
170 gnm_func_get_name (fn_def
, FALSE
),
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
));
182 go_plugin_use_unref (service
->plugin
);
184 go_plugin_use_ref (service
->plugin
);
189 delayed_ref_notify (GOPlugin
*plugin
, GnmFunc
*fd
)
191 g_signal_handlers_disconnect_by_func (plugin
,
192 G_CALLBACK (delayed_ref_notify
),
195 /* We cannot do this until after the plugin has been activated. */
196 plugin_service_function_group_func_ref_notify (fd
, 1);
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
);
211 (sfg
->function_name_list
, char, fname
,
214 fd
= gnm_func_lookup (fname
, NULL
);
217 g_printerr ("Reusing placeholder for %s\n", fname
);
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
,
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
),
232 G_CALLBACK (delayed_ref_notify
),
235 g_warning ("Multiple definitions of function %s -- this cannot be good!", fname
);
238 service
->is_active
= TRUE
;
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
;
257 plugin_service_function_group_get_description (GOPluginService
*service
)
259 GnmPluginServiceFunctionGroup
*sfg
= GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service
);
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\"",
272 n_functions
, category_name
);
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
;
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 /****************************************************************************/
307 typedef GOPluginServiceSimpleClass PluginServiceUIClass
;
308 struct GnmPluginServiceUI_
{
309 GOPluginServiceSimple base
;
315 GnmPluginServiceUICallbacks cbs
;
319 plugin_service_ui_init (PluginServiceUI
*s
)
321 GO_PLUGIN_SERVICE (s
)->cbs_ptr
= &s
->cbs
;
325 s
->cbs
.plugin_func_exec_action
= NULL
;
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
);
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
);
361 go_error_info_print (load_error
);
362 go_error_info_free (load_error
);
367 plugin_service_ui_read_xml (GOPluginService
*service
, xmlNode
*tree
, GOErrorInfo
**ret_error
)
369 PluginServiceUI
*service_ui
= GNM_PLUGIN_SERVICE_UI (service
);
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."));
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
;
386 gboolean always_available
;
389 for (ptr
= verbs_node
->xmlChildrenNode
; ptr
!= NULL
; ptr
= ptr
->next
) {
390 if (xmlIsBlankNode (ptr
) || ptr
->name
== NULL
||
391 strcmp (CXML2C (ptr
->name
), "action"))
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");
398 ? xml2c (xmlNodeGetContent (label_node
))
401 label_node
= go_xml_get_child_by_name_by_lang (ptr
, "label");
402 if (label_node
!= NULL
) {
405 lang
= go_xml_node_get_cstr (label_node
, "lang");
407 label
= xml2c (xmlNodeGetContent (label_node
));
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
);
419 if (NULL
!= icon
) xmlFree (icon
);
421 GO_SLIST_PREPEND (actions
, action
);
424 GO_SLIST_REVERSE (actions
);
426 service_ui
->file_name
= file_name
;
427 service_ui
->actions
= actions
;
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
;
437 GError
*error
= NULL
;
441 GO_INIT_RET_ERROR_INFO (ret_error
);
443 if (strncmp (uifile
, "res:", 4) == 0) {
445 gconstpointer data
= go_rsm_lookup (uifile
+ 4, &len
);
447 ? gsf_input_memory_new (data
, len
, FALSE
)
449 } else if (strncmp (uifile
, "data:", 5) == 0) {
450 const char *data
= uifile
+ 5;
451 src
= gsf_input_memory_new (data
, strlen (data
), FALSE
);
453 char *full_file_name
= g_path_is_absolute (uifile
)
456 (go_plugin_get_dir_name (service
->plugin
),
459 src
= gsf_input_stdio_new (full_file_name
, &error
);
460 g_free (full_file_name
);
465 src
= gsf_input_uncompress (src
);
466 len
= gsf_input_size (src
);
467 xml_ui
= g_strndup (gsf_input_read (src
, len
, NULL
), len
);
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
,
475 xml_ui
, tdomain
, service
);
478 g_object_unref (src
);
479 service
->is_active
= TRUE
;
483 *ret_error
= go_error_info_new_printf
484 (_("Cannot read UI description from %s: %s"),
486 error
? error
->message
: "?");
487 g_clear_error (&error
);
489 g_object_unref (src
);
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
;
504 plugin_service_ui_get_description (GOPluginService
*service
)
506 PluginServiceUI
*service_ui
= GNM_PLUGIN_SERVICE_UI (service
);
509 n_actions
= g_slist_length (service_ui
->actions
);
510 return g_strdup_printf (
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",
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
;
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
;
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
);
565 res
= ssol
->cbs
.creator (factory
, param
);
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
);
577 cb_load_and_functional (GnmSolverFactory
*factory
,
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
);
593 functional
= ssol
->cbs
.functional
;
594 return (functional
== NULL
|| functional (factory
, wbcg
));
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
;
606 plugin_service_solver_finalize (GObject
*obj
)
608 PluginServiceSolver
*ssol
= GNM_PLUGIN_SERVICE_SOLVER (obj
);
609 GObjectClass
*parent_class
;
612 g_object_unref (ssol
->factory
);
614 parent_class
= g_type_class_peek (GO_TYPE_PLUGIN_SERVICE
);
615 parent_class
->finalize (obj
);
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
;
637 *ret_error
= go_error_info_new_str (_("Invalid solver model type."));
642 s_id
= go_xml_node_get_cstr (tree
, "id");
645 information_node
= go_xml_get_child_by_name (tree
, "information");
646 if (information_node
!= NULL
) {
648 go_xml_get_child_by_name_by_lang (information_node
,
651 s_name
= xmlNodeGetContent (node
);
655 if (!s_id
|| !s_name
) {
656 *ret_error
= go_error_info_new_str (_("Missing fields in plugin file"));
658 ssol
->factory
= gnm_solver_factory_new (CXML2C (s_id
),
662 cb_load_and_functional
);
663 g_object_set_data (G_OBJECT (ssol
->factory
), "ssol", ssol
);
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
;
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
;
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
);
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
727 GnmFuncDescriptor
*module_fn_info_array
;
728 GHashTable
*function_indices
;
729 } ServiceLoaderDataFunctionGroup
;
732 function_group_loader_data_free (gpointer data
)
734 ServiceLoaderDataFunctionGroup
*ld
= data
;
736 g_hash_table_destroy (ld
->function_indices
);
741 gnm_plugin_loader_module_func_desc_load (GOPluginService
*service
,
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
];
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
;
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
);
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
);
810 GnmModulePluginUIActions
*module_ui_actions_array
;
811 GHashTable
*ui_actions_hash
;
812 } ServiceLoaderDataUI
;
815 ui_loader_data_free (gpointer data
)
817 ServiceLoaderDataUI
*ld
= data
;
819 g_hash_table_destroy (ld
->ui_actions_hash
);
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
;
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
);
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
);
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
;
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
);
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
);
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
;
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
),
909 g_module_symbol (loader_module
->handle
, symname
, (gpointer
)&creator
);
912 *ret_error
= go_error_info_new_printf (
913 _("Module file \"%s\" has invalid format."),
914 loader_module
->module_file_name
);
918 symname
= g_strconcat (go_plugin_service_get_id (service
),
919 "_solver_factory_functional",
921 g_module_symbol (loader_module
->handle
, symname
, (gpointer
)&functional
);
924 cbs
= go_plugin_service_get_cbs (service
);
925 cbs
->creator
= creator
;
926 cbs
->functional
= functional
;
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
);
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
);
956 cbs
->functional
= NULL
;
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 /****************************************************************************/
977 gnm_plugins_init (GOCmdContext
*context
)
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
)),
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");
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 (),
997 gnm_conf_get_plugins_activate_newplugins (),
998 gnm_plugin_loader_module_get_type ());