diff options
Diffstat (limited to 'tools/glade/glade/gbwidget.c')
-rw-r--r-- | tools/glade/glade/gbwidget.c | 4434 |
1 files changed, 4434 insertions, 0 deletions
diff --git a/tools/glade/glade/gbwidget.c b/tools/glade/glade/gbwidget.c new file mode 100644 index 00000000..06969c46 --- /dev/null +++ b/tools/glade/glade/gbwidget.c @@ -0,0 +1,4434 @@ +/* Gtk+ User Interface Builder + * Copyright (C) 1998 Damon Chaplin + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> + +#include <gtk/gtk.h> +#include "gladeconfig.h" + +#ifdef USE_GNOME +#include <gnome.h> +#endif + +#include "editor.h" +#include "gb.h" +#include "gbwidget.h" +#include "glade.h" +#include "glade_atk.h" +#include "glade_gnome.h" +#include "glade_gtk12lib.h" +#include "glade_plugin.h" +#include "glade_project.h" +#include "load.h" +#include "palette.h" +#include "property.h" +#include "save.h" +#include "tree.h" +#include "utils.h" + +#ifdef USE_GNOME +#include "glade_gnomelib.h" +#endif + +#ifdef USE_GNOME_DB +#include "glade_gnomedblib.h" +#endif + +/* + * The hash table associates a Gtk widget class name with a GbWidget struct, + * which contains a table of functions used by the builder, e.g. creating a + * new widget, saving the widget etc. + */ +static GHashTable *gb_widget_table = NULL; + +/* Tooltips for the created widgets */ +static GtkTooltips *gb_widget_tooltips; + + +/* This is the GTK+ stock id string, used in GtkImageMenuItem widgets. */ +const gchar *GladeMenuItemStockIDKey = "GladeMenuItemStockIDKey"; + +/* This is the old Gnome stock index key. */ +const gchar *GladeMenuItemStockIndexKey = "GladeMenuItemStockIndexKey"; + +/* This is the key we use to store the stock icon name or full pathname. */ +const gchar *GladeIconKey = "GladeIconKey"; + +/* This is used to save a pointer to the GladeWidgetInfo inside a widget while + loading, so we can resolve ATK relations afterwards. */ +const gchar *GladeWidgetInfoKey = "GladeWidgetInfoKey"; + + +static void gb_widget_init_widget_lib (GladePaletteSectionData *sections); + +static void on_widget_destroy (GtkWidget * widget, + gpointer data); + +#ifdef GLADE_STYLE_SUPPORT +static void show_color_properties (GdkColor colors[], + gchar * name); +#endif +static void show_accelerators (GtkWidget * widget, + GbWidgetGetArgData * data); +static void show_signals (GtkWidget * widget, + GbWidgetGetArgData * data); + +static void set_standard_properties (GtkWidget * widget, + GbWidgetSetArgData * data); +static void set_position_properties (GtkWidget * widget, + GbWidgetSetArgData * data); +static void set_special_child_properties (GtkWidget * widget, + GbWidgetSetArgData * data); +static void set_lang_specific_properties (GtkWidget * widget, + GbWidgetSetArgData * data); +#if 0 +static void apply_style (GtkWidget * widget, + GbWidgetSetArgData * data); +static gboolean apply_colors (GtkWidget * widget, + GbWidgetSetArgData * data, + GdkColor colors[], + GdkColor new_colors[], + gchar * name); +#endif +static void apply_accelerators (GtkWidget * widget, + GbWidgetSetArgData * data); +static void apply_signals (GtkWidget * widget, + GbWidgetSetArgData * data); + +static void add_standard_top_menu_items (GtkWidget * widget, + GbWidgetCreateMenuData * data); +static void add_standard_bottom_menu_items (GtkWidget * widget, + GbWidgetCreateMenuData * data); + +static void get_standard_properties (GtkWidget * widget, + GbWidgetGetArgData * data); + +static void get_position_properties (GtkWidget * widget, + GbWidgetGetArgData * data); +static void get_lang_specific_properties (GtkWidget * widget, + GbWidgetGetArgData * data); + +static void save_accelerators (GtkWidget * widget, + GbWidgetGetArgData * data); +static void save_signals (GtkWidget * widget, + GbWidgetGetArgData * data); + + +static void gb_widget_add_alignment (GtkWidget * menuitem, + GtkWidget * widget); +static void gb_widget_remove_alignment (GtkWidget * menuitem, + GtkWidget * widget); +static void gb_widget_add_event_box (GtkWidget * menuitem, + GtkWidget * widget); +static void gb_widget_remove_event_box (GtkWidget * menuitem, + GtkWidget * widget); +static void gb_widget_redisplay_window (GtkWidget * menuitem, + GtkWidget * widget); +static void gb_widget_add_scrolled_window (GtkWidget * menuitem, + GtkWidget * widget); +static void gb_widget_remove_scrolled_window (GtkWidget * menuitem, + GtkWidget * widget); + +static void table_foreach (GtkTable * table, + GtkCallback callback, + gpointer callback_data); +static void box_foreach (GtkBox *box, + GtkCallback callback, + gpointer callback_data); + +static gint find_notebook_page (GtkNotebook * notebook, + GtkWidget * current_child, + GtkWidget **page, + GtkWidget **tab_label); + + +/* These aren't included in the Bonobo headers, so we declare them here to + avoid warnings. */ +#ifdef USE_GNOME +void bonobo_dock_item_get_floating_position (BonoboDockItem *item, + gint *x, gint *y); +gboolean bonobo_dock_item_detach (BonoboDockItem *item, gint x, gint y); +#endif + +/************************************************************************* + * Initialization functions + *************************************************************************/ + +void +gb_widgets_init () +{ + gb_widget_table = g_hash_table_new (g_str_hash, g_str_equal); + +#ifdef GLADE_STYLE_SUPPORT + gb_widget_reset_gb_styles (); +#endif + + /* Create tooltips */ + gb_widget_tooltips = gtk_tooltips_new (); + + gb_widget_init_widget_lib (get_gtk_widgets()); +#ifdef USE_GNOME + gb_widget_init_widget_lib (get_gnome_widgets()); +#endif +#ifdef USE_GNOME_DB + gb_widget_init_widget_lib (get_gnome_db_widgets()); +#endif + + glade_plugin_load_plugins (); +} + +static void +gb_widget_init_widget_lib (GladePaletteSectionData *sections) +{ + gint index, j; + GladeWidgetInitData *gwid; + GladePaletteSectionData *palsec; + GbWidget *(*init_func) (); + GbWidget *gbwidget; + + index = 0; + while (1) + { + j = 0; + palsec = §ions[index]; + index++; + if (!palsec->section) + break; + while (1) + { + gwid = &palsec->widgets[j]; + j++; + if (!gwid->name) + break; + init_func = gwid->init_func; + gbwidget = (*init_func) (); + gb_widget_register_gbwidget (gwid->name, gbwidget); + palette_add_gbwidget (gbwidget, palsec->section, gwid->name); + } + } +} + + +/* Adds a GbWidget to the hash of known GbWidgets. The class_id is copied. */ +void +gb_widget_register_gbwidget (const gchar *class_id, + GbWidget *gbwidget) +{ + gbwidget->class_id = g_strdup (class_id); + g_hash_table_insert (gb_widget_table, gbwidget->class_id, gbwidget); +} + + +/* This returns the GbWidget struct corresponding to the given class name. */ +GbWidget * +gb_widget_lookup_class (const gchar *class_id) +{ + GbWidget *gbwidget; + + gbwidget = (GbWidget *) g_hash_table_lookup (gb_widget_table, class_id); + + return gbwidget; +} + + +/* This returns the GbWidget struct corresponding to the given widget. */ +GbWidget * +gb_widget_lookup (GtkWidget *widget) +{ + GladeWidgetData *wdata; + + wdata = gtk_object_get_data (GTK_OBJECT (widget), + GB_WIDGET_DATA_KEY); + + if (!wdata) { + const gchar *class_name; + + MSG ("Widget has no associated GladeWidgetData"); + + /* Fall back to the original way we used to get the GbWidget*, since + some widgets currently don't have a GladeWidgetData attached. */ + class_name = gtk_type_name (GTK_OBJECT_TYPE (widget)); + return (GbWidget *) g_hash_table_lookup (gb_widget_table, class_name); + } + + return wdata->gbwidget; +} + + +gchar* +gb_widget_get_class_id (GtkWidget *widget) +{ + GbWidget *gbwidget; + + gbwidget = gb_widget_lookup (widget); + + if (gbwidget) + return gbwidget->class_id; + else + return (char*) gtk_type_name (GTK_OBJECT_TYPE (widget)); +} + + +void +gb_widget_init_struct (GbWidget * gbwidget) +{ + gbwidget->pixmap_struct = NULL; + gbwidget->class_id = NULL; + gbwidget->gdkpixmap = NULL; + gbwidget->mask = NULL; + gbwidget->tooltip = NULL; + gbwidget->pixbuf = NULL; + gbwidget->properties_page_number = GB_PROPERTIES_NOT_CREATED; + gbwidget->child_properties_page_number = GB_PROPERTIES_NOT_CREATED; + + gbwidget->gb_widget_new = NULL; + gbwidget->gb_widget_create_from_widget = NULL; + + gbwidget->gb_widget_create_properties = NULL; + gbwidget->gb_widget_get_properties = NULL; + gbwidget->gb_widget_set_properties = NULL; + + gbwidget->gb_widget_add_child = NULL; + gbwidget->gb_widget_get_child = NULL; + + gbwidget->gb_widget_create_child_properties = NULL; + gbwidget->gb_widget_get_child_properties = NULL; + gbwidget->gb_widget_set_child_properties = NULL; + + gbwidget->gb_widget_write_add_child_source = NULL; + + gbwidget->gb_widget_create_popup_menu = NULL; + + gbwidget->gb_widget_write_source = NULL; + + gbwidget->gb_widget_destroy = NULL; +} + + + +/************************************************************************* + * Functions for creating & destroying GbWidgets + *************************************************************************/ + +GtkWidget * +gb_widget_new (const gchar * class_id, GtkWidget * parent) +{ + return gb_widget_new_full (class_id, TRUE, parent, NULL, 0, 0, NULL, + GB_CREATING, NULL); +} + + +/* Creates a new widget. + * class_id is the name of the widget class, e.g. 'GtkLabel' + * create_default_name is TRUE if you want a default name to be created, + * e.g. 'label1'. + * parent is the widget that the new widget will be added beneath, so that + * the callback function knows where to put the widget. + * current_child is the widget that the new widget will replace, or NULL + * if the new widget is just being added. It is used when replacing + * placeholders. + * x & y are the coordinates of the new widget if it is being added to a + * GtkFixed container. + * callback is the function to call once the widget is created to actually + * add it to the parent. Some widgets require dialog boxes for creating + * them (e.g. the dialog box to set the number of rows/cols in a table). + * So we need to provide a function to be called after this is done + * (we could have possibly chosen to use modal dialogs instead.) + * action is either GB_CREATING or GB_LOADING. When loading widgets we don't + * want dialog boxes to pop up when they are being created. + * loading_data is set when action is GB_LOADING, and contains the data used + * while loading, so that the GbWidgets can get any properties they need to + * create the widget without popping up a dialog box. + */ +GtkWidget * +gb_widget_new_full (const gchar * class_id, gboolean create_default_name, + GtkWidget * parent, GtkWidget * current_child, + gint x, gint y, GbWidgetNewCallback callback, + GbWidgetAction action, GbWidgetSetArgData * loading_data) +{ + GbWidgetNewData *data; + GtkWidget *new_widget; + GbWidget *gbwidget; + GType type; + + gbwidget = gb_widget_lookup_class (class_id); + g_return_val_if_fail (gbwidget != NULL, NULL); + + /* Note that for custom widgets this won't be found, and so will be 0. */ + type = g_type_from_name (class_id); +#if 0 + g_print ("Class Name: %s Type: %i\n", class_id, type); +#endif + + data = g_new (GbWidgetNewData, 1); + /* Don't set data->name to NULL, since many widgets use it to set the label + of the new widget. */ + data->project = current_project; + data->name = create_default_name ? glade_project_new_widget_name (data->project, class_id) : g_strdup (""); + data->callback = callback; + data->parent = parent; + if (parent) { + gtk_widget_ref (parent); + if (!gb_widget_lookup (parent)) + MSG2 ("Registering unknown widget '%s' as parent of '%s'", + G_OBJECT_TYPE_NAME (parent), class_id); + } + data->current_child = current_child; + if (current_child) + gtk_widget_ref (current_child); + data->x = x; + data->y = y; + data->widget_data = glade_widget_data_new (gbwidget); + data->action = action; + data->loading_data = loading_data; + + if (gbwidget->gb_widget_new) + new_widget = (gbwidget->gb_widget_new) (data); + else if (type != 0) + new_widget = gtk_widget_new (type, NULL); + else + g_return_val_if_fail ((new_widget = NULL), NULL); + + /* If the widget has been created immediately, then we can finish it off now, + and free the GbWidgetNewData struct, otherwise we leave that to the + dialog. */ + if (new_widget) + { + gb_widget_initialize (new_widget, data); + if (data->callback) + (*data->callback) (new_widget, data); + gb_widget_free_new_data (data); + } + + return new_widget; +} + +static void +gb_widget_real_initialize (GtkWidget * widget, GladeWidgetData * wdata) +{ + g_return_if_fail (wdata != NULL); + g_return_if_fail (wdata->gbwidget != NULL); + + /* Make sure GtkMenu widgets have visible set to FALSE. */ + if (GTK_IS_MENU (widget) && wdata) + wdata->flags &= ~GLADE_VISIBLE; + + gtk_object_set_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY, wdata); + gtk_signal_connect (GTK_OBJECT (widget), "destroy", + GTK_SIGNAL_FUNC (on_widget_destroy), NULL); + + editor_add_mouse_signals (widget); + editor_add_draw_signals (widget); + editor_add_key_signals (widget); + + if (GTK_IS_WINDOW (widget)) + gtk_window_add_accel_group (GTK_WINDOW (widget), + glade_get_global_accel_group ()); + +#ifdef USE_GNOME + /* FIXME: GnomeLibs 1.0.1 workaround - floating GnomeDockItem's don't work + properly if we show them before adding to the GnomeDock. */ + if (!GTK_IS_WINDOW (widget) && !GTK_IS_MENU (widget) + && !BONOBO_IS_DOCK_ITEM (widget)) + gtk_widget_show (widget); +#else + if (!GTK_IS_WINDOW (widget) && !GTK_IS_MENU (widget)) + gtk_widget_show (widget); +#endif +} + +/* This turns a normal widget into a GbWidget, adding a GladeWidgetData struct + and the necessary signals. The widget must have its parent set already. + If name is non-NULL, the widget's name will be set to the name with a + unique ID added on to it, e.g. "ok_button1". + NOTE: when loading, you should not create the names of any widgets, + since it may clash with a widget loaded later. Instead leave the name as + NULL. glade_project_ensure_widgets_named () will be called after the + project is loaded, and any widgets without names will have names + generated for them. */ +void +gb_widget_create_from (GtkWidget *widget, + const gchar *name) +{ + gb_widget_create_from_full (widget, name, NULL); +} + + +void +gb_widget_create_from_full (GtkWidget *widget, + const gchar *name, + GladeWidgetData *wdata) +{ + GbWidget *gbwidget; + const char *class_id; + + MSG1 ("In create_from, widget name: %s", gtk_widget_get_name (widget)); + if (name) + { + char *wname = glade_project_new_widget_name (current_project, name); + gtk_widget_set_name (widget, wname); + g_free (wname); + } + + if (GLADE_IS_CUSTOM_WIDGET (widget)) + class_id = "Custom"; + else + class_id = gtk_type_name (GTK_OBJECT_TYPE (widget)); + + gbwidget = (GbWidget *) g_hash_table_lookup (gb_widget_table, class_id); + g_return_if_fail (gbwidget != NULL); + + if (!wdata) + wdata = glade_widget_data_new (gbwidget); + gb_widget_real_initialize (widget, wdata); + + gbwidget = gb_widget_lookup (widget); + g_return_if_fail (gbwidget != NULL); + + /* Call any function the GbWidget has for setting up the widget to be used + within Glade. */ + if (gbwidget->gb_widget_create_from_widget) + { + GbWidgetCreateFromData data; + + data.project = current_project; + (gbwidget->gb_widget_create_from_widget) (widget, &data); + } +} + + +void +gb_widget_initialize (GtkWidget * widget, GbWidgetNewData * data) +{ + if (data->name && data->name[0] != '\0') + gtk_widget_set_name (widget, data->name); + gb_widget_real_initialize (widget, data->widget_data); + + /* Now we set the widget's real style */ + /* FIXME: check if style should be propagated down from an ancestor? */ +#if 0 + if (widget->style != data->widget_data->gbstyle->style) + { + gtk_widget_set_style (widget, data->widget_data->gbstyle->style); + } +#endif + + /* FIXME: GTK workarounds to make sure that some widgets have reasonable + sizes initially. Quite a few widgets default to a width and height of 0, + which means that if there is little space available they will disappear, + so we may need to do more here. */ + if (GTK_IS_ARROW (widget)) + gtk_widget_set_usize (widget, 16, 16); + + /* Set this to NULL so we don't try to free it later. */ + data->widget_data = NULL; +} + +/* This returns TRUE if it is OK to complete the new() procedure, i.e. that + the widget to replace or the parent widget still exist. It is used after + the OK button is pressed in the dialog boxes for creating new tables/boxes. + FIXME: I'm not too sure what we should do here. */ +gboolean +gb_widget_can_finish_new (GbWidgetNewData * data) +{ + if (data->current_child) + { + if (data->current_child->parent == NULL) + return FALSE; + } + else if (data->parent) + { + if (data->parent->parent == NULL && !GTK_IS_WINDOW (data->parent)) + return FALSE; + } + return TRUE; +} + + +void +gb_widget_free_new_data (GbWidgetNewData * data) +{ + g_free (data->name); + g_free (data->widget_data); + if (data->parent) + gtk_widget_unref (data->parent); + if (data->current_child) + gtk_widget_unref (data->current_child); + g_free (data); +} + + +static void +on_widget_destroy (GtkWidget * widget, gpointer user_data) +{ + GbWidget *gbwidget; + GladeWidgetData *widget_data; + GbWidgetDestroyData data; + + MSG1 ("IN on_widget_destroy widget:%s", gtk_widget_get_name (widget)); + + /* Make sure we don't try to show its properties after it is destroyed. */ + if (property_get_widget () == widget) + property_set_widget (NULL); + + /* If the entire project is being destroyed, we don't need to update the + selection or the widget tree. */ + if (!(GTK_OBJECT_FLAGS (current_project) & GTK_IN_DESTRUCTION)) + { + editor_remove_widget_from_selection (widget); + tree_remove_widget (widget); + } + + editor_on_widget_destroyed (widget); + + gbwidget = gb_widget_lookup (widget); + g_return_if_fail (gbwidget != NULL); + + /* Call the GbWidget destroy function, if it has one. */ + data.project = current_project; + if (gbwidget->gb_widget_destroy) + (gbwidget->gb_widget_destroy) (widget, &data); + + widget_data = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + + /* Release the ID. */ + if (widget->name) + glade_project_release_widget_name (current_project, widget->name); + + glade_widget_data_free (widget_data); + + g_free (gb_widget_get_child_name (widget)); + + MSG1 ("OUT on_widget_destroy widget:%s", gtk_widget_get_name (widget)); +} + + +/************************************************************************* + * Functions for getting/setting the child name of a widget. + * The child name is used to identify special widgets which have to + * be treated differently. e.g. Dialog buttons. + *************************************************************************/ + +static const gchar *glade_child_name_key = "glade-child-name"; +static GQuark glade_child_name_key_id = 0; + + +/* Returns the child name of the widget. */ +gchar* +gb_widget_get_child_name (GtkWidget *widget) +{ + if (!glade_child_name_key_id) + glade_child_name_key_id = g_quark_from_static_string (glade_child_name_key); + return gtk_object_get_data_by_id (GTK_OBJECT (widget), + glade_child_name_key_id); +} + + +/* Sets the child name of the widget. The child_name string is duplicated. */ +void +gb_widget_set_child_name (GtkWidget *widget, const gchar *child_name) +{ + if (!glade_child_name_key_id) + glade_child_name_key_id = g_quark_from_static_string (glade_child_name_key); + /* Free any existing child name. */ + g_free (gtk_object_get_data_by_id (GTK_OBJECT (widget), + glade_child_name_key_id)); + gtk_object_set_data_by_id (GTK_OBJECT (widget), glade_child_name_key_id, + g_strdup (child_name)); +} + + +/************************************************************************* + * Functions for creating the page of properties specific to this widget + *************************************************************************/ + +/* Returns the page number of the new page in the notebook which contains the + widget's specific properties, or GB_PROPERTIES_NOT_NEEDED if it has none. */ +gint +gb_widget_create_properties (GtkWidget * widget) +{ + GtkWidget *page; + gint page_number; + GbWidgetCreateArgData data; + GbWidget *gbwidget; + gboolean add_border_width = FALSE; + + gbwidget = gb_widget_lookup (widget); + g_return_val_if_fail (gbwidget != NULL, GB_PROPERTIES_NOT_NEEDED); + + /* We always create the border width property, but hide it when it isn't + needed. Some specific widgets don't need it, e.g. GtkDialog action areas. + */ + /*if (glade_util_uses_border_width (widget))*/ + add_border_width = TRUE; + + if (gbwidget->gb_widget_create_properties || add_border_width) + { + /* Create skeleton of properties page, so gbwidget just has to add + properties */ + page = gtk_table_new (1, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (page), 1); + gtk_widget_show (page); + page_number = property_add_gbwidget_page (page); + property_set_table_position (page, 0); + + /* If widget is a container add a border width property */ + if (add_border_width) + { + gchar *class_id, buf[128]; + + class_id = gb_widget_get_class_id (widget); + sprintf (buf, "%s::border_width", class_id); + property_add_int_range (buf, _("Border Width:"), + _("The width of the border around the container"), + 0, 1000, 1, 10, 1); + } + + data.project = current_project; + if (gbwidget->gb_widget_create_properties) + (gbwidget->gb_widget_create_properties) (widget, &data); + return page_number; + } + else + { + return GB_PROPERTIES_NOT_NEEDED; + } +} + + +/************************************************************************* + * Functions for creating the page of place properties specific to this + * widget. + *************************************************************************/ + +/* Returns the page number of the new page in the notebook which contains the + widget's properties which applt to any children of the widget, + or GB_PROPERTIES_NOT_NEEDED if no extra properties are needed for its + children. */ +gint +gb_widget_create_child_properties (GtkWidget * widget) +{ + static GHashTable *page_hash_table = NULL; + + GtkWidget *page; + gint page_number; + GbWidgetCreateChildArgData data; + GbWidget *gbwidget; + + /* Create a hash table to contain functions already called to create child + packing properties together with the page numbers they returned. + This lets us use the same functions for multiple widgets, e.g. GtkHBox + and GtkVBox, GtkHPaned and GtkVPaned. */ + if (page_hash_table == NULL) + page_hash_table = g_hash_table_new (NULL, NULL); + + gbwidget = gb_widget_lookup (widget); + g_return_val_if_fail (gbwidget != NULL, GB_PROPERTIES_NOT_NEEDED); + + if (gbwidget->gb_widget_create_child_properties) + { + /* First see if the function has already been called. Note that the + page numbers in the hash have 1 added to them so we can detect empty + values (they won't clash with page 0). */ + /* FIXME: ANSI forbids casting function pointer to data pointer. */ + page_number = GPOINTER_TO_INT (g_hash_table_lookup (page_hash_table, (gconstpointer) gbwidget->gb_widget_create_child_properties)); + if (page_number) + return page_number - 1; + + /* Create skeleton of properties page, so gbwidget just has to add + properties */ + page = gtk_table_new (10, 3, FALSE); + gtk_widget_show (page); + page_number = property_add_child_packing_page (page); + /* FIXME: ANSI forbids casting function pointer to data pointer. */ + g_hash_table_insert (page_hash_table, + (gpointer) gbwidget->gb_widget_create_child_properties, + GINT_TO_POINTER (page_number + 1)); + property_set_table_position (page, 0); + + data.project = current_project; + (gbwidget->gb_widget_create_child_properties) (widget, &data); + return page_number; + } + else + { + return GB_PROPERTIES_NOT_NEEDED; + } +} + + +/************************************************************************* + * Functions for showing the widget's properties + *************************************************************************/ + +void +gb_widget_show_properties (GtkWidget * widget) +{ + GbWidgetGetArgData data; + GbWidget *gbwidget, *parent_gbwidget = NULL; + GladeWidgetData *widget_data; + gint page, child_packing_page; + + /* If properties of widget are already shown, just return */ + if (property_get_widget () == widget) + return; + + /* If widget is a placeholder reset the properties window and return */ + if (GB_IS_PLACEHOLDER (widget)) + { + property_set_widget (NULL); + return; + } + + gbwidget = gb_widget_lookup (widget); + g_return_if_fail (gbwidget != NULL); + + /* Turn off auto-apply so we can set properties without the 'changed' + callbacks calling gb_widget_apply_properties (). */ + property_set_auto_apply (FALSE); + + /* Need this here to make sure properties notebook is sensitive */ + property_set_widget (widget); + + page = gbwidget->properties_page_number; + /* If widget's properties page hasn't been created, create it now */ + if (page == GB_PROPERTIES_NOT_CREATED) + { + page = gb_widget_create_properties (widget); + gbwidget->properties_page_number = page; + } + + /* Show the widget's own properties page if it has one. + Need to show the page before setting properties because of the + Text widget - it must be realized before setting the text :-( */ + if (page == GB_PROPERTIES_NOT_NEEDED) + property_hide_gbwidget_page (); + else + property_show_gbwidget_page (page); + + /* Now see if the parent has child packing properties that need to be + created or shown. */ + if (widget->parent) + { + parent_gbwidget = gb_widget_lookup (widget->parent); + + /* parent_gbwidget may be NULL, e.g. for GnomeDockItems. */ + if (parent_gbwidget) + { + child_packing_page = parent_gbwidget->child_properties_page_number; + /* If widget's properties page hasn't been created, create it now */ + if (child_packing_page == GB_PROPERTIES_NOT_CREATED) + { + child_packing_page = gb_widget_create_child_properties (widget->parent); + parent_gbwidget->child_properties_page_number = child_packing_page; + } + + if (child_packing_page == GB_PROPERTIES_NOT_NEEDED) + property_hide_child_packing_page (); + else + property_show_child_packing_page (child_packing_page); + } + } + else + { + property_hide_child_packing_page (); + } + + widget_data = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + g_return_if_fail (widget_data != NULL); + data.project = current_project; + data.action = GB_SHOWING; + data.widget_data = widget_data; + data.widget = widget; + + get_standard_properties (widget, &data); + + if (gbwidget->gb_widget_get_properties) + (gbwidget->gb_widget_get_properties) (widget, &data); + + if (parent_gbwidget && parent_gbwidget->gb_widget_get_child_properties) + (parent_gbwidget->gb_widget_get_child_properties) (widget->parent, widget, + &data); + + /* Turn auto-apply back on again */ + property_set_auto_apply (TRUE); +} + + +/* This is called when the widget's size or position has changed, so that we + should update the values shown in the properties editor. */ +void +gb_widget_show_position_properties (GtkWidget * widget) +{ + GbWidgetGetArgData data; + GladeWidgetData *widget_data; + + /* Make sure this is the widget shown in the properties editor. */ + if (property_get_widget () != widget) + return; + + widget_data = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + g_return_if_fail (widget_data != NULL); + data.project = current_project; + data.action = GB_SHOWING; + data.widget_data = widget_data; + + property_set_auto_apply (FALSE); + get_position_properties (widget, &data); + property_set_auto_apply (TRUE); +} + + + +#ifdef GLADE_STYLE_SUPPORT +void +gb_widget_show_style (GtkWidget * widget) +{ + GladeWidgetData *wdata; + GbStyle *gbstyle; + GtkStyle *style = widget->style; + gchar buffer[128]; + gint i; + + wdata = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + g_return_if_fail (wdata != NULL); + gbstyle = wdata->gbstyle; + + property_set_bool (GbStylePropagate, wdata->flags & GLADE_STYLE_PROPAGATE); + + property_set_dialog (GbStyleName, wdata->flags & GLADE_STYLE_IS_UNNAMED ? + "" : gbstyle->name, NULL); + property_set_font (GbStyleFont, style->font, gbstyle->xlfd_fontname); + + /* Colors */ + show_color_properties (style->fg, "fg"); + show_color_properties (style->bg, "bg"); + show_color_properties (style->text, "text"); + show_color_properties (style->base, "base"); + + /* Background pixmaps */ + for (i = 0; i < GB_NUM_STYLE_STATES; i++) + { + sprintf (buffer, "GtkStyle::%s[%s]", GbBgPixmapName, GbStateNames[i]); + property_set_bgpixmap (buffer, style->bg_pixmap[i], + gbstyle->bg_pixmap_filenames[i]); + } +} + + +static void +show_color_properties (GdkColor colors[], gchar * name) +{ + gint state; + gchar buf[128]; + + for (state = 0; state < GB_NUM_STYLE_STATES; state++) + { + sprintf (buf, "GtkStyle::%s[%s]", name, GbStateNames[state]); + property_set_color (buf, &colors[state]); + } +} +#endif + + +static void +show_accelerators (GtkWidget * widget, GbWidgetGetArgData * data) +{ + GList *element = data->widget_data->accelerators; + property_clear_accelerators (); + while (element) + { + property_add_accelerator ((GladeAccelerator *) element->data); + element = element->next; + } +} + + +static void +show_signals (GtkWidget * widget, GbWidgetGetArgData * data) +{ + GList *element = data->widget_data->signals; + property_clear_signals (); + MSG1 ("Num signals: %i", g_list_length (element)); + while (element) + { + property_add_signal ((GladeSignal *) element->data); + element = element->next; + } +} + + + +/************************************************************************* + * Functions for applying properties to a widget + *************************************************************************/ + +void +gb_widget_apply_properties (GtkWidget * widget, GtkWidget * property) +{ + GbWidgetSetArgData data; + GbWidget *gbwidget; + GladeWidgetData *widget_data; + + MSG1 ("Applying properties: %s", gtk_widget_get_name (widget)); + + gbwidget = gb_widget_lookup (widget); + g_return_if_fail (gbwidget != NULL); + + widget_data = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + g_return_if_fail (widget_data != NULL); + + data.project = current_project; + data.action = GB_APPLYING; + data.widget_data = widget_data; + data.widget = widget; + data.property_to_apply = property; + + set_standard_properties (widget, &data); + + MSG ("Calling widget's own apply_properties"); + if (gbwidget->gb_widget_set_properties) + (gbwidget->gb_widget_set_properties) (widget, &data); + MSG ("Called widget's own apply_properties"); +} + + +/* Copies the signals from the GladeWidgetInfo into a list of GladeSignal + structs. */ +static GList* +copy_signals (GbWidgetSetArgData * data, GladeWidgetInfo *widget_info) +{ + GList *signals = NULL; + GladeSignalInfo *signal_info; + gint i; + + signal_info = widget_info->signals; + for (i = 0; i < widget_info->n_signals; i++) + { + GladeSignal *signal = g_new0 (GladeSignal, 1); + + signal->name = g_strdup (signal_info[i].name); + signal->handler = g_strdup (signal_info[i].handler); + signal->object = g_strdup (signal_info[i].object); + signal->after = signal_info[i].after ? TRUE : FALSE; + signal->data = NULL; /* Not supported anymore. */ + signal->last_modification_time = load_parse_date (data, signal_info[i].last_modification_time); + + if (data->status == GLADE_STATUS_INVALID_VALUE) + { + g_warning ("Invalid date value: %s", signal_info[i].last_modification_time); + data->status = GLADE_STATUS_OK; + } + + signals = g_list_prepend (signals, signal); + } + + /* Reverse the list so it stays in the original order. */ + return g_list_reverse (signals); +} + + +/* Copies the accelerators from the GladeWidgetInfo into a list of + GladeAccelerator structs. */ +static GList* +copy_accels (GladeWidgetInfo *widget_info) +{ + GList *accels = NULL; + GladeAccelInfo *accel_info; + gint i; + + accel_info = widget_info->accels; + for (i = 0; i < widget_info->n_accels; i++) + { + GladeAccelerator *accel = g_new0 (GladeAccelerator, 1); + + accel->key = g_strdup (gdk_keyval_name (accel_info[i].key)); + accel->modifiers = accel_info[i].modifiers; + accel->signal = g_strdup (accel_info[i].signal); + + accels = g_list_prepend (accels, accel); + } + + /* Reverse the list so it stays in the original order. */ + return g_list_reverse (accels); +} + + +static void +set_standard_properties (GtkWidget * widget, GbWidgetSetArgData * data) +{ + GladeWidgetData *wdata = data->widget_data; + gchar *name, *tooltip, *events_str, *ext_events; + gboolean visible, sensitive, can_default, has_default, can_focus, has_focus; + gint events, i; + + /* Properties on widget page */ + /* When pasting, we may want to discard the names from the XML, and generate + a new name instead. */ + if (data->action == GB_LOADING) + { + if (data->xml_buffer && data->discard_names) + { + gchar *class_id = gb_widget_get_class_id (widget); + name = glade_project_new_widget_name (data->project, class_id); + gtk_widget_set_name (widget, name); + g_free (name); + } + else + { + gtk_widget_set_name (widget, data->widget_info->name); + } + + if (glade_util_is_component (widget)) + glade_project_component_changed (data->project, widget); + + /* We have to check if the name has a trailing ID and if so we reserve + it so no other widget can use it. */ + glade_project_reserve_name (data->project, gtk_widget_get_name (widget)); + } + else + { + name = gb_widget_input_string (data, GbName); + if (data->apply) + { + tree_rename_widget (widget, name); + gtk_widget_set_name (widget, name); + property_update_title (); + + /* If widget is a toplevel window/dialog set the component's name in + the project window */ + if (glade_util_is_component (widget)) + glade_project_component_changed (data->project, widget); + } + } + + /* If widget is a container, show the border width */ + if (glade_util_uses_border_width (widget)) + { + gchar buf[128]; + gint border_width; + gchar *class_id; + + class_id = gb_widget_get_class_id (widget); + sprintf (buf, "%s::border_width", class_id); + border_width = gb_widget_input_int (data, buf); + if (data->apply && GTK_CONTAINER (widget)->border_width != border_width) + { + gtk_container_set_border_width (GTK_CONTAINER (widget), + border_width); + if (data->action == GB_APPLYING) + editor_refresh_widget (widget); + } + } + + /* Language-specific properties. */ + set_lang_specific_properties (widget, data); + + /* Special child properties page */ + set_special_child_properties (widget, data); + + /* Properties on standard page */ + set_position_properties (widget, data); + + /* Visible property. Note that we create widgets with visible set to TRUE + by default, but the property is FALSE by default in the XML file. So + when loading we make sure we set the flag to the appropriate value. */ + visible = gb_widget_input_bool (data, GbVisible); + if (data->apply || data->action == GB_LOADING) + { + if (!data->apply) + visible = FALSE; + if (visible) + wdata->flags |= GLADE_VISIBLE; + else + wdata->flags &= ~GLADE_VISIBLE; + } + + sensitive = gb_widget_input_bool (data, GbSensitive); + if (data->apply) + { + if (sensitive) + wdata->flags |= GLADE_SENSITIVE; + else + wdata->flags &= ~GLADE_SENSITIVE; + } + + tooltip = gb_widget_input_string (data, GbTooltip); + if (data->apply) + { + g_free (wdata->tooltip); + if (tooltip && tooltip[0] == '\0') + tooltip = NULL; + wdata->tooltip = g_strdup (tooltip); + + /* SPECIAL CODE: toolitems have a special function. */ + if (GTK_IS_TOOL_ITEM (widget)) + gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (widget), + gb_widget_tooltips, tooltip, NULL); + else + gtk_tooltips_set_tip (gb_widget_tooltips, widget, tooltip, NULL); + } + + can_default = gb_widget_input_bool (data, GbCanDefault); + if (data->apply) + { + if (can_default) + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_DEFAULT); + if (data->action == GB_APPLYING) + editor_refresh_widget (widget); + } + + has_default = gb_widget_input_bool (data, GbHasDefault); + if (data->apply) + { + if (has_default) + wdata->flags |= GLADE_GRAB_DEFAULT; + else + wdata->flags &= ~GLADE_GRAB_DEFAULT; + } + + /* Different widgets have different default values for CAN_FOCUS, so when + we load an XML file we must make sure that we always set it or unset it. + Also, since we don't save the can_focus flag if it is false, we must make + sure that we apply it anyway when loading. */ + can_focus = gb_widget_input_bool (data, GbCanFocus); + if (!data->apply) + can_focus = FALSE; + if (data->apply || data->action == GB_LOADING) + { + if (can_focus) + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + } + has_focus = gb_widget_input_bool (data, GbHasFocus); + if (data->apply) + { + if (has_focus) + wdata->flags |= GLADE_GRAB_FOCUS; + else + wdata->flags &= ~GLADE_GRAB_FOCUS; + } + + /* Events & ext events. */ + if (!GTK_WIDGET_NO_WINDOW (widget)) + { + if (data->action == GB_APPLYING) + { + events = property_events_string_to_value (gb_widget_input_string (data, + GbEvents)); + if (data->apply) + wdata->events = events; + } + else + { + events_str = gb_widget_input_string (data, GbEvents); + if (data->apply) + { + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + if (glade_util_strstr (events_str, GbEventMaskSymbols[i])) + wdata->events |= GbEventMaskValues[i]; + } + } + } + + ext_events = gb_widget_input_choice (data, GbExtEvents); + if (data->apply) + { + for (i = 0; GbExtensionModeChoices[i]; i++) + { + if (!strcmp (ext_events, GbExtensionModeChoices[i]) + || !strcmp (ext_events, GbExtensionModeSymbols[i])) + { + gtk_widget_set_extension_events (widget, GbExtensionModeValues + [i]); + break; + } + } + } + } + + if (data->action == GB_APPLYING) + { +#if 0 + apply_style (widget, data); +#endif + apply_accelerators (widget, data); + apply_signals (widget, data); + + glade_atk_set_properties (widget, data); + } + else + { + data->widget_data->signals = copy_signals (data, data->widget_info); + data->widget_data->accelerators = copy_accels (data->widget_info); + + /* ATK properties are loaded later, after all widgets are created. */ + } +} + + +static void +set_special_child_properties (GtkWidget * widget, + GbWidgetSetArgData * data) +{ + GtkWidget *parent = widget->parent; + GbWidget *gbparent; + + if (!parent) + return; + + /* When pasting a widget to replace an existing widget, the child properties + will already have been set, so we just return. */ + if (data->action == GB_LOADING && data->xml_buffer && data->replacing_widget) + return; + + gbparent = gb_widget_lookup (parent); + if (!gbparent) + { + MSG1 ("Unknown parent type %s", G_OBJECT_TYPE_NAME (parent)); + } + + /* Tell the load functions to use the child properties array. */ + data->loading_type = GB_CHILD_PROPERTIES; + + if (gbparent && gbparent->gb_widget_set_child_properties) + { + (gbparent->gb_widget_set_child_properties) (parent, widget, data); + } + + data->loading_type = GB_STANDARD_PROPERTIES; +} + + +static void +set_position_properties (GtkWidget * widget, + GbWidgetSetArgData * data) +{ + GladeWidgetData *wdata = data->widget_data; + gint w, h; + gboolean applyWidth, applyHeight; + gboolean set_usize = FALSE; + + w = gb_widget_input_int (data, GbWidth); + applyWidth = data->apply; + h = gb_widget_input_int (data, GbHeight); + applyHeight = data->apply; + + /* When loading we need to remember which values have been set explicitly. */ + if (data->action == GB_LOADING) + { + if (applyWidth) + wdata->flags |= GLADE_WIDTH_SET; + if (applyHeight) + wdata->flags |= GLADE_HEIGHT_SET; + } + +#if 0 + g_print ("In set_position_properties X:%i Y:%i W:%i H:%i\n", x, y, w, h); +#endif + if (GTK_IS_WINDOW (widget)) + { + if (applyWidth && wdata->width != w) + { + wdata->width = w; + set_usize = TRUE; + } + if (applyHeight && wdata->height != h) + { + wdata->height = h; + set_usize = TRUE; + } + + if (set_usize) + { + gint w = wdata->flags & GLADE_WIDTH_SET ? wdata->width : -1; + gint h = wdata->flags & GLADE_HEIGHT_SET ? wdata->height : -1; + gb_widget_set_usize (widget, w, h); + } + } + else if (widget->parent && (GTK_IS_FIXED (widget->parent) + || GTK_IS_LAYOUT (widget->parent))) + { + /* When pasting a widget to replace an existing widget, the size & + position will be set in the replace_child function. */ + if (data->action == GB_LOADING && data->xml_buffer + && data->replacing_widget) + { + return; + } + + + if (applyWidth && wdata->width != w) + { + wdata->width = w; + set_usize = TRUE; + } + if (applyHeight && wdata->height != h) + { + wdata->height = h; + set_usize = TRUE; + } + if (set_usize) + gb_widget_set_usize (widget, wdata->width, wdata->height); + } + else + { + if (applyWidth && wdata->width != w) + { + wdata->width = w; + set_usize = TRUE; + } + if (applyHeight && wdata->height != h) + { + wdata->height = h; + set_usize = TRUE; + } + + if (set_usize) + { + gint w = wdata->flags & GLADE_WIDTH_SET ? wdata->width : -1; + gint h = wdata->flags & GLADE_HEIGHT_SET ? wdata->height : -1; + gb_widget_set_usize (widget, w, h); + } + MSG2 ("*** Width set:%i Height set:%i", wdata->flags & GLADE_WIDTH_SET, + wdata->flags & GLADE_HEIGHT_SET); + } +} + + +#if 0 +static void +apply_style (GtkWidget * widget, + GbWidgetSetArgData * data) +{ + GladeWidgetData *wdata = data->widget_data; + GtkStyle *style = widget->style, *old_style = NULL; + GbStyle *gbstyle = wdata->gbstyle, *new_gbstyle; + GdkFont *font = NULL; + gchar *style_name, *xlfd_fontname; + GdkColor fg[GB_NUM_STYLE_STATES]; + GdkColor bg[GB_NUM_STYLE_STATES]; + GdkColor text[GB_NUM_STYLE_STATES]; + GdkColor base[GB_NUM_STYLE_STATES]; + GdkPixmap *bg_pixmap[GB_NUM_STYLE_STATES]; + gchar *bg_pixmap_filenames[GB_NUM_STYLE_STATES]; + gint recreate = FALSE, redraw = FALSE, i; + gchar buf[128], *filename; + gboolean named_style; + + style_name = gb_widget_input_dialog (data, GbStyleName); + named_style = (style_name[0] == '\0') ? FALSE : TRUE; + if (data->apply) + { + if (named_style) + { + new_gbstyle = (GbStyle *) g_hash_table_lookup (gb_style_hash, style_name); + g_return_if_fail (new_gbstyle != NULL); + if (new_gbstyle != gbstyle) + { + gbstyle = new_gbstyle; + gb_widget_set_gb_style (widget, gbstyle); + wdata->flags &= ~GLADE_STYLE_IS_UNNAMED; + redraw = TRUE; + } + } + else + { + wdata->flags |= GLADE_STYLE_IS_UNNAMED; + } + } + + font = gb_widget_input_font (data, GbStyleFont, &xlfd_fontname); + if (data->apply) + { + if (font != style->font) + recreate = TRUE; + } + + recreate |= apply_colors (widget, data, style->fg, fg, "fg"); + recreate |= apply_colors (widget, data, style->bg, bg, "bg"); + recreate |= apply_colors (widget, data, style->text, text, "text"); + recreate |= apply_colors (widget, data, style->base, base, "base"); + + /* Background pixmaps */ + for (i = 0; i < GB_NUM_STYLE_STATES; i++) + { + sprintf (buf, "GtkStyle::%s[%s]", GbBgPixmapName, GbStateNames[i]); + bg_pixmap[i] = gb_widget_input_bgpixmap (data, buf, &filename); + bg_pixmap_filenames[i] = filename; + if (data->apply) + { + if (bg_pixmap[i] != style->bg_pixmap[i]) + recreate = TRUE; + } + } + + if (recreate) + { + old_style = style; + + /* If the widget is supposedly using an unnamed GbStyle, but currently is + actually using a named GbStyle (for convenience), then we need to + create a copy of the GbStyle and place our new style in it. */ + if ((wdata->flags & GLADE_STYLE_IS_UNNAMED) && gbstyle->name) + { + gbstyle = gb_widget_copy_gb_style (gbstyle); + g_free (gbstyle->name); + gbstyle->name = NULL; + } + + style = gtk_style_new (); + for (i = 0; i < GB_NUM_STYLE_STATES; i++) + { + style->fg[i] = fg[i]; + style->bg[i] = bg[i]; + style->text[i] = text[i]; + style->base[i] = base[i]; + style->bg_pixmap[i] = bg_pixmap[i]; + if (bg_pixmap[i]) + gdk_pixmap_ref (bg_pixmap[i]); + + if (gbstyle->bg_pixmap_filenames[i] != bg_pixmap_filenames[i]) + { + g_free (gbstyle->bg_pixmap_filenames[i]); + gbstyle->bg_pixmap_filenames[i] = g_strdup (bg_pixmap_filenames + [i]); + } + } + if (font) + { + gdk_font_unref (style->font); + style->font = font; + gdk_font_ref (style->font); + } + if (strcmp (gbstyle->xlfd_fontname, xlfd_fontname)) + { + g_free (gbstyle->xlfd_fontname); + gbstyle->xlfd_fontname = g_strdup (xlfd_fontname); + } + + gbstyle->style = style; + gtk_style_ref (style); + gb_widget_set_gb_style (widget, gbstyle); + } + + + /* If a named style has been changed/recreated we have to update all + widget's that use it. */ + if (recreate || redraw) + { + if (named_style) + { + gb_widget_update_gb_styles (gbstyle, gbstyle); + } + else + { + editor_refresh_widget (widget); + } + } + + if (old_style) + gtk_style_unref (old_style); +} + + +/* This makes sure a widget's gbstyle & its style are up to date, and + if the propagate flag is set it also updates any children. + But it won't change a descendant's style if it has been set explicitly. + FIXME: only propagates one level at present, and always sets child's style, + even if it has a different GbStyle! */ +void +gb_widget_set_gb_style (GtkWidget * widget, + GbStyle * gbstyle) +{ + GladeWidgetData *wdata; + + if (!GB_IS_PLACEHOLDER (widget)) + { + if (widget->style != gbstyle->style) + gtk_widget_set_style (widget, gbstyle->style); + } + + wdata = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + if (wdata) + { + if (wdata->gbstyle != gbstyle) + { + gb_widget_unref_gb_style (wdata->gbstyle); + wdata->gbstyle = gbstyle; + gb_widget_ref_gb_style (gbstyle); + } + /* If propagate style flags is set, propagate style to children */ + if (wdata->flags & GLADE_STYLE_PROPAGATE) + gb_widget_children_foreach (widget, (GtkCallback) gb_widget_set_gb_style, + gbstyle); + } +} + + +static gboolean +apply_colors (GtkWidget * widget, GbWidgetSetArgData * data, GdkColor colors[], + GdkColor new_colors[], gchar * name) +{ + gint state; + gchar buf[128]; + GdkColor *color; + gboolean need_redraw = FALSE; + + for (state = 0; state < GB_NUM_STYLE_STATES; state++) + { + sprintf (buf, "GtkStyle::%s[%s]", name, GbStateNames[state]); + + color = gb_widget_input_color (data, buf); + if (data->apply) + { + new_colors[state] = *color; + if (!gdk_color_equal (&new_colors[state], &colors[state])) + need_redraw = TRUE; + } + else + { + /* Copy the old values across. */ + new_colors[state] = colors[state]; + } + } + return need_redraw; +} +#endif + + +/* Currently this frees all the GladeAccelerators and creates them from scratch */ +static void +apply_accelerators (GtkWidget * widget, GbWidgetSetArgData * data) +{ + GladeWidgetData *wdata; + + if (data->property_to_apply == NULL + || property_is_accel_clist (data->property_to_apply)) + { + wdata = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + g_return_if_fail (wdata != NULL); + + glade_widget_data_set_accels (wdata, property_get_accelerators ()); + } +} + + +/* Currently this frees all the GladeSignals and creates them from scratch. */ +static void +apply_signals (GtkWidget * widget, GbWidgetSetArgData * data) +{ + GladeWidgetData *wdata; + + if (data->property_to_apply == NULL + || property_is_signal_clist (data->property_to_apply)) + { + wdata = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + g_return_if_fail (wdata != NULL); + + glade_widget_data_set_signals (wdata, property_get_signals ()); + } +} + + + +/************************************************************************* + * Functions for showing the popup context-sensitive menu of a widget + *************************************************************************/ + +static void +popup_done (GtkWidget *widget, gpointer data) +{ + gtk_widget_destroy (widget); +} + +void +gb_widget_show_popup_menu (GtkWidget * widget, + GdkEventButton * event) +{ + GtkWidget *menu = NULL; + GbWidget *gbwidget; + const gchar *name; + GtkWidget *menuitem, *ancestor, *submenu, *child; + GbWidgetCreateMenuData data; + + gbwidget = gb_widget_lookup (widget); + g_return_if_fail (gbwidget != NULL); + + name = gtk_widget_get_name (widget); + if (GB_IS_PLACEHOLDER (widget)) + name = "Placeholder"; + + menu = gtk_menu_new (); + menuitem = gtk_menu_item_new_with_label (name); + gtk_widget_show (menuitem); + gtk_widget_set_sensitive (menuitem, FALSE); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + + data.project = current_project; + data.menu = menu; + data.child = NULL; + add_standard_top_menu_items (widget, &data); + + if (gbwidget->gb_widget_create_popup_menu) + (gbwidget->gb_widget_create_popup_menu) (widget, &data); + + add_standard_bottom_menu_items (widget, &data); + + child = widget; + ancestor = widget->parent; + while (ancestor) + { + name = gtk_widget_get_name (ancestor); + if (GB_IS_PLACEHOLDER (ancestor)) + name = "Placeholder"; + + /* Skip widgets which aren't GbWidgets */ + if (GB_IS_GB_WIDGET (ancestor)) + { + /* Add a separator */ + menuitem = gtk_menu_item_new (); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + gtk_widget_show (menuitem); + + menuitem = gtk_menu_item_new_with_label (name); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (menu), menuitem); + + /* Create submenu */ + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu); + + data.menu = submenu; + data.child = child; + add_standard_top_menu_items (ancestor, &data); + + /* Call ancestors function to add any menu items */ + gbwidget = gb_widget_lookup (ancestor); + if (gbwidget != NULL && gbwidget->gb_widget_create_popup_menu) + (gbwidget->gb_widget_create_popup_menu) (ancestor, &data); + + add_standard_bottom_menu_items (ancestor, &data); + } + child = ancestor; + ancestor = ancestor->parent; + } + + /* Automatically destroy the menu when it is hidden. */ + gtk_signal_connect_after (GTK_OBJECT (menu), "selection-done", + GTK_SIGNAL_FUNC (popup_done), menu); + + MSG ("showing popup menu"); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, + event->button, event->time); +} + + +static void +add_standard_top_menu_items (GtkWidget * widget, GbWidgetCreateMenuData * data) +{ + GtkWidget *menuitem; + + menuitem = gtk_menu_item_new_with_label (_("Select")); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (editor_on_select_activate), widget); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); +} + + +static void +add_standard_bottom_menu_items (GtkWidget * widget, GbWidgetCreateMenuData * data) +{ + GtkWidget *menuitem; + gboolean can_delete; + + can_delete = (editor_can_delete_widget (widget) == NULL) ? TRUE : FALSE; + + /* For widgets which can handle scrolling, we add commands to add or remove + a parent scrolled window. */ + if (GTK_WIDGET_CLASS (G_OBJECT_GET_CLASS (widget))->set_scroll_adjustments_signal) + { + if (widget->parent && GTK_IS_SCROLLED_WINDOW (widget->parent)) + { + menuitem = gtk_menu_item_new_with_label (_("Remove Scrolled Window")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_remove_scrolled_window), + widget); + } + else + { + menuitem = gtk_menu_item_new_with_label (_("Add Scrolled Window")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_add_scrolled_window), + widget); + } + } + + /* We assume that we can only insert/remove alignments and event boxes if + the widget can be deleted. */ + if (can_delete + && !GTK_IS_WINDOW (widget) + && !GTK_IS_MISC (widget) + && !GTK_IS_ALIGNMENT (widget) + && !GTK_IS_MENU (widget) + && !GTK_IS_MENU_ITEM (widget) + && !GB_IS_PLACEHOLDER (widget)) + { + if (GTK_IS_ALIGNMENT (widget->parent)) + { + menuitem = gtk_menu_item_new_with_label (_("Remove Alignment")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_remove_alignment), widget); + } + else + { + menuitem = gtk_menu_item_new_with_label (_("Add Alignment")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_add_alignment), widget); + } + } + + if (can_delete + && GTK_WIDGET_NO_WINDOW (widget) + && !GTK_IS_EVENT_BOX (widget) + && !GB_IS_PLACEHOLDER (widget)) + { + if (GTK_IS_EVENT_BOX (widget->parent)) + { + menuitem = gtk_menu_item_new_with_label (_("Remove Event Box")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_remove_event_box), widget); + } + else + { + menuitem = gtk_menu_item_new_with_label (_("Add Event Box")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_add_event_box), widget); + } + } + + if (GTK_IS_WINDOW (widget)) + { + menuitem = gtk_menu_item_new_with_label (_("Redisplay")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_redisplay_window), + widget); + } + + menuitem = gtk_separator_menu_item_new (); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + + /* Only show the 'Cut' item if the widget can be deleted. */ + if (can_delete) + { + menuitem = gtk_menu_item_new_with_label (_("Cut")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (editor_on_cut_activate), widget); + } + + menuitem = gtk_menu_item_new_with_label (_("Copy")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (editor_on_copy_activate), widget); + + /* Only show the 'Paste' item if the widget can be deleted. */ + if (can_delete) + { + menuitem = gtk_menu_item_new_with_label (_("Paste")); + gtk_widget_show (menuitem); + if (!widget->parent) + gtk_widget_set_sensitive (menuitem, FALSE); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (editor_on_paste_activate), widget); + } + + /* Only show the 'Delete' item if the widget can be deleted. */ + if (can_delete) + { + menuitem = gtk_menu_item_new_with_label (_("Delete")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (editor_on_delete_activate), widget); + } +} + + + +/************************************************************************* + * Functions for loading project files + *************************************************************************/ + +/* This reads in a widget, and calls itself recursively to read it's children. + It assumes a '<widget>' has just been read, and reads up to and including + '</widget>'. The newly-created widget is returned, mainly so that + glade_clipboard_paste () can show any windows which are pasted. */ +GtkWidget* +gb_widget_load (GtkWidget * widget, GbWidgetSetArgData * data, GtkWidget * parent) +{ + gchar *class_id; + const gchar *child_name; + GbWidget *gbwidget, *ancestor_gbwidget; + GtkWidget *ancestor; + GladeWidgetData *wdata; + GladeChildInfo *child_info; + GladeWidgetInfo *widget_info; + gint i; + gboolean skip_child; + + data->action = GB_LOADING; + data->loading_type = GB_STANDARD_PROPERTIES; + + child_info = data->child_info; + widget_info = data->widget_info; + + class_id = widget_info ? widget_info->class : NULL; + MSG1 ("Reading Class: %s", class_id ? class_id : "(placeholder)"); + + child_name = child_info ? child_info->internal_child : NULL; + + /* Migrate toolbar buttons from old XML files. See the set_properties + functions in gbtoolbutton.c etc. for the code that reads in the old + properties. */ + if (class_id) + { + if (!strcmp (class_id, "button")) + { + class_id = "GtkToolButton"; + } + else if (!strcmp (class_id, "toggle")) + { + class_id = "GtkToggleToolButton"; + } + else if (!strcmp (class_id, "radio")) + { + class_id = "GtkRadioToolButton"; + } + } + + /* SPECIAL CODE: CList/CTree title buttons don't save the child name now, so + we have to add it. */ + if (parent && GTK_IS_CLIST (parent)) + child_name = GladeChildCListTitle; + + /* SPECIAL CODE: Ignore the old 'BonoboDock:contents' child name. */ + if (child_name && !strcmp (child_name, "BonoboDock:contents")) + child_name = NULL; + + /* SPECIAL CODE: when pasting a widget to replace an existing widget, check + if the child name should be transferred to the new widget. */ + if (data->xml_buffer && data->replacing_widget && child_name) + { + /* These child names should be removed from the widget when it is pasted, + since they identify the old position the widget. */ + if (!strcmp (child_name, GladeChildOKButton) + || !strcmp (child_name, GladeChildCancelButton) + || !strcmp (child_name, GladeChildHelpButton) + || !strcmp (child_name, GladeChildApplyButton) + || !strcmp (child_name, GladeChildSaveButton) + || !strcmp (child_name, GladeChildCloseButton) + || !strcmp (child_name, GladeChildDialogVBox) + || !strcmp (child_name, GladeChildDialogActionArea) + || !strcmp (child_name, GladeChildComboEntry) + || !strcmp (child_name, GladeChildComboList) + || !strcmp (child_name, GladeChildFontSelection) + || !strcmp (child_name, GladeChildColorSelection) + || !strcmp (child_name, GladeChildGnomeAppDock) + || !strcmp (child_name, GladeChildGnomeAppBar) + || !strcmp (child_name, GladeChildGnomeEntry) + ) + { + child_name = NULL; + } + } + + if (!class_id) + { + MSG ("found placeholder"); + widget = editor_new_placeholder (); + if (!gb_widget_add_child (parent, data, widget)) + { + data->status = GLADE_STATUS_ERROR; + return NULL; + } + if (child_name) + gb_widget_set_child_name (widget, child_name); + return NULL; + } + + gbwidget = gb_widget_lookup_class (class_id); + if (gbwidget == NULL) + { + MSG ("Load error"); + data->status = GLADE_STATUS_CLASS_UNKNOWN; + g_warning ("Unknown widget class: %s", class_id); + return NULL; + } + + /* If this is a special child of a widget, step through the ancestors of + the widget and try to find it. Note that some special child widgets + have to be created normally, and then added by the parent container, + e.g. notebook tabs and clist titles - see gb_widget_add_child(). */ + if (child_name) + { + MSG1 ("Child name: %s", child_name); + ancestor = parent; + while (ancestor) + { + ancestor_gbwidget = gb_widget_lookup (ancestor); + if (ancestor_gbwidget && ancestor_gbwidget->gb_widget_get_child) + { + widget = (ancestor_gbwidget->gb_widget_get_child) (ancestor, + child_name); + if (widget) + break; + } + + ancestor = ancestor->parent; + } + +#if 0 + if (!widget) + g_print ("Child widget %s not found - may be a problem\n", child_name); +#endif + } + + /* If this is a standard widget, we need to create it and add it to its + parent. If the widget has already been created by its parent, we can + just set the properties. */ + if (widget == NULL) + { + MSG ("widget == NULL, has not been created by parent."); + widget = gb_widget_new_full (class_id, FALSE, parent, NULL, + 0, 0, NULL, GB_LOADING, data); + if (!widget) + { + data->status = GLADE_STATUS_ERROR; + return NULL; + } + + wdata = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + data->widget_data = wdata; + data->widget = widget; + + /* We get the gbwidget from the widget data, as it may be different from + the original one. Currently only Bonobo controls do this. */ + gbwidget = wdata->gbwidget; + g_assert (gbwidget); + + if (parent == NULL) + { + glade_project_add_component (data->project, widget); + } + else + { + if (!gb_widget_add_child (parent, data, widget)) + { + data->status = GLADE_STATUS_ERROR; + return NULL; + } + } + + /* This must come after add_child so we can set special child properties, + and also we may need to realize the widget. It must also come after + glade_project_add_component(), in case setting any properties results + in a signal being emitted from the project. */ + set_standard_properties (widget, data); + + tree_add_widget (widget); + } + else + { + MSG ("widget != NULL, has been created by parent."); + wdata = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + if (wdata == NULL) + g_warning ("Null widget_data\n"); + data->widget_data = wdata; + data->widget = widget; + set_standard_properties (widget, data); + tree_add_widget (widget); + } + + if (child_name) + gb_widget_set_child_name (widget, child_name); + + if (data->status != GLADE_STATUS_OK) + { + MSG ("Load error"); + return NULL; + } + + MSG ("Calling widgets set_properties()"); + if (gbwidget->gb_widget_set_properties) + (gbwidget->gb_widget_set_properties) (widget, data); + MSG ("Called widgets set_properties()"); + + if (data->status != GLADE_STATUS_OK) + { + MSG ("Load error"); + return NULL; + } + + /* Save a pointer to the widget in the all_widgets hash, when loading and + when pasting. Note that when pasting we use the name before any renaming + is done, so ATK relation targets within the widget hierarchy being + pasted will still be correct. */ + if (data->all_widgets) + { + const gchar *widget_name = widget_info->name; + if (widget_name && *widget_name) + { +#if 0 + g_print ("Saving pointer to widget: %s, %p\n", widget_name, widget); +#endif + g_hash_table_insert (data->all_widgets, (gpointer) widget_name, + widget); + } + } + + /* Save a pointer to the GladeWidgetInfo in the widget, which we use to + load the ATK properties after all widgets are created. */ + gtk_object_set_data (GTK_OBJECT (widget), GladeWidgetInfoKey, widget_info); + + /* When pasting, we only ever replace the top widget. All children are added + as normal. */ + data->replacing_widget = NULL; + + MSG ("Loading children"); + + /* load children. */ + for (i = 0; i < widget_info->n_children; i++) + { + data->child_info = &widget_info->children[i]; + data->widget_info = data->child_info->child; + + /* We have to reset the widget_data since this may have been changed + while loading the last child (and its children) */ + data->widget_data = wdata; + if (widget->parent && !gb_widget_lookup (widget->parent)) + { + MSG1 ("Unusual widget here of type '%s'", + G_OBJECT_TYPE_NAME (widget->parent)); + } + + /* Skip the hscrollbar & vscrollbar internal children, from + GtkScrolledWindow. libglade-convert outputs these, but we don't + use them. */ + skip_child = FALSE; + if (data->child_info && data->child_info->internal_child) + { + if (!strcmp (data->child_info->internal_child, "vscrollbar") + || !strcmp (data->child_info->internal_child, "hscrollbar")) + skip_child = TRUE; + } + + if (!skip_child) + gb_widget_load (NULL, data, widget); + + /* Reset these, just in case. */ + data->child_info = child_info; + data->widget_info = widget_info; + + if (data->status != GLADE_STATUS_OK) + { + MSG ("Load error"); + return NULL; + } + } + + /* If the widget is a table, we make sure all empty cells have placeholders + in them. */ + if (GTK_IS_TABLE (widget)) + { + gb_table_update_placeholders (widget, -1, -1); + } + + return widget; +} + + +/* Adds a widget to a parent, when loading or pasting from the clipboard. + Returns TRUE on success. */ +gboolean +gb_widget_add_child (GtkWidget * parent, + GbWidgetSetArgData * data, + GtkWidget * child) +{ + GbWidget *gbwidget; + + MSG2 ("Adding %s to %s", gtk_widget_get_name (child), + parent ? gtk_widget_get_name (parent) : "NULL"); + + g_return_val_if_fail (parent != NULL, FALSE); + + /* If we are pasting, the top widget replaces the parent rather than being + added to it. */ + if (data->xml_buffer && data->replacing_widget) + { + return gb_widget_replace_child (parent, data->replacing_widget, child); + } + + gbwidget = gb_widget_lookup (parent); + g_return_val_if_fail (gbwidget != NULL, FALSE); + + if (gbwidget->gb_widget_add_child) + { + gbwidget->gb_widget_add_child (parent, child, data); + } + else if (GTK_IS_BIN (parent)) + { + if (GTK_BIN (parent)->child) + gtk_container_remove (GTK_CONTAINER (parent), GTK_BIN (parent)->child); + gtk_container_add (GTK_CONTAINER (parent), child); + } + else if (GTK_IS_CONTAINER (parent)) + { + gtk_container_add (GTK_CONTAINER (parent), child); + } + + return TRUE; +} + + +/************************************************************************* + * Functions for saving project files + *************************************************************************/ + +void +gb_widget_save (GtkWidget * widget, + GbWidgetGetArgData * data) +{ + GbWidget *gbwidget, *parent_gbwidget; + GladeWidgetData *widget_data; + gchar *child_name, *class_id, *id, *class_to_save; + gboolean is_toplevel; + + class_id = class_to_save = gb_widget_get_class_id (widget); + child_name = gb_widget_get_child_name (widget); + is_toplevel = glade_util_is_component (widget); + + /* Bonobo Controls are save with a class of 'BonoboWidget'. */ +#ifdef USE_GNOME + if (gtk_object_get_data (GTK_OBJECT (widget), Moniker)) + class_to_save = "BonoboWidget"; +#endif + + /* SPECIAL CODE: CList/CTree title buttons don't save the child name. */ + if (child_name && !strcmp (child_name, GladeChildCListTitle)) + { + child_name = NULL; + } + + /* SPECIAL CODE: Don't save a placeholder if its parent is a table, since we + don't really want placeholders in the XML when the interface is finished, + but it is quite possible that some table cells will be left blank. */ + if (GB_IS_PLACEHOLDER (widget)) + { + if (!GTK_IS_TABLE (widget->parent)) + { + save_newline (data); + + if (!is_toplevel) + save_child_start_tag (data, child_name); + + save_placeholder (data); + + if (!is_toplevel) + save_end_tag (data, "child"); + } + return; + } + + /* Write out widget data and any child widgets */ + widget_data = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + + /* If this isn't a gbwidget, skip it, but save any child gbwidgets. */ + if (!widget_data) + { + /* Recursively save children. */ + gb_widget_children_foreach (widget, (GtkCallback) gb_widget_save, data); + return; + } + + data->action = GB_SAVING; + data->widget_data = widget_data; + data->widget = widget; + + save_newline (data); + if (!is_toplevel) + save_child_start_tag (data, child_name); + + id = (char*) gtk_widget_get_name (widget); + save_widget_start_tag (data, class_to_save, id); + + get_standard_properties (widget, data); + + /* Call gbwidgets save function for any extra properties */ + gbwidget = gb_widget_lookup_class (class_id); + g_return_if_fail (gbwidget != NULL); + + if (gbwidget->gb_widget_get_properties) + (gbwidget->gb_widget_get_properties) (widget, data); + + glade_atk_save_properties (widget, data); + + save_signals (widget, data); + save_accelerators (widget, data); + + /* Recursively save children. */ + gb_widget_children_foreach (widget, (GtkCallback) gb_widget_save, data); + + save_end_tag (data, "widget"); + + /* Call parent widget's function to save child packing properties. */ + if (widget->parent) + { + parent_gbwidget = gb_widget_lookup (widget->parent); + /* parent_gbwidget may be NULL, e.g. for GnomeDockItems. */ + if (parent_gbwidget + && parent_gbwidget->gb_widget_get_child_properties) + (parent_gbwidget->gb_widget_get_child_properties) (widget->parent, + widget, data); +#ifdef USE_GNOME + else if (BONOBO_IS_DOCK_ITEM (widget)) + { + gb_bonobo_dock_item_save_packing_properties (widget->parent, widget, + data); + } +#endif + } + + if (!is_toplevel) + save_end_tag (data, "child"); +} + + +/* This outputs all the standard widget properties, for showing or saving. + Note that when saving we try not to save default values. */ +static void +get_standard_properties (GtkWidget * widget, + GbWidgetGetArgData * data) +{ + gboolean can_default, has_default, can_focus, has_focus, visible, sensitive; + GladeWidgetData *wdata = data->widget_data; + gchar *name, *class_id; + GbWidgetAction action = data->action; + gchar border_width_property_buf[128]; + gboolean border_width_visible = FALSE; + + /* The class and name (id) are saved in gb_widget_save, so don't save them + here. */ + name = (char*) gtk_widget_get_name (widget); + class_id = gb_widget_get_class_id (widget); + if (action != GB_SAVING) + { + gb_widget_output_string (data, GbName, name); + gb_widget_output_string (data, GbClass, class_id); + } + + /* If widget is a container, save the border width */ + sprintf (border_width_property_buf, "%s::border_width", class_id); + if (glade_util_uses_border_width (widget)) + { + gint border_width; + border_width_visible = TRUE; + border_width = GTK_CONTAINER (widget)->border_width; + if (action != GB_SAVING || border_width > 0) + { + gb_widget_output_int (data, border_width_property_buf, border_width); + } + } + if (action == GB_SHOWING) + { + property_set_visible (border_width_property_buf, border_width_visible); + } + + get_position_properties (widget, data); + get_lang_specific_properties (widget, data); + + visible = wdata->flags & GLADE_VISIBLE; + if (action != GB_SAVING || visible) + gb_widget_output_bool (data, GbVisible, visible); + sensitive = wdata->flags & GLADE_SENSITIVE; + if (action != GB_SAVING || !sensitive) + gb_widget_output_bool (data, GbSensitive, sensitive); + + /* Only widgets with windows can have tooltips at present. Though buttons + seem to be a special case, as they are NO_WINDOW widgets but have + InputOnly windows, so tooltip still work. In GTK+ 2 menuitems are like + buttons. */ + if (!GTK_WIDGET_NO_WINDOW (widget) || GTK_IS_BUTTON (widget) + || GTK_IS_MENU_ITEM (widget) || GTK_IS_TOOL_BUTTON (widget) + || GTK_IS_EVENT_BOX (widget)) + { + if (action != GB_SAVING || wdata->tooltip) + gb_widget_output_translatable_string (data, GbTooltip, wdata->tooltip); + if (action == GB_SHOWING) + property_set_sensitive (GbTooltip, TRUE); + } + else if (action == GB_SHOWING) + { + /* N/A stands for 'Not Applicable'. It is used when a standard widget + property does not apply to the current widget. e.g. widgets without + windows can't use the Events property. This appears in the property + editor and so should be a short abbreviation. */ + gb_widget_output_string (data, GbTooltip, _("N/A")); + property_set_sensitive (GbTooltip, FALSE); + } + + can_default = GTK_WIDGET_CAN_DEFAULT (widget); + if (action != GB_SAVING || can_default) + gb_widget_output_bool (data, GbCanDefault, can_default); + has_default = wdata ? wdata->flags & GLADE_GRAB_DEFAULT : 0; + if (action != GB_SAVING || has_default) + gb_widget_output_bool (data, GbHasDefault, has_default); + + can_focus = GTK_WIDGET_CAN_FOCUS (widget); + if (action != GB_SAVING || can_focus) + gb_widget_output_bool (data, GbCanFocus, can_focus); + has_focus = wdata ? wdata->flags & GLADE_GRAB_FOCUS : 0; + if (action != GB_SAVING || has_focus) + gb_widget_output_bool (data, GbHasFocus, has_focus); + + if (!GTK_WIDGET_NO_WINDOW (widget)) + { + GdkExtensionMode ext_mode = gtk_widget_get_extension_events (widget); + gint i; + + if (action == GB_SAVING) + { + if (wdata && wdata->events) + { + gchar buffer[1024]; + gboolean written_first = FALSE; + + buffer[0] = '\0'; + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + if (wdata->events & GbEventMaskValues[i]) + { + if (written_first) + strcat (buffer, " | "); + strcat (buffer, GbEventMaskSymbols[i]); + written_first = TRUE; + } + } + + gb_widget_output_string (data, GbEvents, buffer); + } + } + else + { + gb_widget_output_string (data, GbEvents, + property_events_value_to_string (wdata->events)); + } + + /* Don't save default extension mode ('None') */ + if (action != GB_SAVING || ext_mode != GDK_EXTENSION_EVENTS_NONE) + { + for (i = 0; GbExtensionModeChoices[i]; i++) + { + if (GbExtensionModeValues[i] == ext_mode) + gb_widget_output_choice (data, GbExtEvents, i, + GbExtensionModeSymbols[i]); + } + } + if (action == GB_SHOWING) + { + property_set_sensitive (GbEvents, TRUE); + property_set_sensitive (GbExtEvents, TRUE); + } + } + else if (action == GB_SHOWING) + { + gb_widget_output_dialog (data, GbEvents, _("N/A"), NULL); + property_set_sensitive (GbEvents, FALSE); + gb_widget_output_choice (data, GbExtEvents, 0, GbExtensionModeSymbols[0]); + property_set_sensitive (GbExtEvents, FALSE); + } + + if (action == GB_SHOWING) + { +#ifdef GLADE_STYLE_SUPPORT + gb_widget_show_style (widget); +#endif + show_accelerators (widget, data); + show_signals (widget, data); + + glade_atk_get_properties (widget, data); + } + else + { +#ifdef GLADE_STYLE_SUPPORT + save_style (widget, data); +#endif + + /* These need to be saved in gb_widget_save(), since they must be after + all the widget properties. */ +#if 0 + save_signals (widget, data); + save_accelerators (widget, data); +#endif + } +} + + +/* This is used when saving or displaying properties. */ +static void +get_lang_specific_properties (GtkWidget * widget, GbWidgetGetArgData * data) +{ guint i; + + GladeWidgetData *wdata = data->widget_data; + GbWidgetAction action = data->action; + + /* Note that when saving we only save the property if it is not the + default value. Otherwise we get lots of unnecessary XML output. */ + + /* C options. */ + if (action != GB_SAVING || wdata->source_file) + gb_widget_output_filename (data, GbCSourceFile, wdata->source_file); + if (action != GB_SAVING || wdata->public_field == 0) + gb_widget_output_bool (data, GbCPublic, wdata->public_field); + if (data->action == GB_SHOWING) + { + /* We only want the source file set for toplevel widgets. */ + if (glade_util_is_component (widget)) + property_set_sensitive (GbCSourceFile, TRUE); + else + property_set_sensitive (GbCSourceFile, FALSE); + } + + /* C++ options. */ + data->agent = "glademm"; + if (action != GB_SAVING || wdata->cxx_separate_class == 1) + gb_widget_output_bool (data, GbCxxSeparateClass, wdata->cxx_separate_class); + if (action == GB_SHOWING) + property_set_sensitive (GbCxxSeparateFile, wdata->cxx_separate_class); + if (action != GB_SAVING || wdata->cxx_separate_file == 1) + gb_widget_output_bool (data, GbCxxSeparateFile, wdata->cxx_separate_file); + if (action != GB_SAVING || wdata->cxx_visibility != 0) + for (i = 0; GbCxxVisibilityChoices[i]; i++) + { + if (GbCxxVisibilityValues[i] == wdata->cxx_visibility) + gb_widget_output_choice (data, GbCxxVisibility, i, + GbCxxVisibilitySymbols[i]); + } + data->agent = NULL; +} + + +/* This is used when loading or applying properties. */ +static void +set_lang_specific_properties (GtkWidget * widget, GbWidgetSetArgData * data) +{ + GladeWidgetData *wdata = data->widget_data; + gchar *filename; + gboolean public_field; + gboolean cxx_separate_file, cxx_separate_class; + gchar *cxx_visibility; + + /* C options. */ + filename = gb_widget_input_filename (data, GbCSourceFile); + if (data->apply) + { + g_free (wdata->source_file); + wdata->source_file = g_strdup (filename); + } + public_field = gb_widget_input_bool (data, GbCPublic); + if (data->apply) + wdata->public_field = public_field ? 1 : 0; + + /* C++ options. */ + data->agent = "glademm"; + cxx_separate_class = gb_widget_input_bool (data, GbCxxSeparateClass); + if (data->apply) + { + wdata->cxx_separate_class = cxx_separate_class ? 1 : 0; + if (!wdata->cxx_separate_class) + wdata->cxx_separate_file = 0; + + if (property_get_widget () == widget) + { + property_set_sensitive (GbCxxSeparateFile, wdata->cxx_separate_class); + property_set_auto_apply (FALSE); + property_set_bool (GbCxxSeparateFile, FALSE); + property_set_auto_apply (TRUE); + } + } + + cxx_separate_file = gb_widget_input_bool (data, GbCxxSeparateFile); + if (data->apply) + wdata->cxx_separate_file = cxx_separate_file ? 1 : 0; + + cxx_visibility = gb_widget_input_choice (data, GbCxxVisibility); + if (data->apply) + { + guint i; + for (i = 0; GbCxxVisibilityChoices[i]; i++) + { + if (!strcmp (cxx_visibility, GbCxxVisibilityChoices[i]) + || !strcmp (cxx_visibility, GbCxxVisibilitySymbols[i])) + { + wdata->cxx_visibility = GbCxxVisibilityValues[i]; + break; + } + } + } + data->agent = NULL; +} + + +static void +get_position_properties (GtkWidget * widget, + GbWidgetGetArgData * data) +{ + GladeWidgetData *wdata = data->widget_data; + gboolean wh_applicable = TRUE; + gboolean show_wh_buttons = TRUE; + +#if 0 + g_print ("In get_pos: %s WS:%s HS:%s ", + gtk_widget_get_name (widget), + wdata->flags & GLADE_WIDTH_SET ? "1" : "0", + wdata->flags & GLADE_HEIGHT_SET ? "1" : "0"); +#endif + + /* Don't bother if widget's area hasn't been allocated yet, unless it is a + window. */ + /* Note: When using the widget tree to view widget properties the widget's + area may not have been allocated but we mustn't just return here. */ +#if 0 + if (data->action == GB_SHOWING && (wdata->flags & GLADE_SIZE_NOT_ALLOCATED) + && !GTK_IS_WINDOW (widget)) + return; +#endif + + if (GTK_IS_MENU (widget)) + { + if (data->action == GB_SHOWING) + { + gb_widget_output_int (data, GbWidth, widget->requisition.width); + gb_widget_output_int (data, GbHeight, widget->requisition.height); + } + wh_applicable = FALSE; + } + else if (GTK_IS_WINDOW (widget)) + { + /* Toplevel window widgets */ + if (data->action == GB_SHOWING) + { + gb_widget_output_int (data, GbWidth, wdata->width); + gb_widget_output_int (data, GbHeight, wdata->height); + } + else + { + if (wdata->flags & GLADE_WIDTH_SET) + gb_widget_output_int (data, GbWidth, wdata->width); + if (wdata->flags & GLADE_HEIGHT_SET) + gb_widget_output_int (data, GbHeight, wdata->height); + } + } + else if (widget->parent && (GTK_IS_FIXED (widget->parent) + || GTK_IS_LAYOUT (widget->parent))) + { + /* Widgets in fixed or layout containers. Note that for widgets in a + layout the allocation is relative to the window origin and changes as + the layout is scrolled, so don't output that as the x & y coords. */ + gb_widget_output_int (data, GbWidth, wdata->width); + gb_widget_output_int (data, GbHeight, wdata->height); + show_wh_buttons = FALSE; + } + else + { + /* Widgets in standard containers */ + if (data->action == GB_SHOWING) + { + /* If the width or height has been set explicitly we show the size + set, else we show the current requisition. We always remember + what we have shown in wdata->width & height so we know if the + user changes it. */ + if (!(wdata->flags & GLADE_WIDTH_SET)) + wdata->width = widget->requisition.width; + gb_widget_output_int (data, GbWidth, wdata->width); + + if (!(wdata->flags & GLADE_HEIGHT_SET)) + wdata->height = widget->requisition.height; + gb_widget_output_int (data, GbHeight, wdata->height); + + } + else + { + /* Only save if user has set it explicitly. */ + if (wdata->flags & GLADE_WIDTH_SET) + gb_widget_output_int (data, GbWidth, wdata->width); + if (wdata->flags & GLADE_HEIGHT_SET) + gb_widget_output_int (data, GbHeight, wdata->height); + } + } + + /* Show the buttons for setting the size & positions explicitly, if + applicable, and set the values sensitive if they are currently set. */ + if (data->action == GB_SHOWING) + { + if (wh_applicable) + { + gboolean wsensitive = (wdata->flags & GLADE_WIDTH_SET) ? TRUE : FALSE; + gboolean hsensitive = (wdata->flags & GLADE_HEIGHT_SET) ? TRUE : FALSE; + + property_set_sensitive_full (GbWidth, TRUE, wsensitive, + show_wh_buttons); + property_set_sensitive_full (GbHeight, TRUE, hsensitive, + show_wh_buttons); + } + else + { + property_set_sensitive_full (GbWidth, FALSE, FALSE, FALSE); + property_set_sensitive_full (GbHeight, FALSE, FALSE, FALSE); + } + } + +#if 0 + g_print ("\n"); +#endif +} + + +static void +save_accelerators (GtkWidget * widget, GbWidgetGetArgData * data) +{ + GList *item; + GladeAccelerator *accel; + + if (data->widget_data == NULL) + return; + + item = data->widget_data->accelerators; + while (item) + { + accel = (GladeAccelerator *) item->data; + + save_accelerator (data, accel->modifiers, accel->key, accel->signal); + + item = item->next; + } +} + + +static void +save_signals (GtkWidget * widget, GbWidgetGetArgData * data) +{ + GList *item; + GladeSignal *signal; + + if (data->widget_data == NULL) + return; + + item = data->widget_data->signals; + while (item) + { + signal = (GladeSignal *) item->data; + + save_signal (data, signal->name, signal->handler, signal->after, + signal->object, signal->last_modification_time); + + item = item->next; + } +} + + + +/************************************************************************* + * Functions for replacing widgets in the user interface + *************************************************************************/ + +/* + * Replace a child with a new child. Used to replace placeholders with + * a widget when adding widgets, and also to replace widgets with + * placeholders when deleting. Returns TRUE on success. + * NOTE: gbwidgets do not currently have their own function for this, + * but we'll probably add it at some point. + */ +gboolean +gb_widget_replace_child (GtkWidget * widget, + GtkWidget * current_child, + GtkWidget * new_child) +{ + gchar *child_name, *new_child_name; + + /* Copy the child name to the new widget if necessary. */ + child_name = gb_widget_get_child_name (current_child); + new_child_name = gb_widget_get_child_name (new_child); + + /* We never copy these child names to the new child widget. + The "vscrollbar" and "hscrollbar" child names come from libglade-convert, + but we don't use them and we need to get rid of them. */ + if (child_name && (!strcmp (child_name, "vscrollbar") + || !strcmp (child_name, "hscrollbar"))) + { + child_name = NULL; + } + + /* Copy the old child name across. */ + gb_widget_set_child_name (new_child, child_name); + +#ifdef USE_GNOME + if (BONOBO_IS_DOCK_ITEM (widget)) + { + gboolean is_floating; + gint x, y; + + /* GnomeDockItem widgets which are floating automatically disappear when + the child is removed, so we remember the position and try to show it + again in the same place after adding the new widget. */ + is_floating = BONOBO_DOCK_ITEM (widget)->is_floating ? TRUE : FALSE; + if (is_floating) + bonobo_dock_item_get_floating_position (BONOBO_DOCK_ITEM (widget), + &x, &y); + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_widget_hide (new_child); + gtk_container_add (GTK_CONTAINER (widget), new_child); + gtk_widget_show (new_child); + + if (is_floating) + bonobo_dock_item_detach (BONOBO_DOCK_ITEM (widget), x, y); + } + else if (GTK_IS_FRAME (widget)) +#else + if (GTK_IS_FRAME (widget)) +#endif + { + /* If this is the frame's label widget, we replace that. */ + if (gtk_frame_get_label_widget (GTK_FRAME (widget)) == current_child) + { + gtk_frame_set_label_widget (GTK_FRAME (widget), new_child); + } + else + { + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_container_add (GTK_CONTAINER (widget), new_child); + } + } + else if (GTK_IS_TOOL_ITEM (widget)) + { + /* For a GtkToolItem, if the current child is a placeholder and the + GtkToolItem is not a GbWidget or the new child is a GtkToolItem, + we replace the GtkToolItem instead. Otherwise we replace the + placeholder as usual. */ + if (GB_IS_PLACEHOLDER (current_child) + && (!GB_IS_GB_WIDGET (widget) || GTK_IS_TOOL_ITEM (new_child))) + { + return gb_widget_replace_child (widget->parent, widget, new_child); + } + else + { + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_container_add (GTK_CONTAINER (widget), new_child); + } + } + else if (GTK_IS_EXPANDER (widget)) + { + /* If this is the expander's label widget, we replace that. */ + if (gtk_expander_get_label_widget (GTK_EXPANDER (widget)) == current_child) + { + gtk_expander_set_label_widget (GTK_EXPANDER (widget), new_child); + } + else + { + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_container_add (GTK_CONTAINER (widget), new_child); + } + } + else if (GTK_IS_BIN (widget)) + { + /* For a bin, we just delete the existing child and add the new one. */ + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_container_add (GTK_CONTAINER (widget), new_child); + } + else if (GTK_IS_BOX (widget)) + { + /* For a box, we find out the position of the current child, delete it, + add the new one, and move it into position with reorder_child(). + If the existing child is a placeholder and the new one is a menubar + or toolbar, we set the packing so it doesn't expand, as that is + probably what the user wants. */ + gboolean expand, fill; + guint padding; + GtkPackType pack_type; + gint pos = glade_util_get_box_pos (GTK_BOX (widget), current_child); + g_return_val_if_fail (pos != -1, FALSE); + gtk_box_query_child_packing (GTK_BOX (widget), current_child, + &expand, &fill, &padding, &pack_type); + + /* If we are replacing a placeholder (i.e. we are adding a new widget), + we try to set the expand & fill options to reasonable defaults. */ + if (GB_IS_PLACEHOLDER (current_child)) + { + if (GTK_IS_LABEL (new_child) + || GTK_IS_BUTTON (new_child) + || GTK_IS_OPTION_MENU (new_child) + || GTK_IS_PROGRESS (new_child) + || GTK_IS_PROGRESS_BAR (new_child) + || GTK_IS_MENU_BAR (new_child) + || GTK_IS_TOOLBAR (new_child) + || GTK_IS_STATUSBAR (new_child)) + { + expand = FALSE; + fill = FALSE; + } + + /* In a vbox, entry & combo widgets should not expand/fill either. */ + if (GTK_IS_VBOX (widget)) + { + if (GTK_IS_ENTRY (new_child) + || GTK_IS_COMBO (new_child) + || GTK_IS_SPIN_BUTTON (new_child) +#ifdef USE_GNOME + || GNOME_IS_DATE_EDIT (new_child) + || GNOME_IS_FILE_ENTRY (new_child) + || GNOME_IS_PIXMAP_ENTRY (new_child) +#endif + ) + { + expand = FALSE; + fill = FALSE; + } + } + } + + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_container_add (GTK_CONTAINER (widget), new_child); + + gtk_box_set_child_packing (GTK_BOX (widget), new_child, expand, fill, + padding, pack_type); + gtk_box_reorder_child (GTK_BOX (widget), new_child, pos); + + } + else if (GTK_IS_TOOLBAR (widget)) + { + gint pos; + + pos = gtk_toolbar_get_item_index (GTK_TOOLBAR (widget), + GTK_TOOL_ITEM (current_child)); + g_return_val_if_fail (pos != -1, FALSE); + + /* FIXME: GTK+/GNOME 2 bug workaround - something keeps a ref to the + initial buttons added to a GnomeApp, which causes when they eventually + get destroyed later and we try to remove them from the tree. So we + remove them from the tree here. */ + tree_remove_widget (current_child); + + gtk_container_remove (GTK_CONTAINER (widget), current_child); + + /* If the new child is a GtkToolItem, we can simply add it at the old + position. */ + if (GTK_IS_TOOL_ITEM (new_child)) + { + gtk_toolbar_insert (GTK_TOOLBAR (widget), + GTK_TOOL_ITEM (new_child), pos); + } + /* If the new child is a placeholder, we need to insert a + GtkToolItem above it (but not a GbWidget). */ + else if (GB_IS_PLACEHOLDER (new_child)) + { + GtkWidget *toolitem = (GtkWidget*) gtk_tool_item_new (); + gtk_widget_show (toolitem); + gtk_container_add (GTK_CONTAINER (toolitem), new_child); + gtk_toolbar_insert (GTK_TOOLBAR (widget), + GTK_TOOL_ITEM (toolitem), pos); + } + /* If the new child is not a GtkToolItem, we need to insert a + GtkToolItem above it, but use a GbWidget so its properties can be + set as required. */ + else + { + GtkWidget *toolitem = gb_widget_new ("GtkToolItem", NULL); + gtk_widget_show (toolitem); + gtk_container_add (GTK_CONTAINER (toolitem), new_child); + gtk_toolbar_insert (GTK_TOOLBAR (widget), + GTK_TOOL_ITEM (toolitem), pos); + tree_add_widget (toolitem); + } + } + else if (GTK_IS_LIST (widget)) + { + /* For a list, we find out the position of the current child, delete it, + and add the new one at the same position. */ + gint pos = gtk_list_child_position (GTK_LIST (widget), current_child); + GList glist = + {NULL, NULL, NULL}; + glist.data = current_child; + gtk_list_remove_items (GTK_LIST (widget), &glist); + glist.data = new_child; + gtk_list_insert_items (GTK_LIST (widget), &glist, pos); + + } + else if (GTK_IS_NOTEBOOK (widget)) + { + /* For a notebook, we find out the position of the current child, delete + it, and add the new one at the same position. If the current_child is + a notebook tab, just replace it. */ + GtkWidget *page, *tab_label; + gint pos; + + pos = find_notebook_page (GTK_NOTEBOOK (widget), current_child, + &page, &tab_label); + g_return_val_if_fail (pos != -1, FALSE); + + if (page == current_child) + { + gtk_widget_ref (tab_label); + gtk_notebook_remove_page (GTK_NOTEBOOK (widget), pos); + gtk_notebook_insert_page (GTK_NOTEBOOK (widget), + new_child, tab_label, pos); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), pos); + gtk_widget_unref (tab_label); + } + else + { + gtk_widget_ref (page); + gtk_notebook_remove_page (GTK_NOTEBOOK (widget), pos); + gtk_notebook_insert_page (GTK_NOTEBOOK (widget), + page, new_child, pos); + gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), pos); + gtk_widget_unref (page); + } + + } + else if (GTK_IS_PANED (widget)) + { + /* For paned, we find out the position of the current child, delete it, + and add the new one at the same position. */ + gint pos = (GTK_PANED (widget)->child1 == current_child) ? 1 : 2; + gtk_container_remove (GTK_CONTAINER (widget), current_child); + if (pos == 1) + gtk_paned_add1 (GTK_PANED (widget), new_child); + else + gtk_paned_add2 (GTK_PANED (widget), new_child); + + } + else if (GTK_IS_TABLE (widget)) + { + /* For a table, we find out the position & size of the current child, + delete it, and add the new one in the same place. */ + gint left, right, top, bottom; + GtkTableChild *tchild; + GtkAttachOptions xoptions, yoptions; + + tchild = glade_util_find_table_child (GTK_TABLE (widget), current_child); + g_return_val_if_fail (tchild != NULL, FALSE); + + left = tchild->left_attach; + right = tchild->right_attach; + top = tchild->top_attach; + bottom = tchild->bottom_attach; + + xoptions = 0; + if (tchild->xexpand) + xoptions |= GTK_EXPAND; + if (tchild->xshrink) + xoptions |= GTK_SHRINK; + if (tchild->xfill) + xoptions |= GTK_FILL; + + yoptions = 0; + if (tchild->yexpand) + yoptions |= GTK_EXPAND; + if (tchild->yshrink) + yoptions |= GTK_SHRINK; + if (tchild->yfill) + yoptions |= GTK_FILL; + + /* If we are replacing a placeholder (i.e. we are adding a new widget), + we try to set the expand & fill options to reasonable defaults. */ + if (GB_IS_PLACEHOLDER (current_child)) + { + if (GTK_IS_LABEL (new_child) + || GTK_IS_BUTTON (new_child) + || GTK_IS_OPTION_MENU (new_child) + || GTK_IS_PROGRESS (new_child) + || GTK_IS_PROGRESS_BAR (new_child) + || GTK_IS_MENU_BAR (new_child) + || GTK_IS_TOOLBAR (new_child) + || GTK_IS_STATUSBAR (new_child)) + { + xoptions = GTK_FILL; + yoptions = 0; + } + else if (GTK_IS_ENTRY (new_child) + || GTK_IS_COMBO (new_child) + || GTK_IS_SPIN_BUTTON (new_child) +#ifdef USE_GNOME + || GNOME_IS_DATE_EDIT (new_child) + || GNOME_IS_FILE_ENTRY (new_child) + || GNOME_IS_PIXMAP_ENTRY (new_child) +#endif + ) + { + xoptions = GTK_EXPAND | GTK_FILL; + yoptions = 0; + } + } + + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_table_attach (GTK_TABLE (widget), new_child, + left, right, top, bottom, xoptions, yoptions, 0, 0); + + /* Note that if we have just added a placeholder, but there is already + a widget at the same position in the table, the placeholder will be + destroyed immediately. Thus don't rely on the new widget still being + alive after calling gb_widget_replace_child(). */ + gb_table_update_placeholders (widget, -1, -1); + } +#if GLADE_SUPPORTS_GTK_TREE + else if (GTK_IS_TREE (widget)) + { + /* For a tree, we find out the position of the current child, delete it, + and add the new one at the same position. */ + gint pos = gtk_tree_child_position (GTK_TREE (widget), current_child); + GList glist = + {NULL, NULL, NULL}; + glist.data = current_child; + gtk_tree_remove_items (GTK_TREE (widget), &glist); + gtk_tree_insert (GTK_TREE (widget), new_child, pos); + + } +#endif + else if (GTK_IS_CLIST (widget)) + { + /* For a clist, we check if the widget is a column title and if it is + we replace it. */ + gint pos; + GtkCList *clist; + + clist = GTK_CLIST(widget); + + for (pos = 0; pos < clist->columns; pos++) + { + if (clist->column[pos].button == current_child) + { + gtk_clist_set_column_widget(clist, pos, new_child); + } + } + } + else if (GTK_IS_FIXED (widget) || GTK_IS_LAYOUT (widget)) + { + GladeWidgetData *wdata; + gint x, y, w, h; + + /* For a fixed, we find the position and size of the existing child, + remove it, and add the new one in the same place. */ + wdata = gtk_object_get_data (GTK_OBJECT (current_child), + GB_WIDGET_DATA_KEY); + g_return_val_if_fail (wdata != NULL, FALSE); + + gtk_container_child_get (GTK_CONTAINER (widget), current_child, + "x", &x, + "y", &y, + NULL); + w = wdata->width; + h = wdata->height; + + wdata = gtk_object_get_data (GTK_OBJECT (new_child), GB_WIDGET_DATA_KEY); + g_return_val_if_fail (wdata != NULL, FALSE); + wdata->flags |= GLADE_WIDTH_SET | GLADE_HEIGHT_SET; + wdata->width = w; + wdata->height = h; + + /* Reset the widget's uposition, just in case it gets added to a standard + container. I don't think we need this for GTK+ 2. */ + /*gtk_widget_set_uposition (current_child, -1, -1);*/ + + /* FIXME: GTK+ 1.2.3 bug workaround. We need to ref the widget to stop + gtk_layout_remove() from issuing a warning. */ + gtk_widget_ref (current_child); + gtk_container_remove (GTK_CONTAINER (widget), current_child); + gtk_widget_unref (current_child); + + if (GTK_IS_FIXED (widget)) + { + gtk_fixed_put (GTK_FIXED (widget), new_child, x, y); + /*gtk_widget_set_uposition (new_child, x, y);*/ + } + else + { + gtk_layout_put (GTK_LAYOUT (widget), new_child, x, y); + } + gb_widget_set_usize (new_child, w, h); + } + +#ifdef USE_GNOME + else if (BONOBO_IS_DOCK (widget)) + { + /* For a GnomeDock, we call bonobo_dock_set_client_area (). It removes + the existing child automatically. */ + bonobo_dock_set_client_area (BONOBO_DOCK (widget), new_child); + } + +#endif + + else if (GTK_IS_CONTAINER (widget)) + { + /* General code for container - has to remove all children and add back + NOTE: this may not work for specialised containers. + NOTE: need to ref widgets? */ + g_warning (_("replacing child of container - not implemented yet\n")); + return FALSE; + } + + return TRUE; +} + + +/************************************************************************* + * Functions for showing/hiding tooltips of the widgets + *************************************************************************/ + +gboolean +gb_widget_get_show_tooltips () +{ + return GTK_TOOLTIPS (gb_widget_tooltips)->enabled; +} + + +void +gb_widget_set_show_tooltips (gboolean show) +{ + if (show) + gtk_tooltips_enable (gb_widget_tooltips); + else + gtk_tooltips_disable (gb_widget_tooltips); +} + + +void +gb_widget_reset_tooltips () +{ + gtk_object_destroy (GTK_OBJECT (gb_widget_tooltips)); + gb_widget_tooltips = gtk_tooltips_new (); +} + + + +/************************************************************************* + * Misc. Functions + *************************************************************************/ + + +/* This is a GTK bug workaround for combo widgets. They should manage the + size of their GtkEntry, but they don't at present, so we have to set its + size explicitly (and also in the source code output). + I think this has been fixed. It seems to be OK in GTK+ 1.2.3. */ +void +gb_widget_set_usize (GtkWidget *widget, + gint w, + gint h) +{ +#if 0 + if (GTK_IS_COMBO (widget)) + gtk_widget_set_usize (GTK_COMBO (widget)->entry, + w - 16 < 0 ? -1 : w - 16, h); +#endif + gtk_widget_set_usize (widget, w, h); +} + + +static gint +find_notebook_page (GtkNotebook * notebook, GtkWidget * current_child, + GtkWidget **page, GtkWidget **tab_label) +{ + gint nchildren, i; + GtkWidget *tmp_page, *tmp_tab_label; + + nchildren = g_list_length (notebook->children); + for (i = 0; i < nchildren; i++) + { + tmp_page = gtk_notebook_get_nth_page (notebook, i); + tmp_tab_label = gtk_notebook_get_tab_label (notebook, tmp_page); + + if (tmp_page == current_child || tmp_tab_label == current_child) + { + *page = tmp_page; + *tab_label = tmp_tab_label; + return i; + } + } + + return -1; +} + +static void +dummy_detach (GtkWidget *attach_widget, + GtkMenu *menu) +{ + ; +} + +/* + * FIXME: MAJOR HACK. + * + * GtkOptionMenu places the currently selected item inside its button, and + * removes it from the menu. So we have to hack around that here so that the + * menu items are all output correctly, in the XML and the source code. + * + * We remove the menu from the original option menu and add it to a temporary + * one with the same name. + */ +static void +option_menu_foreach (GtkOptionMenu *option, + GtkCallback callback, + gpointer data) +{ + GtkWidget *menu = option->menu; + GtkWidget *temp; + int history; + + if (!menu) + return; + + temp = gtk_option_menu_new (); + gtk_widget_set_name (temp, gtk_widget_get_name (GTK_WIDGET (option))); + gtk_object_ref (GTK_OBJECT (temp)); + gtk_object_sink (GTK_OBJECT (temp)); + + history = gtk_option_menu_get_history (option); + + gtk_object_ref (GTK_OBJECT (menu)); + + gtk_option_menu_set_menu (option, gtk_menu_new ()); + gtk_menu_attach_to_widget (GTK_MENU (menu), temp, dummy_detach); + (*callback) (menu, data); + gtk_menu_detach (GTK_MENU (menu)); + gtk_option_menu_set_menu (option, menu); + + gtk_object_unref (GTK_OBJECT (menu)); + + gtk_option_menu_set_history (option, history); + + gtk_object_unref (GTK_OBJECT (temp)); +} + +static void +combo_foreach (GtkCombo *combo, + GtkCallback callback, + gpointer data) +{ + (*callback) (combo->entry, data); + (*callback) (combo->list, data); + (*callback) (combo->button, data); +} + + +/* This calls the given callback for each child of a widget. It gets round + some of the quirks of the different versions of GTK, and descends menus + as well. */ +void +gb_widget_children_foreach (GtkWidget *widget, + GtkCallback callback, + gpointer data) +{ + /* SPECIAL CODE: for table, so we output in the reverse order. */ + if (GTK_IS_TABLE (widget)) + table_foreach (GTK_TABLE (widget), callback, data); + else if (GTK_IS_COMBO (widget)) + combo_foreach (GTK_COMBO (widget), callback, data); + else if (GTK_IS_BOX (widget)) + box_foreach (GTK_BOX (widget), callback, data); + else if (GTK_IS_OPTION_MENU (widget)) + option_menu_foreach (GTK_OPTION_MENU (widget), callback, data); + else if (GTK_IS_CONTAINER (widget)) + gtk_container_forall (GTK_CONTAINER (widget), callback, data); + + /* SPECIAL CODE: for menu items, descend to child menus. */ + if (GTK_IS_MENU_ITEM (widget) && GTK_MENU_ITEM (widget)->submenu) + (*callback) (GTK_MENU_ITEM (widget)->submenu, data); +} + + +/* This function is used to iterate through the table children in reverse. + It is needed so we output the XML file in the same order each time. */ +static void +table_foreach (GtkTable * table, + GtkCallback callback, + gpointer callback_data) +{ + GList *children; + GtkTableChild *child; + + children = g_list_last (table->children); + while (children) + { + child = children->data; + children = children->prev; + + (*callback) (child->widget, callback_data); + } +} + + +static void +box_foreach (GtkBox *box, + GtkCallback callback, + gpointer callback_data) +{ + GList *children; + GtkBoxChild *child; + + children = box->children; + while (children) + { + child = children->data; + children = children->next; + + (* callback) (child->widget, callback_data); + } +} + +/************************************************************************* + * Common popup menu callbacks + *************************************************************************/ + +static void +gb_widget_add_alignment (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *alignment, *parent; + + parent = widget->parent; + alignment = gb_widget_new ("GtkAlignment", parent); + + gtk_widget_ref (widget); + if (!gb_widget_replace_child (parent, widget, alignment)) + { + glade_util_show_message_box (_("Couldn't insert GtkAlignment widget."), + parent); + gtk_widget_destroy (alignment); + gtk_widget_unref (widget); + return; + } + if (GTK_BIN (alignment)->child) + gtk_container_remove (GTK_CONTAINER (alignment), + GTK_BIN (alignment)->child); + gtk_container_add (GTK_CONTAINER (alignment), widget); + gtk_widget_unref (widget); + tree_insert_widget_parent (alignment, widget); +} + + +static void +gb_widget_remove_alignment (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *alignment, *parent; + + alignment = widget->parent; + g_return_if_fail (GTK_IS_ALIGNMENT (alignment)); + parent = alignment->parent; + + gtk_widget_ref (widget); + gtk_widget_ref (alignment); + + /* Remove the alignment and all children from the tree. */ + tree_remove_widget (alignment); + + gtk_container_remove (GTK_CONTAINER (alignment), widget); + + if (gb_widget_replace_child (parent, alignment, widget)) + { + /* Now add the widget and its children back to the tree. */ + tree_add_widget (widget); + } + else + { + glade_util_show_message_box (_("Couldn't remove GtkAlignment widget."), + parent); + /* Try to put it back as it was. */ + gtk_container_add (GTK_CONTAINER (alignment), widget); + tree_add_widget (alignment); + } + + gtk_widget_unref (alignment); + gtk_widget_unref (widget); +} + + +static void +gb_widget_add_event_box (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *event_box, *parent; + + parent = widget->parent; + event_box = gb_widget_new ("GtkEventBox", parent); + + gtk_widget_ref (widget); + if (!gb_widget_replace_child (parent, widget, event_box)) + { + glade_util_show_message_box (_("Couldn't insert GtkEventBox widget."), + parent); + gtk_widget_destroy (event_box); + gtk_widget_unref (widget); + return; + } + if (GTK_BIN (event_box)->child) + gtk_container_remove (GTK_CONTAINER (event_box), + GTK_BIN (event_box)->child); + gtk_container_add (GTK_CONTAINER (event_box), widget); + gtk_widget_unref (widget); + tree_insert_widget_parent (event_box, widget); +} + +static void +gb_widget_remove_event_box (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *event_box, *parent; + + event_box = widget->parent; + g_return_if_fail (GTK_IS_EVENT_BOX (event_box)); + parent = event_box->parent; + + gtk_widget_ref (widget); + gtk_widget_ref (event_box); + + /* Remove the event box and all children from the tree. */ + tree_remove_widget (event_box); + + gtk_container_remove (GTK_CONTAINER (event_box), widget); + + if (gb_widget_replace_child (parent, event_box, widget)) + { + /* Now add the widget and its children back to the tree. */ + tree_add_widget (widget); + } + else + { + glade_util_show_message_box (_("Couldn't remove GtkEventBox widget."), + parent); + /* Try to put it back as it was. */ + gtk_container_add (GTK_CONTAINER (event_box), widget); + tree_add_widget (event_box); + } + gtk_widget_unref (event_box); + gtk_widget_unref (widget); +} + + +static void +gb_widget_redisplay_window (GtkWidget * menuitem, + GtkWidget * widget) +{ + g_return_if_fail (GTK_IS_WINDOW (widget)); + + /* See also editor_on_key_press_event() in editor.c. */ + glade_util_close_window (widget); + gtk_window_reshow_with_initial_size (GTK_WINDOW (widget)); +} + + +static void +gb_widget_add_scrolled_window (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *scrolledwin, *parent; + + parent = widget->parent; + scrolledwin = gb_widget_new ("GtkScrolledWindow", parent); + + gtk_widget_ref (widget); + if (!gb_widget_replace_child (parent, widget, scrolledwin)) + { + glade_util_show_message_box (_("Couldn't insert GtkScrolledWindow widget."), parent); + gtk_widget_destroy (scrolledwin); + gtk_widget_unref (widget); + return; + } + if (GTK_BIN (scrolledwin)->child) + gtk_container_remove (GTK_CONTAINER (scrolledwin), + GTK_BIN (scrolledwin)->child); + gtk_container_add (GTK_CONTAINER (scrolledwin), widget); + gtk_widget_unref (widget); + tree_insert_widget_parent (scrolledwin, widget); +} + + +static void +gb_widget_remove_scrolled_window (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *scrolledwin, *parent; + + scrolledwin = widget->parent; + g_return_if_fail (GTK_IS_SCROLLED_WINDOW (scrolledwin)); + parent = scrolledwin->parent; + + gtk_widget_ref (widget); + gtk_widget_ref (scrolledwin); + + /* Remove the alignment and all children from the tree. */ + tree_remove_widget (scrolledwin); + + gtk_container_remove (GTK_CONTAINER (scrolledwin), widget); + + if (gb_widget_replace_child (parent, scrolledwin, widget)) + { + /* Now add the widget and its children back to the tree. */ + tree_add_widget (widget); + } + else + { + glade_util_show_message_box (_("Couldn't remove GtkScrolledWindow widget."), parent); + /* Try to put it back as it was. */ + gtk_container_add (GTK_CONTAINER (scrolledwin), widget); + tree_add_widget (scrolledwin); + } + + gtk_widget_unref (scrolledwin); + gtk_widget_unref (widget); +} + + +/************************************************************************* + * Common functions used by gbwidgets. + *************************************************************************/ + +/* This gets the child label for buttons or menuitems or subclasses. + This is for showing or saving. */ +void +gb_widget_output_child_label (GtkWidget * widget, GbWidgetGetArgData * data, + const gchar * Label) +{ + GtkWidget *child; + gchar *label_text; + + child = GTK_BIN (widget)->child; + + /* Note that we don't want to save the child label if it is a GbWidget, + since it will be saved as a separate widget. */ + if (child && GTK_IS_LABEL (child) && !GB_IS_GB_WIDGET (child)) + { + label_text = glade_util_get_label_text (child); + gb_widget_output_translatable_text (data, Label, label_text); + g_free (label_text); + + if (data->action == GB_SHOWING) + property_set_sensitive (Label, TRUE); + + /* All our menuitems use underlined accelerators. */ + if (data->action == GB_SAVING && GTK_IS_MENU_ITEM (widget)) + gb_widget_output_bool (data, "use_underline", TRUE); + } + else + { + if (data->action == GB_SHOWING) + { + gb_widget_output_translatable_text (data, Label, ""); + property_set_sensitive (Label, FALSE); + } + } +} + + +/* This sets the child label for buttons/items/menuitems or subclasses. + This is for applying or loading. */ +void +gb_widget_input_child_label (GtkWidget * widget, GbWidgetSetArgData * data, + const gchar * Label) +{ + GtkWidget *child, *label; + gchar *label_text; + + child = GTK_BIN (widget)->child; + + label_text = gb_widget_input_text (data, Label); + if (data->apply) + { + if (child && GTK_IS_LABEL (child)) + { + gtk_label_set_text_with_mnemonic (GTK_LABEL (child), label_text); + } + else + { + if (child != NULL) + gtk_container_remove (GTK_CONTAINER (widget), child); + if (GTK_IS_MENU_ITEM (widget)) + { + label = gtk_accel_label_new (""); + gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), + widget); + } + else + { + label = gtk_label_new (""); + } + gtk_container_add (GTK_CONTAINER (widget), label); + gtk_label_set_text_with_mnemonic (GTK_LABEL (label), label_text); + gtk_widget_show (label); + + /* Simple child labels are given different alignments according to + the parent. GtkButton and GtkToggleButton are centred. All the + others are aligned left. See the GTK _new_with_label() fns. */ + if (data->action == GB_LOADING) + { + if (GTK_IS_CHECK_BUTTON (widget) + || GTK_IS_ITEM (widget)) + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + } + } + } + /* This isn't very nice. When a text property is got from the property + editor (i.e. when action is GB_APPLYING) it needs to be freed after. */ + if (data->action == GB_APPLYING) + g_free (label_text); +} + + +void +gb_widget_create_child_label_popup_menu (GtkWidget *widget, + GbWidgetCreateMenuData *data) +{ + GtkWidget *menuitem; + + if (GTK_IS_LABEL (GTK_BIN (widget)->child)) + { + menuitem = gtk_menu_item_new_with_label (_("Remove Label")); + gtk_widget_show (menuitem); + gtk_container_add (GTK_CONTAINER (data->menu), menuitem); + gtk_signal_connect (GTK_OBJECT (menuitem), "activate", + GTK_SIGNAL_FUNC (gb_widget_remove_label), widget); + } +} + + +void +gb_widget_remove_label (GtkWidget * menuitem, + GtkWidget * widget) +{ + GtkWidget *child; + + g_return_if_fail (GTK_IS_BIN (widget)); + + child = GTK_BIN (widget)->child; + if (child && GTK_IS_LABEL (child)) + editor_delete_widget (child); +} + + +/************************************************************************* + * Adjustment convenience functions - handles all 6 values. + *************************************************************************/ +void +gb_widget_output_adjustment (GbWidgetGetArgData * data, + const gchar * Values[], + GtkAdjustment * adjustment, + gchar *saved_property_name) +{ + /* Adjustments are now saved as a single property with all 6 values in a + string, e.g. '0 0 100 1 10 10'. */ + if (data->action == GB_SAVING) + { + gchar buffer[256]; + + sprintf (buffer, "%.12g %.12g %.12g %.12g %.12g %.12g", + adjustment->value, + adjustment->lower, + adjustment->upper, + adjustment->step_increment, + adjustment->page_increment, + adjustment->page_size); + save_string (data, saved_property_name, buffer); + } + else + { + if (Values[0]) + gb_widget_output_float (data, Values[0], adjustment->value); + if (Values[1]) + gb_widget_output_float (data, Values[1], adjustment->lower); + if (Values[2]) + gb_widget_output_float (data, Values[2], adjustment->upper); + if (Values[3]) + gb_widget_output_float (data, Values[3], adjustment->step_increment); + if (Values[4]) + gb_widget_output_float (data, Values[4], adjustment->page_increment); + if (Values[5]) + gb_widget_output_float (data, Values[5], adjustment->page_size); + } +} + + +gboolean +gb_widget_input_adjustment (GbWidgetSetArgData * data, + const gchar * Values[], + GtkAdjustment * adjustment, + gchar *saved_property_name) +{ + gfloat value, lower, upper, step_inc, page_inc, page_size, tmp; + + /* Adjustments are now saved as a single property with all 6 values in a + string, e.g. '0 0 100 1 10 10'. */ + if (data->action == GB_LOADING) + { + gchar *value = gb_widget_input_string (data, saved_property_name); + if (data->apply) + { + gchar *ptr = value; + + adjustment->value = g_strtod (ptr, &ptr); + adjustment->lower = g_strtod (ptr, &ptr); + adjustment->upper = g_strtod (ptr, &ptr); + adjustment->step_increment = g_strtod (ptr, &ptr); + adjustment->page_increment = g_strtod (ptr, &ptr); + adjustment->page_size = g_strtod (ptr, &ptr); + + return TRUE; + } + else + return FALSE; + } + + value = adjustment->value; + lower = adjustment->lower; + upper = adjustment->upper; + step_inc = adjustment->step_increment; + page_inc = adjustment->page_increment; + page_size = adjustment->page_size; + + if (Values[0]) + { + tmp = gb_widget_input_float (data, Values[0]); + if (data->apply) + value = tmp; + } + + if (Values[1]) + { + tmp = gb_widget_input_float (data, Values[1]); + if (data->apply) + lower = tmp; + } + + if (Values[2]) + { + tmp = gb_widget_input_float (data, Values[2]); + if (data->apply) + upper = tmp; + } + + if (Values[3]) + { + tmp = gb_widget_input_float (data, Values[3]); + if (data->apply) + step_inc = tmp; + } + + if (Values[4]) + { + tmp = gb_widget_input_float (data, Values[4]); + if (data->apply) + page_inc = tmp; + } + + if (Values[5]) + { + tmp = gb_widget_input_float (data, Values[5]); + if (data->apply) + page_size = tmp; + } + + /* Only return TRUE if one or more of the properties have changed. */ + if (adjustment->value != value + || adjustment->lower != lower + || adjustment->upper != upper + || adjustment->step_increment != step_inc + || adjustment->page_increment != page_inc + || adjustment->page_size != page_size) + { + adjustment->value = value; + adjustment->lower = lower; + adjustment->upper = upper; + adjustment->step_increment = step_inc; + adjustment->page_increment = page_inc; + adjustment->page_size = page_size; + return TRUE; + } + return FALSE; +} + + +/************************************************************************* + * Functions to input/output properties. + *************************************************************************/ + +/* Inputting Properties - Loading or Applying. */ + +gchar* +gb_widget_input_string (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_string (data, property); + else + return property_get_string (property, data->widget, + data->property_to_apply, &data->apply); +} + + +/* NOTE: You must free the returned string if data->action == GB_APPLYING */ +gchar* +gb_widget_input_text (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_text (data, property); + else + return property_get_text (property, data->widget, + data->property_to_apply, &data->apply); +} + + +gint +gb_widget_input_int (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_int (data, property); + else + return property_get_int (property, data->property_to_apply, + &data->apply); +} + + +gint +gb_widget_input_optional_int (GbWidgetSetArgData *data, + const gchar *property, + gboolean *is_set) +{ + gint value; + + if (data->action == GB_LOADING) + { + value = load_int (data, property); + if (is_set) + *is_set = data->apply; + return value; + } + else + return property_get_optional_int (property, data->property_to_apply, + &data->apply, is_set); +} + + +gfloat +gb_widget_input_float (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_float (data, property); + else + return property_get_float (property, data->property_to_apply, + &data->apply); +} + + +gboolean +gb_widget_input_bool (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_bool (data, property); + else + return property_get_bool (property, data->property_to_apply, + &data->apply); +} + + +gchar* +gb_widget_input_choice (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_choice (data, property); + else + return property_get_choice (property, data->property_to_apply, + &data->apply); +} + + +gchar* +gb_widget_input_combo (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_combo (data, property); + else + return property_get_combo (property, data->property_to_apply, + &data->apply); +} + + +GdkColor* +gb_widget_input_color (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_color (data, property); + else + return property_get_color (property, data->property_to_apply, + &data->apply); +} + + +GdkPixmap* +gb_widget_input_bgpixmap (GbWidgetSetArgData *data, + const gchar *property, + gchar **filename) +{ + if (data->action == GB_LOADING) + return load_bgpixmap (data, property, filename); + else + return property_get_bgpixmap (property, data->property_to_apply, + &data->apply, filename); +} + + +gpointer +gb_widget_input_dialog (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_dialog (data, property); + else + return property_get_dialog (property, data->property_to_apply, + &data->apply); +} + + +gchar* +gb_widget_input_filename (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_filename (data, property); + else + return property_get_filename (property, data->property_to_apply, + &data->apply); +} + + +gchar* +gb_widget_input_pixmap_filename (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_pixmap_filename (data, property); + else + return property_get_filename (property, data->property_to_apply, + &data->apply); +} + + +GdkFont* +gb_widget_input_font (GbWidgetSetArgData *data, + const gchar *property, + gchar **xlfd_fontname) +{ + if (data->action == GB_LOADING) + return load_font (data, property, xlfd_fontname); + else + return property_get_font (property, data->property_to_apply, + &data->apply, xlfd_fontname); +} + + +gchar* +gb_widget_input_stock_item (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_string (data, property); + else + return property_get_stock_item (property, data->property_to_apply, + &data->apply); +} + + +gchar* +gb_widget_input_icon (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_icon (data, property); + else + return property_get_icon (property, data->property_to_apply, + &data->apply); +} + +gchar* +gb_widget_input_named_icon (GbWidgetSetArgData *data, + const gchar *property) +{ + if (data->action == GB_LOADING) + return load_string (data, property); + else + return property_get_named_icon (property, data->property_to_apply, + &data->apply); +} + +gint +gb_widget_input_enum (GbWidgetSetArgData *data, + GType enum_type, + const char **labels, + int *values, + const gchar *property) +{ + gint retval = 0; + int i; + char *s; + + if (data->action == GB_LOADING) + { + s = load_string (data, property); + if (data->apply && s) + retval = glade_enum_from_string (enum_type, s); + } + else + { + s = property_get_choice (property, data->property_to_apply, + &data->apply); + if (data->apply && s) + { + for (i = 0; labels[i]; i++) + { + if (!strcmp (labels[i], s)) + { + retval = values[i]; + break; + } + } + } + } + return retval; +} + +/* Outputting Properties - Saving or Showing. */ +void +gb_widget_output_string (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_string (data, property, value); + else + property_set_string (property, value); +} + + +void +gb_widget_output_translatable_string (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_translatable_string (data, property, value); + else + property_set_translatable_string (property, value, data->widget); +} + + +void +gb_widget_output_text (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_text (data, property, value); + else + property_set_text (property, value); +} + + +void +gb_widget_output_translatable_text (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_translatable_text (data, property, value); + else + property_set_translatable_text (property, value, data->widget); +} + + +void +gb_widget_output_translatable_text_in_lines (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_translatable_text_in_lines (data, property, value); + else + property_set_translatable_text (property, value, data->widget); +} + + +void +gb_widget_output_int (GbWidgetGetArgData *data, + const gchar *property, + gint value) +{ +#if 0 + if (property == GbX) + g_print ("X: %i ", value); + else if (property == GbY) + g_print ("Y: %i ", value); + else if (property == GbWidth) + g_print ("W: %i ", value); + else if (property == GbHeight) + g_print ("H: %i ", value); +#endif + + if (data->action == GB_SAVING) + save_int (data, property, value); + else + property_set_int (property, value); +} + + +void +gb_widget_output_optional_int (GbWidgetGetArgData *data, + const gchar *property, + gint value, + gboolean is_set) +{ + if (data->action == GB_SAVING) + { + if (is_set) + save_int (data, property, value); + } + else + property_set_optional_int (property, value, is_set); +} + + +void +gb_widget_output_float (GbWidgetGetArgData *data, + const gchar *property, + gfloat value) +{ + if (data->action == GB_SAVING) + save_float (data, property, value); + else + property_set_float (property, value); +} + + +void +gb_widget_output_bool (GbWidgetGetArgData *data, + const gchar *property, + gint value) +{ + if (data->action == GB_SAVING) + save_bool (data, property, value); + else + property_set_bool (property, value); +} + + +void +gb_widget_output_choice (GbWidgetGetArgData *data, + const gchar *property, + gint value, + const gchar *symbol) +{ + if (data->action == GB_SAVING) + save_choice (data, property, symbol); + else + property_set_choice (property, value); +} + + +void +gb_widget_output_combo (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_combo (data, property, value); + else + property_set_combo (property, value); +} + + +void +gb_widget_output_color (GbWidgetGetArgData *data, + const gchar *property, + GdkColor *value) +{ + if (data->action == GB_SAVING) + save_color (data, property, value); + else + property_set_color (property, value); +} + + +void +gb_widget_output_bgpixmap (GbWidgetGetArgData *data, + const gchar *property, + GdkPixmap *value, + const gchar *filename) +{ + if (data->action == GB_SAVING) + save_bgpixmap (data, property, filename); + else + property_set_bgpixmap (property, value, filename); +} + + +void +gb_widget_output_dialog (GbWidgetGetArgData *data, + const gchar *property, + const gchar *string, + gconstpointer value) +{ + if (data->action == GB_SAVING) + save_dialog (data, property, value); + else + property_set_dialog (property, string, value); +} + + +/* FIXME: I think this is broken. It should save it relative to the XML file.*/ +void +gb_widget_output_filename (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_filename (data, property, value); + else + property_set_filename (property, value); +} + + +void +gb_widget_output_pixmap_filename (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_pixmap_filename (data, property, value); + else + property_set_filename (property, value); +} + + +void +gb_widget_output_font (GbWidgetGetArgData *data, + const gchar *property, + GdkFont *value, + const gchar *xlfd_fontname) +{ + if (data->action == GB_SAVING) + save_font (data, property, xlfd_fontname); + else + property_set_font (property, value, xlfd_fontname); +} + + +void +gb_widget_output_stock_item (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_string (data, property, value); + else + property_set_stock_item (property, value); +} + + +void +gb_widget_output_icon (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_icon (data, property, value); + else + property_set_icon (property, value); +} + +void +gb_widget_output_named_icon (GbWidgetGetArgData *data, + const gchar *property, + const gchar *value) +{ + if (data->action == GB_SAVING) + save_string (data, property, value); + else + property_set_named_icon (property, value); +} + +void +gb_widget_output_enum (GbWidgetGetArgData *data, + GType enum_type, + int *values, + int n_values, + const gchar *property, + gint value) +{ + const char *s; + int i; + + if (data->action == GB_SAVING) + { + s = glade_string_from_enum (enum_type, value); + save_string (data, property, s); + } + else + { + for (i = 0; i < n_values; i++) + if (values[i] == value) + break; + + property_set_choice (property, i < n_values ? values[i] : 0); + } +} |