/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 2001 Ximian, Inc. * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: * Chema Celorio */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "glade.h" #include "glade-widget.h" #include "glade-widget-adaptor.h" #include "glade-editor.h" #include "glade-signal-editor.h" #include "glade-parameter.h" #include "glade-property.h" #include "glade-property-class.h" #include "glade-command.h" #include "glade-debug.h" #include "glade-marshallers.h" #include "glade-project.h" #include "glade-utils.h" #include "glade-editor-property.h" enum { PROP_0, PROP_SHOW_INFO, PROP_SHOW_CONTEXT_INFO }; enum { GTK_DOC_SEARCH, LAST_SIGNAL }; static GtkVBoxClass *parent_class = NULL; static guint glade_editor_signals[LAST_SIGNAL] = { 0 }; static void glade_editor_reset_dialog (GladeEditor *editor); static void glade_editor_gtk_doc_search_cb (GladeEditorProperty *eprop, const gchar *book, const gchar *page, const gchar *search, GladeEditor *editor) { /* Just act as a hub for search signals here */ g_signal_emit (G_OBJECT (editor), glade_editor_signals[GTK_DOC_SEARCH], 0, book, page, search); } static void glade_editor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GladeEditor *editor = GLADE_EDITOR (object); switch (prop_id) { case PROP_SHOW_INFO: if (g_value_get_boolean (value)) glade_editor_show_info (editor); else glade_editor_hide_info (editor); break; case PROP_SHOW_CONTEXT_INFO: if (g_value_get_boolean (value)) glade_editor_show_context_info (editor); else glade_editor_hide_context_info (editor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_editor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GladeEditor *editor = GLADE_EDITOR (object); switch (prop_id) { case PROP_SHOW_INFO: g_value_set_boolean (value, editor->show_info); break; case PROP_SHOW_CONTEXT_INFO: g_value_set_boolean (value, editor->show_context_info); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void glade_editor_class_init (GladeEditorClass *klass) { GObjectClass *object_class; parent_class = g_type_class_peek_parent (klass); object_class = G_OBJECT_CLASS (klass); object_class->set_property = glade_editor_set_property; object_class->get_property = glade_editor_get_property; klass->gtk_doc_search = NULL; /* Properties */ g_object_class_install_property (object_class, PROP_SHOW_INFO, g_param_spec_boolean ("show-info", _("Show info"), _("Whether to show an informational " "button for the loaded widget"), FALSE, G_PARAM_READABLE)); g_object_class_install_property (object_class, PROP_SHOW_CONTEXT_INFO, g_param_spec_boolean ("show-context-info", _("Show context info"), _("Whether to show an informational button for " "each property and signal in the editor"), FALSE, G_PARAM_READABLE)); /** * GladeEditor::gtk-doc-search: * @gladeeditor: the #GladeEditor which received the signal. * @arg1: the (#gchar *) book to search or %NULL * @arg2: the (#gchar *) page to search or %NULL * @arg3: the (#gchar *) search string or %NULL * * Emitted when the editor requests that a doc-search be performed. */ glade_editor_signals[GTK_DOC_SEARCH] = g_signal_new ("gtk-doc-search", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GladeEditorClass, gtk_doc_search), NULL, NULL, glade_marshal_VOID__STRING_STRING_STRING, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); } static GtkWidget * glade_editor_notebook_page (GladeEditor *editor, const gchar *name) { GtkWidget *alignment; GtkWidget *sw; GtkWidget *label_widget; GtkWidget *image; static gchar *path; static gint page = 0; /* alignment is needed to ensure property labels have some padding on the left */ alignment = gtk_alignment_new (0.5, 0, 1, 0); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 6, 0); /* construct tab label widget */ if (g_utf8_collate (name, _("Accessibility")) == 0) { path = g_build_filename (glade_app_get_pixmaps_dir (), "atk.png", NULL); image = gtk_image_new_from_file (path); label_widget = gtk_event_box_new (); gtk_container_add (GTK_CONTAINER (label_widget), image); gtk_widget_show (label_widget); gtk_widget_show (image); glade_util_widget_set_tooltip (label_widget, name); } else { label_widget = gtk_label_new_with_mnemonic (name); } /* configure page container */ if (g_utf8_collate (name, _("_Signals")) == 0) { gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 1, 1); gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 0, 0); gtk_container_set_border_width (GTK_CONTAINER (alignment), 6); gtk_notebook_insert_page (GTK_NOTEBOOK (editor->notebook), alignment, label_widget, page++); } else { /* wrap the alignment in a scrolled window */ sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), GTK_WIDGET (alignment)); gtk_container_set_border_width (GTK_CONTAINER (sw), 6); gtk_notebook_insert_page (GTK_NOTEBOOK (editor->notebook), sw, label_widget, page++); } return alignment; } static void glade_editor_on_reset_click (GtkButton *button, GladeEditor *editor) { glade_editor_reset_dialog (editor); } static void glade_editor_on_docs_click (GtkButton *button, GladeEditor *editor) { gchar *book; if (editor->loaded_widget) { g_object_get (editor->loaded_widget->adaptor, "book", &book, NULL); g_signal_emit (G_OBJECT (editor), glade_editor_signals[GTK_DOC_SEARCH], 0, book, editor->loaded_widget->adaptor->name, NULL); g_free (book); } } static GtkWidget * glade_editor_create_info_button (GladeEditor *editor) { GtkWidget *button; GtkWidget *image; button = gtk_button_new (); image = glade_util_get_devhelp_icon (GTK_ICON_SIZE_BUTTON); gtk_widget_show (image); gtk_container_add (GTK_CONTAINER (button), image); glade_util_widget_set_tooltip (button, _("View documentation for the selected widget")); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_editor_on_docs_click), editor); return button; } static GtkWidget * glade_editor_create_reset_button (GladeEditor *editor) { GtkWidget *image; GtkWidget *button; button = gtk_button_new (); image = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_BUTTON); gtk_container_add (GTK_CONTAINER (button), image); glade_util_widget_set_tooltip (button, _("Reset widget properties to their defaults")); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_editor_on_reset_click), editor); return button; } static void glade_editor_init (GladeEditor *editor) { GtkSizeGroup *size_group; GtkWidget *hbox; editor->notebook = gtk_notebook_new (); editor->page_widget = glade_editor_notebook_page (editor, _("_General")); editor->page_packing = glade_editor_notebook_page (editor, _("_Packing")); editor->page_common = glade_editor_notebook_page (editor, _("_Common")); editor->page_signals = glade_editor_notebook_page (editor, _("_Signals")); editor->page_atk = glade_editor_notebook_page (editor, _("Accessibility")); editor->widget_tables = NULL; editor->packing_etable = NULL; editor->loading = FALSE; gtk_container_set_border_width (GTK_CONTAINER (editor->notebook), 0); gtk_box_pack_start (GTK_BOX (editor), editor->notebook, TRUE, TRUE, 0); hbox = gtk_hbox_new (FALSE, 6); gtk_container_set_border_width (GTK_CONTAINER (hbox), 0); gtk_box_pack_start (GTK_BOX (editor), hbox, FALSE, FALSE, 0); size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH); /* Reset button */ editor->reset_button = glade_editor_create_reset_button (editor); gtk_box_pack_end (GTK_BOX (hbox), editor->reset_button, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, editor->reset_button); /* Documentation button */ editor->info_button = glade_editor_create_info_button (editor); gtk_box_pack_end (GTK_BOX (hbox), editor->info_button, FALSE, FALSE, 0); gtk_size_group_add_widget (size_group, editor->info_button); g_object_unref(size_group); gtk_widget_show_all (GTK_WIDGET (editor)); if (editor->show_info) gtk_widget_show (editor->info_button); else gtk_widget_hide (editor->info_button); gtk_widget_hide (GTK_WIDGET (editor)); } GType glade_editor_get_type (void) { static GType type = 0; if (!type) { static const GTypeInfo info = { sizeof (GladeEditorClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) glade_editor_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (GladeEditor), 0, (GInstanceInitFunc) glade_editor_init }; type = g_type_register_static (GTK_TYPE_VBOX, "GladeEditor", &info, 0); } return type; } /** * glade_editor_new: * * Returns: a new #GladeEditor */ GladeEditor * glade_editor_new (void) { GladeEditor *editor; editor = g_object_new (GLADE_TYPE_EDITOR, "spacing", 6, NULL); return editor; } /* * We call this function when the user changes the widget name using the * entry on the properties editor. */ static void glade_editor_widget_name_changed (GtkWidget *editable, GladeEditor *editor) { GladeWidget *widget; gchar *new_name; g_return_if_fail (GTK_IS_EDITABLE (editable)); g_return_if_fail (GLADE_IS_EDITOR (editor)); if (editor->loading) return; widget = editor->loaded_widget; new_name = gtk_editable_get_chars (GTK_EDITABLE (editable), 0, -1); glade_command_set_name (widget, new_name); g_free (new_name); } static gboolean glade_editor_widget_name_focus_out (GtkWidget *entry, GdkEventFocus *event, GladeEditor *editor) { glade_editor_widget_name_changed (entry, editor); return FALSE; } static void glade_editor_table_attach (GtkWidget *table, GtkWidget *child, gint pos, gint row) { gtk_table_attach (GTK_TABLE (table), child, pos, pos+1, row, row +1, pos ? GTK_EXPAND | GTK_FILL : GTK_FILL, GTK_EXPAND | GTK_FILL, 3, 1); } static GladeEditorProperty * glade_editor_table_append_item (GladeEditorTable *table, GladePropertyClass *klass, gboolean from_query_dialog) { GladeEditorProperty *property; property = glade_editor_property_new (klass, from_query_dialog == FALSE); gtk_widget_show (GTK_WIDGET (property)); gtk_widget_show_all (property->eventbox); if (table->editor->show_context_info && from_query_dialog == FALSE) glade_editor_property_show_info (property); else glade_editor_property_hide_info (property); g_signal_connect (G_OBJECT (property), "gtk-doc-search", G_CALLBACK (glade_editor_gtk_doc_search_cb), table->editor); glade_editor_table_attach (table->table_widget, property->eventbox, 0, table->rows); glade_editor_table_attach (table->table_widget, GTK_WIDGET (property), 1, table->rows); table->rows++; return property; } static void glade_editor_table_append_name_field (GladeEditorTable *table) { GtkWidget *label; /* Name */ label = gtk_label_new (_("Name:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_widget_show (label); table->name_entry = gtk_entry_new (); gtk_widget_show (table->name_entry); g_signal_connect (G_OBJECT (table->name_entry), "activate", G_CALLBACK (glade_editor_widget_name_changed), table->editor); g_signal_connect (G_OBJECT (table->name_entry), "focus-out-event", G_CALLBACK (glade_editor_widget_name_focus_out), table->editor); glade_editor_table_attach (table->table_widget, label, 0, table->rows); glade_editor_table_attach (table->table_widget, table->name_entry, 1, table->rows); table->rows++; } static void glade_editor_table_append_class_field (GladeEditorTable *table) { GtkWidget *label; GtkWidget *class_entry; /* Class */ label = gtk_label_new (_("Class:")); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_widget_show (label); class_entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (class_entry), table->adaptor->name); gtk_editable_set_editable (GTK_EDITABLE (class_entry), FALSE); gtk_widget_show (class_entry); glade_editor_table_attach (table->table_widget, label, 0, table->rows); glade_editor_table_attach (table->table_widget, class_entry, 1, table->rows); table->rows++; } static gint glade_editor_property_class_comp (gconstpointer a, gconstpointer b) { const GladePropertyClass *ca = a, *cb = b; if (ca->pspec->owner_type == cb->pspec->owner_type) { gdouble result = ca->weight - cb->weight; /* Avoid cast to int */ if (result < 0.0) return -1; else if (result > 0.0) return 1; else return 0; } else { if (g_type_is_a (ca->pspec->owner_type, cb->pspec->owner_type)) return (ca->common || ca->packing) ? 1 : -1; else return (ca->common || ca->packing) ? -1 : 1; } } static GList * glade_editor_get_sorted_properties (GladeWidgetAdaptor *adaptor) { GList *l, *a = NULL, *b = NULL; for (l = adaptor->properties; l && l->data; l = g_list_next (l)) { GladePropertyClass *klass = l->data; if (klass->common || klass->packing) a = g_list_prepend (a, klass); else b = g_list_prepend (b, klass); } a = g_list_sort (a, glade_editor_property_class_comp); b = g_list_sort (b, glade_editor_property_class_comp); return g_list_concat (a, b); } static gboolean glade_editor_table_append_items (GladeEditorTable *table, GladeWidgetAdaptor *adaptor, GladeEditorTableType type) { GladeEditorProperty *property; GladePropertyClass *property_class; GList *list, *sorted_list; sorted_list = glade_editor_get_sorted_properties (adaptor); for (list = sorted_list; list != NULL; list = list->next) { property_class = (GladePropertyClass *) list->data; if (!glade_property_class_is_visible (property_class)) continue; if (type == TABLE_TYPE_QUERY && !property_class->query) continue; else if (type == TABLE_TYPE_COMMON && !property_class->common) continue; else if (type == TABLE_TYPE_GENERAL && property_class->common) continue; else if (type == TABLE_TYPE_ATK && (property_class->type == GPC_NORMAL || property_class->type == GPC_ACCEL_PROPERTY)) continue; else if (type != TABLE_TYPE_ATK && (property_class->type != GPC_NORMAL && property_class->type != GPC_ACCEL_PROPERTY)) continue; property = glade_editor_table_append_item (table, property_class, type == TABLE_TYPE_QUERY); table->properties = g_list_prepend (table->properties, property); } g_list_free (sorted_list); return TRUE; } static GladeEditorTable * glade_editor_table_new (void) { GladeEditorTable *table; table = g_new0 (GladeEditorTable, 1); table->table_widget = gtk_table_new (0, 0, FALSE); g_object_ref (G_OBJECT(table->table_widget)); return table; } static void glade_editor_table_free (GladeEditorTable *etable) { g_object_unref (G_OBJECT(etable->table_widget)); g_free (etable); } static GladeEditorTable * glade_editor_table_create (GladeEditor *editor, GladeWidgetAdaptor *adaptor, GladeEditorTableType type) { GladeEditorTable *table; g_return_val_if_fail (GLADE_IS_EDITOR (editor), NULL); g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL); table = glade_editor_table_new (); table->editor = editor; table->adaptor = adaptor; table->type = type; if (type == TABLE_TYPE_GENERAL) { glade_editor_table_append_class_field (table); glade_editor_table_append_name_field (table); } if (!glade_editor_table_append_items (table, adaptor, type)) return NULL; gtk_widget_show (table->table_widget); return table; } static GladeEditorTable * glade_editor_get_table_from_class (GladeEditor *editor, GladeWidgetAdaptor *adaptor, GladeEditorTableType type) { GladeEditorTable *table; GList *list; for (list = editor->widget_tables; list; list = list->next) { table = list->data; if (type != table->type) continue; if (table->adaptor == adaptor) return table; } table = glade_editor_table_create (editor, adaptor, type); g_return_val_if_fail (table != NULL, NULL); editor->widget_tables = g_list_prepend (editor->widget_tables, table); return table; } static void glade_editor_load_page (GladeEditor *editor, GladeWidgetAdaptor *adaptor, GladeEditorTableType type) { GladeEditorTable *table; GtkContainer *container = NULL; GtkWidget *scrolled_window; GList *list, *children; GtkAdjustment *adj; /* Remove the old table that was in this container */ switch (type) { case TABLE_TYPE_GENERAL: container = GTK_CONTAINER (editor->page_widget); break; case TABLE_TYPE_COMMON: container = GTK_CONTAINER (editor->page_common); break; case TABLE_TYPE_ATK: container = GTK_CONTAINER (editor->page_atk); break; case TABLE_TYPE_PACKING: case TABLE_TYPE_QUERY: default: g_critical ("Unreachable code reached !"); break; } children = gtk_container_get_children (container); for (list = children; list; list = list->next) { GtkWidget *widget = list->data; g_return_if_fail (GTK_IS_WIDGET (widget)); gtk_widget_ref (widget); gtk_container_remove (container, widget); } g_list_free (children); if (!adaptor) return; table = glade_editor_get_table_from_class (editor, adaptor, type); /* Attach the new table */ gtk_container_add (GTK_CONTAINER (container), table->table_widget); /* Enable tabbed keynav in the editor */ if (table) { scrolled_window = gtk_widget_get_parent (GTK_WIDGET (container)); scrolled_window = gtk_widget_get_parent (scrolled_window); /* FIXME: Save pointer to the scrolled window (or just the adjustments) before hand. */ g_assert (GTK_IS_SCROLLED_WINDOW (scrolled_window)); adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window)); gtk_container_set_focus_vadjustment (GTK_CONTAINER (table->table_widget), adj); adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled_window)); gtk_container_set_focus_hadjustment (GTK_CONTAINER (table->table_widget), adj); } } /** * glade_editor_update_widget_name: * @editor: a #GladeEditor * * TODO: write me */ void glade_editor_update_widget_name (GladeEditor *editor) { GladeEditorTable *table = glade_editor_get_table_from_class (editor, editor->loaded_adaptor, TABLE_TYPE_GENERAL); g_signal_handlers_block_by_func (G_OBJECT (table->name_entry), glade_editor_widget_name_changed, editor); gtk_entry_set_text (GTK_ENTRY (table->name_entry), editor->loaded_widget->name); g_signal_handlers_unblock_by_func (G_OBJECT (table->name_entry), glade_editor_widget_name_changed, editor); } static void glade_editor_load_signal_page (GladeEditor *editor) { if (editor->signal_editor == NULL) { editor->signal_editor = glade_signal_editor_new ((gpointer) editor); gtk_container_add (GTK_CONTAINER (editor->page_signals), glade_signal_editor_get_widget (editor->signal_editor)); } } static void glade_editor_load_widget_class (GladeEditor *editor, GladeWidgetAdaptor *adaptor) { glade_editor_load_page (editor, adaptor, TABLE_TYPE_GENERAL); glade_editor_load_page (editor, adaptor, TABLE_TYPE_COMMON); glade_editor_load_page (editor, adaptor, TABLE_TYPE_ATK); glade_editor_load_signal_page (editor); editor->loaded_adaptor = adaptor; } static gint glade_editor_property_comp (gconstpointer a, gconstpointer b) { const GladeProperty *prop_a = a, *prop_b = b; return glade_editor_property_class_comp (prop_a->klass, prop_b->klass); } static void glade_editor_load_packing_page (GladeEditor *editor, GladeWidget *widget) { GladeEditorProperty *editor_property; GladeProperty *property; GladeWidget *parent; GList *list, *sorted_list; GtkWidget *child; /* Remove the old properties */ if ((child = gtk_bin_get_child (GTK_BIN (editor->page_packing))) != NULL) gtk_container_remove (GTK_CONTAINER (editor->page_packing), child); /* Free the packing editor table */ if (editor->packing_etable) editor->packing_etable = (glade_editor_table_free (editor->packing_etable), NULL); /* Free the packing editor properties (we gave ownership to * packing_etable->table_widget, so no need to unref them here). */ editor->packing_eprops = (g_list_free (editor->packing_eprops), NULL); /* if the widget is a toplevel there are no packing properties */ if (widget == NULL || (parent = glade_widget_get_parent (widget)) == NULL) return; /* Now add the new properties */ editor->packing_etable = glade_editor_table_new (); editor->packing_etable->editor = editor; editor->packing_etable->type = TABLE_TYPE_PACKING; /* Sort packing properties by weight */ sorted_list = g_list_copy (widget->packing_properties); sorted_list = g_list_sort (sorted_list, glade_editor_property_comp); for (list = sorted_list; list && list->data; list = list->next) { property = GLADE_PROPERTY (list->data); if (glade_property_class_is_visible (property->klass) == FALSE) continue; editor_property = glade_editor_table_append_item (editor->packing_etable, property->klass, FALSE); editor->packing_eprops = g_list_prepend (editor->packing_eprops, editor_property); glade_editor_property_load (editor_property, property); } g_list_free (sorted_list); gtk_widget_show (editor->packing_etable->table_widget); gtk_container_add (GTK_CONTAINER (editor->page_packing), editor->packing_etable->table_widget); } static void glade_editor_close_cb (GladeProject *project, GladeEditor *editor) { /* Detected project we are viewing was closed, * detatch from editor. */ glade_editor_load_widget (editor, NULL); } static void glade_editor_load_table (GladeEditor *editor, GladeWidget *widget, GladeEditorTableType type) { GladeEditorProperty *property; GladeEditorTable *table; GList *list; table = glade_editor_get_table_from_class (editor, widget->adaptor, type); if (table->name_entry) gtk_entry_set_text (GTK_ENTRY (table->name_entry), widget->name); for (list = table->properties; list; list = list->next) { property = list->data; glade_editor_property_load_by_widget (property, widget); } } static void glade_editor_load_widget_real (GladeEditor *editor, GladeWidget *widget) { GladeWidgetAdaptor *adaptor; GladeProject *project; gchar *book; /* Disconnect from last widget */ if (editor->loaded_widget != NULL) { project = glade_widget_get_project (editor->loaded_widget); g_signal_handler_disconnect (G_OBJECT (project), editor->project_closed_signal_id); } /* Load the GladeWidgetClass */ adaptor = widget ? widget->adaptor : NULL; if (editor->loaded_adaptor != adaptor || adaptor == NULL) glade_editor_load_widget_class (editor, adaptor); glade_editor_load_packing_page (editor, widget); glade_signal_editor_load_widget (editor->signal_editor, widget); /* we are just clearing, we are done */ if (widget == NULL) { gtk_widget_set_sensitive (editor->reset_button, FALSE); gtk_widget_set_sensitive (editor->info_button, FALSE); editor->loaded_widget = NULL; return; } gtk_widget_set_sensitive (editor->reset_button, TRUE); g_object_get (editor->loaded_adaptor, "book", &book, NULL); gtk_widget_set_sensitive (editor->info_button, book != NULL); g_free (book); editor->loading = TRUE; /* Load each GladeEditorProperty from 'widget' */ glade_editor_load_table (editor, widget, TABLE_TYPE_GENERAL); glade_editor_load_table (editor, widget, TABLE_TYPE_COMMON); glade_editor_load_table (editor, widget, TABLE_TYPE_ATK); editor->loaded_widget = widget; editor->loading = FALSE; /* Connect to new widget */ project = glade_widget_get_project (editor->loaded_widget); editor->project_closed_signal_id = g_signal_connect (G_OBJECT (project), "close", G_CALLBACK (glade_editor_close_cb), editor); } /** * glade_editor_load_widget: * @editor: a #GladeEditor * @widget: a #GladeWidget * * Load @widget into @editor. If @widget is %NULL, clear the editor. */ void glade_editor_load_widget (GladeEditor *editor, GladeWidget *widget) { g_return_if_fail (GLADE_IS_EDITOR (editor)); g_return_if_fail (widget == NULL || GLADE_IS_WIDGET (widget)); if (editor->loaded_widget == widget) return; glade_editor_load_widget_real (editor, widget); } /** * glade_editor_refresh: * @editor: a #GladeEditor * * Synchronize @editor with the currently loaded widget. */ void glade_editor_refresh (GladeEditor *editor) { g_return_if_fail (GLADE_IS_EDITOR (editor)); glade_editor_load_widget_real (editor, editor->loaded_widget); } static void query_dialog_style_set_cb (GtkWidget *dialog, GtkStyle *previous_style, gpointer user_data) { gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 12); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 12); gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 0); gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6); } gboolean glade_editor_query_dialog (GladeEditor *editor, GladeWidget *widget) { GtkWidget *dialog; GladeEditorTable *table; gchar *title; GList *list; GladeEditorProperty *property; gint answer; gboolean retval = TRUE; title = g_strdup_printf (_("Create a %s"), widget->adaptor->name); dialog = gtk_dialog_new_with_buttons (title, NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); g_free (title); gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); table = glade_editor_get_table_from_class (editor, widget->adaptor, TABLE_TYPE_QUERY); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), table->table_widget, FALSE, FALSE, 6); for (list = table->properties; list; list = list->next) { property = list->data; glade_editor_property_load_by_widget (property, widget); } g_signal_connect (dialog, "style-set", G_CALLBACK (query_dialog_style_set_cb), NULL); answer = gtk_dialog_run (GTK_DIALOG (dialog)); /* * If user cancel's we cancel the whole "create operation" by * return FALSE. glade_widget_new() will see the FALSE, and * take care of canceling the "create" operation. */ if (answer == GTK_RESPONSE_CANCEL) retval = FALSE; gtk_container_remove (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), table->table_widget); gtk_widget_destroy (dialog); return retval; } enum { COLUMN_ENABLED = 0, COLUMN_PROP_NAME, COLUMN_PROPERTY, COLUMN_PARENT, COLUMN_CHILD, COLUMN_DEFAULT, COLUMN_NDEFAULT, COLUMN_DEFSTRING, NUM_COLUMNS }; static void glade_editor_reset_toggled (GtkCellRendererToggle *cell, gchar *path_str, GtkTreeModel *model) { GtkTreePath *path = gtk_tree_path_new_from_string (path_str); GtkTreeIter iter; gboolean enabled; /* get toggled iter */ gtk_tree_model_get_iter (model, &iter, path); gtk_tree_model_get (model, &iter, COLUMN_ENABLED, &enabled, -1); gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COLUMN_ENABLED, !enabled, -1); gtk_tree_path_free (path); } static GtkWidget * glade_editor_reset_view (GladeEditor *editor) { GtkWidget *view_widget; GtkTreeModel *model; GtkCellRenderer *renderer; GtkTreeViewColumn *column; model = (GtkTreeModel *)gtk_tree_store_new (NUM_COLUMNS, G_TYPE_BOOLEAN, /* Enabled value */ G_TYPE_STRING, /* Property name */ GLADE_TYPE_PROPERTY, /* The property */ G_TYPE_BOOLEAN, /* Parent node ? */ G_TYPE_BOOLEAN, /* Child node ? */ G_TYPE_BOOLEAN, /* Has default value */ G_TYPE_BOOLEAN, /* Doesn't have defaut */ G_TYPE_STRING); /* Default string */ view_widget = gtk_tree_view_new_with_model (model); g_object_set (G_OBJECT (view_widget), "enable-search", FALSE, NULL); /********************* fake invisible column *********************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, "visible", FALSE, NULL); column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column); gtk_tree_view_column_set_visible (column, FALSE); gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view_widget), column); /************************ enabled column ************************/ renderer = gtk_cell_renderer_toggle_new (); g_object_set (G_OBJECT (renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, "activatable", TRUE, NULL); g_signal_connect (renderer, "toggled", G_CALLBACK (glade_editor_reset_toggled), model); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view_widget), COLUMN_ENABLED, _("Reset"), renderer, "sensitive", COLUMN_NDEFAULT, "activatable", COLUMN_NDEFAULT, "active", COLUMN_ENABLED, "visible", COLUMN_CHILD, NULL); /********************* property name column *********************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, "weight", PANGO_WEIGHT_BOLD, NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view_widget), COLUMN_PROP_NAME, _("Property"), renderer, "text", COLUMN_PROP_NAME, "weight-set", COLUMN_PARENT, NULL); /******************* default indicator column *******************/ renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", FALSE, "style", PANGO_STYLE_ITALIC, "foreground", "Gray", NULL); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view_widget), COLUMN_DEFSTRING, NULL, renderer, "text", COLUMN_DEFSTRING, "visible", COLUMN_DEFAULT, NULL); return view_widget; } static void glade_editor_populate_reset_view (GladeEditor *editor, GtkTreeView *tree_view) { GtkTreeStore *model = (GtkTreeStore *)gtk_tree_view_get_model (tree_view); GtkTreeIter property_iter, general_iter, common_iter, atk_iter, *iter; GList *list; GladeProperty *property; gboolean def; g_return_if_fail (editor->loaded_widget != NULL); gtk_tree_store_append (model, &general_iter, NULL); gtk_tree_store_set (model, &general_iter, COLUMN_PROP_NAME, _("General"), COLUMN_PROPERTY, NULL, COLUMN_PARENT, TRUE, COLUMN_CHILD, FALSE, COLUMN_DEFAULT, FALSE, COLUMN_NDEFAULT, FALSE, -1); gtk_tree_store_append (model, &common_iter, NULL); gtk_tree_store_set (model, &common_iter, COLUMN_PROP_NAME, _("Common"), COLUMN_PROPERTY, NULL, COLUMN_PARENT, TRUE, COLUMN_CHILD, FALSE, COLUMN_DEFAULT, FALSE, COLUMN_NDEFAULT, FALSE, -1); gtk_tree_store_append (model, &atk_iter, NULL); gtk_tree_store_set (model, &atk_iter, COLUMN_PROP_NAME, _("Accessibility"), COLUMN_PROPERTY, NULL, COLUMN_PARENT, TRUE, COLUMN_CHILD, FALSE, COLUMN_DEFAULT, FALSE, COLUMN_NDEFAULT, FALSE, -1); /* General & Common */ for (list = editor->loaded_widget->properties; list; list = list->next) { property = list->data; if (glade_property_class_is_visible (property->klass) == FALSE) continue; if (property->klass->type != GPC_NORMAL && property->klass->type != GPC_ACCEL_PROPERTY) iter = &atk_iter; else if (property->klass->common) iter = &common_iter; else iter = &general_iter; def = glade_property_default (property); gtk_tree_store_append (model, &property_iter, iter); gtk_tree_store_set (model, &property_iter, COLUMN_ENABLED, !def, COLUMN_PROP_NAME, property->klass->name, COLUMN_PROPERTY, property, COLUMN_PARENT, FALSE, COLUMN_CHILD, TRUE, COLUMN_DEFAULT, def, COLUMN_NDEFAULT, !def, COLUMN_DEFSTRING, _("(default)"), -1); } } static gboolean glade_editor_reset_selection_changed_cb (GtkTreeSelection *selection, GtkTextView *desc_view) { GtkTreeIter iter; GladeProperty *property = NULL; GtkTreeModel *model = NULL; GtkTextBuffer *text_buffer; const gchar *message = _("Select the properties that you want to reset to their default values"); /* Find selected data and show property blurb here */ if (gtk_tree_selection_get_selected (selection, &model, &iter)) { text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (desc_view)); gtk_tree_model_get (model, &iter, COLUMN_PROPERTY, &property, -1); gtk_text_buffer_set_text (text_buffer, property ? glade_property_get_tooltip (property) : message, -1); if (property) g_object_unref (G_OBJECT (property)); } return TRUE; } static gboolean glade_editor_reset_foreach_selection (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gboolean select) { gboolean def; gtk_tree_model_get (model, iter, COLUMN_DEFAULT, &def, -1); /* Dont modify rows that are already default */ if (def == FALSE) gtk_tree_store_set (GTK_TREE_STORE (model), iter, COLUMN_ENABLED, select, -1); return FALSE; } static void glade_editor_reset_select_all_clicked (GtkButton *button, GtkTreeView *tree_view) { GtkTreeModel *model = gtk_tree_view_get_model (tree_view); gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) glade_editor_reset_foreach_selection, GINT_TO_POINTER (TRUE)); } static void glade_editor_reset_unselect_all_clicked (GtkButton *button, GtkTreeView *tree_view) { GtkTreeModel *model = gtk_tree_view_get_model (tree_view); gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) glade_editor_reset_foreach_selection, GINT_TO_POINTER (FALSE)); } static gboolean glade_editor_reset_accumulate_selected_props (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GList **accum) { GladeProperty *property; gboolean enabled, def; gtk_tree_model_get (model, iter, COLUMN_PROPERTY, &property, COLUMN_ENABLED, &enabled, COLUMN_DEFAULT, &def, -1); if (property && enabled && !def) *accum = g_list_prepend (*accum, property); if (property) g_object_unref (G_OBJECT (property)); return FALSE; } static GList * glade_editor_reset_get_selected_props (GtkTreeModel *model) { GList *ret = NULL; gtk_tree_model_foreach (model, (GtkTreeModelForeachFunc) glade_editor_reset_accumulate_selected_props, &ret); return ret; } static void glade_editor_reset_properties (GList *props) { GList *list, *sdata_list = NULL; GCSetPropData *sdata; GladeProperty *prop; GladeProject *project = NULL; for (list = props; list; list = list->next) { prop = list->data; project = glade_widget_get_project (prop->widget); sdata = g_new (GCSetPropData, 1); sdata->property = prop; sdata->old_value = g_new0 (GValue, 1); sdata->new_value = g_new0 (GValue, 1); glade_property_get_value (prop, sdata->old_value); glade_property_get_default (prop, sdata->new_value); sdata_list = g_list_prepend (sdata_list, sdata); } if (project) /* GladeCommand takes ownership of allocated list, ugly but practicle */ glade_command_set_properties_list (project, sdata_list); } static void glade_editor_reset_dialog (GladeEditor *editor) { GtkTreeSelection *selection; GtkWidget *dialog, *parent; GtkWidget *vbox, *hbox, *label, *sw, *button; GtkWidget *tree_view, *description_view; gint res; GList *list; parent = gtk_widget_get_toplevel (GTK_WIDGET (editor)); dialog = gtk_dialog_new_with_buttons (_("Reset Widget Properties"), GTK_WINDOW (parent), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); vbox = gtk_vbox_new (FALSE, 6); gtk_widget_show (vbox); gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0); /* Checklist */ label = gtk_label_new_with_mnemonic (_("_Properties:")); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request (sw, 400, 200); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); tree_view = glade_editor_reset_view (editor); if (editor->loaded_widget) glade_editor_populate_reset_view (editor, GTK_TREE_VIEW (tree_view)); gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); gtk_widget_show (tree_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view); gtk_container_add (GTK_CONTAINER (sw), tree_view); /* Select all / Unselect all */ hbox = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END); gtk_widget_show (hbox); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); button = gtk_button_new_with_mnemonic (_("_Select All")); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (button), 6); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_editor_reset_select_all_clicked), tree_view); button = gtk_button_new_with_mnemonic (_("_Unselect All")); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (button), 6); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (glade_editor_reset_unselect_all_clicked), tree_view); /* Description */ label = gtk_label_new_with_mnemonic (_("Property _Description:")); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_widget_show (sw); gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request (sw, 400, 80); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); description_view = gtk_text_view_new (); gtk_text_view_set_editable (GTK_TEXT_VIEW (description_view), FALSE); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (description_view), GTK_WRAP_WORD); gtk_widget_show (description_view); gtk_label_set_mnemonic_widget (GTK_LABEL (label), description_view); gtk_container_add (GTK_CONTAINER (sw), description_view); /* Update description */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (glade_editor_reset_selection_changed_cb), description_view); /* Run the dialog */ res = gtk_dialog_run (GTK_DIALOG (dialog)); if (res == GTK_RESPONSE_OK) { /* get all selected properties and reset properties through glade_command */ if ((list = glade_editor_reset_get_selected_props (gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)))) != NULL) { glade_editor_reset_properties (list); g_list_free (list); } } gtk_widget_destroy (dialog); } void glade_editor_show_info (GladeEditor *editor) { g_return_if_fail (GLADE_IS_EDITOR (editor)); if (editor->show_info != TRUE) { editor->show_info = TRUE; gtk_widget_show (editor->info_button); g_object_notify (G_OBJECT (editor), "show-info"); } } void glade_editor_hide_info (GladeEditor *editor) { g_return_if_fail (GLADE_IS_EDITOR (editor)); if (editor->show_info != FALSE) { editor->show_info = FALSE; gtk_widget_hide (editor->info_button); g_object_notify (G_OBJECT (editor), "show-info"); } } void glade_editor_show_context_info (GladeEditor *editor) { GList *list, *props; GladeEditorTable *etable; g_return_if_fail (GLADE_IS_EDITOR (editor)); if (editor->show_context_info != TRUE) { editor->show_context_info = TRUE; for (list = editor->widget_tables; list; list = list->next) { etable = list->data; for (props = etable->properties; props; props = props->next) glade_editor_property_show_info (GLADE_EDITOR_PROPERTY (props->data)); } if (editor->packing_etable) { etable = editor->packing_etable; for (props = etable->properties; props; props = props->next) glade_editor_property_show_info (GLADE_EDITOR_PROPERTY (props->data)); } g_object_notify (G_OBJECT (editor), "show-context-info"); } } void glade_editor_hide_context_info (GladeEditor *editor) { GList *list, *props; GladeEditorTable *etable; g_return_if_fail (GLADE_IS_EDITOR (editor)); if (editor->show_context_info != FALSE) { editor->show_context_info = FALSE; for (list = editor->widget_tables; list; list = list->next) { etable = list->data; for (props = etable->properties; props; props = props->next) glade_editor_property_hide_info (GLADE_EDITOR_PROPERTY (props->data)); } if (editor->packing_etable) { etable = editor->packing_etable; for (props = etable->properties; props; props = props->next) glade_editor_property_hide_info (GLADE_EDITOR_PROPERTY (props->data)); } g_object_notify (G_OBJECT (editor), "show-context-info"); } }