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); 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, 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; 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 { 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: 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) { 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); 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; 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; 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 } 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; 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 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); 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) { 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); 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; 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 */ 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); 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 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); 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, 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); 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 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