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