1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2010 - 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.
15  *
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 "gdaui-basic-form.h"
26 #include "marshallers/gdaui-marshal.h"
27 #include "gdaui-enums.h"
28 #include "internal/utility.h"
29 #include "gdaui-data-entry.h"
30 #include <libgda-ui/data-entries/gdaui-entry-combo.h>
31 #include <libgda-ui/gdaui-data-proxy.h>
32 #include <libgda-ui/gdaui-data-selector.h>
33 #include <libgda-ui/gdaui-raw-form.h>
34 #include <libgda-ui/gdaui-easy.h>
35 #include <libgda/binreloc/gda-binreloc.h>
36 #include <libgda/gda-debug-macros.h>
37 
38 #define SPACING 3
39 static void gdaui_basic_form_class_init (GdauiBasicFormClass * class);
40 static void gdaui_basic_form_init (GdauiBasicForm *wid);
41 static void gdaui_basic_form_dispose (GObject *object);
42 
43 static void gdaui_basic_form_set_property (GObject *object,
44 					   guint param_id,
45 					   const GValue *value,
46 					   GParamSpec *pspec);
47 static void gdaui_basic_form_get_property (GObject *object,
48 					   guint param_id,
49 					   GValue *value,
50 					   GParamSpec *pspec);
51 static void gdaui_basic_form_widget_grab_focus (GtkWidget *widget);
52 
53 typedef struct {
54 	GdaHolder *holder;
55 	gulong     signal_id;
56 } SignalData;
57 
58 typedef enum {
59 	PACKING_TABLE,
60 } PackingType;
61 
62 typedef struct {
63 	GtkWidget *grid_table;
64 	gint top;
65 } PackingTable;
66 
67 typedef struct {
68 	GdauiBasicForm *form;
69 	GdauiDataEntry *entry; /* ref held here */
70 	GtkWidget      *label; /* ref held here */
71 	gchar          *label_title;
72 	gboolean        prog_hidden; /* status as requested by the programmer */
73 	gboolean        hidden; /* real status of the data entry */
74 	gboolean        not_null; /* TRUE if @entry's contents can't be NULL */
75 	gboolean        forward_param_updates; /* forward them to the GdauiDataEntry widgets ? */
76 
77 	gulong          entry_shown_id; /* signal ID */
78 	gulong          label_shown_id; /* signal ID */
79 
80 	gulong          entry_contents_modified_id; /* signal ID */
81 	gulong          entry_contents_activated_id; /* signal ID */
82 
83 	GdaHolder      *single_param;
84 	SignalData      single_signal;
85 
86 	GdauiSetGroup  *group;
87 	GArray         *group_signals; /* array of SignalData */
88 
89 	/* entry packing function */
90 	PackingType     packing_type;
91 	union {
92 		PackingTable table;
93 	}               pack;
94 
95 } SingleEntry;
96 static void real_gdaui_basic_form_entry_set_visible (GdauiBasicForm *form,
97 						      SingleEntry *sentry, gboolean show);
98 static SingleEntry *get_single_entry_for_holder (GdauiBasicForm *form, GdaHolder *param);
99 static SingleEntry *get_single_entry_for_id (GdauiBasicForm *form, const gchar *id);
100 static void create_entry_widget (SingleEntry *sentry);
101 static void create_entries (GdauiBasicForm *form);
102 static void pack_entry_widget (SingleEntry *sentry);
103 static void pack_entries_in_table (GdauiBasicForm *form);
104 static void pack_entries_in_xml_layout (GdauiBasicForm *form, xmlNodePtr form_node);
105 static void unpack_entries (GdauiBasicForm *form);
106 static void destroy_entries (GdauiBasicForm *form);
107 static gchar *create_text_label_for_sentry (SingleEntry *sentry, gchar **out_title);
108 
109 static void gdaui_basic_form_show_entry_actions (GdauiBasicForm *form, gboolean show_actions);
110 static void gdaui_basic_form_set_entries_auto_default (GdauiBasicForm *form, gboolean auto_default);
111 
112 static void get_rid_of_set (GdaSet *paramlist, GdauiBasicForm *form);
113 static void paramlist_public_data_changed_cb (GdauiSet *paramlist, GdauiBasicForm *form);
114 static void paramlist_param_attr_changed_cb (GdaSet *paramlist, GdaHolder *param,
115 					     const gchar *att_name, const GValue *att_value, GdauiBasicForm *form);
116 static void paramlist_holder_type_set_cb (GdaSet *paramlist, GdaHolder *param,
117 					  GdauiBasicForm *form);
118 
119 static void entry_contents_modified (GdauiDataEntry *entry, SingleEntry *sentry);
120 static void entry_contents_activated (GdauiDataEntry *entry, GdauiBasicForm *form);
121 static void parameter_changed_cb (GdaHolder *param, SingleEntry *sentry);
122 
123 static void mark_not_null_entry_labels (GdauiBasicForm *form, gboolean show_mark);
findFirstEqualType(DumpableObjectType type,DumpableObject ** objs,int numObjs)124 enum {
125 	HOLDER_CHANGED,
126 	ACTIVATED,
127 	LAYOUT_CHANGED,
128 	POPULATE_POPUP,
129 	LAST_SIGNAL
130 };
131 
132 /* properties */
133 enum {
134 	PROP_0,
findFirstDifferentType(DumpableObjectType type,DumpableObject ** objs,int numObjs,int start)135 	PROP_XML_LAYOUT,
136 	PROP_PARAMLIST,
137 	PROP_HEADERS_SENSITIVE,
138 	PROP_SHOW_ACTIONS,
139 	PROP_ENTRIES_AUTO_DEFAULT,
140 	PROP_CAN_VEXPAND
141 };
142 
143 typedef struct {
144 	GtkSizeGroup *size_group; /* ref held here */
145 	GdauiBasicFormPart part;
146 } SizeGroup;
147 static void
148 size_group_free (SizeGroup *sg)
149 {
150 	g_object_unref (sg->size_group);
151 	g_free (sg);
152 }
153 
154 struct _GdauiBasicFormPriv
155 {
156 	GdaSet     *set;
sortDataAndIndexObjectsBySize(DumpableObject ** objs,int numObjs)157 	GdauiSet   *set_info;
158 	GSList     *s_entries;/* list of SingleEntry pointers */
159 	GHashTable *place_holders; /* key = place holder ID, value = a GtkWidget pointer */
160 
161 	GtkWidget  *top_container;
162 
163 	gboolean    show_actions;
164 	gboolean    entries_auto_default;
165 
166 	GSList     *size_groups; /* list of SizeGroup pointers */
167 
168 	GtkWidget  *mainbox;
169 
170 	/* unknown value color */
171 	gdouble     red;
172 	gdouble     green;
173 	gdouble     blue;
174 	gdouble     alpha;
175 };
176 
177 
178 static guint gdaui_basic_form_signals[LAST_SIGNAL] = { 0, 0 };
179 
180 /* get a pointer to the parents to be able to call their destructor */
181 static GObjectClass *parent_class = NULL;
182 
183 GType
184 gdaui_basic_form_get_type (void)
185 {
DOSizeCompare(const void * p1,const void * p2)186 	static GType type = 0;
187 
188 	if (G_UNLIKELY (type == 0)) {
189 		static const GTypeInfo info = {
190 			sizeof (GdauiBasicFormClass),
191 			(GBaseInitFunc) NULL,
192 			(GBaseFinalizeFunc) NULL,
193 			(GClassInitFunc) gdaui_basic_form_class_init,
194 			NULL,
195 			NULL,
196 			sizeof (GdauiBasicForm),
197 			0,
198 			(GInstanceInitFunc) gdaui_basic_form_init,
199 			0
200 		};
201 
202 		type = g_type_register_static (GTK_TYPE_BOX, "GdauiBasicForm", &info, 0);
203 	}
204 
205 	return type;
206 }
207 
208 static void
209 gdaui_basic_form_class_init (GdauiBasicFormClass *klass)
210 {
211 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
212 
213 	parent_class = g_type_class_peek_parent (klass);
214 	GTK_WIDGET_CLASS (klass)->grab_focus = gdaui_basic_form_widget_grab_focus;
215 
216 	/* signals */
217 	/**
218 	 * GdauiBasicForm::holder-changed:
sortDumpableObjectsByTypeName(DumpableObject ** objs,int numObjs)219 	 * @form: #GdauiBasicForm
220 	 * @param: the #GdaHolder that changed
221 	 * @is_user_modif: TRUE if the modification has been initiated by a user modification
222 	 *
223 	 * Emitted when a GdaHolder changed in @form
224 	 */
225 	gdaui_basic_form_signals[HOLDER_CHANGED] =
226 		g_signal_new ("holder-changed",
227 			      G_TYPE_FROM_CLASS (object_class),
228 			      G_SIGNAL_RUN_FIRST,
229 			      G_STRUCT_OFFSET (GdauiBasicFormClass, holder_changed),
230 			      NULL, NULL,
231 			      _gdaui_marshal_VOID__OBJECT_BOOLEAN, G_TYPE_NONE, 2,
232 			      GDA_TYPE_HOLDER, G_TYPE_BOOLEAN);
233 	/**
234 	 * GdauiBasicForm::activated:
235 	 * @form: #GdauiBasicForm
236 	 *
237 	 * Emitted when the use has activated any of the #GdaDataEntry widget
238 	 * in @form.
239 	 */
240 	gdaui_basic_form_signals[ACTIVATED] =
241 		g_signal_new ("activated",
242 			      G_TYPE_FROM_CLASS (object_class),
243 			      G_SIGNAL_RUN_FIRST,
244 			      G_STRUCT_OFFSET (GdauiBasicFormClass, activated),
245 			      NULL, NULL,
246 			      _gdaui_marshal_VOID__VOID, G_TYPE_NONE, 0);
247 
248 	/**
249 	 * GdauiBasicForm::layout-changed:
250 	 * @form: #GdauiBasicForm
251 	 *
252 	 * Emitted when the form's layout changes
253 	 */
254 	gdaui_basic_form_signals[LAYOUT_CHANGED] =
255 		g_signal_new ("layout-changed",
256 			      G_TYPE_FROM_CLASS (object_class),
257 			      G_SIGNAL_RUN_FIRST,
258 			      G_STRUCT_OFFSET (GdauiBasicFormClass, layout_changed),
259 			      NULL, NULL,
260 			      _gdaui_marshal_VOID__VOID, G_TYPE_NONE, 0);
261 
262 	/**
263 	 * GdauiBasicForm::populate-popup:
264 	 * @form: #GdauiBasicForm
265 	 * @menu: a #GtkMenu to modify
266 	 *
267 	 * Connect this signal and modify the popup menu.
268 	 *
269 	 * Since: 4.2.4
270 	 */
271 	gdaui_basic_form_signals[POPULATE_POPUP] =
272 		g_signal_new ("populate-popup",
273                               G_TYPE_FROM_CLASS (object_class),
274                               G_SIGNAL_RUN_FIRST,
275                               0,
276                               NULL, NULL,
277                               _gdaui_marshal_VOID__OBJECT, G_TYPE_NONE,
278 			      1, GTK_TYPE_MENU);
279 
280 	klass->holder_changed = NULL;
281 	klass->activated = NULL;
282 	klass->layout_changed = NULL;
283 	object_class->dispose = gdaui_basic_form_dispose;
284 
285 	/* Properties */
286         object_class->set_property = gdaui_basic_form_set_property;
287         object_class->get_property = gdaui_basic_form_get_property;
288 
289 	g_object_class_install_property (object_class, PROP_XML_LAYOUT,
290 					 g_param_spec_pointer ("xml-layout",
291 							       _("Pointer to an XML layout specification  (as an xmlNodePtr to a <gdaui_form> node)"), NULL,
292 							       G_PARAM_WRITABLE));
293 	g_object_class_install_property (object_class, PROP_PARAMLIST,
294 					 g_param_spec_pointer ("paramlist",
295 							       _("List of parameters to show in the form"), NULL,
296                                                                G_PARAM_READABLE | G_PARAM_WRITABLE));
297 	/**
298 	 * GdauiBasicForm:headers-sensitive:
299 	 *
300 	 * Deprecated
301 	 */
302 	g_object_class_install_property (object_class, PROP_HEADERS_SENSITIVE,
303 					 g_param_spec_boolean ("headers-sensitive",
304 							       "",
305 							       NULL, FALSE,
306 							       G_PARAM_READABLE | G_PARAM_WRITABLE));
307 	g_object_class_install_property (object_class, PROP_SHOW_ACTIONS,
308 					 g_param_spec_boolean ("show-actions",
309 							       _("Show Entry actions"),
310 							       NULL, FALSE,
311 							       G_PARAM_READABLE | G_PARAM_WRITABLE));
312 	g_object_class_install_property (object_class, PROP_ENTRIES_AUTO_DEFAULT,
313 					 g_param_spec_boolean ("entries-auto-default",
314 							       _("Entries Auto-default"),
315 							       NULL, FALSE,
316 							       G_PARAM_READABLE | G_PARAM_WRITABLE));
317 	g_object_class_install_property (object_class, PROP_CAN_VEXPAND,
318 					 g_param_spec_boolean ("can-expand-v",
319 							       _("TRUE if expanding the form vertically makes sense"),
320 							       NULL, FALSE,
321 							       G_PARAM_READABLE));
322 
323 }
324 
325 static void
326 hidden_entry_mitem_toggled_cb (GtkCheckMenuItem *check, GdauiBasicForm *form)
327 {
328 	SingleEntry *sentry;
329 	sentry = g_object_get_data (G_OBJECT (check), "s");
330 	g_assert (sentry);
331 	real_gdaui_basic_form_entry_set_visible (form, sentry,
332 						 gtk_check_menu_item_get_active (check));
333 }
334 
335 static void
336 do_popup_menu (GdauiBasicForm *form, GdkEventButton *event)
337 {
338 	int button, event_time;
339 	GtkWidget *menu, *submenu, *mitem;
340 	GSList *list;
341 
342 	menu = gtk_menu_new ();
343 	mitem = gtk_menu_item_new_with_label (_("Shown data entries"));
344 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
345 	gtk_widget_show (mitem);
346 
347 	submenu = gtk_menu_new ();
348 	gtk_widget_show (submenu);
349 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), submenu);
350 
351 	for (list = form->priv->s_entries; list; list = list->next) {
sortDumpableObjects(DumpableObject ** objs,int numObjs,DumpId preBoundaryId,DumpId postBoundaryId)352 		SingleEntry *sentry = (SingleEntry*) list->data;
353 		if (sentry->prog_hidden)
354 			continue;
355 		mitem = gtk_check_menu_item_new_with_label (sentry->label_title);
356 		gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mitem), !sentry->hidden);
357 		gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem);
358 		gtk_widget_show (mitem);
359 
360 		g_object_set_data (G_OBJECT (mitem), "s", sentry);
361 		g_signal_connect (mitem, "toggled",
362 				  G_CALLBACK (hidden_entry_mitem_toggled_cb), form);
363 	}
364 
365 	if (event) {
366 		button = event->button;
367 		event_time = event->time;
368 	}
369 	else {
370 		button = 0;
371 		event_time = gtk_get_current_event_time ();
372 	}
373 
374 	/* allow listeners to add their custom menu items */
375 	g_signal_emit (G_OBJECT (form), gdaui_basic_form_signals [POPULATE_POPUP], 0, GTK_MENU (menu));
376 
377 	gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
378 			button, event_time);
379 }
380 
381 static gboolean
382 popup_menu_cb (G_GNUC_UNUSED GtkWidget *wid, GdauiBasicForm *form)
383 {
384 	do_popup_menu (form, NULL);
385 	return TRUE;
386 }
387 
388 static gboolean
389 button_press_event_cb (G_GNUC_UNUSED GtkWidget *wid, GdkEventButton *event, GdauiBasicForm *form)
390 {
391 	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
392 		do_popup_menu (form, event);
393 		return TRUE;
394 	}
395 
396 	return FALSE;
397 }
398 
399 static void
400 gdaui_basic_form_init (GdauiBasicForm *wid)
401 {
402 	GtkWidget *evbox;
403 	wid->priv = g_new0 (GdauiBasicFormPriv, 1);
TopoSort(DumpableObject ** objs,int numObjs,DumpableObject ** ordering,int * nOrdering)404 	wid->priv->set = NULL;
405 	wid->priv->s_entries = NULL;
406 	wid->priv->place_holders = NULL;
407 	wid->priv->top_container = NULL;
408 
409 	wid->priv->show_actions = FALSE;
410 	wid->priv->entries_auto_default = FALSE;
411 
412 	gtk_orientable_set_orientation (GTK_ORIENTABLE (wid), GTK_ORIENTATION_VERTICAL);
413 
414 	evbox = gtk_event_box_new ();
415 	gtk_widget_show (evbox);
416 	gtk_box_pack_start (GTK_BOX (wid), evbox, TRUE, TRUE, 0);
417 	wid->priv->mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
418 
419 	gtk_widget_show (wid->priv->mainbox);
420 	gtk_container_add (GTK_CONTAINER (evbox), wid->priv->mainbox);
421 	g_object_set (evbox, "visible-window", FALSE, NULL);
422 
423 	g_signal_connect (evbox, "popup-menu",
424 			  G_CALLBACK (popup_menu_cb), wid);
425 	g_signal_connect (evbox, "button-press-event",
426 			  G_CALLBACK (button_press_event_cb), wid);
427 
428 	wid->priv->red = .98;
429 	wid->priv->green = .93;
430 	wid->priv->blue = .25;
431 	wid->priv->alpha = .50;
432 }
433 
434 /**
435  * gdaui_basic_form_new:
436  * @data_set: a #GdaSet structure
437  *
438  * Creates a new #GdauiBasicForm widget using all the #GdaHolder objects provided in @data_set.
439  *
440  * The global layout is rendered using a table (a #GtkTable), and an entry is created for each
441  * node of @data_set.
442  *
443  * Returns: (transfer full): the new widget
444  *
445  * Since: 4.2
446  */
447 GtkWidget *
448 gdaui_basic_form_new (GdaSet *data_set)
449 {
450 	GObject *obj;
451 
452 	obj = g_object_new (GDAUI_TYPE_BASIC_FORM, "paramlist", data_set, NULL);
453 
454 	return (GtkWidget *) obj;
455 }
456 
457 static void
458 widget_shown_cb (GtkWidget *wid, SingleEntry *sentry)
459 {
460 	g_assert ((wid == (GtkWidget*) sentry->entry) || (wid == sentry->label));
461 	if (sentry->hidden)
462 		gtk_widget_hide (wid);
463 }
464 
465 static void
466 get_rid_of_set (GdaSet *paramlist, GdauiBasicForm *form)
467 {
468 	GSList *list;
469 
470 	g_assert (paramlist == form->priv->set);
471 
472 	/* unref the paramlist */
473 	g_signal_handlers_disconnect_by_func (form->priv->set_info,
474 					      G_CALLBACK (paramlist_public_data_changed_cb), form);
475 
476 	g_signal_handlers_disconnect_by_func (paramlist,
477 					      G_CALLBACK (paramlist_param_attr_changed_cb), form);
478 	g_signal_handlers_disconnect_by_func (paramlist,
479 					      G_CALLBACK (paramlist_holder_type_set_cb), form);
480 
481 	g_object_unref (form->priv->set);
482 	form->priv->set = NULL;
483 
484 	if (form->priv->set_info) {
485 		g_object_unref (form->priv->set_info);
486 		form->priv->set_info = NULL;
487 	}
488 
489 	/* render all the entries non sensitive */
490 	for (list = form->priv->s_entries; list; list = list->next)
491 		gdaui_data_entry_set_editable (GDAUI_DATA_ENTRY (((SingleEntry*)list->data)->entry), FALSE);
492 }
493 
494 static void
495 paramlist_holder_type_set_cb (G_GNUC_UNUSED GdaSet *paramlist, GdaHolder *param,
496 			      GdauiBasicForm *form)
497 {
498 	SingleEntry *sentry;
499 
500 	sentry = get_single_entry_for_holder (form, param);
501 	if (sentry) {
502 		create_entry_widget (sentry);
503 		gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (sentry->entry),
504 						 form->priv->show_actions ? GDA_VALUE_ATTR_ACTIONS_SHOWN : 0,
505 						 GDA_VALUE_ATTR_ACTIONS_SHOWN);
506 		pack_entry_widget (sentry);
507 		gdaui_basic_form_entry_set_visible (form, param, !sentry->hidden);
508 	}
509 }
510 
511 
512 static void
513 paramlist_public_data_changed_cb (G_GNUC_UNUSED GdauiSet *paramlist, GdauiBasicForm *form)
514 {
515 	/* here we want to re-define all the data entry widgets */
516 	destroy_entries (form);
517 	create_entries (form);
518 	pack_entries_in_table (form);
519 	g_signal_emit (G_OBJECT (form), gdaui_basic_form_signals[LAYOUT_CHANGED], 0);
520 }
521 
522 static void
523 paramlist_param_attr_changed_cb (G_GNUC_UNUSED GdaSet *paramlist, GdaHolder *param,
524 				 const gchar *att_name, G_GNUC_UNUSED const GValue *att_value,
525 				 GdauiBasicForm *form)
526 {
527 	SingleEntry *sentry;
528 
529 	sentry = get_single_entry_for_holder (form, param);
530 
531 	if (!strcmp (att_name, GDA_ATTRIBUTE_IS_DEFAULT)) {
532 		GtkWidget *entry = NULL;
533 		if (sentry)
534 			entry = (GtkWidget*) sentry->entry;
535 		if (entry) {
536 			guint attrs = 0;
537 			guint mask = 0;
538 			const GValue *defv;
539 			gboolean toset;
540 
541 			defv = gda_holder_get_default_value (param);
542 			attrs |= defv ? GDA_VALUE_ATTR_CAN_BE_DEFAULT : 0;
543 			mask |= GDA_VALUE_ATTR_CAN_BE_DEFAULT;
544 
545 			toset = gda_holder_get_not_null (param);
546 			attrs |= toset ? 0 : GDA_VALUE_ATTR_CAN_BE_NULL;
547 			mask |= GDA_VALUE_ATTR_CAN_BE_NULL;
addHeapElement(int val,int * heap,int heapLength)548 
549 			defv = gda_holder_get_attribute (param, GDA_ATTRIBUTE_IS_DEFAULT);
550 			if (defv && (G_VALUE_TYPE (defv) == G_TYPE_BOOLEAN) && g_value_get_boolean (defv)) {
551 				attrs |= GDA_VALUE_ATTR_IS_DEFAULT;
552 				mask |= GDA_VALUE_ATTR_IS_DEFAULT;
553 			}
554 
555 			g_signal_handlers_block_by_func (G_OBJECT (entry),
556 							 G_CALLBACK (entry_contents_modified), sentry);
557 			gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (entry), attrs, mask);
558 			g_signal_handlers_unblock_by_func (G_OBJECT (entry),
559 							   G_CALLBACK (entry_contents_modified), sentry);
560 		}
561 	}
562 	else if (!strcmp (att_name, GDAUI_ATTRIBUTE_PLUGIN)) {
563 		if (sentry) {
564 			/* recreate an entry widget */
565 			create_entry_widget (sentry);
566 			gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (sentry->entry),
567 							 form->priv->show_actions ? GDA_VALUE_ATTR_ACTIONS_SHOWN : 0,
568 							 GDA_VALUE_ATTR_ACTIONS_SHOWN);
569 			pack_entry_widget (sentry);
570 			gdaui_basic_form_entry_set_visible (form, param, !sentry->hidden);
571 		}
572 		else
573 			paramlist_public_data_changed_cb (form->priv->set_info, form);
574 	}
575 	else if (!strcmp (att_name, GDA_ATTRIBUTE_NAME) ||
576 		 !strcmp (att_name, GDA_ATTRIBUTE_DESCRIPTION)) {
577 		if (sentry) {
578 			gchar *str, *title;
removeHeapElement(int * heap,int heapLength)579 			GdaSetSource *ss;
580 			str = create_text_label_for_sentry (sentry, &title);
581 			gtk_label_set_text (GTK_LABEL (sentry->label), str);
582 			g_free (str);
583 			g_free (sentry->label_title);
584 			sentry->label_title = title;
585 			ss =  gda_set_group_get_source (gdaui_set_group_get_group (sentry->group));
586 			if (!ss) {
587 				g_object_get (G_OBJECT (param), "description", &title, NULL);
588 				if (title && *title)
589 					gtk_widget_set_tooltip_text (sentry->label, title);
590 				g_free (title);
591 			}
592 			else {
593 				title = g_object_get_data (G_OBJECT (gda_set_source_get_data_model (ss)),
594 							   "descr");
595 				if (title && *title)
596 					gtk_widget_set_tooltip_text (sentry->label, title);
597 			}
598 		}
599 		else
600 			paramlist_public_data_changed_cb (form->priv->set_info, form);
601 	}
602 }
603 
604 static void
605 gdaui_basic_form_dispose (GObject *object)
606 {
607 	GdauiBasicForm *form;
608 
609 	g_return_if_fail (object != NULL);
610 	g_return_if_fail (GDAUI_IS_BASIC_FORM (object));
611 	form = GDAUI_BASIC_FORM (object);
612 
613 	if (form->priv) {
614 		/* paramlist */
615 		if (form->priv->set)
616 			get_rid_of_set (form->priv->set, form);
617 
618 		destroy_entries (form);
619 
620 		if (form->priv->size_groups) {
621 			g_slist_foreach (form->priv->size_groups, (GFunc) size_group_free, NULL);
622 			g_slist_free (form->priv->size_groups);
623 		}
findDependencyLoops(DumpableObject ** objs,int nObjs,int totObjs)624 
625 		/* the private area itself */
626 		g_free (form->priv);
627 		form->priv = NULL;
628 	}
629 
630 	/* for the parent class */
631 	parent_class->dispose (object);
632 }
633 
634 
635 static void
636 gdaui_basic_form_set_property (GObject *object,
637 			       guint param_id,
638 			       const GValue *value,
639 			       GParamSpec *pspec)
640 {
641 	GdauiBasicForm *form;
642 
643         form = GDAUI_BASIC_FORM (object);
644         if (form->priv) {
645                 switch (param_id) {
646 		case PROP_XML_LAYOUT: {
647 			/* node should be a "gdaui_form" node */
648 			xmlNodePtr node = g_value_get_pointer (value);
649 			if (node) {
650 				g_return_if_fail (node);
651 				g_return_if_fail (!strcmp ((gchar*) node->name, "gdaui_form"));
652 
653 				pack_entries_in_xml_layout (form, node);
654 			}
655 			else
656 				pack_entries_in_table (form);
657 
658 			g_signal_emit (G_OBJECT (form), gdaui_basic_form_signals[LAYOUT_CHANGED], 0);
659 			break;
660 		}
661 		case PROP_PARAMLIST:
662 			if (form->priv->set) {
663 				get_rid_of_set (form->priv->set, form);
664 				destroy_entries (form);
665 			}
666 
667 			form->priv->set = g_value_get_pointer (value);
668 			if (form->priv->set) {
669 				g_return_if_fail (GDA_IS_SET (form->priv->set));
670 
671 				g_object_ref (form->priv->set);
672 				form->priv->set_info = _gdaui_set_new (GDA_SET (form->priv->set));
673 
674 				g_signal_connect (form->priv->set_info, "public-data-changed",
675 						  G_CALLBACK (paramlist_public_data_changed_cb), form);
676 				g_signal_connect (form->priv->set, "holder-attr-changed",
677 						  G_CALLBACK (paramlist_param_attr_changed_cb), form);
678 				g_signal_connect (form->priv->set, "holder-type-set",
679 						  G_CALLBACK (paramlist_holder_type_set_cb), form);
680 
681 				create_entries (form);
682 				pack_entries_in_table (form);
683 				g_signal_emit (G_OBJECT (form), gdaui_basic_form_signals[LAYOUT_CHANGED], 0);
684 			}
685 			break;
686 		case PROP_HEADERS_SENSITIVE:
687 			break;
688 		case PROP_SHOW_ACTIONS:
689 			gdaui_basic_form_show_entry_actions (form, g_value_get_boolean (value));
690 			break;
691 		case PROP_ENTRIES_AUTO_DEFAULT:
692 			gdaui_basic_form_set_entries_auto_default (form, g_value_get_boolean (value));
693 			break;
694 		default:
695 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
696 			break;
697 		}
698 	}
699 }
700 
701 static void
702 gdaui_basic_form_get_property (GObject *object,
703 			       guint param_id,
704 			       GValue *value,
705 			       GParamSpec *pspec)
706 {
707 	GdauiBasicForm *form;
708 
709         form = GDAUI_BASIC_FORM (object);
710         if (form->priv) {
711                 switch (param_id) {
712 		case PROP_PARAMLIST:
713 			g_value_set_pointer (value, form->priv->set);
714 			break;
715 		case PROP_HEADERS_SENSITIVE:
716 			break;
717 		case PROP_SHOW_ACTIONS:
718 			g_value_set_boolean (value, form->priv->show_actions);
719 			break;
findLoop(DumpableObject * obj,DumpId startPoint,bool * processed,DumpId * searchFailed,DumpableObject ** workspace,int depth)720 		case PROP_ENTRIES_AUTO_DEFAULT:
721 			g_value_set_boolean (value, form->priv->entries_auto_default);
722 			break;
723 		case PROP_CAN_VEXPAND: {
724 			gboolean can_expand = FALSE;
725 			can_expand = gtk_widget_compute_expand (GTK_WIDGET (form),
726 								GTK_ORIENTATION_VERTICAL);
727 			g_value_set_boolean (value, can_expand);
728 			break;
729 		}
730 		default:
731 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
732 			break;
733                 }
734         }
735 }
736 
737 static void
738 disconnect_single_entry_signals (SingleEntry *sentry)
739 {
740 	if (sentry->entry)
741 		g_signal_handler_disconnect (sentry->entry, sentry->entry_contents_modified_id);
742 
743 	sentry->entry_contents_modified_id = 0;
744 	if (sentry->entry)
745 		g_signal_handler_disconnect (sentry->entry, sentry->entry_contents_activated_id);
746 	sentry->entry_contents_activated_id = 0;
747 
748 	if (sentry->single_signal.signal_id > 0) {
749 		SignalData *sd = &(sentry->single_signal);
750 		g_signal_handler_disconnect (sd->holder, sd->signal_id);
751 		g_object_unref ((GObject*) sd->holder);
752 		sd->signal_id = 0;
753 		sd->holder = NULL;
754 	}
755 	if (sentry->group_signals) {
756 		gsize i;
757 		for (i = 0; i < sentry->group_signals->len; i++) {
758 			SignalData sd = g_array_index (sentry->group_signals, SignalData,
759 						       i);
760 			g_signal_handler_disconnect (sd.holder, sd.signal_id);
761 			g_object_unref ((GObject*) sd.holder);
762 		}
763 		g_array_free (sentry->group_signals, TRUE);
764 		sentry->group_signals = NULL;
765 	}
766 }
767 
768 /*
769  * unpack_entries
770  *
771  * Remove entries from the GTK container in which they are
772  */
773 static void
774 unpack_entries (GdauiBasicForm *form)
775 {
776 	GSList *list;
777 	for (list = form->priv->s_entries; list; list = list->next) {
778 		SingleEntry *sentry = (SingleEntry *) list->data;
779 		if (sentry->entry) {
780 			GtkWidget *parent;
781 			parent = gtk_widget_get_parent ((GtkWidget*) sentry->entry);
782 			if (parent)
783 				gtk_container_remove (GTK_CONTAINER (parent), (GtkWidget*) sentry->entry);
784 		}
785 		if (sentry->label) {
786 			GtkWidget *parent;
787 			parent = gtk_widget_get_parent (sentry->label);
788 			if (parent)
789 				gtk_container_remove (GTK_CONTAINER (parent), sentry->label);
790 		}
791 	}
792 }
793 
794 static void
795 destroy_entries (GdauiBasicForm *form)
796 {
797 	/* destroy all the widgets */
798 	if (form->priv->s_entries) {
799 		GSList *list;
800 		for (list = form->priv->s_entries; list; list = list->next) {
801 			SingleEntry *sentry = (SingleEntry *) list->data;
802 
803 			disconnect_single_entry_signals (sentry);
804 
repairTypeFuncLoop(DumpableObject * typeobj,DumpableObject * funcobj)805 			g_object_unref ((GObject *) sentry->entry);
806 			g_object_unref ((GObject *) sentry->label);
807 			g_free (sentry->label_title);
808 			g_free (sentry);
809 		}
810 		g_slist_free (form->priv->s_entries);
811 		form->priv->s_entries = NULL;
812 	}
813 
814 	if (form->priv->top_container) {
815 		gtk_widget_destroy (form->priv->top_container);
816 		form->priv->top_container = NULL;
817 		if (form->priv->place_holders) {
818 			g_hash_table_destroy (form->priv->place_holders);
819 			form->priv->place_holders = NULL;
820 		}
821 	}
822 }
823 
824 static gchar *
825 create_text_label_for_sentry (SingleEntry *sentry, gchar **out_title)
826 {
827 	gchar *label = NULL;
828 	GdaSetSource *ss;
829 	g_assert (out_title);
830 	ss = gda_set_group_get_source (gdaui_set_group_get_group (sentry->group));
831 	if (!ss) {
832 		g_object_get (G_OBJECT (sentry->single_param), "name", out_title, NULL);
833 		if (!*out_title)
834 			*out_title = g_strdup (_("Value"));
835 		label = g_strdup_printf ("%s:", *out_title);
repairViewRuleLoop(DumpableObject * viewobj,DumpableObject * ruleobj)836 		/*
837 		g_object_get (G_OBJECT (param), "description", &title, NULL);
838 		if (title && *title)
839 			gtk_widget_set_tooltip_text (sentry->label, title);
840 		g_free (title);
841 		*/
842 
843 	}
844 	else {
845 		GSList *params;
846 		gchar *title = NULL;
847 
848 		label = g_object_get_data (G_OBJECT (gda_set_source_get_data_model (ss)), "name");
849 		if (label)
850 			title = g_strdup (label);
851 		else {
852 			GString *tstring = NULL;
853 			for (params = gda_set_group_get_nodes (gdaui_set_group_get_group (sentry->group));
854 			                 params; params = params->next) {
855 				g_object_get (gda_set_node_get_holder (GDA_SET_NODE (params->data)),
856 					      "name", &title, NULL);
857 				if (title) {
858 					if (tstring)
859 						g_string_append (tstring, ",\n");
860 					else
861 						tstring = g_string_new ("");
862 					g_string_append (tstring, title);
863 				}
864 			}
865 			if (tstring)
866 				title = g_string_free (tstring, FALSE);
867 		}
868 
869 		if (!title)
870 			title = g_strdup (_("Value"));
871 
872 		label = g_strdup_printf ("%s:", title);
873 		*out_title = title;
874 	}
875 
876 	return label;
877 }
878 
879 static void
880 create_entry_widget (SingleEntry *sentry)
881 {
882 	GdauiSetGroup *group;
883 	GdaSetGroup *sg;
884 	GtkWidget *entry;
885 	gboolean editable = TRUE;
886 	GValue *ref_value = NULL;
887 
888 	disconnect_single_entry_signals (sentry);
889 	if (sentry->entry) {
repairMatViewBoundaryMultiLoop(DumpableObject * boundaryobj,DumpableObject * nextobj)890 		GtkWidget *parent;
891 		parent = gtk_widget_get_parent ((GtkWidget*) sentry->entry);
892 		if (parent)
893 			gtk_container_remove (GTK_CONTAINER (parent), (GtkWidget*) sentry->entry);
894 
895 		if (sentry->entry_shown_id) {
896 			g_signal_handler_disconnect (sentry->entry, sentry->entry_shown_id);
897 			sentry->entry_shown_id = 0;
898 		}
899 
900 		const GValue *cvalue;
901 		cvalue = gdaui_data_entry_get_reference_value (sentry->entry);
902 		if (cvalue)
903 			ref_value = gda_value_copy (cvalue);
904 		editable = gdaui_data_entry_get_editable (sentry->entry);
905 		g_object_unref ((GObject *) sentry->entry);
906 		sentry->entry = NULL;
907 	}
908 
909 	if (sentry->label) {
910 		GtkWidget *parent;
911 		parent = gtk_widget_get_parent (sentry->label);
repairTableConstraintLoop(DumpableObject * tableobj,DumpableObject * constraintobj)912 		if (parent)
913 			gtk_container_remove (GTK_CONTAINER (parent), sentry->label);
914 
915 		if (sentry->label_shown_id) {
916 			g_signal_handler_disconnect (sentry->label, sentry->label_shown_id);
917 			sentry->label_shown_id = 0;
918 		}
919 
920 		g_object_unref ((GObject *) sentry->label);
921 		sentry->label = NULL;
922 	}
923 
924 	group = sentry->group;
925 	sg = gdaui_set_group_get_group (group);
926 	if (! gda_set_group_get_source (sg)) {
927 		/* there is only one non-constrained parameter */
928 		GdaHolder *param;
repairTableConstraintMultiLoop(DumpableObject * tableobj,DumpableObject * constraintobj)929 		GType type;
930 		const GValue *val, *default_val, *value;
931 		gboolean nnul;
932 		const gchar *plugin = NULL;
933 		const GValue *plugin_val;
934 
935 		g_assert (gda_set_group_get_n_nodes (sg) == 1); /* only 1 item in the list */
936 
937 		param = GDA_HOLDER (gda_set_node_get_holder (gda_set_group_get_node (sg)));
938 		sentry->single_param = param;
939 
940 		val = gda_holder_get_value (param);
941 		default_val = gda_holder_get_default_value (param);
942 		nnul = gda_holder_get_not_null (param);
943 		sentry->not_null = nnul;
944 
945 		/* determine initial value */
repairTableAttrDefLoop(DumpableObject * tableobj,DumpableObject * attrdefobj)946 		type = gda_holder_get_g_type (param);
947 		value = val;
948 		if (!value && default_val &&
949 		    (G_VALUE_TYPE ((GValue *) default_val) == type))
950 			value = default_val;
951 
952 		/* create entry */
953 		plugin_val = gda_holder_get_attribute (param, GDAUI_ATTRIBUTE_PLUGIN);
repairTableAttrDefMultiLoop(DumpableObject * tableobj,DumpableObject * attrdefobj)954 		if (plugin_val) {
955 			if (G_VALUE_TYPE (plugin_val) == G_TYPE_STRING)
956 				plugin = g_value_get_string (plugin_val);
957 			else
958 				g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"),
959 					   GDAUI_ATTRIBUTE_PLUGIN);
960 		}
961 		entry = GTK_WIDGET (gdaui_new_data_entry (type, plugin));
962 
963 		/* set current value */
964 		if (gda_holder_is_valid (param))
965 			gdaui_data_entry_set_value (GDAUI_DATA_ENTRY (entry), val);
966 		else
967 			gdaui_data_entry_set_value (GDAUI_DATA_ENTRY (entry), NULL);
968 
repairDomainConstraintLoop(DumpableObject * domainobj,DumpableObject * constraintobj)969 		if (ref_value) {
970 			gdaui_data_entry_set_reference_value (GDAUI_DATA_ENTRY (entry), ref_value);
971 			gda_value_free (ref_value);
972 		}
973 		else if (!nnul ||
974 			 (nnul && value &&
975 			  (G_VALUE_TYPE ((GValue *) value) != GDA_TYPE_NULL)))
976 			gdaui_data_entry_set_reference_value (GDAUI_DATA_ENTRY (entry), value);
repairDomainConstraintMultiLoop(DumpableObject * domainobj,DumpableObject * constraintobj)977 
978 		if (default_val) {
979 			gdaui_data_entry_set_default_value (GDAUI_DATA_ENTRY (entry), default_val);
980 			gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (entry),
981 							 GDA_VALUE_ATTR_CAN_BE_DEFAULT,
982 							 GDA_VALUE_ATTR_CAN_BE_DEFAULT);
983 			if (gda_holder_value_is_default (param))
984 				gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (entry),
985 							 GDA_VALUE_ATTR_IS_DEFAULT,
986 							 GDA_VALUE_ATTR_IS_DEFAULT);
987 		}
988 
989 		gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (entry),
990 						 nnul ? 0 : GDA_VALUE_ATTR_CAN_BE_NULL,
repairIndexLoop(DumpableObject * partedindex,DumpableObject * partindex)991 						 GDA_VALUE_ATTR_CAN_BE_NULL);
992 
993 		/* connect to the parameter's changes */
994 		sentry->single_signal.holder = g_object_ref (param);
995 		sentry->single_signal.signal_id = g_signal_connect (G_OBJECT (param), "changed",
996 								    G_CALLBACK (parameter_changed_cb),
997 								    sentry);
998 
999 		/* label */
1000 		gchar *title;
1001 		gchar *str;
1002 		str = create_text_label_for_sentry (sentry, &title);
1003 		sentry->label = gtk_label_new (str);
1004 		g_free (str);
repairDependencyLoop(DumpableObject ** loop,int nLoop)1005 		g_object_ref_sink (sentry->label);
1006 		sentry->label_title = title;
1007 		gtk_misc_set_alignment (GTK_MISC (sentry->label), 0., 0.);
1008 		gtk_widget_show (sentry->label);
1009 
1010 		g_object_get (G_OBJECT (param), "description", &title, NULL);
1011 		if (title && *title)
1012 			gtk_widget_set_tooltip_text (sentry->label, title);
1013 		g_free (title);
1014 	}
1015 	else {
1016 		/* several parameters depending on the values of a GdaDataModel object */
1017 		GSList *plist;
1018 		gboolean nullok = TRUE;
1019 
1020 		entry = gdaui_entry_combo_new (sentry->form->priv->set_info, gdaui_set_group_get_source (group));
1021 
1022 		/* connect to the parameter's changes */
1023 		sentry->group_signals = g_array_new (FALSE, FALSE, sizeof (SignalData));
1024 		for (plist = gda_set_group_get_nodes (gdaui_set_group_get_group (group)); plist; plist = plist->next) {
1025 			GdaHolder *param;
1026 
1027 			param = gda_set_node_get_holder (GDA_SET_NODE (plist->data));
1028 			if (gda_holder_get_not_null (param))
1029 				nullok = FALSE;
1030 
1031 			SignalData sd;
1032 			sd.holder = g_object_ref (param);
1033 			sd.signal_id = g_signal_connect (G_OBJECT (param), "changed",
1034 							 G_CALLBACK (parameter_changed_cb),
1035 							 sentry);
1036 			g_array_append_val (sentry->group_signals, sd);
1037 		}
1038 		gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (entry),
1039 						 nullok ? GDA_VALUE_ATTR_CAN_BE_NULL : 0,
1040 						 GDA_VALUE_ATTR_CAN_BE_NULL);
1041 		sentry->not_null = !nullok;
1042 
1043 		/* label */
1044 		gchar *title = NULL;
1045 		gchar *str;
1046 		GdaSetSource *ss;
1047 
1048 		str = create_text_label_for_sentry (sentry, &title);
1049 		sentry->label = gtk_label_new (str);
1050 		g_free (str);
1051 		g_object_ref_sink (sentry->label);
1052 		sentry->label_title = title;
1053 		gtk_misc_set_alignment (GTK_MISC (sentry->label), 0., 0.);
1054 		gtk_widget_show (sentry->label);
1055 		ss = gda_set_group_get_source (gdaui_set_group_get_group (group));
1056 		title = g_object_get_data (G_OBJECT (gda_set_source_get_data_model (ss)), "descr");
1057 		if (title && *title)
1058 			gtk_widget_set_tooltip_text (sentry->label, title);
1059 
1060 	}
1061 	gtk_widget_set_hexpand (sentry->label, FALSE);
1062 	sentry->entry = GDAUI_DATA_ENTRY (entry);
1063 	g_object_ref_sink (sentry->entry);
1064 	gdaui_data_entry_set_editable (sentry->entry, editable);
1065 	gdaui_data_entry_set_unknown_color (sentry->entry, sentry->form->priv->red,
1066 					    sentry->form->priv->green, sentry->form->priv->blue,
1067 					    sentry->form->priv->alpha);
1068 
1069 	GSList *list;
1070 	for (list = sentry->form->priv->size_groups; list; list = list->next) {
1071 		SizeGroup *sg = (SizeGroup*) list->data;
1072 		switch (sg->part) {
1073 		case GDAUI_BASIC_FORM_LABELS:
1074 			if (sentry->label)
1075 				gtk_size_group_add_widget (sg->size_group, sentry->label);
1076 			break;
1077 		case GDAUI_BASIC_FORM_ENTRIES:
1078 			if (sentry->entry)
1079 				gtk_size_group_add_widget (sg->size_group, GTK_WIDGET (sentry->entry));
1080 			break;
1081 		default:
1082 			g_assert_not_reached ();
1083 		}
1084 	}
1085 
1086 	/* connect the entry's changes */
1087 	sentry->entry_contents_modified_id = g_signal_connect (G_OBJECT (entry), "contents-modified",
1088 							       G_CALLBACK (entry_contents_modified),
1089 							       sentry);
1090 
1091 	sentry->entry_contents_activated_id = g_signal_connect (G_OBJECT (entry), "contents-activated",
1092 								G_CALLBACK (entry_contents_activated),
1093 								sentry->form);
1094 }
1095 
1096 /*
1097  * create the entries in the widget
1098  */
1099 static void
1100 create_entries (GdauiBasicForm *form)
1101 {
1102 	GSList *list;
1103 
1104 	/* parameters list management */
1105 	if (!form->priv->set || !form->priv->set_info->groups_list)
1106 		/* nothing to do */
1107 		return;
1108 
1109 	/* creating all the data entries, and putting them into the form->priv->entries list */
1110 	for (list = form->priv->set_info->groups_list; list; list = list->next) {
1111 		SingleEntry *sentry;
1112 
1113 		sentry = g_new0 (SingleEntry, 1);
1114 		sentry->form = form;
1115 		sentry->forward_param_updates = TRUE;
1116 		form->priv->s_entries = g_slist_append (form->priv->s_entries, sentry);
1117 
1118 		sentry->group = GDAUI_SET_GROUP (list->data);
1119 		create_entry_widget (sentry);
1120 	}
1121 
1122 	/* Set the Show actions in the entries */
1123 	gdaui_basic_form_show_entry_actions (form, form->priv->show_actions);
1124 	/* Set the Auto entries default in the entries */
1125 	gdaui_basic_form_set_entries_auto_default (form, form->priv->entries_auto_default);
1126 
1127 	gdaui_basic_form_reset (form);
1128 }
1129 
1130 static void
1131 pack_entry_widget (SingleEntry *sentry)
1132 {
1133 	switch (sentry->packing_type) {
1134 	case PACKING_TABLE: {
1135 		/* label for the entry */
1136 		gint i = sentry->pack.table.top;
1137 		GtkGrid *grid = GTK_GRID (sentry->pack.table.grid_table);
1138 		GtkWidget *parent;
1139 
1140 		if (sentry->label) {
1141 			parent = gtk_widget_get_parent (sentry->label);
1142 			if (parent)
1143 				gtk_container_remove (GTK_CONTAINER (parent), sentry->label);
1144 			gtk_grid_attach (grid, sentry->label, 0, i, 1, 1);
1145 		}
1146 
1147 		/* add the entry itself to the table */
1148 		parent = gtk_widget_get_parent ((GtkWidget*) sentry->entry);
1149 		if (parent)
1150 			gtk_container_remove (GTK_CONTAINER (parent), (GtkWidget*) sentry->entry);
1151 		gtk_grid_attach (grid, (GtkWidget*) sentry->entry, 1, i, 1, 1);
1152 
1153 		gtk_widget_show ((GtkWidget*) sentry->entry);
1154 		if (sentry->label)
1155 			g_object_set (G_OBJECT (sentry->label), "can-focus", FALSE, NULL);
1156 		break;
1157 	}
1158 	default:
1159 		TO_IMPLEMENT;
1160 	}
1161 }
1162 
1163 /*
1164  * create the entries in the widget
1165  */
1166 static void
1167 pack_entries_in_table (GdauiBasicForm *form)
1168 {
1169 	GtkWidget *grid;
1170 	gint i;
1171 	GSList *list;
1172 
1173 	unpack_entries (form);
1174 	if (form->priv->top_container) {
1175 		gtk_widget_destroy (form->priv->top_container);
1176 		form->priv->top_container = NULL;
1177 		if (form->priv->place_holders) {
1178 			g_hash_table_destroy (form->priv->place_holders);
1179 			form->priv->place_holders = NULL;
1180 		}
1181 	}
1182 
1183 	/* creating a table for all the entries */
1184 	grid = gtk_grid_new ();
1185 	gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING);
1186 	gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING);
1187 	form->priv->top_container = grid;
1188 	gtk_box_pack_start (GTK_BOX (form->priv->mainbox), grid, TRUE, TRUE, 0);
1189 	for (list = form->priv->s_entries, i = 0;
1190 	     list;
1191 	     list = list->next, i++) {
1192 		SingleEntry *sentry = (SingleEntry *) list->data;
1193 
1194 		sentry->packing_type = PACKING_TABLE;
1195 		sentry->pack.table.grid_table = grid;
1196 		sentry->pack.table.top = i;
1197 
1198 		pack_entry_widget (sentry);
1199 	}
1200 	mark_not_null_entry_labels (form, TRUE);
1201 	gtk_widget_show (grid);
1202 }
1203 
1204 static GtkWidget *load_xml_layout_children (GdauiBasicForm *form, xmlNodePtr parent_node);
1205 static GtkWidget *load_xml_layout_column (GdauiBasicForm *form, xmlNodePtr box_node);
1206 static GtkWidget *load_xml_layout_section (GdauiBasicForm *form, xmlNodePtr section_node);
1207 
1208 static void
1209 pack_entries_in_xml_layout (GdauiBasicForm *form, xmlNodePtr form_node)
1210 {
1211 	GtkWidget *top;
1212 
1213 	unpack_entries (form);
1214 	if (form->priv->top_container) {
1215 		gtk_widget_destroy (form->priv->top_container);
1216 		form->priv->top_container = NULL;
1217 		if (form->priv->place_holders) {
1218 			g_hash_table_destroy (form->priv->place_holders);
1219 			form->priv->place_holders = NULL;
1220 		}
1221 	}
1222 
1223 	top = load_xml_layout_children (form, form_node);
1224 	gtk_box_pack_start (GTK_BOX (form->priv->mainbox), top, TRUE, TRUE, 0);
1225 	gtk_widget_show_all (top);
1226 	mark_not_null_entry_labels (form, TRUE);
1227 }
1228 
1229 /*
1230  * Loads all @parent_node's children and returns the corresponding widget
1231  * @parent_node can be a "gdaui_form", "gdaui_section",
1232  * it _CANNOT_ be a "gdaui_entry"
1233  */
1234 static GtkWidget *
1235 load_xml_layout_children (GdauiBasicForm *form, xmlNodePtr parent_node)
1236 {
1237 	GtkWidget *top = NULL;
1238 	xmlChar *prop;
1239 	typedef enum {
1240 		TOP_BOX,
1241 		TOP_PANED
1242 	} TopContainerType;
1243 	TopContainerType ctype = TOP_BOX;
1244 	gint pos = 0;
1245 
1246 	prop = xmlGetProp (parent_node, BAD_CAST "container");
1247 	if (prop) {
1248 		if (xmlStrEqual (prop, BAD_CAST "columns")) {
1249 			top = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1250 			ctype = TOP_BOX;
1251 		}
1252 		if (xmlStrEqual (prop, BAD_CAST "rows")) {
1253 			top = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1254 			ctype = TOP_BOX;
1255 		}
1256 		else if (xmlStrEqual (prop, BAD_CAST "hpaned")) {
1257 			top = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
1258 			ctype = TOP_PANED;
1259 		}
1260 		else if (xmlStrEqual (prop, BAD_CAST "vpaned")) {
1261 			top = gtk_paned_new (GTK_ORIENTATION_VERTICAL);
1262 			ctype = TOP_PANED;
1263 		}
1264 		else
1265 			g_warning ("Unknown container type '%s', ignoring", (gchar*) prop);
1266 		xmlFree (prop);
1267 	}
1268 	if (!top)
1269 		top = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1270 
1271 	xmlNodePtr child;
1272 	for (child = parent_node->children; child; child = child->next) {
1273 		if (child->type != XML_ELEMENT_NODE)
1274 			continue;
1275 
1276 		GtkWidget *wid = NULL;
1277 
1278 		if (xmlStrEqual (child->name, BAD_CAST "gdaui_column"))
1279 			wid = load_xml_layout_column (form, child);
1280 		else if (xmlStrEqual (child->name, BAD_CAST "gdaui_section"))
1281 			wid = load_xml_layout_section (form, child);
1282 		else
1283 			g_warning ("Unknown node type '%s', ignoring", (gchar*) child->name);
1284 
1285 		if (wid) {
1286 			switch (ctype) {
1287 			case TOP_BOX:
1288 				gtk_box_pack_start (GTK_BOX (top), wid, TRUE, TRUE, SPACING);
1289 				break;
1290 			case TOP_PANED:
1291 				if (pos == 0)
1292 					gtk_paned_add1 (GTK_PANED (top), wid);
1293 				else if (pos == 1)
1294 					gtk_paned_add2 (GTK_PANED (top), wid);
1295 				else
1296 					g_warning ("Paned container can't have more than two children");
1297 				break;
1298 			default:
1299 				g_assert_not_reached ();
1300 			}
1301 			pos++;
1302 		}
1303 	}
1304 
describeDumpableObject(DumpableObject * obj,char * buf,int bufsize)1305 	return top;
1306 }
1307 
1308 static GtkWidget *
1309 load_xml_layout_column (GdauiBasicForm *form, xmlNodePtr box_node)
1310 {
1311 	GtkWidget *grid;
1312 	grid = gtk_grid_new ();
1313 
1314 	xmlNodePtr child;
1315 	gint i;
1316 	for (i = 0, child = box_node->children; child; i++, child = child->next) {
1317 		if (child->type != XML_ELEMENT_NODE)
1318 			continue;
1319 
1320 		if (xmlStrEqual (child->name, BAD_CAST "gdaui_entry")) {
1321 			xmlChar *name;
1322 			name = xmlGetProp (child, BAD_CAST "name");
1323 			if (name) {
1324 				SingleEntry *sentry;
1325 				sentry = get_single_entry_for_id (form, (gchar*) name);
1326 				if (sentry) {
1327 					sentry->packing_type = PACKING_TABLE;
1328 					sentry->pack.table.grid_table = grid;
1329 					sentry->pack.table.top = i;
1330 
1331 					xmlChar *plugin;
1332 					plugin = xmlGetProp (child, BAD_CAST "plugin");
1333 					if (plugin && sentry->single_param) {
1334 						GValue *value;
1335 						value = gda_value_new_from_string ((gchar*) plugin, G_TYPE_STRING);
1336 						gda_holder_set_attribute_static (sentry->single_param,
1337 										 GDAUI_ATTRIBUTE_PLUGIN, value);
1338 						gda_value_free (value);
1339 					}
1340 					if (plugin)
1341 						xmlFree (plugin);
1342 
1343 					xmlChar *label;
1344 					label = xmlGetProp (child, BAD_CAST "label");
1345 					if (label) {
1346 						g_free (sentry->label_title);
1347 						sentry->label_title = g_strdup ((gchar*) label);
1348 						if (sentry->label) {
1349 							gtk_widget_destroy (sentry->label);
1350 							g_object_unref (sentry->label);
1351 						}
1352 						sentry->label = gtk_label_new ((gchar*) label);
1353 						g_object_ref_sink (sentry->label);
1354 						gtk_misc_set_alignment (GTK_MISC (sentry->label), 0., 0.);
1355 
1356 						xmlFree (label);
1357 					}
1358 
1359 					pack_entry_widget (sentry);
1360 				}
1361 				else
1362 					g_warning ("Could not find entry named '%s', ignoring",
1363 						   (gchar*) name);
1364 				xmlFree (name);
1365 			}
1366 		}
1367 		else if (xmlStrEqual (child->name, BAD_CAST "gdaui_placeholder")) {
1368 			xmlChar *id;
1369 			id = xmlGetProp (child, BAD_CAST "id");
1370 			if (id) {
1371 				GtkWidget *ph;
1372 				xmlChar *label;
1373 				label = xmlGetProp (child, BAD_CAST "label");
1374 
1375 				if (label) {
1376 					GtkWidget *wid;
1377 					wid = gtk_label_new ((gchar*) label);
1378 					gtk_grid_attach (GTK_GRID (grid), wid, 0, i, 1, 1);
1379 					xmlFree (label);
1380 				}
1381 
1382 				if (! form->priv->place_holders)
1383 					form->priv->place_holders = g_hash_table_new_full (g_str_hash, g_str_equal,
1384 											   g_free, g_object_unref);
1385 				ph = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1386 				g_hash_table_insert (form->priv->place_holders, g_strdup ((gchar*) id),
1387 						     g_object_ref_sink ((GObject*)ph));
1388 				gtk_grid_attach (GTK_GRID (grid), ph, 1, i, 1, 1);
1389 				xmlFree (id);
1390 			}
1391 		}
1392 		else
1393 			g_warning ("Unknown node type '%s', ignoring", (gchar*) child->name);
1394 	}
1395 
1396 	gtk_grid_set_row_spacing (GTK_GRID (grid), SPACING);
1397 	gtk_grid_set_column_spacing (GTK_GRID (grid), SPACING);
1398 	return grid;
1399 }
1400 
1401 static GtkWidget *
1402 load_xml_layout_section (GdauiBasicForm *form, xmlNodePtr section_node)
1403 {
1404 	xmlChar *title;
1405 	GtkWidget *hbox, *label, *sub, *retval;
1406 
1407 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
1408 	title = xmlGetProp (section_node, BAD_CAST "title");
1409 	if (title) {
1410 		gchar *str;
1411 		str = g_markup_printf_escaped ("<b>%s</b>", (gchar*) title);
1412 		xmlFree (title);
1413 
1414 		label = gtk_label_new ("");
1415 		gtk_label_set_markup (GTK_LABEL (label), str);
1416 		g_free (str);
1417 		gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
1418 
1419 		GtkWidget *vbox;
1420 		vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1421 		gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
1422 		gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1423 		retval = vbox;
1424 	}
1425 	else
1426 		retval = hbox;
1427 
1428 	label = gtk_label_new ("  ");
1429 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1430 
1431 	sub = load_xml_layout_children (form, section_node);
1432 	gtk_box_pack_start (GTK_BOX (hbox), sub, TRUE, TRUE, 0);
1433 
1434 	return retval;
1435 }
1436 
1437 /*
1438  * if @show_mark is TRUE, display the label as bold
1439  */
1440 static void
1441 mark_not_null_entry_labels (GdauiBasicForm *form, gboolean show_mark)
1442 {
1443 	GSList *list;
1444 
1445 	for (list = form->priv->s_entries; list; list = list->next) {
1446 		SingleEntry *sentry = (SingleEntry*) list->data;
1447 		gchar *str;
1448 
1449 		str = _gdaui_utility_markup_title (sentry->label_title, !show_mark || !sentry->not_null);
1450 		if (show_mark && sentry->not_null)
1451 			gtk_label_set_markup (GTK_LABEL (sentry->label), str);
1452 		else
1453 			gtk_label_set_text (GTK_LABEL (sentry->label), str);
1454 		g_free (str);
1455 	}
1456 }
1457 
1458 static void
1459 entry_contents_activated (G_GNUC_UNUSED GdauiDataEntry *entry, GdauiBasicForm *form)
1460 {
1461 	g_signal_emit (G_OBJECT (form), gdaui_basic_form_signals[ACTIVATED], 0);
1462 }
1463 
1464 static void
1465 entry_contents_modified (GdauiDataEntry *entry, SingleEntry *sentry)
1466 {
1467 	GdaHolder *param;
1468 	GdaValueAttribute attr;
1469 
1470 	attr = gdaui_data_entry_get_attributes (entry);
1471 	param = sentry->single_param;
1472 	if (param) { /* single parameter */
1473 		GValue *value;
1474 
1475 		sentry->forward_param_updates = FALSE;
1476 
1477 		/* parameter's value */
1478 		value = gdaui_data_entry_get_value (entry);
1479 		if (attr & GDA_VALUE_ATTR_IS_DEFAULT)
1480 			gda_holder_set_value_to_default (param);
1481 		else if (attr & GDA_VALUE_ATTR_DATA_NON_VALID) {
1482 			gda_holder_force_invalid (param);
1483 			g_signal_emit (G_OBJECT (sentry->form), gdaui_basic_form_signals[HOLDER_CHANGED],
1484 				       0, param, TRUE);
1485 		}
1486 		else if (gda_holder_set_value (param, value, NULL))
1487 			g_signal_emit (G_OBJECT (sentry->form), gdaui_basic_form_signals[HOLDER_CHANGED],
1488 				       0, param, TRUE);
1489 		else {
1490 			/* GdaHolder refused value => reset GdaDataEntry */
1491 			g_signal_handler_block (G_OBJECT (entry),
1492 						sentry->entry_contents_modified_id);
1493 			gdaui_data_entry_set_value (entry,
1494 						    gda_holder_is_valid (param) ?
1495 						    gda_holder_get_value (param) : NULL);
1496 			g_signal_handler_unblock (G_OBJECT (entry),
1497 						  sentry->entry_contents_modified_id);
1498 		}
1499 		gda_value_free (value);
1500 		sentry->forward_param_updates = TRUE;
1501 	}
1502 	else { /* multiple parameters */
1503 		GSList *params;
1504 		GSList *values, *list;
1505 		GdauiSetGroup *group;
1506 
1507 		group = sentry->group;
1508 		params = gda_set_group_get_nodes (gdaui_set_group_get_group (group));
1509 		values = gdaui_entry_combo_get_values (GDAUI_ENTRY_COMBO (entry));
1510 		g_assert (g_slist_length (params) == g_slist_length (values));
1511 
1512 		for (list = values; list; list = list->next, params = params->next) {
1513 			/* REM: if there is more than one value in 'params', then a
1514 			 * signal is emitted for each param that is changed,
1515 			 * and there is no way for the listener of that signal to know if it
1516 			 * the end of the "holder-changed" sequence. What could be done is:
1517 			 * - adding another boolean to tell if that signal is the
1518 			 *   last one in the "holder-changed" sequence, or
1519 			 * - modify the signal to add a list of parameters which are changed
1520 			 *   and emit only one signal.
1521 			 */
1522 			GdaHolder *param;
1523 			sentry->forward_param_updates = FALSE;
1524 
1525 			/* parameter's value */
1526 			param = gda_set_node_get_holder (GDA_SET_NODE (params->data));
1527 			gda_holder_set_value (param, (GValue *)(list->data), NULL);
1528 			g_signal_emit (G_OBJECT (sentry->form), gdaui_basic_form_signals[HOLDER_CHANGED],
1529 				       0, param, TRUE);
1530 			sentry->forward_param_updates = TRUE;
1531 		}
1532 		g_slist_free (values);
1533 
1534 #ifdef PROXY_STORE_EXTRA_VALUES
1535 		/* updating the GdaDataProxy if there is one */
1536 		if (GDA_IS_DATA_MODEL_ITER (sentry->form->priv->set)) {
1537 			GdaDataProxy *proxy;
1538 			gint proxy_row;
1539 
1540 			proxy_row = gda_data_model_iter_get_row ((GdaDataModelIter *) sentry->form->priv->set);
1541 
1542 			g_object_get (G_OBJECT (sentry->form->priv->set), "data-model", &proxy, NULL);
1543 			if (GDA_IS_DATA_PROXY (proxy)) {
1544 				GSList *all_new_values;
1545 				gint i, col;
1546 
1547 				all_new_values = gdaui_entry_combo_get_all_values (GDAUI_ENTRY_COMBO (entry));
1548 				for (i = 0; i < group->nodes_source->shown_n_cols; i++) {
1549 					GValue *value;
1550 
1551 					col = group->nodes_source->shown_cols_index[i];
1552 					value = (GValue *) g_slist_nth_data (all_new_values, col);
1553 					gda_data_proxy_set_model_row_value (proxy,
1554 									    group->nodes_source->data_model,
1555 									    proxy_row, col, value);
1556 				}
1557 				g_slist_free (all_new_values);
1558 			}
1559 			g_object_unref (proxy);
1560 		}
1561 #endif
1562 	}
1563 }
1564 
1565 
1566 /*
1567  * Called when a parameter changes
1568  * We emit a "holder-changed" signal only if the 'sentry->forward_param_updates' is TRUE, which means
1569  * the param change does not come from a GdauiDataEntry change.
1570  */
1571 static void
1572 parameter_changed_cb (GdaHolder *param, SingleEntry *sentry)
1573 {
1574 	const GValue *value = gda_holder_get_value (param);
1575 	GdauiDataEntry *entry;
1576 
1577 	entry = sentry->entry;
1578 	if (sentry->forward_param_updates) {
1579 		gboolean param_valid;
1580 		gboolean default_if_invalid = FALSE;
1581 
1582 		/* There can be a feedback from the entry if the param is invalid and "set-default-if-invalid"
1583 		   exists and is TRUE */
1584 		param_valid = gda_holder_is_valid (param);
1585 		if (!param_valid)
1586 			if (g_object_class_find_property (G_OBJECT_GET_CLASS (entry),
1587 							  "set-default-if-invalid"))
1588 				g_object_get (G_OBJECT (entry),
1589 					      "set-default-if-invalid", &default_if_invalid, NULL);
1590 
1591 		/* updating the corresponding entry */
1592 		if (! default_if_invalid) {
1593 			g_signal_handler_block (G_OBJECT (entry),
1594 						sentry->entry_contents_modified_id);
1595 			g_signal_handler_block (G_OBJECT (entry),
1596 						sentry->entry_contents_activated_id);
1597 		}
1598 
1599 		if (sentry->single_param)
1600 			gdaui_data_entry_set_value (entry, param_valid ? value : NULL);
1601 		else {
1602 			GSList *values = NULL;
1603 			GSList *list;
1604 			gboolean allnull = TRUE;
1605 
1606 			for (list = gda_set_group_get_nodes (gdaui_set_group_get_group (sentry->group));
1607 			     list; list = list->next) {
1608 				const GValue *pvalue;
1609 				pvalue = gda_holder_get_value (gda_set_node_get_holder (GDA_SET_NODE (list->data)));
1610 				values = g_slist_append (values, (GValue *) pvalue);
1611 				if (allnull && pvalue &&
1612 				    (G_VALUE_TYPE ((GValue *) pvalue) != GDA_TYPE_NULL))
1613 					allnull = FALSE;
1614 			}
1615 
1616 			if (!allnull)
1617 				gdaui_entry_combo_set_values (GDAUI_ENTRY_COMBO (entry), values);
1618 			else
1619 				gdaui_entry_combo_set_values (GDAUI_ENTRY_COMBO (entry), NULL);
1620 
1621 			g_slist_free (values);
1622 		}
1623 
1624 		if (! default_if_invalid) {
1625 			g_signal_handler_unblock (G_OBJECT (entry),
1626 						  sentry->entry_contents_modified_id);
1627 			g_signal_handler_unblock (G_OBJECT (entry),
1628 						  sentry->entry_contents_activated_id);
1629 		}
1630 
1631 		gdaui_entry_shell_set_unknown (GDAUI_ENTRY_SHELL (entry),
1632 					       !gda_holder_is_valid (param));
1633 
1634 		g_signal_emit (G_OBJECT (sentry->form), gdaui_basic_form_signals[HOLDER_CHANGED], 0,
1635 			       param, FALSE);
1636 	}
1637 	else
1638 		gdaui_entry_shell_set_unknown (GDAUI_ENTRY_SHELL (entry),
1639 					       !gda_holder_is_valid (param));
1640 
1641 	/* correctly update the NULLOK status */
1642 	GdaValueAttribute attr;
1643 	gboolean nullok;
1644 	attr = gdaui_data_entry_get_attributes (entry);
1645 	nullok = !gda_holder_get_not_null (param);
1646 	if (((attr & GDA_VALUE_ATTR_CAN_BE_NULL) && !nullok) ||
1647 	    (! (attr & GDA_VALUE_ATTR_CAN_BE_NULL) && nullok))
1648 		gdaui_data_entry_set_attributes (entry, nullok ? GDA_VALUE_ATTR_CAN_BE_NULL : 0,
1649 						 GDA_VALUE_ATTR_CAN_BE_NULL);
1650 }
1651 
1652 /**
1653  * gdaui_basic_form_set_as_reference:
1654  * @form: a #GdauiBasicForm widget
1655  *
1656  * Tells @form that the current values in the different entries are
1657  * to be considered as the original values for all the entries; the immediate
1658  * consequence is that any sub-sequent call to gdaui_basic_form_has_changed()
1659  * will return %FALSE (of course until any entry is changed).
1660  *
1661  * Since: 4.2
1662  */
1663 void
1664 gdaui_basic_form_set_as_reference (GdauiBasicForm *form)
1665 {
1666 	GSList *list;
1667 	GdaHolder *param;
1668 
1669 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1670 
1671 	for (list = form->priv->s_entries; list; list = list->next) {
1672 		SingleEntry *sentry = (SingleEntry*) list->data;
1673 
1674 		if (sentry->single_param) {
1675 			/* non combo entry */
1676 			param = sentry->single_param;
1677 			g_signal_handler_block (G_OBJECT (sentry->entry),
1678 						sentry->entry_contents_modified_id);
1679 			gdaui_data_entry_set_reference_value (GDAUI_DATA_ENTRY (sentry->entry),
1680 							     gda_holder_get_value (param));
1681 			g_signal_handler_unblock (G_OBJECT (sentry->entry),
1682 						  sentry->entry_contents_modified_id);
1683 		}
1684 		else {
1685 			/* Combo entry */
1686 			GSList *values = NULL;
1687 			GSList *params;
1688 			gboolean allnull = TRUE;
1689 
1690 			for (params = gda_set_group_get_nodes (gdaui_set_group_get_group (sentry->group));
1691 			     params; params = params->next) {
1692 				const GValue *pvalue;
1693 				pvalue = gda_holder_get_value (gda_set_node_get_holder (GDA_SET_NODE (params->data)));
1694 				values = g_slist_append (values, (GValue *) pvalue);
1695 				if (allnull && pvalue &&
1696 				    (G_VALUE_TYPE ((GValue *) pvalue) != GDA_TYPE_NULL))
1697 					allnull = FALSE;
1698 			}
1699 
1700 			if (!allnull)
1701 				gdaui_entry_combo_set_reference_values (GDAUI_ENTRY_COMBO (sentry->entry), values);
1702 			else
1703 				gdaui_entry_combo_set_reference_values (GDAUI_ENTRY_COMBO (sentry->entry), NULL);
1704 
1705 			g_slist_free (values);
1706 		}
1707 	}
1708 }
1709 
1710 /**
1711  * gdaui_basic_form_is_valid:
1712  * @form: a #GdauiBasicForm widget
1713  *
1714  * Tells if the form can be used as-is (if all the parameters do have some valid values)
1715  *
1716  * Returns: %TRUE if the form is valid
1717  *
1718  * Since: 4.2
1719  */
1720 gboolean
1721 gdaui_basic_form_is_valid (GdauiBasicForm *form)
1722 {
1723 	g_return_val_if_fail (GDAUI_IS_BASIC_FORM (form), FALSE);
1724 
1725 	return gda_set_is_valid (form->priv->set, NULL);
1726 }
1727 
1728 /**
1729  * gdaui_basic_form_get_data_set:
1730  * @form: a #GdauiBasicForm widget
1731  *
1732  * Get a pointer to the #GdaSet object which
1733  * is modified by @form
1734  *
1735  * Returns: (transfer none): a pointer to the #GdaSet
1736  *
1737  * Since: 4.2
1738  */
1739 GdaSet *
1740 gdaui_basic_form_get_data_set (GdauiBasicForm *form)
1741 {
1742 	g_return_val_if_fail (GDAUI_IS_BASIC_FORM (form), NULL);
1743 
1744 	return form->priv->set;
1745 }
1746 
1747 /**
1748  * gdaui_basic_form_has_changed:
1749  * @form: a #GdauiBasicForm widget
1750  *
1751  * Tells if the form has had at least on entry changed since @form was created or
1752  * gdaui_basic_form_set_as_reference() has been called.
1753  *
1754  * Returns: %TRUE if one entry has changed at least
1755  *
1756  * Since: 4.2
1757  */
1758 gboolean
1759 gdaui_basic_form_has_changed (GdauiBasicForm *form)
1760 {
1761 	GSList *list;
1762 
1763 	g_return_val_if_fail (GDAUI_IS_BASIC_FORM (form), FALSE);
1764 
1765 	for (list = form->priv->s_entries; list; list = list->next) {
1766 		SingleEntry *sentry = (SingleEntry*) list->data;
1767 		if (! (gdaui_data_entry_get_attributes (GDAUI_DATA_ENTRY (sentry->entry)) &
1768 		       GDA_VALUE_ATTR_IS_UNCHANGED))
1769 			return TRUE;
1770 	}
1771 	return FALSE;
1772 }
1773 
1774 /*
1775  * gdaui_basic_form_show_entry_actions
1776  * @form: a #GdauiBasicForm widget
1777  * @show_actions: a boolean
1778  *
1779  * Show or hide the actions button available at the end of each data entry
1780  * in the form
1781  */
1782 static void
1783 gdaui_basic_form_show_entry_actions (GdauiBasicForm *form, gboolean show_actions)
1784 {
1785 	GSList *list;
1786 	guint show;
1787 
1788 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1789 
1790 	show = show_actions ? GDA_VALUE_ATTR_ACTIONS_SHOWN : 0;
1791 	form->priv->show_actions = show_actions;
1792 
1793 	for (list = form->priv->s_entries; list; list = list->next) {
1794 		SingleEntry *sentry = (SingleEntry*) list->data;
1795 		gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (sentry->entry), show,
1796 						 GDA_VALUE_ATTR_ACTIONS_SHOWN);
1797 		/* mark_not_null_entry_labels (form, show_actions); */
1798 	}
1799 }
1800 
1801 /**
1802  * gdaui_basic_form_reset:
1803  * @form: a #GdauiBasicForm widget
1804  *
1805  * Resets all the entries in the form to their
1806  * original values
1807  *
1808  * Since: 4.2
1809  */
1810 void
1811 gdaui_basic_form_reset (GdauiBasicForm *form)
1812 {
1813 	GSList *list;
1814 
1815 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1816 
1817 	for (list = form->priv->s_entries; list; list = list->next) {
1818 		SingleEntry *sentry = (SingleEntry*) list->data;
1819 
1820 		if (!sentry->single_param) {
1821 			/* Combo entry */
1822 			GSList *values = NULL;
1823 
1824 			values = gdaui_entry_combo_get_reference_values (GDAUI_ENTRY_COMBO (sentry->entry));
1825 			gdaui_entry_combo_set_values (GDAUI_ENTRY_COMBO (sentry->entry), values);
1826 			g_slist_free (values);
1827 		}
1828 		else {
1829 			/* non combo entry */
1830 			const GValue *value;
1831 
1832 			value = gdaui_data_entry_get_reference_value (GDAUI_DATA_ENTRY (sentry->entry));
1833 			gdaui_data_entry_set_value (GDAUI_DATA_ENTRY (sentry->entry), value);
1834 		}
1835 	}
1836 }
1837 
1838 static void
1839 real_gdaui_basic_form_entry_set_visible (GdauiBasicForm *form, SingleEntry *sentry, gboolean show)
1840 {
1841 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1842 	g_return_if_fail (sentry);
1843 
1844 	if (sentry->entry) {
1845 		if (show) {
1846 			if (sentry->entry_shown_id) {
1847 				g_signal_handler_disconnect (sentry->entry, sentry->entry_shown_id);
1848 				sentry->entry_shown_id = 0;
1849 			}
1850 			if (sentry->label_shown_id) {
1851 				g_signal_handler_disconnect (sentry->label, sentry->label_shown_id);
1852 				sentry->label_shown_id = 0;
1853 			}
1854 			gtk_widget_show ((GtkWidget*) sentry->entry);
1855 			if (sentry->label)
1856 				gtk_widget_show (sentry->label);
1857 		}
1858 		else {
1859 			if (! sentry->entry_shown_id)
1860 				sentry->entry_shown_id = g_signal_connect_after (sentry->entry, "show",
1861 										 G_CALLBACK (widget_shown_cb), sentry);
1862 			if (sentry->label && !sentry->label_shown_id)
1863 				sentry->label_shown_id = g_signal_connect_after (sentry->label, "show",
1864 										 G_CALLBACK (widget_shown_cb), sentry);
1865 			gtk_widget_hide ((GtkWidget*) sentry->entry);
1866 			if (sentry->label)
1867 				gtk_widget_hide (sentry->label);
1868 		}
1869 		sentry->hidden = !show;
1870 	}
1871 }
1872 
1873 /**
1874  * gdaui_basic_form_entry_set_visible:
1875  * @form: a #GdauiBasicForm widget
1876  * @holder: a #GdaHolder object
1877  * @show: set to %TRUE to show the data entry, and to %FALSE to hide it
1878  *
1879  * Shows or hides the #GdauiDataEntry in @form which corresponds to the
1880  * @holder data holder
1881  *
1882  * Since: 4.2
1883  */
1884 void
1885 gdaui_basic_form_entry_set_visible (GdauiBasicForm *form, GdaHolder *holder, gboolean show)
1886 {
1887 	SingleEntry *sentry;
1888 
1889 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1890 	g_return_if_fail (GDA_IS_HOLDER (holder));
1891 
1892 	sentry = get_single_entry_for_holder (form, holder);
1893 	if (!sentry) {
1894 		g_warning (_("Can't find data entry for GdaHolder"));
1895 		return;
1896 	}
1897 
1898 	real_gdaui_basic_form_entry_set_visible (form, sentry, show);
1899 	sentry->prog_hidden = !show;
1900 }
1901 
1902 static void
1903 gdaui_basic_form_widget_grab_focus (GtkWidget *widget)
1904 {
1905 	gdaui_basic_form_entry_grab_focus (GDAUI_BASIC_FORM (widget), NULL);
1906 }
1907 
1908 /**
1909  * gdaui_basic_form_entry_grab_focus:
1910  * @form: a #GdauiBasicForm widget
1911  * @holder: (allow-none): a #GdaHolder object, or %NULL
1912  *
1913  * Makes the data entry corresponding to @holder grab the focus for the window it's in. If @holder is %NULL,
1914  * then the focus is on the first entry which needs attention.
1915  *
1916  * Since: 4.2
1917  */
1918 void
1919 gdaui_basic_form_entry_grab_focus (GdauiBasicForm *form, GdaHolder *holder)
1920 {
1921 	GtkWidget *entry = NULL;
1922 
1923 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1924 
1925 	if (holder) {
1926 		g_return_if_fail (GDA_IS_HOLDER (holder));
1927 		entry = gdaui_basic_form_get_entry_widget (form, holder);
1928 	}
1929 
1930 	if (!entry && form->priv->set) {
1931 		GSList *list;
1932 		for (list = form->priv->set->holders; list; list = list->next) {
1933 			GdaHolder *holder;
1934 			holder = GDA_HOLDER (list->data);
1935 			if (!gda_holder_is_valid (holder)) {
1936 				entry = gdaui_basic_form_get_entry_widget (form, holder);
1937 				if (entry)
1938 					break;
1939 			}
1940 		}
1941 	}
1942 	if (entry)
1943 		gdaui_data_entry_grab_focus (GDAUI_DATA_ENTRY (entry));
1944 }
1945 
1946 /**
1947  * gdaui_basic_form_entry_set_editable:
1948  * @form: a #GdauiBasicForm widget
1949  * @holder: (allow-none): a #GdaHolder object; or %NULL
1950  * @editable: %TRUE if corresponding data entry must be editable
1951  *
1952  * Sets the #GdauiDataEntry in @form which corresponds to the
1953  * @holder parameter editable or not. If @holder is %NULL, then all the parameters
1954  * are concerned.
1955  *
1956  * Since: 4.2
1957  */
1958 void
1959 gdaui_basic_form_entry_set_editable (GdauiBasicForm *form, GdaHolder *holder, gboolean editable)
1960 {
1961 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1962 
1963 	if (holder) {
1964 		SingleEntry *sentry;
1965 		sentry = get_single_entry_for_holder (form, holder);
1966 		if (sentry)
1967 			gdaui_data_entry_set_editable (sentry->entry, editable);
1968 	}
1969 	else {
1970 		GSList *list;
1971 		for (list = form->priv->s_entries; list; list = list->next)
1972 			gdaui_data_entry_set_editable (GDAUI_DATA_ENTRY (((SingleEntry*) list->data)->entry),
1973 						       editable);
1974 	}
1975 }
1976 
1977 
1978 /*
1979  * gdaui_basic_form_set_entries_auto_default
1980  * @form: a #GdauiBasicForm widget
1981  * @auto_default:
1982  *
1983  * Sets wether all the #GdauiDataEntry entries in the form must default
1984  * to a default value if they are assigned a non valid value.
1985  * Depending on the real type of entry, it will provide a default value
1986  * which the user does not need to modify if it is OK.
1987  *
1988  * For example a date entry can by default display the current date.
1989  */
1990 static void
1991 gdaui_basic_form_set_entries_auto_default (GdauiBasicForm *form, gboolean auto_default)
1992 {
1993 	GSList *list;
1994 
1995 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
1996 
1997 	form->priv->entries_auto_default = auto_default;
1998 	for (list = form->priv->s_entries; list; list = list->next) {
1999 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (((SingleEntry*) list->data)->entry),
2000 						  "set-default-if-invalid"))
2001 			g_object_set (G_OBJECT (((SingleEntry*) list->data)->entry),
2002 				      "set-default-if-invalid", auto_default, NULL);
2003 	}
2004 }
2005 
2006 /**
2007  * gdaui_basic_form_set_entries_to_default:
2008  * @form: a #GdauiBasicForm widget
2009  *
2010  * For each entry in the form, sets it to a default value if it is possible to do so.
2011  *
2012  * Since: 4.2
2013  */
2014 void
2015 gdaui_basic_form_set_entries_to_default (GdauiBasicForm *form)
2016 {
2017 	GSList *list;
2018 	guint attrs;
2019 
2020 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
2021 
2022 	for (list = form->priv->s_entries; list; list = list->next) {
2023 		SingleEntry *sentry = (SingleEntry*) list->data;
2024 		attrs = gdaui_data_entry_get_attributes (GDAUI_DATA_ENTRY (sentry->entry));
2025 		if (attrs & GDA_VALUE_ATTR_CAN_BE_DEFAULT)
2026 			gdaui_data_entry_set_attributes (GDAUI_DATA_ENTRY (sentry->entry),
2027 							 GDA_VALUE_ATTR_IS_DEFAULT, GDA_VALUE_ATTR_IS_DEFAULT);
2028 	}
2029 }
2030 
2031 static void form_holder_changed (GdauiBasicForm *form, GdaHolder *param, gboolean is_user_modif, GtkDialog *dlg);
2032 
2033 static SingleEntry *
2034 get_single_entry_for_holder (GdauiBasicForm *form, GdaHolder *param)
2035 {
2036 	GSList *list;
2037 	for (list = form->priv->s_entries; list; list = list->next) {
2038 		SingleEntry *sentry = (SingleEntry *) list->data;
2039 		if (sentry->single_param && (sentry->single_param == param))
2040 			return sentry;
2041 		else if (! sentry->single_param) {
2042 			/* multiple parameters */
2043 			GSList *params;
2044 
2045 			for (params = gda_set_group_get_nodes (gdaui_set_group_get_group (sentry->group)); params;
2046 			     params = params->next) {
2047 				if (gda_set_node_get_holder (GDA_SET_NODE (params->data)) == (gpointer) param)
2048 					return sentry;
2049 			}
2050 		}
2051 	}
2052 	return NULL;
2053 }
2054 
2055 static SingleEntry *
2056 get_single_entry_for_id (GdauiBasicForm *form, const gchar *id)
2057 {
2058 	GSList *list;
2059 	g_return_val_if_fail (id, NULL);
2060 
2061 	for (list = form->priv->s_entries; list; list = list->next) {
2062 		SingleEntry *sentry = (SingleEntry *) list->data;
2063 		if (sentry->label_title && !strcmp (sentry->label_title, id))
2064 			return sentry;
2065 		if (sentry->single_param) {
2066 			const gchar *hid;
2067 			hid = gda_holder_get_id (sentry->single_param);
2068 			if (hid && !strcmp (hid, id))
2069 				return sentry;
2070 		}
2071 	}
2072 	return NULL;
2073 }
2074 
2075 /**
2076  * gdaui_basic_form_get_entry_widget:
2077  * @form: a #GdauiBasicForm widget
2078  * @holder: a #GdaHolder object
2079  *
2080  * Get the #GdauiDataEntry in @form which corresponds to the @holder place.
2081  *
2082  * Returns: (transfer none): the requested widget, or %NULL if not found
2083  *
2084  * Since: 4.2
2085  */
2086 GtkWidget *
2087 gdaui_basic_form_get_entry_widget (GdauiBasicForm *form, GdaHolder *holder)
2088 {
2089 	SingleEntry *sentry;
2090 	g_return_val_if_fail (GDAUI_IS_BASIC_FORM (form), NULL);
2091 	g_return_val_if_fail (GDA_IS_HOLDER (holder), NULL);
2092 
2093 	sentry = get_single_entry_for_holder (form, holder);
2094 	if (sentry)
2095 		return GTK_WIDGET (sentry->entry);
2096 	else
2097 		return NULL;
2098 }
2099 
2100 /**
2101  * gdaui_basic_form_get_label_widget:
2102  * @form: a #GdauiBasicForm widget
2103  * @holder: a #GdaHolder object
2104  *
2105  * Get the label in @form which corresponds to the @holder holder.
2106  *
2107  * Returns: (transfer none): the requested widget, or %NULL if not found
2108  *
2109  * Since: 4.2
2110  */
2111 GtkWidget *
2112 gdaui_basic_form_get_label_widget (GdauiBasicForm *form, GdaHolder *holder)
2113 {
2114 	SingleEntry *sentry;
2115 	g_return_val_if_fail (GDAUI_IS_BASIC_FORM (form), NULL);
2116 	g_return_val_if_fail (GDA_IS_HOLDER (holder), NULL);
2117 
2118 	sentry = get_single_entry_for_holder (form, holder);
2119 	if (sentry)
2120 		return sentry->label;
2121 	else
2122 		return NULL;
2123 }
2124 
2125 /**
2126  * gdaui_basic_form_new_in_dialog:
2127  * @data_set: a #GdaSet object
2128  * @parent: (allow-none): the parent window for the new dialog, or %NULL
2129  * @title: (allow-none): the title of the dialog window, or %NULL
2130  * @header: (allow-none): a helper text displayed at the top of the dialog, or %NULL
2131  *
2132  * Creates a new #GdauiBasicForm widget in the same way as gdaui_basic_form_new()
2133  * and puts it into a #GtkDialog widget. The returned dialog has the "Ok" and "Cancel" buttons
2134  * which respectively return GTK_RESPONSE_ACCEPT and GTK_RESPONSE_REJECT.
2135  *
2136  * The #GdauiBasicForm widget is attached to the dialog using the user property
2137  * "form".
2138  *
2139  * Returns: (transfer full): the new #GtkDialog widget
2140  *
2141  * Since: 4.2
2142  */
2143 GtkWidget *
2144 gdaui_basic_form_new_in_dialog (GdaSet *data_set, GtkWindow *parent,
2145 				const gchar *title, const gchar *header)
2146 {
2147 	GtkWidget *form;
2148 	GtkWidget *dlg;
2149 	const gchar *rtitle;
2150 
2151 	form = gdaui_basic_form_new (data_set);
2152 
2153 	rtitle = title;
2154 	if (!rtitle)
2155 		rtitle = _("Values to be defined");
2156 
2157 	dlg = gtk_dialog_new_with_buttons (rtitle, parent,
2158 					   GTK_DIALOG_MODAL,
2159 					   GTK_STOCK_CANCEL,
2160 					   GTK_RESPONSE_REJECT,
2161 					   GTK_STOCK_OK,
2162 					   GTK_RESPONSE_ACCEPT,
2163 					   NULL);
2164 	gtk_dialog_set_default_response (GTK_DIALOG (dlg), GTK_RESPONSE_ACCEPT);
2165 	if (header && *header) {
2166 		GtkWidget *label;
2167 		gchar *str;
2168 
2169 		label = gtk_label_new (NULL);
2170 		gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2171 		str = g_markup_printf_escaped ("<b>%s:</b>", header);
2172 		gtk_label_set_markup (GTK_LABEL (label), str);
2173 		g_free (str);
2174 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))),
2175 				    label, FALSE, FALSE, SPACING);
2176 		gtk_widget_show (label);
2177 	}
2178 
2179 	gboolean can_expand;
2180 	can_expand = gtk_widget_compute_expand (GTK_WIDGET (form), GTK_ORIENTATION_VERTICAL);
2181 	gtk_container_set_border_width (GTK_CONTAINER (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg)))), 4);
2182 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dlg))), form,
2183 			    can_expand, can_expand, 10);
2184 
2185 	g_signal_connect (G_OBJECT (form), "holder-changed",
2186 			  G_CALLBACK (form_holder_changed), dlg);
2187 	g_object_set_data (G_OBJECT (dlg), "form", form);
2188 
2189 	gtk_widget_show_all (form);
2190 	form_holder_changed (GDAUI_BASIC_FORM (form), NULL, FALSE, GTK_DIALOG (dlg));
2191 
2192 	return dlg;
2193 }
2194 
2195 static void
2196 form_holder_changed (GdauiBasicForm *form, G_GNUC_UNUSED GdaHolder *param,
2197 		     G_GNUC_UNUSED gboolean is_user_modif, GtkDialog *dlg)
2198 {
2199 	gboolean valid;
2200 
2201 	valid = gdaui_basic_form_is_valid (form);
2202 
2203 	gtk_dialog_set_response_sensitive (dlg, GTK_RESPONSE_ACCEPT, valid);
2204 }
2205 
2206 /**
2207  * gdaui_basic_form_set_layout_from_file:
2208  * @form: a #GdauiBasicForm
2209  * @file_name: XML file name to use
2210  * @form_name: the name of the form to use, in @file_name
2211  *
2212  * Sets a form layout according an XML description contained in @file_name, for the form identified
2213  * by the @form_name name (as an XML layout file can contain the descriptions of several forms and grids).
2214  *
2215  * Since: 4.2
2216  */
2217 void
2218 gdaui_basic_form_set_layout_from_file (GdauiBasicForm *form, const gchar *file_name, const gchar *form_name)
2219 {
2220 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
2221 	g_return_if_fail (file_name);
2222         g_return_if_fail (form_name);
2223 
2224 	xmlDocPtr doc;
2225         doc = xmlParseFile (file_name);
2226         if (doc == NULL) {
2227                 g_warning (_("'%s' document not parsed successfully"), file_name);
2228                 return;
2229         }
2230 
2231         xmlDtdPtr dtd = NULL;
2232 
2233 	gchar *file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "dtd", "gdaui-layout.dtd", NULL);
2234         if (g_file_test (file, G_FILE_TEST_EXISTS))
2235                 dtd = xmlParseDTD (NULL, BAD_CAST file);
2236         if (dtd == NULL) {
2237                 g_warning (_("'%s' DTD not parsed successfully. "
2238                              "XML data layout validation will not be "
2239                              "performed (some errors may occur)"), file);
2240         }
2241         g_free (file);
2242 
2243         /* Get the root element node */
2244         xmlNodePtr root_node = NULL;
2245         root_node = xmlDocGetRootElement (doc);
2246 
2247         /* Must have root element, a name and the name must be "gdaui_layouts" */
2248         if (!root_node || !root_node->name ||
2249             ! xmlStrEqual (root_node->name, BAD_CAST "gdaui_layouts")) {
2250                 xmlFreeDoc (doc);
2251                 return;
2252         }
2253 
2254         xmlNodePtr node;
2255         for (node = root_node->children; node; node = node->next) {
2256                 if ((node->type == XML_ELEMENT_NODE) &&
2257                     xmlStrEqual (node->name, BAD_CAST "gdaui_form")) {
2258                         xmlChar *str;
2259                         str = xmlGetProp (node, BAD_CAST "name");
2260 			if (str) {
2261 				if (!strcmp ((gchar*) str, form_name)) {
2262 					g_object_set (G_OBJECT (form), "xml-layout", node, NULL);
2263 					xmlFree (str);
2264 					break;
2265 				}
2266                                 xmlFree (str);
2267                         }
2268                 }
2269         }
2270 
2271         /* Free the document */
2272         xmlFreeDoc (doc);
2273 }
2274 
2275 /**
2276  * gdaui_basic_form_get_place_holder:
2277  * @form: a #GdauiBasicForm
2278  * @placeholder_id: the name of the requested place holder
2279  *
2280  * Retreives a pointer to a place holder widget. This feature is only available if a specific
2281  * layout has been defined for @form using gdaui_basic_form_set_layout_from_file().
2282  *
2283  * Returns: (transfer none): a pointer to the requested place holder, or %NULL if not found
2284  *
2285  * Since: 4.2
2286  */
2287 GtkWidget *
2288 gdaui_basic_form_get_place_holder (GdauiBasicForm *form, const gchar *placeholder_id)
2289 {
2290 	g_return_val_if_fail (GDAUI_IS_BASIC_FORM (form), NULL);
2291 	g_return_val_if_fail (placeholder_id, NULL);
2292 
2293 	if (! form->priv->place_holders)
2294 		return NULL;
2295 	return g_hash_table_lookup (form->priv->place_holders, placeholder_id);
2296 }
2297 
2298 /**
2299  * gdaui_basic_form_add_to_size_group:
2300  * @form: a #GdauiBasicForm
2301  * @size_group: a #GtkSizeGroup object
2302  * @part: specifies which widgets in @form are concerned
2303  *
2304  * Add @form's widgets specified by @part to @size_group
2305  * (the widgets can then be removed using gdaui_basic_form_remove_from_size_group()).
2306  *
2307  * Since: 4.2
2308  */
2309 void
2310 gdaui_basic_form_add_to_size_group (GdauiBasicForm *form, GtkSizeGroup *size_group, GdauiBasicFormPart part)
2311 {
2312 	GSList *list;
2313 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
2314 	g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
2315 
2316 	SizeGroup *sg;
2317 	sg = g_new (SizeGroup, 1);
2318 	sg->size_group = g_object_ref (size_group);
2319 	sg->part = part;
2320 	form->priv->size_groups = g_slist_append (form->priv->size_groups, sg);
2321 
2322 	for (list = form->priv->s_entries; list; list = list->next) {
2323 		SingleEntry *se = (SingleEntry*) list->data;
2324 		switch (part) {
2325 		case GDAUI_BASIC_FORM_LABELS:
2326 			if (se->label)
2327 				gtk_size_group_add_widget (size_group, se->label);
2328 			break;
2329 		case GDAUI_BASIC_FORM_ENTRIES:
2330 			if (se->entry)
2331 				gtk_size_group_add_widget (size_group, GTK_WIDGET (se->entry));
2332 			break;
2333 		default:
2334 			g_assert_not_reached ();
2335 		}
2336 	}
2337 }
2338 
2339 /**
2340  * gdaui_basic_form_remove_from_size_group:
2341  * @form: a #GdauiBasicForm
2342  * @size_group: a #GtkSizeGroup object
2343  * @part: specifies which widgets in @form are concerned
2344  *
2345  * Removes @form's widgets specified by @part from @size_group
2346  * (the widgets must have been added using gdaui_basic_form_add_to_size_group()).
2347  *
2348  * Since: 4.2
2349  */
2350 void
2351 gdaui_basic_form_remove_from_size_group (GdauiBasicForm *form, GtkSizeGroup *size_group, GdauiBasicFormPart part)
2352 {
2353 	GSList *list;
2354 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
2355 	g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
2356 
2357 	SizeGroup *sg;
2358 	for (list = form->priv->size_groups; list; list = list->next) {
2359 		sg = (SizeGroup*) list->data;
2360 		if (sg->size_group == size_group) {
2361 			form->priv->size_groups = g_slist_remove (form->priv->size_groups, sg);
2362 			size_group_free (sg);
2363 			break;
2364 		}
2365 		sg = NULL;
2366 	}
2367 
2368 	if (!sg) {
2369 		g_warning (_("size group was not taken into account using gdaui_basic_form_add_to_size_group()"));
2370 		return;
2371 	}
2372 
2373 	for (list = form->priv->s_entries; list; list = list->next) {
2374 		SingleEntry *se = (SingleEntry*) list->data;
2375 		switch (part) {
2376 		case GDAUI_BASIC_FORM_LABELS:
2377 			if (se->label)
2378 				gtk_size_group_remove_widget (size_group, se->label);
2379 			break;
2380 		case GDAUI_BASIC_FORM_ENTRIES:
2381 			if (se->entry)
2382 				gtk_size_group_remove_widget (size_group, GTK_WIDGET (se->entry));
2383 			break;
2384 		default:
2385 			g_assert_not_reached ();
2386 		}
2387 	}
2388 }
2389 
2390 /**
2391  * gdaui_basic_form_set_unknown_color:
2392  * @form: a #GdauiBasicForm widget
2393  * @red: the red component of a color
2394  * @green: the green component of a color
2395  * @blue: the blue component of a color
2396  * @alpha: the alpha component of a color
2397  *
2398  * Defines the color to be used when @form displays an invalid value. Any value not
2399  * between 0. and 1. will result in the default hard coded values to be used (grayish).
2400  *
2401  * Since: 5.0.3
2402  */
2403 void
2404 gdaui_basic_form_set_unknown_color (GdauiBasicForm *form, gdouble red, gdouble green,
2405 				    gdouble blue, gdouble alpha)
2406 {
2407 	g_return_if_fail (GDAUI_IS_BASIC_FORM (form));
2408 	form->priv->red = red;
2409 	form->priv->green = green;
2410 	form->priv->blue = blue;
2411 	form->priv->alpha = alpha;
2412 
2413 	GSList *list;
2414 	for (list = form->priv->s_entries; list; list = list->next) {
2415 		SingleEntry *se;
2416 		se = (SingleEntry *) list->data;
2417 		gdaui_data_entry_set_unknown_color (se->entry, form->priv->red,
2418 						    form->priv->green, form->priv->blue,
2419 						    form->priv->alpha);
2420 	}
2421 }
2422