diff options
Diffstat (limited to 'tools/glade/glade/gbsource.c')
-rw-r--r-- | tools/glade/glade/gbsource.c | 1222 |
1 files changed, 1222 insertions, 0 deletions
diff --git a/tools/glade/glade/gbsource.c b/tools/glade/glade/gbsource.c new file mode 100644 index 00000000..6fcd1649 --- /dev/null +++ b/tools/glade/glade/gbsource.c @@ -0,0 +1,1222 @@ +#include <gtk/gtk.h> +#include "gladeconfig.h" + +#ifdef USE_GNOME +#include <gnome.h> +#include <bonobo.h> +#endif + +#include "gb.h" +#include "gbwidget.h" +#include "source.h" +#include "glade_gnome.h" +#include "glade_atk.h" + +/* These are used for outputting signal handler prototypes. */ +#define GB_PARAM_INDENT 40 +#define GB_PARAM_TYPE_WIDTH 16 + +static GHashTable *glade_signal_hash = NULL; + +static void gb_widget_write_signals_source (GtkWidget * widget, + GbWidgetWriteSourceData * data); +static void gb_widget_write_signal_connection_source (GbWidgetWriteSourceData * data, + const gchar *signal_name, + const gchar *connect_object, + gboolean connect_after, + const gchar *handler_data, + const gchar *handler); +static gchar *get_type_name (GtkType type, gboolean * is_pointer); +static gchar *get_gdk_event (gchar * signal_name); +static gchar **lookup_signal_arg_names (gchar * type, gchar * signal_name, + gint num_args_expected); +static void gb_widget_write_accelerators_source (GtkWidget * widget, + GbWidgetWriteSourceData * data); + +/************************************************************************* + * Functions for writing C source code + *************************************************************************/ + +void +gb_widget_write_source (GtkWidget * widget, + GbWidgetWriteSourceData * data) +{ + GtkWidget *parent; + GbWidget *gbwidget; + GladeWidgetData *widget_data; + gchar *class_id, *child_name, *widget_name = NULL, *real_widget_name = NULL; + gint source_len; + + /* This is a temporary(?) kludge so that the code for GtkDialogs is OK. + We stop the source code for the action_area from being written. + GtkDialog & GnomeDialog need to output the code for their vbox & + action_area children themselves, since they need to output special code + to access them, e.g. "GTK_DIALOG (dialog1)->vbox". However, the vbox + is the parent of the action_area, and so we have to stop the action_area + being output using the standard code since that won't work. + The problem is due to the dialogs having 2 special children, where one + is a descendant of the other. I don't think this occurs anywhere else. */ + child_name = gb_widget_get_child_name (widget); + if (child_name && data->create_widget) + { + if (!strcmp (child_name, GladeChildDialogActionArea)) + return; + } + + parent = data->parent; + + class_id = gb_widget_get_class_id (widget); +#if 0 + g_print ("class_id: %s\n", class_id ? class_id : ""); +#endif + data->write_children = TRUE; + + widget_data = gtk_object_get_data (GTK_OBJECT (widget), GB_WIDGET_DATA_KEY); + /* If this isn't a gbwidget, skip it. */ + if (widget_data) + { + gbwidget = gb_widget_lookup_class (class_id); + g_return_if_fail (gbwidget != NULL); + + /* For custom widgets, we don't have a default widget to compare with, + so all properties should be set. */ + if (GLADE_IS_CUSTOM_WIDGET (widget)) + { + data->standard_widget = NULL; + } + else + { + /* This stores newly-created widgets, which we use to get default + values from. Though we don't use them much at present. */ + data->standard_widget = (GtkWidget *) g_hash_table_lookup (data->standard_widgets, class_id); + if (data->standard_widget == NULL) + { +#ifdef USE_GNOME + /* FIXME: GnomeLibs 1.0.1 workaround - gtk_object_newv doesn't + return a valid GnomeAppBar or GnomeDateEdit, so we create it + ourself. */ + if (!strcmp (class_id, "GnomeAppBar")) + data->standard_widget = gnome_appbar_new (TRUE, TRUE, GNOME_PREFERENCES_NEVER); + else if (!strcmp (class_id, "GnomeDateEdit")) + data->standard_widget = gnome_date_edit_new ((time_t) 0, TRUE, + TRUE); + /* We don't create standard widgets for Bonobo controls, for + now. There may be a better way to get default values for + them. */ + else if (!data->standard_widget && !BONOBO_IS_WIDGET (widget)) + data->standard_widget = GTK_WIDGET (g_object_newv (gtk_type_from_name (class_id), 0, NULL)); +#else + if (!data->standard_widget) + data->standard_widget = GTK_WIDGET (g_object_newv (gtk_type_from_name (class_id), 0, NULL)); +#endif + + if (data->standard_widget) + g_hash_table_insert (data->standard_widgets, class_id, + data->standard_widget); + } + } + + real_widget_name = source_create_valid_identifier (gtk_widget_get_name (widget)); + if (data->use_component_struct && widget_data->public_field) + widget_name = g_strdup_printf ("%s->%s", data->component_name, + real_widget_name); + else + widget_name = real_widget_name; + + data->widget_data = widget_data; + data->real_wname = real_widget_name; + data->wname = widget_name; + if (gbwidget->gb_widget_write_source) + (gbwidget->gb_widget_write_source) (widget, data); + else + source_add (data, " /* Skipping %s: unimplemented. */\n", class_id); + + /* Make sure there is a blank line after each widget, for readability. */ + source_len = data->source_buffers[GLADE_SOURCE]->len; + if (source_len > 2 + && (data->source_buffers[GLADE_SOURCE]->str[source_len - 1] != '\n' + || data->source_buffers[GLADE_SOURCE]->str[source_len - 2] != '\n')) + source_add (data, "\n"); + + data->wname = NULL; + data->real_wname = NULL; + data->parent = widget; + } + else if (GB_IS_PLACEHOLDER (widget) && parent) + { + if (GTK_IS_NOTEBOOK (parent)) + { + /* SPECIAL CODE: If notebook pages are empty (i.e. placeholders), + we create dummy widgets instead, so it still runs OK. */ + if (child_name == NULL) + { + gchar *wname, *parent_name; + + wname = "empty_notebook_page"; + /* Make sure the dummy widget is declared. */ + source_ensure_decl (data, " GtkWidget *empty_notebook_page;\n"); + + parent_name = (char*) gtk_widget_get_name (parent); + parent_name = source_create_valid_identifier (parent_name); + source_add (data, + " %s = gtk_vbox_new (FALSE, 0);\n" + " gtk_widget_show (%s);\n" + " gtk_container_add (GTK_CONTAINER (%s), %s);\n" + "\n", + wname, wname, parent_name, wname); + g_free (parent_name); + } + else + { + /* For empty notebook tabs, we increment the 'last_child' written + value. */ + gint col = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (parent), + "last_child")); + gtk_object_set_data (GTK_OBJECT (parent), "last_child", + GINT_TO_POINTER (col + 1)); + } + } + else if (GTK_IS_CLIST (parent)) + { + /* For empty clist/ctree titles, we increment the 'last_child' + written value. */ + if (child_name && !strcmp (child_name, GladeChildCListTitle)) + { + gint col = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (parent), + "last_child")); + gtk_object_set_data (GTK_OBJECT (parent), "last_child", + GINT_TO_POINTER (col + 1)); + } + } + } + + /* Recursively write source for children. + We need to reset the parent after the children have been written. */ + data->create_widget = TRUE; + if (data->write_children) + gb_widget_children_foreach (widget, (GtkCallback) gb_widget_write_source, data); + + /* We need to reset some of the members of the GbWidgetWriteSourceData struct + so that they work OK for the next sibling. */ + data->parent = parent; + + /* SPECIAL CODE: For GtkOptionMenu, we have to set the menu after all the + children are created. */ + if (GTK_IS_OPTION_MENU (widget) && GB_IS_GB_WIDGET (widget) + && GTK_OPTION_MENU (widget)->menu + && GB_IS_GB_WIDGET (GTK_OPTION_MENU (widget)->menu)) + { + gchar *menu_name = source_create_valid_identifier (gtk_widget_get_name (GTK_OPTION_MENU (widget)->menu)); + source_add (data, + " gtk_option_menu_set_menu (GTK_OPTION_MENU (%s), %s);\n\n", + widget_name, menu_name); + g_free (menu_name); + } + + /* SPECIAL CODE: Finish off the GnomeUIInfo struct if we are building a + Gnome menu. */ +#ifdef USE_GNOME + if (data->project->gnome_support && GTK_IS_MENU_SHELL (widget) + && GB_IS_GB_WIDGET (widget)) + { + glade_gnome_finish_menu_source (GTK_MENU_SHELL (widget), data); + } +#endif + + if (widget_name != real_widget_name) + g_free (real_widget_name); + g_free (widget_name); +} + + +/* This is called by each GbWidget's write_source function to write all the + common source code, including code to add the widget to its parent. */ +void +gb_widget_write_standard_source (GtkWidget * widget, + GbWidgetWriteSourceData * data) +{ + GladeWidgetData *wdata = data->widget_data; + gint i, width, height, can_focus, can_default; + + /* FIXME: unfinished. public fields were to be added to the component struct. + Private fields would just be declared within the creation function. */ + if (wdata->public_field) + source_add_decl (data, " GtkWidget *%s;\n", data->real_wname); + else + source_add_decl (data, " GtkWidget *%s;\n", data->real_wname); + + if (data->set_widget_names) + source_add (data, " gtk_widget_set_name (%s, \"%s\");\n", data->wname, + data->real_wname); + + if (!data->use_component_struct) + { + /* For toplevel widgets we don't ref the widget, since if we do it never + gets destroyed. This may be a bug in GTK+ 1.2.3. */ + if (data->parent == NULL) + { + source_add_to_buffer (data, GLADE_OBJECT_HOOKUP, + " GLADE_HOOKUP_OBJECT_NO_REF (%s, %s, %s);\n", + data->component_name, + data->wname, + source_make_string (data->real_wname, FALSE)); + } + else + { + source_add_to_buffer (data, GLADE_OBJECT_HOOKUP, + " GLADE_HOOKUP_OBJECT (%s, %s, %s);\n", + data->component_name, + data->wname, + source_make_string (data->real_wname, FALSE)); + } + } + + /* SPECIAL CODE: menus are not shown here. */ + if (widget->parent && wdata->flags & GLADE_VISIBLE && !GTK_IS_MENU (widget)) + { +#ifdef USE_GNOME + /* FIXME: GnomeDruidPageStandard bug workaround. It needs show_all(). */ + if (GNOME_IS_DRUID_PAGE_STANDARD (widget)) + source_add (data, " gtk_widget_show_all (%s);\n", data->wname); + else + source_add (data, " gtk_widget_show (%s);\n", data->wname); +#else + source_add (data, " gtk_widget_show (%s);\n", data->wname); +#endif + } + + /* Output code to add widget to parent. */ + gb_widget_write_add_child_source (widget, data); + + if (wdata->flags & (GLADE_WIDTH_SET | GLADE_HEIGHT_SET)) + { + width = wdata->flags & GLADE_WIDTH_SET ? wdata->width : -1; + height = wdata->flags & GLADE_HEIGHT_SET ? wdata->height : -1; + + /* GTK BUG WORKAROUND - a combo should manage the size of its entry. + I think this isn't needed any more (GTK+ 1.2.3). */ +#if 0 + if (GTK_IS_COMBO (widget)) + source_add(data, + " gtk_widget_set_size_request (GTK_COMBO (%s)->entry, %i, %i);\n", + data->wname, width - 16 < 0 ? -1 : width - 16, height); +#endif + + source_add (data, " gtk_widget_set_size_request (%s, %i, %i);\n", + data->wname, width, height); + } + + + if (glade_util_uses_border_width (widget)) + { + if (GTK_CONTAINER (widget)->border_width != 0) + { + source_add (data, + " gtk_container_set_border_width (GTK_CONTAINER (%s), %i);\n", + data->wname, GTK_CONTAINER (widget)->border_width); + } + } + + + /* FIXME: Kludge to set separator menu items insensitive, so that they are + skipped when using the cursor keys to move up/down the menu. */ + if (!(wdata->flags & GLADE_SENSITIVE) + || (GTK_IS_MENU_ITEM (widget) && GTK_BIN (widget)->child == NULL)) + source_add (data, " gtk_widget_set_sensitive (%s, FALSE);\n", data->wname); + + can_focus = GTK_WIDGET_CAN_FOCUS (widget); + if (!data->standard_widget + || can_focus != GTK_WIDGET_CAN_FOCUS (data->standard_widget)) + { + if (can_focus) + source_add (data, " gtk_widget_set_can_focus(%s, TRUE);\n", + data->wname); + else + source_add (data, " gtk_widget_set_can_focus(%s, FALSE);\n", + data->wname); + } + can_default = GTK_WIDGET_CAN_DEFAULT (widget); + if (!data->standard_widget + || can_default != GTK_WIDGET_CAN_DEFAULT (data->standard_widget)) + { + if (can_default) + source_add (data, " gtk_widget_set_can_default(%s, TRUE);\n", + data->wname); + else + source_add (data, " gtk_widget_set_can_default(%s, FALSE);\n", + data->wname); + } + + if (wdata->flags & GLADE_GRAB_FOCUS) + { + if (data->focus_widget) + g_warning ("Multiple widgets with 'Has Focus' set: %s, %s", + data->focus_widget, data->real_wname); + else + data->focus_widget = g_strdup (data->wname); + } + + if (wdata->flags & GLADE_GRAB_DEFAULT) + { + if (data->default_widget) + g_warning ("Multiple widgets with 'Has Default' set: %s, %s", + data->default_widget, data->real_wname); + else + data->default_widget = g_strdup (data->wname); + } + + if (wdata->tooltip) + { + gboolean translatable, context; + gchar *comments; + + glade_util_get_translation_properties (widget, GbTooltip, &translatable, + &comments, &context); + source_add_translator_comments (data, translatable, comments); + + data->need_tooltips = TRUE; + + /* GtkToolItem subclasses use a special function, since otherwise they + wouldn't work as they don't have a window. */ + if (GTK_IS_TOOL_ITEM (widget)) + { + source_add (data, + " gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (%s), tooltips, %s, NULL);\n", + data->wname, + source_make_string_full (wdata->tooltip, data->use_gettext && translatable, context)); + } + else + { + source_add (data, + " gtk_widget_set_tooltip_text (%s, %s);\n", + data->wname, + source_make_string_full (wdata->tooltip, data->use_gettext && translatable, context)); + } + } + + if (!GTK_WIDGET_NO_WINDOW (widget)) + { + GdkExtensionMode ext_mode = gtk_widget_get_extension_events (widget); + gboolean written_first = FALSE; + + if (wdata->events) + { + source_add (data, " gtk_widget_set_events (%s, ", data->wname); + for (i = 0; i < GB_EVENT_MASKS_COUNT; i++) + { + if (wdata->events & GbEventMaskValues[i]) + { + if (!written_first) + source_add (data, "%s", GbEventMaskSymbols[i]); + else + source_add (data, " | %s", GbEventMaskSymbols[i]); + written_first = TRUE; + } + } + source_add (data, ");\n"); + } + + if (ext_mode != GDK_EXTENSION_EVENTS_NONE) + { + for (i = 0; GbExtensionModeChoices[i]; i++) + { + if (GbExtensionModeValues[i] == ext_mode) + source_add (data, " gtk_widget_set_extension_events (%s, %s);\n", + data->wname, GbExtensionModeSymbols[i]); + } + } + } + + gb_widget_write_signals_source (widget, data); + gb_widget_write_accelerators_source (widget, data); + glade_atk_write_source (widget, data); +} + + +void +gb_widget_write_add_child_source (GtkWidget * widget, + GbWidgetWriteSourceData * data) +{ + GbWidget *gbparent; + GtkWidget *parent; + gchar *parent_name; + + /* If the widget is created automatically by its parent, we don't need + to add source to add it. */ + if (!data->create_widget) + return; + + /* SPECIAL CODE: to handle menus. */ + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = data->parent; + + /* Return if no parent exists */ + if (!parent) + return; + + parent_name = (char*) gtk_widget_get_name (parent); + parent_name = source_create_valid_identifier (parent_name); + + MSG2 ("Adding %s to %s", data->wname, parent_name); + + /* If the GbWidget has its own function to output source code to add a child, + we use that, else we just output the default "gtk_container_add ()". */ + gbparent = gb_widget_lookup (parent); + if (gbparent && gbparent->gb_widget_write_add_child_source) + { + (gbparent->gb_widget_write_add_child_source) (parent, parent_name, + widget, data); + } + else + { + source_add (data, " gtk_container_add (GTK_CONTAINER (%s), %s);\n", + parent_name, data->wname); + } + + g_free (parent_name); +} + + +static void +gb_widget_write_signals_source (GtkWidget * widget, + GbWidgetWriteSourceData * data) +{ + GList *item; + GladeSignal *signal; + gboolean skip_handler_code; + gchar *handler; + + item = data->widget_data->signals; + while (item) + { + signal = (GladeSignal *) item->data; + item = item->next; + skip_handler_code = FALSE; + + /* If we're appending new/changed signals and this is an old one, then + skip it. Note that we also skip it if the last mod time is 0, which + will happen for XML files created before the last mod tag was added.*/ + MSG2 ("Creating Signals:%s Handler: %s", + data->creating_callback_files ? "TRUE" : "FALSE", signal->handler); + MSG1 ("LastMod:%s", ctime (&signal->last_modification_time)); + MSG1 ("LastWrite:%s", ctime (&data->last_write_time)); + + if (!data->creating_callback_files + && (signal->last_modification_time <= data->last_write_time)) + { + MSG1 ("Skipping signal: %s", signal->handler); + skip_handler_code = TRUE; + } + + if (!signal->name) + { + /* FIXME: store warnings */ + g_warning ("Signal name not set"); + continue; + } + + if (!signal->handler) + { + /* FIXME: store warnings */ + g_warning ("Signal handler not set"); + continue; + } + + /* Make sure handler function name is valid. */ + handler = source_create_valid_identifier (signal->handler); + + /* Output code to connect signal. */ + gb_widget_write_signal_connection_source (data, signal->name, + signal->object, signal->after, + signal->data, handler); + + /* We don't need to output code for standard GTK functions. */ + if (!strcmp (handler, "gtk_widget_show") + || !strcmp (handler, "gtk_widget_hide") + || !strcmp (handler, "gtk_widget_grab_focus") + || !strcmp (handler, "gtk_widget_destroy") + || !strcmp (handler, "gtk_window_activate_default") + || !strcmp (handler, "gtk_true") + || !strcmp (handler, "gtk_false") + || !strcmp (handler, "gtk_main_quit")) + skip_handler_code = TRUE; + + + /* If we're appending new/changed signals and this is an old one, we + don't want to output the source code for it. */ + if (!skip_handler_code) + { + gb_widget_write_signal_handler_source (widget, data, signal->name, + handler); + } + g_free (handler); + } +} + + +/* This outputs a signal handler declaration and empty function. handler + should be a valid identifier. */ +void +gb_widget_write_signal_handler_source (GtkWidget *widget, + GbWidgetWriteSourceData * data, + const gchar *signal_name, + const gchar *handler) +{ + guint signal_id; + GSignalQuery query_info; + gint param, i; + gchar buffer[1024]; + gchar *ret_type, *pos, *type_name, *arg_name, *object_name, *object_arg; + gchar *object_arg_start; + gboolean is_pointer; + gint param_num, widget_num, event_num, callback_num; + gint handler_len, object_name_len, type_name_len, num_spaces; + gint *arg_num; + gchar **arg_names; + gchar *signal_name_copy; + + /* Check if we have already output the handler. */ + if (g_hash_table_lookup (data->handlers_output, handler)) + return; + + /* Remember that we have output the handler. */ + g_hash_table_insert (data->handlers_output, g_strdup (handler), "Output"); + + + signal_id = gtk_signal_lookup (signal_name, GTK_OBJECT_TYPE (widget)); + /* If a project is converted from GTK+ 1.2.x to 2.x.x it may have some + signals which aren't valid any more. So we output a warning. */ + if (signal_id == 0) + { + g_warning ("Invalid signal: '%s' for widget: '%s'", signal_name, + data->wname); + return; + } + + g_signal_query (signal_id, &query_info); + + /* Output the return type and function name. */ + ret_type = get_type_name (query_info.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer); + pos = buffer; + sprintf (pos, "%s%s\n%s", ret_type, is_pointer ? "*" : "", handler); + pos += strlen (pos); + + handler_len = strlen (handler); + if (handler_len >= GB_PARAM_INDENT - 1) + { + *pos++ = '\n'; + for (i = 0; i < GB_PARAM_INDENT; i++) + *pos++ = ' '; + } + else + { + num_spaces = GB_PARAM_INDENT - handler_len - 1; + for (i = 0; i < num_spaces; i++) + *pos++ = ' '; + } + + /* Convert signal name to use underscores rather than dashes '-'. */ + signal_name_copy = g_strdup (query_info.signal_name); + for (i = 0; signal_name_copy[i]; i++) + { + if (signal_name_copy[i] == '-') + signal_name_copy[i] = '_'; + } + + /* Output the signal parameters. */ + object_name = (char*) g_type_name (query_info.itype); + arg_names = lookup_signal_arg_names (object_name, signal_name_copy, + query_info.n_params + 1); + + + widget_num = 0; + + /* Output the signal object type and the argument name. We assume the + type is a pointer - I think that is OK. We remove "Gtk" and convert + to lower case for the argument name. */ + sprintf (pos, "(%s ", object_name); + pos += strlen (pos); + object_name_len = strlen (object_name); + if (object_name_len + 1 < GB_PARAM_TYPE_WIDTH) + { + num_spaces = GB_PARAM_TYPE_WIDTH - object_name_len - 1; + for (i = 0; i < num_spaces; i++) + *pos++ = ' '; + } + + if (arg_names) + { + sprintf (pos, "*%s,\n", arg_names[0]); + pos += strlen (pos); + } + else + { + object_arg = (!strncmp (object_name, "Gtk", 3)) ? object_name + 3 : object_name; + object_arg_start = pos + 1; + sprintf (pos, "*%s", object_arg); + pos += strlen (pos); + g_strdown (object_arg_start); + if (!strncmp (object_arg_start, "widget", 6)) + widget_num = 2; + sprintf (pos, ",\n"); + pos += strlen (pos); + } + + + param_num = 1; + event_num = callback_num = 0; + for (param = 0; param < query_info.n_params; param++) + { + for (i = 0; i < GB_PARAM_INDENT; i++) + *pos++ = ' '; + + /* If the arg name includes the type (with a space between!), just + output that. */ + if (arg_names && strchr (arg_names[param + 1], ' ')) + { + sprintf (pos, "%s,\n", arg_names[param + 1]); + pos += strlen (pos); + } + else + { + type_name = get_type_name (query_info.param_types[param] & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer); + /* Most arguments to the callback are called "arg1", "arg2", etc. + GtkWidgets are called "widget", "widget2", ... + GdkEvents are called "event", "event2", ... + GtkCallbacks are called "callback", "callback2", ... */ + if (!strcmp (type_name, "GtkWidget")) + { + arg_name = "widget"; + arg_num = &widget_num; + } + else if (!strcmp (type_name, "GdkEvent")) + { + type_name = get_gdk_event (signal_name_copy); + arg_name = "event"; + arg_num = &event_num; + is_pointer = TRUE; + } + else if (!strcmp (type_name, "GtkCallback")) + { + arg_name = "callback"; + arg_num = &callback_num; + } + else + { + arg_name = "arg"; + arg_num = ¶m_num; + } + sprintf (pos, "%s ", type_name); + pos += strlen (pos); + type_name_len = strlen (type_name); + if (type_name_len + 1 < GB_PARAM_TYPE_WIDTH) + { + num_spaces = GB_PARAM_TYPE_WIDTH - type_name_len - 1; + for (i = 0; i < num_spaces; i++) + *pos++ = ' '; + } + + if (arg_names) + { + sprintf (pos, "%s%s,\n", is_pointer ? "*" : " ", + arg_names[param + 1]); + pos += strlen (pos); + } + else + { + if (!arg_num || *arg_num == 0) + sprintf (pos, "%s%s,\n", is_pointer ? "*" : " ", arg_name); + else + sprintf (pos, "%s%s%i,\n", is_pointer ? "*" : " ", arg_name, + *arg_num); + pos += strlen (pos); + + if (arg_num) + { + if (*arg_num == 0) + *arg_num = 2; + else + *arg_num += 1; + } + } + } + } + + if (arg_names) + g_strfreev (arg_names); + + /* Add the final user_data parameter, common to all handlers. */ + for (i = 0; i < GB_PARAM_INDENT; i++) + *pos++ = ' '; + sprintf (pos, "gpointer user_data)"); + pos += strlen (pos); + + /* Output the declaration of the handler, which uses the same buffer. */ + source_add_to_buffer (data, GLADE_CALLBACK_DECLARATIONS, + "\n%s;\n", buffer); + + /* Output the empty handler function, returning FALSE if the return type is + bool (i.e. for the GdkEvent handlers). */ + source_add_to_buffer (data, GLADE_CALLBACK_SOURCE, + "\n%s\n{\n\n%s}\n\n", + buffer, + query_info.return_type == GTK_TYPE_BOOL + ? " return FALSE;\n" : ""); + + g_free (signal_name_copy); +} + + +static void +gb_widget_write_signal_connection_source (GbWidgetWriteSourceData * data, + const gchar *signal_name, + const gchar *connect_object, + gboolean connect_after, + const gchar *handler_data, + const gchar *handler) +{ + if (connect_object && connect_object[0]) + { + if (connect_after) + { + source_add_to_buffer (data, GLADE_SIGNAL_CONNECTIONS, + " g_signal_connect_data ((gpointer) %s, \"%s\",\n" + " G_CALLBACK (%s),\n" + " GTK_OBJECT (%s),\n" + " NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);\n", + data->wname, signal_name, handler, + connect_object); + } + else + { + source_add_to_buffer (data, GLADE_SIGNAL_CONNECTIONS, + " g_signal_connect_swapped ((gpointer) %s, \"%s\",\n" + " G_CALLBACK (%s),\n" + " GTK_OBJECT (%s));\n", + data->wname, signal_name, handler, + connect_object); + } + } + else + { + if (connect_after) + { + source_add_to_buffer (data, GLADE_SIGNAL_CONNECTIONS, + " g_signal_connect_after ((gpointer) %s, \"%s\",\n" + " G_CALLBACK (%s),\n" + " %s);\n", + data->wname, signal_name, handler, + handler_data ? handler_data : "NULL"); + } + else + { + source_add_to_buffer (data, GLADE_SIGNAL_CONNECTIONS, + " g_signal_connect ((gpointer) %s, \"%s\",\n" + " G_CALLBACK (%s),\n" + " %s);\n", + data->wname, signal_name, handler, + handler_data ? handler_data : "NULL"); + } + } +} + + +/* Returns the type name to use for a signal argument or return value, given + the GtkType from the signal info. It also sets is_pointer to TRUE if the + argument needs a '*' since it is a pointer. */ +static gchar * +get_type_name (GtkType type, gboolean * is_pointer) +{ + gchar *type_name; + + *is_pointer = FALSE; + type_name = (char*) g_type_name (type); + + switch (type) { + case G_TYPE_NONE: + case G_TYPE_CHAR: + case G_TYPE_UCHAR: + case G_TYPE_BOOLEAN: + case G_TYPE_INT: + case G_TYPE_UINT: + case G_TYPE_LONG: + case G_TYPE_ULONG: + case G_TYPE_FLOAT: + case G_TYPE_DOUBLE: + case G_TYPE_POINTER: + /* These all have normal C type names so they are OK. */ + return type_name; + + case G_TYPE_STRING: + /* A GtkString is really a gchar*. */ + *is_pointer = TRUE; + return "gchar"; + + case G_TYPE_ENUM: + case G_TYPE_FLAGS: + /* We use a gint for both of these. Hopefully a subtype with a decent + name will be registered and used instead, as GTK+ does itself. */ + return "gint"; + + case G_TYPE_BOXED: + /* The boxed type shouldn't be used itself, only subtypes. Though we + return 'gpointer' just in case. */ + return "gpointer"; + + case G_TYPE_PARAM: + /* A GParam is really a GParamSpec*. */ + *is_pointer = TRUE; + return "GParamSpec"; + + default: + break; + } + + if (!strcmp (type_name, "GtkTypeTextIter")) + { + *is_pointer = TRUE; + return "GtkTextIter"; + } + + if (!strcmp (type_name, "GtkTypeTreeIter")) + { + *is_pointer = TRUE; + return "GtkTreeIter"; + } + + if (!strcmp (type_name, "GtkTypeTreePath")) + { + *is_pointer = TRUE; + return "GtkTreePath"; + } + + /* For all GtkObject subtypes we can use the class name with a "*", + e.g. 'GtkWidget *'. */ + if (g_type_is_a (type, G_TYPE_OBJECT)) + *is_pointer = TRUE; + + /* All boxed subtypes will be pointers as well. */ + if (g_type_is_a (type, G_TYPE_BOXED)) + *is_pointer = TRUE; + + /* All pointer subtypes will be pointers as well. */ + if (g_type_is_a (type, G_TYPE_POINTER)) + *is_pointer = TRUE; + + return type_name; +} + + +/* Returns the type name to use for the GdkEvent arg of a signal handler, + based on the signal name. This assumes that there will only be one GdkEvent + arg to the signal handler, but that is OK since for the signals we support + here we know that is true. If any new signals come along with more than one + GdkEvent arg, it will just use "GdkEvent" for all of them, which is still + OK. */ +static gchar * +get_gdk_event (gchar * signal_name) +{ + static gchar *GbGDKEvents[] = + { + "button_press_event", "GdkEventButton", + "button_release_event", "GdkEventButton", + "motion_notify_event", "GdkEventMotion", + "delete_event", "GdkEvent", + "destroy_event", "GdkEvent", + "expose_event", "GdkEventExpose", + "key_press_event", "GdkEventKey", + "key_release_event", "GdkEventKey", + "enter_notify_event", "GdkEventCrossing", + "leave_notify_event", "GdkEventCrossing", + "configure_event", "GdkEventConfigure", + "focus_in_event", "GdkEventFocus", + "focus_out_event", "GdkEventFocus", + "map_event", "GdkEvent", + "unmap_event", "GdkEvent", + "property_notify_event", "GdkEventProperty", + "selection_clear_event", "GdkEventSelection", + "selection_request_event", "GdkEventSelection", + "selection_notify_event", "GdkEventSelection", + "proximity_in_event", "GdkEventProximity", + "proximity_out_event", "GdkEventProximity", + "drag_begin_event", "GdkEventDragBegin", + "drag_request_event", "GdkEventDragRequest", + "drag_end_event", "GdkEventDragRequest", + "drop_enter_event", "GdkEventDropEnter", + "drop_leave_event", "GdkEventDropLeave", + "drop_data_available_event", "GdkEventDropDataAvailable", + "other_event", "GdkEventOther", + "client_event", "GdkEventClient", + "no_expose_event", "GdkEventNoExpose", + NULL + }; + + gint i; + + for (i = 0; GbGDKEvents[i]; i += 2) + { + if (!strcmp (signal_name, GbGDKEvents[i])) + return GbGDKEvents[i + 1]; + } + return "GdkEvent"; +} + + +static void +init_signal_hash (void) +{ + static const gchar *signal_data[][2] = { + /* + * GTK+ Signals. + */ + { "GtkAccelGroup::accel_activate", "accelgroup,object,key,modifier" }, + { "GtkAccelGroup::accel_changed", "accelgroup,key,modifier,closure" }, + { "GtkCellRenderer::editing_started", "cellrenderer,editable,path" }, + { "GtkCellRendererText::edited", "cellrenderertext,path,new_text" }, + { "GtkCellRendererToggle::toggled", "cellrenderertoggle,path" }, + { "GtkCList::click_column", "clist,column" }, + { "GtkCList::extend_selection", "clist,scroll_type,position,auto_start_selection" }, + { "GtkCList::resize_column", "clist,column,width" }, + { "GtkCList::row_move", "clist,source_row,dest_row" }, + { "GtkCList::scroll_horizontal", "clist,scroll_type,position" }, + { "GtkCList::scroll_vertical", "clist,scroll_type,position" }, + { "GtkCList::select_row", "clist,row,column,event" }, + { "GtkCList::set_scroll_adjustments", "clist,hadjustment,vadjustment" }, + { "GtkCList::unselect_row", "clist,row,column,event" }, + { "GtkCTree::change_focus_row_expansion", "ctree,expansion" }, + { "GtkCTree::tree_collapse", "ctree,node" }, + { "GtkCTree::tree_expand", "ctree,node" }, + { "GtkCTree::tree_move", "ctree,node,new_parent,new_sibling" }, + { "GtkCTree::tree_select_row", "ctree,node,column" }, + { "GtkCTree::tree_unselect_row", "ctree,node,column" }, + { "GtkDialog::response", "dialog,response_id" }, + { "GtkEditable::delete_text", "editable,start_pos,end_pos" }, + { "GtkEditable::insert_text", "editable,new_text,new_text_length,position" }, + { "GtkEntry::delete_from_cursor", "entry,type,count" }, + { "GtkEntry::insert_at_cursor", "entry,string" }, + { "GtkEntry::move_cursor", "entry,step,count,extend_selection" }, + { "GtkEntry::populate_popup", "entry,menu" }, + { "GtkIconView::item_activated", "iconview,path" }, + { "GtkIconView::move_cursor", "iconview,step,count" }, + { "GtkIconView::set_scroll_adjustments", "iconview,hadjustment,vadjustment" }, + { "GtkInputDialog::disable_device", "inputdialog,device" }, + { "GtkInputDialog::enable_device", "inputdialog,device" }, + { "GtkIMContext::commit", "imcontext,string" }, + { "GtkIMContext::delete_surrounding", "imcontext,offset,n_chars" }, + { "GtkLabel::move_cursor", "label,step,count,extend_selection" }, + { "GtkLabel::populate_popup", "label,menu" }, + { "GtkLayout::set_scroll_adjustments", "layout,hadjustment,vadjustment" }, + { "GtkListItem::extend_selection", "listitem,scroll_type,position,auto_start_selection" }, + { "GtkListItem::scroll_horizontal", "listitem,scroll_type,position" }, + { "GtkListItem::scroll_vertical", "listitem,scroll_type,position" }, + { "GtkMenuItem::toggle_size_allocate", "menuitem,allocation" }, + { "GtkMenuItem::toggle_size_request", "menuitem,gint *requisition" }, + { "GtkMenuShell::activate_current", "menushell,force_hide" }, + { "GtkMenuShell::cycle_focus", "menushell,direction" }, + { "GtkMenuShell::move_current", "menushell,direction" }, + { "GtkNotebook::change_current_page", "notebook,offset" }, + { "GtkNotebook::focus_tab", "notebook,type" }, + { "GtkNotebook::move_focus_out", "notebook,direction" }, + { "GtkNotebook::select_page", "notebook,move_focus" }, + { "GtkNotebook::switch_page", "notebook,GtkNotebookPage *page,page_num" }, + { "GtkOldEditable::kill_char", "oldeditable,direction" }, + { "GtkOldEditable::kill_line", "oldeditable,direction" }, + { "GtkOldEditable::kill_word", "oldeditable,direction" }, + { "GtkOldEditable::move_cursor", "oldeditable,x,y" }, + { "GtkOldEditable::move_page", "oldeditable,x,y" }, + { "GtkOldEditable::move_to_column", "oldeditable,column" }, + { "GtkOldEditable::move_to_row", "oldeditable,row" }, + { "GtkOldEditable::move_word", "oldeditable,offset" }, + { "GtkOldEditable::set_editable", "oldeditable,is_editable" }, + { "GtkPaned::cycle_child_focus", "paned,reverse" }, + { "GtkPaned::cycle_handle_focus", "paned,reverse" }, + { "GtkPaned::move_handle", "paned,scroll" }, + { "GtkRange::adjust_bounds", "range,value" }, + { "GtkRange::change_value", "range,scroll,value" }, + { "GtkRange::move_slider", "range,scroll" }, + { "GtkScale::format_value", "scale,value" }, + { "GtkScrolledWindow::move_focus_out", "scrolledwindow,direction" }, + { "GtkScrolledWindow::scroll_child", "scrolledwindow,scroll,horizontal" }, + { "GtkSpinButton::change_value", "spinbutton,scroll" }, + { "GtkSpinButton::input", "spinbutton,gdouble *new_value" }, + { "GtkStatusbar::text_popped", "statusbar,context_id,text" }, + { "GtkStatusbar::text_pushed", "statusbar,context_id,text" }, + { "GtkTextBuffer::apply_tag", "textbuffer,tag,start,end" }, + { "GtkTextBuffer::delete_range", "textbuffer,start,end" }, + { "GtkTextBuffer::insert_child_anchor", "textbuffer,iter,anchor" }, + { "GtkTextBuffer::insert_pixbuf", "textbuffer,iter,pixbuf" }, + { "GtkTextBuffer::insert_text", "textbuffer,iter,text,length" }, + { "GtkTextBuffer::mark_deleted", "textbuffer,mark" }, + { "GtkTextBuffer::mark_set", "textbuffer,iter,mark" }, + { "GtkTextBuffer::remove_tag", "textbuffer,tag,start,end" }, + { "GtkText::set_scroll_adjustments", "text,hadjustment,vadjustment" }, + { "GtkTextTag::event", "texttag,object,event,iter" }, + { "GtkTextTagTable::tag_added", "texttagtable,tag" }, + { "GtkTextTagTable::tag_changed", "texttagtable,tag,size_changed" }, + { "GtkTextTagTable::tag_removed", "texttagtable,tag" }, + { "GtkTextView::delete_from_cursor", "textview,type,count" }, + { "GtkTextView::insert_at_cursor", "textview,string" }, + { "GtkTextView::move_cursor", "textview,step,count,extend_selection" }, + { "GtkTextView::move_focus", "textview,direction" }, + { "GtkTextView::page_horizontally", "textview,count,extend_selection" }, + { "GtkTextView::populate_popup", "textview,menu" }, + { "GtkTextView::set_scroll_adjustments", "textview,hadjustment,vadjustment" }, + { "GtkTipsQuery::widget_entered", "tipsquery,widget,tip_text,tip_private" }, + { "GtkTipsQuery::widget_selected", "tipsquery,widget,tip_text,tip_private,event" }, + { "GtkToolbar::orientation_changed", "toolbar,orientation" }, + { "GtkToolbar::style_changed", "toolbar,style" }, + { "GtkTreeModel::row_changed", "treemodel,path,iter" }, + { "GtkTreeModel::row_deleted", "treemodel,path" }, + { "GtkTreeModel::row_has_child_toggled", "treemodel,path,iter" }, + { "GtkTreeModel::row_inserted", "treemodel,path,iter" }, + { "GtkTreeModel::rows_reordered", "treemodel,path,iter,gint *new_order" }, + { "GtkTreeView::expand_collapse_cursor_row", "treeview,logical,expand,open_all" }, + { "GtkTreeView::move_cursor", "treeview,step,count" }, + { "GtkTreeView::row_activated", "treeview,path,column" }, + { "GtkTreeView::row_collapsed", "treeview,iter,path" }, + { "GtkTreeView::row_expanded", "treeview,iter,path" }, + { "GtkTreeView::select_cursor_row", "treeview,start_editing" }, + { "GtkTreeView::set_scroll_adjustments", "treeview,hadjustment,vadjustment" }, + { "GtkTreeView::test_collapse_row", "treeview,iter,path" }, + { "GtkTreeView::test_expand_row", "treeview,iter,path" }, + { "GtkViewport::set_scroll_adjustments", "viewport,hadjustment,vadjustment" }, + { "GtkWidget::child_notify", "widget,pspec" }, + { "GtkWidget::direction_changed", "widget,old_direction" }, + { "GtkWidget::drag_begin", "widget,drag_context" }, + { "GtkWidget::drag_data_delete", "widget,drag_context" }, + { "GtkWidget::drag_data_get", "widget,drag_context,data,info,time" }, + { "GtkWidget::drag_data_received", "widget,drag_context,x,y,data,info,time" }, + { "GtkWidget::drag_drop", "widget,drag_context,x,y,time" }, + { "GtkWidget::drag_end", "widget,drag_context" }, + { "GtkWidget::drag_leave", "widget,drag_context,time" }, + { "GtkWidget::drag_motion", "widget,drag_context,x,y,time" }, + { "GtkWidget::focus", "widget,direction" }, + { "GtkWidget::grab_notify", "widget,was_grabbed" }, + { "GtkWidget::hierarchy_changed", "widget,previous_toplevel" }, + { "GtkWidget::mnemonic_activate", "widget,group_cycling" }, + { "GtkWidget::parent_set", "widget,old_parent" }, + { "GtkWidget::selection_get", "widget,data,info,time" }, + { "GtkWidget::selection_received", "widget,data,time" }, + { "GtkWidget::show_help", "widget,help_type" }, + { "GtkWidget::size_allocate", "widget,allocation" }, + { "GtkWidget::size_request", "widget,requisition" }, + { "GtkWidget::state_changed", "widget,state" }, + { "GtkWidget::style_set", "widget,previous_style" }, + { "GtkWindow::move_focus", "window,direction" }, + + /* + * libgnomeui Signals. + */ + { "GnomeClient::connect", "client,restarted" }, + { "GnomeClient::save_yourself", "client,phase,save_style,shutdown,interact_style,fast" }, + { "GnomeColorPicker::color_set", "colorpicker,red,green,blue,alpha" }, + { "GnomeDialog::clicked", "dialog,button_number" }, + { "GnomeFontPicker::font_set", "fontpicker,font_name" }, + { "GnomeIconList::focus_icon", "iconlist,num" }, + { "GnomeIconList::move_cursor", "iconlist,direction,clear_selection" }, + { "GnomeIconList::select_icon", "iconlist,num,event" }, + { "GnomeIconList::text_changed", "iconlist,num,new_text" }, + { "GnomeIconList::unselect_icon", "iconlist,num,event" }, + { "GnomeMDI::add_child", "mdi,child" }, + { "GnomeMDI::app_created", "mdi,app" }, + { "GnomeMDI::child_changed", "mdi,child" }, + { "GnomeMDI::remove_child", "mdi,child" }, + { "GnomePropertyBox::apply", "propertybox,page_num" }, + { "GnomePropertyBox::help", "propertybox,page_num" }, + + /* + * libgnomedb Signals. + */ + { "GnomeDbBrowser::progress_message", "browser,message" }, + { "GnomeDbDsnConfigDruid::finished", "druid,error" }, + { "GnomeDbForm::model_changed", "form" }, + { "GnomeDbGrid::row_selected", "grid,row" }, + { "GnomeDbGrid::selection_cleared", "grid" }, + { "GnomeDbList::row_selected", "list,row" }, + { "GnomeDbList::selection_cleared", "list" }, + + { NULL, NULL } + }; + + gint i; + + glade_signal_hash = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; signal_data[i][0]; i++) + { + g_hash_table_insert (glade_signal_hash, (char*) signal_data[i][0], + (char*) signal_data[i][1]); + } +} + + +/* This returns argument names to use for some known signals. + The returned array must be freed with g_str_freev(). */ +static gchar ** +lookup_signal_arg_names (gchar * type, gchar * signal_name, + gint num_args_expected) +{ + char *signal_id, *arg_names; + char **arg_array = NULL; + + if (glade_signal_hash == NULL) + init_signal_hash (); + + signal_id = g_strdup_printf ("%s::%s", type, signal_name); + arg_names = g_hash_table_lookup (glade_signal_hash, signal_id); +#if 0 + g_print ("Found signal: %s args: %s\n", signal_id, arg_names); +#endif + + if (arg_names) + { + gint num_args = 0; + + arg_array = g_strsplit (arg_names, ",", -1); + + while (arg_array[num_args]) + num_args++; + + /* Check we got the expected number of parameters. If not, output a + warning and return NULL. */ + if (num_args != num_args_expected) + { + g_warning ("Internal error: argument names invalid for signal: %s." + "Expected %i arguments. Found %i", + signal_id, num_args_expected, num_args); + g_strfreev (arg_array); + arg_array = NULL; + } + } + + g_free (signal_id); + + return arg_array; +} + + +static void +gb_widget_write_accelerators_source (GtkWidget * widget, + GbWidgetWriteSourceData * data) +{ + GList *item = data->widget_data->accelerators; + GladeAccelerator *accel; + + while (item) + { + accel = (GladeAccelerator *) item->data; + item = item->next; + + /* The code to create the accel_group is output in source.c */ + data->need_accel_group = TRUE; + source_add (data, + " gtk_widget_add_accelerator (%s, \"%s\", accel_group,\n" + " GDK_%s, (GdkModifierType) %s,\n" + " GTK_ACCEL_VISIBLE);\n", + data->wname, accel->signal, accel->key, + glade_util_create_modifiers_string (accel->modifiers)); + } +} + |