/* * Copyright (C) 2013 - Juan Ferrer Toribio * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "glade-vn.h" //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SqlBatchEditor #define GLADE_TYPE_BATCH_EDITOR (glade_batch_editor_get_type ()) #define GLADE_BATCH_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_BATCH_EDITOR, GladeBatchEditor)) #define GLADE_BATCH_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_BATCH_EDITOR, GladeBatchEditorClass)) #define GLADE_IS_BATCH_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_BATCH_EDITOR)) #define GLADE_IS_BATCH_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_BATCH_EDITOR)) #define GLADE_BATCH_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GLADE_TYPE_BATCH_EDITOR, GladeBatchEditorClass)) typedef struct _GladeBatchEditor GladeBatchEditor; typedef struct _GladeBatchEditorClass GladeBatchEditorClass; struct _GladeBatchEditor { GtkBox parent; GtkWidget * base; GList * props; }; struct _GladeBatchEditorClass { GtkVBoxClass parent; }; GType glade_batch_editor_get_type (void) G_GNUC_CONST; static GtkWidget * glade_sql_batch_editor_new (GladeWidgetAdaptor * adaptor, GladeEditable * editable) { GtkWidget * frame, * alignment, * vbox; GladeEditorProperty * eprop; GladeBatchEditor * obj; g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL); g_return_val_if_fail (GLADE_IS_EDITABLE (editable), NULL); obj = g_object_new (GLADE_TYPE_BATCH_EDITOR, "orientation", GTK_ORIENTATION_VERTICAL, NULL); obj->base = GTK_WIDGET (editable); gtk_box_pack_start (GTK_BOX (obj), GTK_WIDGET (editable), TRUE, TRUE, 0); eprop = glade_widget_adaptor_create_eprop_by_name (adaptor, "items", FALSE, TRUE); obj->props = g_list_prepend (obj->props, eprop); frame = gtk_frame_new (NULL); gtk_frame_set_label_widget (GTK_FRAME (frame), glade_editor_property_get_item_label (eprop)); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); gtk_box_pack_start (GTK_BOX (obj), frame, FALSE, FALSE, 12); alignment = gtk_alignment_new (0.5F, 0.5F, 1.0F, 1.0F); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 12, 0); gtk_container_add (GTK_CONTAINER (frame), alignment); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (alignment), vbox); gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (eprop), FALSE, FALSE, 4); gtk_widget_set_tooltip_text (vbox, _("List of items")); gtk_widget_show_all (GTK_WIDGET (obj)); return GTK_WIDGET (obj); } static void glade_batch_editor_load (GladeEditable * editable, GladeWidget * widget) { GList *l; GladeBatchEditor * obj = GLADE_BATCH_EDITOR (editable); GladeEditableIface * iface = g_type_default_interface_peek (GLADE_TYPE_EDITABLE); iface->load (editable, widget); if (obj->base) glade_editable_load (GLADE_EDITABLE (obj->base), widget); for (l = obj->props; l; l = l->next) glade_editor_property_load_by_widget (GLADE_EDITOR_PROPERTY (l->data), widget); } static void glade_batch_editor_set_show_name (GladeEditable * editable, gboolean show_name) { GladeBatchEditor * obj = GLADE_BATCH_EDITOR (editable); glade_editable_set_show_name (GLADE_EDITABLE (obj->base), show_name); } static void glade_batch_editor_finalize (GObject * object) { GladeBatchEditor * obj = GLADE_BATCH_EDITOR (object); GObjectClass * parent = g_type_class_peek_parent (GLADE_BATCH_EDITOR_GET_CLASS (obj)); if (obj->props) g_list_free (obj->props); obj->props = NULL; obj->base = NULL; glade_editable_load (GLADE_EDITABLE (obj), NULL); parent->finalize (G_OBJECT (obj)); } static void glade_batch_editor_class_init (GladeBatchEditorClass * k) { GObjectClass * klass = G_OBJECT_CLASS (k); klass->finalize = glade_batch_editor_finalize; } static void glade_batch_editor_init (GladeBatchEditor * obj) { obj->props = NULL; obj->base = NULL; } static void glade_batch_editor_editable_init (GladeEditableIface * iface) { iface->load = glade_batch_editor_load; iface->set_show_name = glade_batch_editor_set_show_name; } G_DEFINE_TYPE_WITH_CODE (GladeBatchEditor, glade_batch_editor, GTK_TYPE_BOX, G_IMPLEMENT_INTERFACE (GLADE_TYPE_EDITABLE, glade_batch_editor_editable_init)); //+++++++++++++++++++++++++++++++++++++++++++++++++++ Items Editor Property typedef struct { GladeEditorProperty parent; GtkTreeView * view; gchar * path; } GladeEPropItems; GLADE_MAKE_EPROP (GladeEPropItems, glade_eprop_items) #define GLADE_TYPE_EPROP_ITEMS (glade_eprop_items_get_type ()) #define GLADE_EPROP_ITEMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_ITEMS, GladeEPropItems)) #define GLADE_EPROP_ITEMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_ITEMS, GladeEPropItemsClass)) #define GLADE_IS_EPROP_ITEMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_ITEMS)) #define GLADE_IS_EPROP_ITEMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_ITEMS)) #define GLADE_EPROP_ITEMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GLADE_EPROP_ITEMS, GladeEPropItemsClass)) enum { ID_COL ,PARAM_COL ,N_COLS }; static void glade_eprop_items_on_param_col_clicked (GtkEntry * entry, GtkEntryIconPosition icon_pos, GdkEvent * event, GladeEditorProperty * eprop) { GtkTreeIter iter; GladeProperty * p = glade_editor_property_get_property (eprop); GladeWidget * widget = glade_property_get_widget (p); GladeProject * project = glade_widget_get_project (widget); GtkListStore * store = ((GladeVnList *) g_value_get_boxed (glade_property_inline_value (p)))->list; gchar * path = GLADE_EPROP_ITEMS (eprop)->path; if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &iter, path)) return; gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, PARAM_COL, &widget, -1); if (glade_editor_property_show_object_dialog (project, _("Select a GvnParam"), NULL, GVN_TYPE_PARAM, NULL, &widget)) { if (widget) gtk_list_store_set (store, &iter, PARAM_COL, widget, -1); else gtk_list_store_set (store, &iter, PARAM_COL, NULL, -1); } } static gboolean glade_eprop_items_on_param_key_pressed (GtkEntry * entry, GdkEventKey * event, GladeEditorProperty * eprop) { if (event->keyval == GDK_KEY_Return) { glade_eprop_items_on_param_col_clicked (entry, 0, NULL, eprop); return TRUE; } else if (event->keyval == GDK_KEY_Delete) { GtkTreeIter iter; gchar * path = GLADE_EPROP_ITEMS (eprop)->path; GladeProperty * p = glade_editor_property_get_property (eprop); GtkListStore * store = ((GladeVnList *) g_value_get_boxed (glade_property_inline_value (p)))->list; if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &iter, path)) { gtk_list_store_set (store, &iter, PARAM_COL, NULL, -1); return TRUE; } } return FALSE; } static void param_col_cell_data (GtkTreeViewColumn * view, GtkCellRenderer * cell, GtkTreeModel * model, GtkTreeIter * iter, gpointer data) { GladeWidget * param; gtk_tree_model_get (model, iter, PARAM_COL, ¶m, -1); if (param) g_object_set (cell, "text", glade_widget_get_name (param), NULL); else g_object_set (cell, "text", "", NULL); } static void glade_eporp_items_on_param_col_editing_started (GtkCellRendererText * cell, GtkEntry * entry, gchar * path, GladeEditorProperty * eprop) { GladeEPropItems * obj = GLADE_EPROP_ITEMS (eprop); if (GTK_IS_ENTRY (entry)) { GList * n; gboolean have_params = FALSE; GladeProperty * p = glade_editor_property_get_property (eprop); GladeProject * project = glade_widget_get_project (glade_property_get_widget (p)); GList * objects = (GList *) glade_project_get_objects (project); for (n = objects; n; n = n->next) if ((have_params = GVN_IS_PARAM (n->data))) break; if (have_params) { g_object_set (entry ,"secondary-icon-name", "content-loading-symbolic" ,"secondary-icon-sensitive", TRUE ,"secondary-icon-tooltip-text", _("Select the parameter from a list") ,NULL); if (obj->path) g_free (obj->path); obj->path = g_strdup (path); g_signal_connect (entry, "icon-press", G_CALLBACK (glade_eprop_items_on_param_col_clicked), eprop); g_signal_connect (entry, "key-press-event", G_CALLBACK (glade_eprop_items_on_param_key_pressed), eprop); } else { g_object_set (entry ,"secondary-icon-name", "dialog-warning-symbolic" ,"secondary-icon-sensitive", FALSE ,"secondary-icon-tooltip-text", _("You need to create at least " "one GvnParam or derived object") ,NULL); g_signal_handlers_disconnect_by_data (entry, eprop); } g_object_set (entry, "editable", FALSE, NULL); } } static void glade_eporp_items_on_id_col_edited (GtkCellRendererText * cell, gchar * path, const gchar * text, GladeEditorProperty * eprop) { GtkTreeIter iter; GladeProperty * p = glade_editor_property_get_property (eprop); GladeVnList * list = g_value_get_boxed (glade_property_inline_value (p)); GtkListStore * store = list->list; if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (store), &iter, path)) return; gtk_list_store_set (store, &iter, ID_COL, text, -1); } static void glade_widget_on_remove_widget (GladeProject * project, GladeWidget * widget, GtkListStore * store) { if (g_type_is_a (G_OBJECT_TYPE (glade_widget_get_object (widget)), GVN_TYPE_PARAM)) { GtkTreeIter iter; GtkTreeModel * m = GTK_TREE_MODEL (store); if (gtk_tree_model_get_iter_first (m, &iter)) do { GladeWidget * param; gtk_tree_model_get (m, &iter, PARAM_COL, ¶m, -1); if (param == widget) gtk_list_store_set (store, &iter, PARAM_COL, NULL, -1); } while (gtk_tree_model_iter_next (m, &iter)); } } static void glade_eporp_items_on_add_clicked (GtkButton * button, GladeEPropItems * obj) { GtkTreeIter iter; GtkTreePath * path; GladeVnList * list; GtkListStore * store; GladeProperty * p = glade_editor_property_get_property (GLADE_EDITOR_PROPERTY (obj)); if (!(list = g_value_get_boxed (glade_property_inline_value (p)))) { list = g_new (GladeVnList, 1); list->list = gtk_list_store_new (N_COLS, G_TYPE_STRING, GLADE_TYPE_WIDGET, G_TYPE_BOOLEAN); glade_property_set (p, list); g_signal_connect (glade_widget_get_project (glade_property_get_widget (p)), "remove-widget", G_CALLBACK (glade_widget_on_remove_widget), list->list); } store = list->list; gtk_tree_view_set_model (obj->view, GTK_TREE_MODEL (store)); gtk_list_store_append (store, &iter); path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); gtk_tree_view_scroll_to_cell (obj->view, path, NULL, FALSE, 0, 0); gtk_tree_view_set_cursor (obj->view, path, NULL, TRUE); gtk_widget_grab_focus (GTK_WIDGET (obj->view)); gtk_tree_path_free (path); } static void glade_eporp_items_on_remove_clicked (GtkButton * button, GladeEPropItems * obj) { GtkTreeIter iter; GtkListStore * store; GtkTreeSelection * selection = gtk_tree_view_get_selection (obj->view); GladeProperty * p; if (!gtk_tree_selection_get_selected (selection, (GtkTreeModel **) &store, &iter)) return; p = glade_editor_property_get_property (GLADE_EDITOR_PROPERTY (obj)); gtk_list_store_remove (store, &iter); if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { gtk_tree_view_set_model (obj->view, NULL); glade_command_set_property (p, NULL); g_signal_handlers_disconnect_by_func (glade_widget_get_project (glade_property_get_widget (p)), glade_widget_on_remove_widget, store); } } static gboolean glade_eprop_items_on_view_key_press (GtkWidget * treeview, GdkEventKey * event, GladeEPropItems * obj) { if (event->keyval == GDK_KEY_Delete) { glade_eporp_items_on_remove_clicked (NULL, obj); return TRUE; } else if ((event->state & GDK_CONTROL_MASK) != 0 && (event->keyval == GDK_KEY_n || event->keyval == GDK_KEY_N)) { glade_eporp_items_on_add_clicked (NULL, obj); return TRUE; } return FALSE; } static GtkWidget * glade_eprop_items_create_input (GladeEditorProperty * eprop) { GtkCellRenderer * cell; GtkTreeViewColumn * column; GladeEPropItems * obj = GLADE_EPROP_ITEMS (eprop); GtkWidget * scroll, * button, * label, * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4), * box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); label = gtk_label_new (_("Add or remove items")); gtk_label_set_use_markup (GTK_LABEL (label), TRUE); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); gtk_misc_set_padding (GTK_MISC (label), 2, 4); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); button = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_icon_name ("list-add-symbolic", GTK_ICON_SIZE_BUTTON)); g_signal_connect (button, "clicked", G_CALLBACK (glade_eporp_items_on_add_clicked), obj); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); button = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_icon_name ("list-remove-symbolic", GTK_ICON_SIZE_BUTTON)); g_signal_connect (button, "clicked", G_CALLBACK (glade_eporp_items_on_remove_clicked), obj); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0); scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (box), scroll, TRUE, TRUE, 0); obj->view = GTK_TREE_VIEW (gtk_tree_view_new ()); g_signal_connect (obj->view, "key-press-event", G_CALLBACK (glade_eprop_items_on_view_key_press), obj); gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (obj->view)); cell = gtk_cell_renderer_text_new (); g_object_set (cell, "editable", TRUE, NULL); column = gtk_tree_view_column_new_with_attributes (_("Identifier"), cell, "text", ID_COL, NULL); gtk_tree_view_column_set_expand (column, TRUE); g_signal_connect (cell, "edited", G_CALLBACK (glade_eporp_items_on_id_col_edited), obj); gtk_tree_view_append_column (obj->view, column); cell = gtk_cell_renderer_text_new (); g_object_set (cell, "editable", TRUE, NULL); column = gtk_tree_view_column_new_with_attributes (_("Parameter"), cell, NULL); gtk_tree_view_column_set_cell_data_func (column, cell, param_col_cell_data, obj, NULL); gtk_tree_view_column_set_expand (column, TRUE); g_signal_connect (cell, "editing-started", G_CALLBACK (glade_eporp_items_on_param_col_editing_started), obj); gtk_tree_view_append_column (obj->view, column); g_object_set (G_OBJECT (box), "height-request", 200, NULL); gtk_widget_show_all (box); return box; } static void glade_eprop_items_finalize (GObject * object) { GladeEPropItems * obj = GLADE_EPROP_ITEMS (object); GObjectClass * parent = g_type_class_peek_parent (GLADE_EPROP_ITEMS_GET_CLASS (obj)); parent->finalize (G_OBJECT (obj)); } static void glade_eprop_items_load (GladeEditorProperty * eprop, GladeProperty * property) { GladeVnList * list; GladeEPropItems * obj = GLADE_EPROP_ITEMS (eprop); GladeEditorPropertyClass * parent_class = g_type_class_peek_parent (GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)); parent_class->load (eprop, property); if (!property) return; if ((list = g_value_get_boxed (glade_property_inline_value (property)))) gtk_tree_view_set_model (obj->view, GTK_TREE_MODEL (list->list)); else gtk_tree_view_set_model (obj->view, NULL); } //+++++++++++++++++++++++++++++++++++++++++++++++++ SqlBatch Widget Adaptor GladeEditorProperty * glade_sql_batch_create_eprop (GladeWidgetAdaptor * adaptor, GladePropertyClass * klass, gboolean use_command) { GladeEditorProperty * eprop; const gchar * prop_id = glade_property_class_id (klass); if (!g_strcmp0 (prop_id, "items")) eprop = g_object_new (GLADE_TYPE_EPROP_ITEMS, "property-class", klass, "use-command", use_command, NULL); else eprop = GWA_GET_CLASS (G_TYPE_OBJECT)->create_eprop (adaptor, klass, use_command); return eprop; } GladeEditable * glade_sql_batch_create_editable (GladeWidgetAdaptor * adaptor, GladeEditorPageType type) { GladeEditable * editable = GWA_GET_CLASS (G_TYPE_OBJECT)->create_editable (adaptor, type); if (type == GLADE_PAGE_GENERAL) return GLADE_EDITABLE (glade_sql_batch_editor_new (adaptor, editable)); return editable; } void glade_sql_batch_write_widget (GladeWidgetAdaptor * adaptor, GladeWidget * widget, GladeXmlContext * context, GladeXmlNode * node) { GtkTreeIter iter; GladeVnList * l; GtkTreeModel * m; GladeXmlNode * items_node; GladeProperty * prop; if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET)) return; GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node); prop = glade_widget_get_property (widget, "items"); // Custom tag for the "items" property, e.g.: // // // if (!(l = g_value_get_boxed (glade_property_inline_value (prop)))) return; m = GTK_TREE_MODEL (l->list); if (!gtk_tree_model_get_iter_first (m, &iter)) return; items_node = glade_xml_node_new (context, "items"); do { gchar * id; GladeWidget * param; GladeXmlNode * item_node; gtk_tree_model_get (m, &iter ,ID_COL, &id ,PARAM_COL, ¶m , -1); item_node = glade_xml_node_new (context, "item"); glade_xml_node_append_child (items_node, item_node); glade_xml_node_set_property_string (item_node, "id", id ? id : ""); glade_xml_node_set_property_string (item_node, "param", param ? glade_widget_get_name (param) : ""); g_free (id); } while (gtk_tree_model_iter_next (m, &iter)); if (!glade_xml_node_get_children (items_node)) glade_xml_node_delete (items_node); else glade_xml_node_append_child (node, items_node); } typedef struct { GtkListStore * store; GtkTreeIter * iter; gchar * param_name; } ParseData; void glade_sql_batch_on_parse_finished (GladeProject * p, ParseData * pd) { GladeWidget * param = glade_project_get_widget_by_name (p, pd->param_name); gtk_list_store_set (pd->store, pd->iter, PARAM_COL, param, -1); g_object_unref (pd->store); gtk_tree_iter_free (pd->iter); g_free (pd->param_name); g_free (pd); } void glade_sql_batch_read_widget (GladeWidgetAdaptor * adaptor, GladeWidget * widget, GladeXmlNode * node) { GladeXmlNode * items_node; GladeProperty * prop; GladeProject * proj; GladeVnList * list; GtkListStore * store; if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET)) return; GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node); if ((items_node = glade_xml_search_child (node, "items")) == NULL) return; proj = glade_widget_get_project (widget); list = g_new (GladeVnList, 1); store = gtk_list_store_new (N_COLS, G_TYPE_STRING, GLADE_TYPE_WIDGET); list->list = store; for (items_node = glade_xml_node_get_children (items_node); items_node; items_node = glade_xml_node_next (items_node)) { GtkTreeIter iter; gint col = PARAM_COL; gchar * id = glade_xml_get_property_string (items_node, "id"), * param_name = glade_xml_get_property_string (items_node, "param"); GladeWidget * param = glade_project_get_widget_by_name (proj, param_name); gtk_list_store_append (store, &iter); if (!param) { // If the parameter hasn't been read yet, load it after the parse ParseData * pd; pd = g_new (ParseData, 1); pd->store = g_object_ref (store); pd->iter = gtk_tree_iter_copy (&iter); pd->param_name = g_strdup (param_name); g_signal_connect (proj, "parse-finished", G_CALLBACK (glade_sql_batch_on_parse_finished), pd); col = -1; } gtk_list_store_set (store, &iter ,ID_COL, id ,col, param ,-1); g_free (id); g_free (param_name); } prop = glade_widget_get_property (widget, "items"); glade_property_set (prop, list); }