1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
D3DCOLOR_ARGB(a: DWORD, r: DWORD, g: DWORD, b: DWORD) -> D3DCOLOR15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301, USA.
20  */
21 
22 #include <string.h>
23 #include <gtk/gtk.h>
24 #include <glib/gi18n-lib.h>
25 #include <libgda/libgda.h>
26 #include <libgda-ui/gdaui-data-entry.h>
27 #include <libgda-ui/gdaui-plugin.h>
28 #include <libgda/binreloc/gda-binreloc.h>
29 #include "data-entries/gdaui-entry-boolean.h"
30 #include "data-entries/gdaui-entry-bin.h"
31 #include "data-entries/gdaui-entry-string.h"
32 #include "data-entries/gdaui-entry-number.h"
33 #include "data-entries/gdaui-entry-time.h"
34 #include "data-entries/gdaui-entry-date.h"
35 #include "data-entries/gdaui-entry-timestamp.h"
36 #include "data-entries/gdaui-entry-none.h"
37 #include "data-entries/gdaui-data-cell-renderer-textual.h"
38 #include "data-entries/gdaui-data-cell-renderer-boolean.h"
39 #include "data-entries/gdaui-data-cell-renderer-bin.h"
40 #include "gdaui-resources.h"
41 
42 /* plugins list */
43 
44 typedef GSList           *(*GdauiPluginInit)     (GError **);
45 static GHashTable *init_plugins_hash (void);
46 GHashTable *gdaui_plugins_hash = NULL; /* key = plugin name, value = GdauiPlugin structure pointer */
47 
48 
49 /**
50  * gdaui_init:
51  *
52  * Initialization of the libgda-ui library, must be called before any usage of the library.
53  *
54  * <itemizedlist>
55  * <listitem><para>Note 1: gtk_init() is not called by this function and should also
56  *   be called (see Note 2 about the importance of the calling order)</para></listitem>
57  * <listitem><para>Note 2: unless you call setlocale() first in your program, calling gtk_init()
58  *    before gdaui_init() will ensure that all information loaded from config files will
59  *    be loaded in the correct locale (as gtk_init() calls setlocale()).</para></listitem>
60  * <listitem><para>Note 3: this funtion also calls gda_init() so it should not be called
61  *    again</para></listitem>
62  * </itemizedlist>
63  *
64  * Since: 4.2
65  */
66 void
67 gdaui_init (void)
68 {
69 	static gboolean initialized = FALSE;
70 
71 	if (initialized) {
72 		gda_log_error (_("Attempt to initialize an already initialized library"));
73 		return;
74 	}
75 
76 	/*
77 	gchar *str;
78 	gdaui_gbr_init ();
79 	str = gdaui_gbr_get_locale_dir_path ();
80 	bindtextdomain (GETTEXT_PACKAGE, str);
81 	g_free (str);
82 	*/
83 
84 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
85 
86 	gda_init ();
87 	if (! gdaui_plugins_hash)
88 		gdaui_plugins_hash = init_plugins_hash ();
89 
90 	/* initialize CSS */
91 	GBytes *css_data;
92 	GError *error = NULL;
93 	_gdaui_register_resource ();
94 	css_data = g_resources_lookup_data ("/gdaui/gdaui.css", G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
95 	if (css_data) {
96 		GtkCssProvider *css_provider;
97 		css_provider = gtk_css_provider_new ();
98 		if (gtk_css_provider_load_from_data (css_provider,
99 						       (gchar*) g_bytes_get_data (css_data, NULL), -1,
100 						       &error))
101 			gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
102 								   GTK_STYLE_PROVIDER (css_provider),
103 								   G_MAXUINT);
104 		else {
105 			g_warning ("Could not parse resource CSS data: %s",
106 				   error && error->message ? error->message : _("No detail"));
107 			g_clear_error (&error);
108 		}
109 		g_object_unref (css_provider);
110 		g_bytes_unref (css_data);
111 	}
112 	else {
113 		g_warning ("Could not load resource CSS data: %s",
114 			   error && error->message ? error->message : _("No detail"));
115 		g_clear_error (&error);
116 	}
117 	_gdaui_unregister_resource ();
118 
119 	initialized = TRUE;
120 }
121 
122 /**
123  * gdaui_new_data_entry:
124  * @type: a #GType
125  * @plugin_name: (allow-none): the name of an entry plugin, or %NULL
126  *
127  * Creates a new #GdauiDataEntry widget, taking into account the requested entry name
128  * if @plugin_name is not %NULL (if no entry of that name is found, then the default data
129  * entry widget will be created).
130  *
131  * The @plugin_name format is interpreted as two parts: &lt;plugin name&gt;:&lt;plugin options&gt;, and
132  * if the plugins has no option, then the ":&lt;plugin options&gt;" part may be omitted.
133  *
134  * Returns: (transfer full): a new #GdauiDataEntry widget, _NEVER_ %NULL
135  */
136 GdauiDataEntry *
137 gdaui_new_data_entry (GType type, const gchar *plugin_name)
138 {
139 	GdaDataHandler *dh;
140 	GdauiDataEntry *entry = NULL;
141 	gchar *spec_options = NULL;
142 
143 	if (!gdaui_plugins_hash)
144 		gdaui_plugins_hash = init_plugins_hash ();
145 
146 	if (type == GDA_TYPE_NULL)
147 		return (GdauiDataEntry *) gdaui_entry_none_new (GDA_TYPE_NULL);
148 
149 	dh = gda_data_handler_get_default (type);
150 
151 	if (plugin_name && *plugin_name) {
152 		GdauiPlugin *plugin_struct;
153 		gchar *plugin = g_strdup (plugin_name);
154 		gchar *ptr, *options = NULL;
155 
156 		for (ptr = plugin; *ptr && (*ptr != ':'); ptr++);
157 		*ptr = 0;
158 		ptr++;
159 		if (ptr < plugin + strlen (plugin_name)) {
160 			options = ptr;
161 			spec_options = g_strdup (options);
162 		}
163 
164 		plugin_struct = g_hash_table_lookup (gdaui_plugins_hash, plugin);
165 		if (plugin_struct && plugin_struct->entry_create_func) {
166 			gboolean allok = TRUE;
167 			if (plugin_struct->nb_g_types > 0) {
168 				guint i;
169 				for (i = 0; i < plugin_struct->nb_g_types; i++) {
170 					if (plugin_struct->valid_g_types[i] == type)
171 						break;
172 				}
173 				if (i == plugin_struct->nb_g_types)
174 					allok = FALSE;
175 			}
176 			if (allok)
177 				entry = (plugin_struct->entry_create_func) (dh, type, options);
178 		}
179 		g_free (plugin);
180 	}
181 
182 	if (!entry) {
183 		if (type == G_TYPE_STRING)
184 			entry = (GdauiDataEntry *) gdaui_entry_string_new (dh, type, spec_options);
185 		else if ((type == G_TYPE_INT64) ||
186 			 (type == G_TYPE_UINT64) ||
187 			 (type == G_TYPE_DOUBLE) ||
188 			 (type == G_TYPE_INT) ||
189 			 (type == G_TYPE_LONG) ||
190 			 (type == GDA_TYPE_NUMERIC) ||
191 			 (type == G_TYPE_FLOAT) ||
192 			 (type == GDA_TYPE_SHORT) ||
193 			 (type == GDA_TYPE_USHORT) ||
194 			 (type == G_TYPE_CHAR) ||
195 			 (type == G_TYPE_UCHAR) ||
196 			 (type == G_TYPE_ULONG) ||
197 			 (type == G_TYPE_UINT))
198 			entry = (GdauiDataEntry *) gdaui_entry_number_new (dh, type, spec_options);
199 		else if (type == G_TYPE_BOOLEAN)
200 			entry = (GdauiDataEntry *) gdaui_entry_boolean_new (dh, G_TYPE_BOOLEAN);
201 		else if ((type == GDA_TYPE_BLOB) ||
202 			 (type == GDA_TYPE_BINARY))
203 			entry = (GdauiDataEntry *) gdaui_entry_bin_new (dh, type);
204 		else if	((type == GDA_TYPE_GEOMETRIC_POINT) ||
205 			 (type == G_TYPE_OBJECT))
206 			entry = (GdauiDataEntry *) gdaui_entry_none_new (type);
207 		else if	(type == GDA_TYPE_TIME)
208 			entry = (GdauiDataEntry *) gdaui_entry_time_new (dh);
209 		else if (type == GDA_TYPE_TIMESTAMP)
210 			entry = (GdauiDataEntry *) gdaui_entry_timestamp_new (dh);
211 		else if (type == G_TYPE_DATE)
212 			entry = (GdauiDataEntry *) gdaui_entry_date_new (dh);
213 		else
214 			entry = (GdauiDataEntry *) gdaui_entry_none_new (type);
215 	}
216 
217 	g_free (spec_options);
218 	return entry;
219 }
220 
221 /*
222  * _gdaui_new_cell_renderer
223  * @type: a #GType
224  * @plugin_name: (allow-none): the name of an entry plugin, or %NULL
225  *
226  * Creates a new #GtkCellRenderer object which is suitable to use in
227  * a #GtkTreeView widget, taking into account the requested entry name
228  * if @plugin_name is not %NULL (if no entry of that name is found, then the default data
229  * entry widget will be created).
230  *
231  * @plugin_name format is interpreted as two parts: &lt;plugin name&gt;:&lt;plugin options&gt;, and
232  * if the plugins has no option, then the ":&lt;plugin options&gt;" part may be omitted.
233  *
234  *
235  * Returns: a new #GtkCellRenderer object, _NEVER_ %NULL
236  */
237 GtkCellRenderer *
238 _gdaui_new_cell_renderer (GType type, const gchar *plugin_name)
239 {
240 	GdaDataHandler *dh;
241 	GtkCellRenderer *cell = NULL;
242 
243 	if (!gdaui_plugins_hash)
244 		gdaui_plugins_hash = init_plugins_hash ();
245 
246 	dh = gda_data_handler_get_default (type);
247 
248 	if (plugin_name && *plugin_name) {
249 		GdauiPlugin *plugin_struct;
250 		gchar *plugin = g_strdup (plugin_name);
251 		gchar *ptr, *options = NULL;
252 
253 		for (ptr = plugin; *ptr && (*ptr != ':'); ptr++);
254 		*ptr = 0;
255 		ptr++;
256 		if (ptr < plugin + strlen (plugin_name))
257 			options = ptr;
258 
259 		plugin_struct = g_hash_table_lookup (gdaui_plugins_hash, plugin);
260 		if (plugin_struct && plugin_struct->cell_create_func)
261 			cell = (plugin_struct->cell_create_func) (dh, type, options);
262 		g_free (plugin);
263 	}
264 
265 	if (!cell) {
266 		if (type == GDA_TYPE_NULL)
267 			cell = gdaui_data_cell_renderer_textual_new (NULL, GDA_TYPE_NULL, NULL);
268 		else if (type == G_TYPE_BOOLEAN)
269 			cell = gdaui_data_cell_renderer_boolean_new (dh, G_TYPE_BOOLEAN);
270 		else if ((type == GDA_TYPE_BLOB) ||
271 			 (type == GDA_TYPE_BINARY))
272 			cell = gdaui_data_cell_renderer_bin_new (dh, type);
273 		else
274 			cell = gdaui_data_cell_renderer_textual_new (dh, type, NULL);
275 	}
276 
277 	return cell;
278 }
279 
280 /**
281  * gdaui_plugin_declare:
282  * @plugin: a pointer to a structure filled to describe the new plugin. All the contained information is copied.
283  *
284  * Adds a new plugin which will be used by the forms and grids. The new plugin, as
285  * described by @plugin can declare a custom widget to be used for forms, grids, or both.
286  *
287  * If a plugin is already declared with the same name as the requested name, then
288  * a warning is issued and the operation fails.
289  */
290 void
291 gdaui_plugin_declare (const GdauiPlugin *plugin)
292 {
293 	GdauiPlugin *np;
294 
295 	g_return_if_fail (plugin);
296 	g_return_if_fail (plugin->plugin_name);
297 	if (!gdaui_plugins_hash)
298 		gdaui_plugins_hash = init_plugins_hash ();
299 	if (g_hash_table_lookup (gdaui_plugins_hash, plugin->plugin_name)) {
300 		g_warning ("Plugin '%s' already declared", plugin->plugin_name);
301 		return;
302 	}
303 	if (((plugin->nb_g_types < 1) && plugin->valid_g_types) ||
304 	    ((plugin->nb_g_types > 0) && !plugin->valid_g_types)) {
305 		g_warning ("Invalid description of plugin accepted types");
306 		return;
307 	}
308 	g_return_if_fail (plugin->entry_create_func || plugin->cell_create_func);
309 
310 	np = g_new0 (GdauiPlugin, 1);
311 	np->plugin_name = g_strdup (plugin->plugin_name);
312 	if (plugin->plugin_descr)
313 		np->plugin_descr = g_strdup (plugin->plugin_descr);
314 	np->plugin_file = g_strdup (plugin->plugin_file);
315 
316 	np->nb_g_types = plugin->nb_g_types;
317 	if (plugin->valid_g_types) {
318 		np->valid_g_types = g_new0 (GType, np->nb_g_types);
319 		memcpy (np->valid_g_types, plugin->valid_g_types, sizeof (GType) * np->nb_g_types);
320 	}
321 
322 	if (plugin->options_xml_spec)
323 		np->options_xml_spec = g_strdup (plugin->options_xml_spec);
324 	np->entry_create_func = plugin->entry_create_func;
325 	np->cell_create_func = plugin->cell_create_func;
326 
327 	g_hash_table_insert (gdaui_plugins_hash, np->plugin_name, np);
328 }
329 
330 static GdauiDataEntry *entry_none_create_func (GdaDataHandler *handler, GType type, const gchar *options);
331 static GdauiDataEntry *entry_boolean_create_func (GdaDataHandler *handler, GType type, const gchar *options);
332 static GdauiDataEntry *entry_bin_create_func (GdaDataHandler *handler, GType type, const gchar *options);
333 static GdauiDataEntry *entry_string_create_func (GdaDataHandler *handler, GType type, const gchar *options);
334 static GdauiDataEntry *entry_number_create_func (GdaDataHandler *handler, GType type, const gchar *options);
335 static GdauiDataEntry *entry_time_create_func (GdaDataHandler *handler, GType type, const gchar *options);
336 static GdauiDataEntry *entry_timestamp_create_func (GdaDataHandler *handler, GType type, const gchar *options);
337 static GdauiDataEntry *entry_date_create_func (GdaDataHandler *handler, GType type, const gchar *options);
338 
339 static GtkCellRenderer *cell_textual_create_func (GdaDataHandler *handler, GType type, const gchar *options);
340 static GtkCellRenderer *cell_boolean_create_func (GdaDataHandler *handler, GType type, const gchar *options);
341 static GtkCellRenderer *cell_bin_create_func (GdaDataHandler *handler, GType type, const gchar *options);
342 
343 static xmlChar *get_spec_with_isocodes (const gchar *file);
344 
345 static GHashTable *
346 init_plugins_hash (void)
347 {
348 	GHashTable *hash;
349 	GdauiPlugin *plugin;
350 	gchar *file;
351 
352 	hash = g_hash_table_new (g_str_hash, g_str_equal); /* key strings are not handled in the hash table */
353 
354 	/* default data entry widgets: they are not plugins but will be stored in GdauiPlugin structures */
355 	plugin = g_new0 (GdauiPlugin, 1);
356 	plugin->plugin_name = "none";
357 	plugin->plugin_descr = "Nothing displayed";
358 	plugin->plugin_file = NULL;
359 	plugin->nb_g_types = 0;
360 	plugin->valid_g_types = NULL;
361 	plugin->options_xml_spec = NULL;
362 	plugin->entry_create_func = entry_none_create_func;
363 	plugin->cell_create_func = NULL;
364 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
365 
366 	plugin = g_new0 (GdauiPlugin, 1);
367 	plugin->plugin_name = "boolean";
368 	plugin->plugin_descr = "Boolean entry";
369 	plugin->plugin_file = NULL;
370 	plugin->nb_g_types = 1;
371 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
372 	plugin->valid_g_types [0] = G_TYPE_BOOLEAN;
373 	plugin->options_xml_spec = NULL;
374 	plugin->entry_create_func = entry_boolean_create_func;
375 	plugin->cell_create_func = cell_boolean_create_func;
376 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
377 
378 	plugin = g_new0 (GdauiPlugin, 1);
379 	plugin->plugin_name = "binary";
380 	plugin->plugin_descr = "Binary data entry";
381 	plugin->plugin_file = NULL;
382 	plugin->nb_g_types = 2;
383 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
384 	plugin->valid_g_types [0] = GDA_TYPE_BLOB;
385 	plugin->valid_g_types [1] = GDA_TYPE_BINARY;
386 	plugin->options_xml_spec = NULL;
387 	plugin->entry_create_func = entry_bin_create_func;
388 	plugin->cell_create_func = cell_bin_create_func;
389 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
390 
391 	plugin = g_new0 (GdauiPlugin, 1);
392 	plugin->plugin_name = "string";
393 	plugin->plugin_descr = "String entry";
394 	plugin->plugin_file = NULL;
395 	plugin->nb_g_types = 1;
396 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
397 	plugin->valid_g_types [0] = G_TYPE_STRING;
398 	plugin->options_xml_spec = NULL;
399 	plugin->entry_create_func = entry_string_create_func;
400 	plugin->cell_create_func = cell_textual_create_func;
401 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
402 	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "ui", "gdaui-entry-string.xml", NULL);
403 	if (! g_file_test (file, G_FILE_TEST_EXISTS)) {
404 		g_message ("Could not find file '%s': '%s' data entry will not report any possible option",
405 			   file, plugin->plugin_name);
406         }
407 	else {
408 		gsize len;
409 		g_file_get_contents (file, &(plugin->options_xml_spec), &len, NULL);
410 	}
411 	g_free (file);
412 
413 	plugin = g_new0 (GdauiPlugin, 1);
414 	plugin->plugin_name = "number";
415 	plugin->plugin_descr = "Numeric entry";
416 	plugin->plugin_file = NULL;
417 	plugin->nb_g_types = 12;
418 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
419 	plugin->valid_g_types [0] = G_TYPE_INT64;
420 	plugin->valid_g_types [1] = G_TYPE_UINT64;
421 	plugin->valid_g_types [2] = G_TYPE_DOUBLE;
422 	plugin->valid_g_types [3] = G_TYPE_INT;
423 	plugin->valid_g_types [4] = GDA_TYPE_NUMERIC;
424 	plugin->valid_g_types [5] = G_TYPE_FLOAT;
425 	plugin->valid_g_types [6] = GDA_TYPE_SHORT;
426 	plugin->valid_g_types [7] = GDA_TYPE_USHORT;
427 	plugin->valid_g_types [8] = G_TYPE_CHAR;
428 	plugin->valid_g_types [9] = G_TYPE_UCHAR;
429 	plugin->valid_g_types [10] = G_TYPE_ULONG;
430 	plugin->valid_g_types [11] = G_TYPE_UINT;
431 	plugin->options_xml_spec = NULL;
432 	plugin->entry_create_func = entry_number_create_func;
433 	plugin->cell_create_func = cell_textual_create_func;
434 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
435 	file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "ui", "gdaui-entry-number.xml", NULL);
436 	xmlChar *xml_spec = get_spec_with_isocodes (file);
437 	if (xml_spec) {
438 		plugin->options_xml_spec = g_strdup ((gchar*) xml_spec);
439 		xmlFree (xml_spec);
440 	}
441 	g_free (file);
442 
443 	plugin = g_new0 (GdauiPlugin, 1);
444 	plugin->plugin_name = "textual";
445 	plugin->plugin_descr = "Textual entry";
446 	plugin->plugin_file = NULL;
447 	plugin->nb_g_types = 1;
448 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
449 	plugin->valid_g_types [0] = G_TYPE_STRING;
450 	plugin->options_xml_spec = NULL;
451 	plugin->entry_create_func = entry_string_create_func;
452 	plugin->cell_create_func = cell_textual_create_func;
453 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
454 
455 	plugin = g_new0 (GdauiPlugin, 1);
456 	plugin->plugin_name = "time";
457 	plugin->plugin_descr = "Time (HH:MM:SS) entry";
458 	plugin->plugin_file = NULL;
459 	plugin->nb_g_types = 1;
460 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
461 	plugin->valid_g_types [0] = GDA_TYPE_TIME;
462 	plugin->options_xml_spec = NULL;
463 	plugin->entry_create_func = entry_time_create_func;
464 	plugin->cell_create_func = NULL;
465 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
466 
467 	plugin = g_new0 (GdauiPlugin, 1);
468 	plugin->plugin_name = "timestamp";
469 	plugin->plugin_descr = "Timestamp (Date + HH:MM:SS) entry";
470 	plugin->plugin_file = NULL;
471 	plugin->nb_g_types = 1;
472 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
473 	plugin->valid_g_types [0] = GDA_TYPE_TIMESTAMP;
474 	plugin->options_xml_spec = NULL;
475 	plugin->entry_create_func = entry_timestamp_create_func;
476 	plugin->cell_create_func = NULL;
477 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
478 
479 	plugin = g_new0 (GdauiPlugin, 1);
480 	plugin->plugin_name = "date";
481 	plugin->plugin_descr = "Date entry";
482 	plugin->plugin_file = NULL;
483 	plugin->nb_g_types = 1;
484 	plugin->valid_g_types = g_new (GType, plugin->nb_g_types);
485 	plugin->valid_g_types [0] = G_TYPE_DATE;
486 	plugin->options_xml_spec = NULL;
487 	plugin->entry_create_func = entry_date_create_func;
488 	plugin->cell_create_func = NULL;
489 	g_hash_table_insert (hash, plugin->plugin_name, plugin);
490 
491 	/* plugins */
492 	GDir *dir;
493 	GError *err = NULL;
494 	gchar *plugins_dir;
495 	gboolean show_status = FALSE;
496 
497 	/* read the plugin directory */
498 	plugins_dir = gda_gbr_get_file_path (GDA_LIB_DIR, LIBGDA_ABI_NAME, "plugins", NULL);
499 	if (g_getenv ("GDAUI_SHOW_PLUGINS_LOADING"))
500 		show_status = TRUE;
501 	if (show_status)
502 		g_print ("Trying to load plugins in %s...\n", plugins_dir);
503 	dir = g_dir_open (plugins_dir, 0, NULL);
504 	if (!dir) {
505 		g_free (plugins_dir);
506 		plugins_dir = g_strdup (PLUGINSDIR);
507 		if (show_status)
508 			g_print ("Trying to load plugins in %s...\n", plugins_dir);
509 		dir = g_dir_open (plugins_dir, 0, NULL);
510 	}
511 	if (!dir && show_status)
512 		g_warning (_("Could not open plugins directory, no plugin loaded."));
513 	else {
514 		const gchar *name;
515 
516 		while ((name = g_dir_read_name (dir))) {
517 			gchar *ext;
518 			GModule *handle;
519 			gchar *path;
520 			GdauiPluginInit plugin_init;
521 			GSList *plugins;
522 
523 			ext = g_strrstr (name, ".");
524 			if (!ext)
525 				continue;
526 			if (strcmp (ext + 1, G_MODULE_SUFFIX))
527 				continue;
528 
529 			path = g_build_path (G_DIR_SEPARATOR_S, plugins_dir, name, NULL);
530 			handle = g_module_open (path, G_MODULE_BIND_LAZY);
531 			if (!handle) {
532 				g_warning (_("Error: %s"), g_module_error ());
533 				g_free (path);
534 				continue;
535 			}
536 
537 			g_module_symbol (handle, "plugin_init", (gpointer*) &plugin_init);
538 			if (plugin_init) {
539 				if (show_status)
540 					g_print (_("Loading file %s...\n"), path);
541 				plugins = plugin_init (&err);
542 				if (err) {
543 					if (show_status)
544 						g_message (_("Plugins load warning: %s"),
545 							   err->message ? err->message : _("No detail"));
546 					if (err)
547 						g_error_free (err);
548 					err = NULL;
549 				}
550 
551 				GSList *list;
552 				for (list = plugins; list; list = list->next) {
553 					GdauiPlugin *plugin;
554 
555 					plugin = (GdauiPlugin *)(list->data);
556 					g_hash_table_insert (hash, plugin->plugin_name, plugin);
557 					if (show_status) {
558 						g_print ("  - loaded %s (%s):", plugin->plugin_name,
559 							 plugin->plugin_descr);
560 						if (plugin->entry_create_func)
561 							g_print (" Entry");
562 						if (plugin->cell_create_func)
563 							g_print (" Cell");
564 						g_print ("\n");
565 					}
566 					plugin->plugin_file = g_strdup (path);
567 				}
568 				g_slist_free (plugins);
569 			}
570 			g_free (path);
571 		}
572 		g_dir_close (dir);
573 	}
574 	g_free (plugins_dir);
575 
576 	return hash;
577 }
578 
579 static GdauiDataEntry *
580 entry_none_create_func (G_GNUC_UNUSED GdaDataHandler *handler, GType type, G_GNUC_UNUSED const gchar *options)
581 {
582 	return (GdauiDataEntry *) gdaui_entry_none_new (type);
583 }
584 
585 static GdauiDataEntry *
586 entry_boolean_create_func (GdaDataHandler *handler, G_GNUC_UNUSED GType type,
587 			   G_GNUC_UNUSED const gchar *options)
588 {
589 	return (GdauiDataEntry *) gdaui_entry_boolean_new (handler, G_TYPE_BOOLEAN);
590 }
591 
592 static GdauiDataEntry *
593 entry_bin_create_func (GdaDataHandler *handler, GType type, G_GNUC_UNUSED const gchar *options)
594 {
595 	return (GdauiDataEntry *) gdaui_entry_bin_new (handler, type);
596 }
597 
598 static GdauiDataEntry *
599 entry_string_create_func (GdaDataHandler *handler, GType type, const gchar *options)
600 {
601 	return (GdauiDataEntry *) gdaui_entry_string_new (handler, type, options);
602 }
603 
604 static GdauiDataEntry *
605 entry_number_create_func (GdaDataHandler *handler, GType type, const gchar *options)
606 {
607 	return (GdauiDataEntry *) gdaui_entry_number_new (handler, type, options);
608 }
609 
610 static GdauiDataEntry *
611 entry_time_create_func (GdaDataHandler *handler, G_GNUC_UNUSED GType type, G_GNUC_UNUSED const gchar *options)
612 {
613 	return (GdauiDataEntry *) gdaui_entry_time_new (handler);
614 }
615 
616 static GdauiDataEntry *
617 entry_timestamp_create_func (GdaDataHandler *handler, G_GNUC_UNUSED GType type,
618 			     G_GNUC_UNUSED const gchar *options)
619 {
620 	return (GdauiDataEntry *) gdaui_entry_timestamp_new (handler);
621 }
622 
623 static GdauiDataEntry *
624 entry_date_create_func (GdaDataHandler *handler, G_GNUC_UNUSED GType type, G_GNUC_UNUSED const gchar *options)
625 {
626 	return (GdauiDataEntry *) gdaui_entry_date_new (handler);
627 }
628 
629 static GtkCellRenderer *
630 cell_textual_create_func (GdaDataHandler *handler, GType type, const gchar *options)
631 {
632 	return gdaui_data_cell_renderer_textual_new (handler, type, options);
633 }
634 
635 static GtkCellRenderer *
636 cell_boolean_create_func (GdaDataHandler *handler, G_GNUC_UNUSED GType type,
637 			  G_GNUC_UNUSED const gchar *options)
638 {
639 	return gdaui_data_cell_renderer_boolean_new (handler, G_TYPE_BOOLEAN);
640 }
641 
642 static GtkCellRenderer *
643 cell_bin_create_func (GdaDataHandler *handler, GType type, G_GNUC_UNUSED const gchar *options)
644 {
645 	return gdaui_data_cell_renderer_bin_new (handler, type);
646 }
647 
648 static xmlNodePtr
649 find_child_node_from_name (xmlNodePtr parent, const gchar *name, const gchar *attr_name, const gchar *attr_value)
650 {
651 	xmlNodePtr node;
652 
653 	if (!parent)
654 		return NULL;
655 
656 	for (node = parent->children; node; node = node->next) {
657 		if (!strcmp ((gchar*) node->name, name)) {
658 			if (attr_name) {
659 				xmlChar *prop;
660 				prop = xmlGetProp (node, BAD_CAST attr_name);
661 				if (prop) {
662 					if (attr_value && !strcmp ((gchar*) prop, attr_value)) {
663 						xmlFree (prop);
664 						break;
665 					}
666 					xmlFree (prop);
667 				}
668 			}
669 			else
670 				break;
671 		}
672 	}
673 	if (!node)
674 		g_warning ("Failed to find the <%s> tag", name);
675 
676 	return node;
677 }
678 
679 static xmlChar *
680 get_spec_with_isocodes (const gchar *file)
681 {
682 	xmlDocPtr spec, isocodes = NULL;
683 	xmlChar *retval = NULL;
684 	gchar *isofile = NULL;
685 	GError *err = NULL;
686 	gchar  *buf = NULL;
687 	int   buf_len;
688 
689 	/*
690 	 * Load iso codes
691 	 */
692 #define ISO_CODES_LOCALESDIR ISO_CODES_PREFIX "/share/locale"
693 
694 	bindtextdomain ("iso_4217", ISO_CODES_LOCALESDIR);
695 	bind_textdomain_codeset ("iso_4217", "UTF-8");
696 
697 	isofile = g_build_filename (ISO_CODES_PREFIX, "share", "xml", "iso-codes", "iso_4217.xml", NULL);
698 	if (g_file_get_contents (isofile, &buf, NULL, &err)) {
699 		isocodes = xmlParseDoc (BAD_CAST buf);
700 		g_free (buf);
701 		buf = NULL;
702 	}
703 
704 	/*
705 	 * Load spec string
706 	 */
707 	spec = xmlParseFile (file);
708 	if (!spec) {
709 		g_warning ("Can't load '%s' file", file);
710 		goto cleanup;
711 	}
712 
713 	if (isocodes) {
714 		/*
715 		 * Merge isocodes into spec
716 		 */
717 		xmlNodePtr node, spec_node;
718 
719 		node = find_child_node_from_name (xmlDocGetRootElement (spec), "sources", NULL, NULL);
720 		node = find_child_node_from_name (node, "gda_array", "name", "currencies");
721 		spec_node = find_child_node_from_name (node, "gda_array_data", NULL, NULL);
722 		xmlUnlinkNode (spec_node);
723 		xmlFreeNode (spec_node);
724 		spec_node = xmlNewChild (node, NULL, BAD_CAST "gda_array_data", NULL);
725 
726 		node = xmlDocGetRootElement (isocodes);
727 		for (node = node->children; node; node = node->next) {
728 			if (!strcmp ((gchar*) node->name, "iso_4217_entry")) {
729 				xmlChar *code, *name;
730 				code = xmlGetProp (node, BAD_CAST "letter_code");
731 				name = xmlGetProp (node, BAD_CAST "currency_name");
732 				if (code && name) {
733 					xmlNodePtr row;
734 					row = xmlNewChild (spec_node, NULL, BAD_CAST "gda_array_row", NULL);
735 					xmlNewChild (row, NULL, BAD_CAST "gda_value", code);
736 					xmlNewChild (row, NULL, BAD_CAST "gda_value", code);
737 					xmlNewChild (row, NULL, BAD_CAST "gda_value",
738 						     BAD_CAST dgettext ("iso_4217", (gchar*) name));
739 				}
740 				if (code)
741 					xmlFree (code);
742 				if (name)
743 					xmlFree (name);
744 			}
745 		}
746 	}
747 	else {
748 		/*
749 		 * No ISO CODES found => no predefined source
750 		 */
751 		xmlNodePtr node;
752 		node = find_child_node_from_name (xmlDocGetRootElement (spec), "sources", NULL, NULL);
753 		node = find_child_node_from_name (node, "gda_array", "name", "currencies");
754 		xmlUnlinkNode (node);
755 		xmlFreeNode (node);
756 
757 		node = find_child_node_from_name (xmlDocGetRootElement (spec), "parameters", NULL, NULL);
758 		node = find_child_node_from_name (node, "parameter", "id", "CURRENCY");
759 		xmlSetProp (node, BAD_CAST "source", NULL);
760 	}
761 
762 	xmlDocDumpMemory (spec, (xmlChar **) &retval, &buf_len);
763 
764  cleanup:
765 	if (spec)
766 		xmlFreeDoc (spec);
767 	if (isocodes)
768 		xmlFreeDoc (isocodes);
769 	g_free (isofile);
770 	g_free (buf);
771 
772 	return retval;
773 }
774 
775 static gchar *gdaui_path = NULL;
776 
777 /**
778  * gdaui_get_default_path:
779  *
780  * Get the default path used when saving a file, or when showing a #GtkFileChooser file chooser.
781  * When the application starts, the default path will be the same as the onde returned by
782  * g_get_current_dir().
783  *
784  * Returns: (transfer none): the default path, or %NULL
785  *
786  * Since: 4.2.9
787  */
788 const gchar *
789 gdaui_get_default_path (void)
790 {
791 	if (! gdaui_path)
792 		gdaui_path = g_get_current_dir ();
793 	return gdaui_path;
794 }
795 
796 /**
797  * gdaui_set_default_path:
798  * @path: (allow-none): a path, or %NULL to unset
799  *
800  * Define the default path used when saving a file, or when showing a #GtkFileChooser file chooser.
801  *
802  * Since: 4.2.9
803  */
804 void
805 gdaui_set_default_path (const gchar *path)
806 {
807 	g_free (gdaui_path);
808 	gdaui_path = NULL;
809 	if (path)
810 		gdaui_path = g_strdup (path);
811 }
812