diff options
Diffstat (limited to 'tools/glade/glade/source.c')
-rw-r--r-- | tools/glade/glade/source.c | 2818 |
1 files changed, 2818 insertions, 0 deletions
diff --git a/tools/glade/glade/source.c b/tools/glade/glade/source.c new file mode 100644 index 00000000..af2a05c4 --- /dev/null +++ b/tools/glade/glade/source.c @@ -0,0 +1,2818 @@ + +/* 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 <sys/types.h> +#include <sys/stat.h> +#include <stdarg.h> +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <errno.h> +#include <ctype.h> +#include <locale.h> + +#include <gtk/gtkmenu.h> + +#include "gladeconfig.h" + +#include "gbwidget.h" +#include "glade_project.h" +#include "source.h" +#include "utils.h" +#ifdef HAVE_OS2_H +#include "source_os2.h" +#endif + +/* Turn this on to add -DG_DISABLE_DEPRECATED etc. flags in generated code, + so we can check if Glade is generating any deprecated code. */ +#if 0 +#define GLADE_ADD_DISABLE_DEPRECATED_FLAGS +#endif + +/* An internal struct to pass data to the source_write_component() callback. */ +typedef struct _GladeSourceCallbackData GladeSourceCallbackData; +struct _GladeSourceCallbackData +{ + GbWidgetWriteSourceData *write_source_data; + FILE *interface_h_fp; + FILE *interface_c_fp; + FILE *callback_h_fp; + FILE *callback_c_fp; +}; + + +static GladeError* source_write_internal (GladeProject * project); + +static void source_write_interface_and_callbacks (GbWidgetWriteSourceData * data); +static void source_begin_interface_and_callbacks_files (GbWidgetWriteSourceData * data, + FILE **interface_h_fp, + FILE **interface_c_fp, + FILE **callback_h_fp, + FILE **callback_c_fp); +static void source_output_interface_and_callbacks_source (GbWidgetWriteSourceData * data, + FILE *interface_h_fp, + FILE *interface_c_fp, + FILE *callback_h_fp, + FILE *callback_c_fp); +static void source_write_component (GtkWidget * component, + GladeSourceCallbackData * source_data); + +static void source_write_interface_h_preamble (GbWidgetWriteSourceData * data, + FILE *fp); +static void source_write_interface_c_preamble (GbWidgetWriteSourceData * data, + FILE *fp); +static void source_write_callback_h_preamble (GbWidgetWriteSourceData * data, + FILE *fp); +static void source_write_callback_c_preamble (GbWidgetWriteSourceData * data, + FILE *fp); +static void source_write_preamble (gchar *project_name, + FILE * fp); + +static void source_write_main_c (GbWidgetWriteSourceData * data); +static void source_write_component_create (GtkWidget * component, + GbWidgetWriteSourceData * data); + +static void source_write_build_files (GbWidgetWriteSourceData * data); +static void source_write_autogen_sh (GbWidgetWriteSourceData * data); + +static void source_write_gtk_build_files (GbWidgetWriteSourceData * data); +static void source_write_gtk_configure_in (GbWidgetWriteSourceData * data); +static void source_write_gtk_makefile_am (GbWidgetWriteSourceData * data); +static void source_write_gtk_makefile_am_pixmaps_targets (GbWidgetWriteSourceData * data, + gchar * directory, + FILE * fp); + +static void source_write_gnome_build_files (GbWidgetWriteSourceData * data); +static void source_write_gnome_configure_in (GbWidgetWriteSourceData * data); +static void source_write_gnome_makefile_am (GbWidgetWriteSourceData * data); +static void source_write_gnome_makefile_am_pixmaps_targets (GbWidgetWriteSourceData * data, + gchar * directory, + FILE * fp); + +static void source_write_common_build_files (GbWidgetWriteSourceData * data); +static void source_write_toplevel_makefile_am (GbWidgetWriteSourceData * data); +static void source_write_extra_dist (GbWidgetWriteSourceData * data, + const gchar *directory, + FILE *fp); +static GladeError* source_create_file_if_not_exist (const gchar *directory, + const gchar *filename, + const gchar *contents); +static void source_write_po_files (GbWidgetWriteSourceData * data); +#if 0 +static void source_write_acconfig_h (GbWidgetWriteSourceData * data); +#endif +static void source_write_support_files (GbWidgetWriteSourceData * data); + +static void source_write_gtk_create_pixmap_functions (GbWidgetWriteSourceData * data, + FILE *fp); +static void source_write_gnome_create_pixmap_functions (GbWidgetWriteSourceData * data, + FILE *fp); + +static gchar* source_is_valid_source_filename (const gchar * filename); +static GladeError* source_backup_file_if_exists (const gchar * filename); + +static gchar * source_make_string_internal (const gchar * text, + gboolean translatable, + gboolean is_static, + gboolean context); +static void source_reset_code_buffers (GbWidgetWriteSourceData * data); +static void source_destroy_standard_widgets_callback (gchar * key, + GtkWidget * widget, + gpointer data); +static void source_free_hash_keys_callback (gchar * key, + gpointer value, + gpointer data); +static gchar* source_get_source_subdirectory (GbWidgetWriteSourceData * data); +static void source_write_no_editing_warning (FILE *fp); +static void source_write_include_files (FILE *fp); + + +/* We need this so that numbers are written in C syntax rather than the + current locale, which may use ',' instead of '.' and then the code + will not compile. This code is from glibc info docs. */ +GladeError* +source_write (GladeProject *project) +{ + gchar *old_locale, *saved_locale; + GladeError *error; + + old_locale = setlocale (LC_NUMERIC, NULL); + saved_locale = g_strdup (old_locale); + setlocale (LC_NUMERIC, "C"); + error = source_write_internal (project); + setlocale (LC_NUMERIC, saved_locale); + g_free (saved_locale); + return error; +} + + +static GladeError* +source_write_internal (GladeProject * project) +{ + GbWidgetWriteSourceData data; + gchar *source_directory, *interface_source_file, *interface_header_file; + gchar *callback_source_file, *callback_header_file, *msg; + gchar *support_source_file, *support_header_file; + gint i; + + source_directory = glade_project_get_source_directory (project); + glade_project_get_source_files (project, + &interface_source_file, + &interface_header_file, + &callback_source_file, + &callback_header_file); + + /* Check that any source filenames that we are going to use are valid. */ + if ((msg = source_is_valid_source_filename (interface_source_file))) + return glade_error_new_general (GLADE_STATUS_ERROR, _("Invalid interface source filename: %s\n%s\n"), interface_source_file, msg); + if ((msg = source_is_valid_source_filename (interface_header_file))) + return glade_error_new_general (GLADE_STATUS_ERROR, _("Invalid interface header filename: %s\n%s\n"), interface_header_file, msg); + + if ((msg = source_is_valid_source_filename (callback_source_file))) + return glade_error_new_general (GLADE_STATUS_ERROR, _("Invalid callbacks source filename: %s\n%s\n"), callback_source_file, msg); + if ((msg = source_is_valid_source_filename (callback_header_file))) + return glade_error_new_general (GLADE_STATUS_ERROR, _("Invalid callbacks header filename: %s\n%s\n"), callback_source_file, msg); + + if (glade_project_get_output_support_files (project)) { + support_source_file = glade_project_get_support_source_file (project); + support_header_file = glade_project_get_support_header_file (project); + if ((msg = source_is_valid_source_filename (support_source_file))) + return glade_error_new_general (GLADE_STATUS_ERROR, _("Invalid support source filename: %s\n%s\n"), support_source_file, msg); + if ((msg = source_is_valid_source_filename (support_header_file))) + return glade_error_new_general (GLADE_STATUS_ERROR, _("Invalid support header filename: %s\n%s\n"), support_header_file, msg); + } + + /* Initialize the GbWidgetWriteSourceData fields. + Note that the error is set to NULL. If anything sets it we know there is + an error. */ + data.project = project; + data.error = NULL; + data.project_name = glade_project_get_name (project); + data.program_name = glade_project_get_program_name (project); + data.interface_c_filename = glade_util_make_absolute_path (source_directory, + interface_source_file); + data.interface_h_filename = glade_util_make_absolute_path (source_directory, + interface_header_file); + data.callback_c_filename = glade_util_make_absolute_path (source_directory, + callback_source_file); + data.callback_h_filename = glade_util_make_absolute_path (source_directory, + callback_header_file); + data.set_widget_names = glade_project_get_use_widget_names (project); + data.use_gettext = glade_project_get_gettext_support (project); + data.use_component_struct = FALSE; + data.creating_callback_files = FALSE; + data.standard_widgets = g_hash_table_new (g_str_hash, g_str_equal); + data.handlers_output = g_hash_table_new (g_str_hash, g_str_equal); + + /* Create the empty source code buffers. */ + for (i = 0; i < GLADE_NUM_SOURCE_BUFFERS; i++) + data.source_buffers[i] = g_string_sized_new (1024); + + /* If the callback.c file doesn't exists, we need to output all signal + handlers & callbacks. If it exists, we only write handlers & callbacks + that have been added/changed since the last time the interface.c file + was written. */ + if (!glade_util_file_exists (data.callback_c_filename)) + { + data.creating_callback_files = TRUE; + } + else + { + data.error = glade_util_file_last_mod_time (data.interface_c_filename, + &data.last_write_time); + + /* If the interface.c file doesn't exist, we have no way of knowing + which callbacks need to be output, so we set the last_write_time + to the current time, i.e. we don't output any callbacks. */ + if (data.error && data.error->status == GLADE_STATUS_SYSTEM_ERROR + && data.error->system_errno == ENOENT) + { + data.last_write_time = time (NULL); + glade_error_free (data.error); + data.error = NULL; + } + } + + /* Make sure the project & source directories exist. */ + if (!data.error) + data.error = glade_util_ensure_directory_exists (glade_project_get_directory (data.project)); + + if (!data.error) + data.error = glade_util_ensure_directory_exists (glade_project_get_source_directory (data.project)); + + /* Now call the main functions to write the source code and support files. */ + if (!data.error) + source_write_build_files (&data); + if (!data.error) + source_write_support_files (&data); + if (!data.error) + source_write_main_c (&data); + if (!data.error) + source_write_interface_and_callbacks (&data); + + /* Now free everything. */ + for (i = 0; i < GLADE_NUM_SOURCE_BUFFERS; i++) + g_string_free (data.source_buffers[i], TRUE); + + g_hash_table_foreach (data.standard_widgets, + (GHFunc) source_destroy_standard_widgets_callback, + NULL); + g_hash_table_destroy (data.standard_widgets); + + g_hash_table_foreach (data.handlers_output, + (GHFunc) source_free_hash_keys_callback, NULL); + g_hash_table_destroy (data.handlers_output); + + g_free (data.interface_c_filename); + g_free (data.interface_h_filename); + g_free (data.callback_c_filename); + g_free (data.callback_h_filename); + + return data.error; +} + + +/************************************************************************* + * Main source files - interface.[hc] & callbacks.[hc] + *************************************************************************/ + +static void +source_write_interface_and_callbacks (GbWidgetWriteSourceData * data) +{ + FILE *interface_h_fp = NULL, *interface_c_fp = NULL; + FILE *callback_h_fp = NULL, *callback_c_fp = NULL; + + /* Backup the two main files, if the backup option is selected. + We don't backup the signals files since we only ever append to them. */ + if (glade_project_get_backup_source_files (data->project)) + { + data->error = source_backup_file_if_exists (data->interface_c_filename); + if (data->error) + return; + + data->error = source_backup_file_if_exists (data->interface_h_filename); + if (data->error) + return; + } + + /* Open all the files and add the standard license and #include stuff. + Note that if an error occurs, data->error is set, and we simply drop + through to close any files which were opened below. */ + source_begin_interface_and_callbacks_files (data, + &interface_h_fp, + &interface_c_fp, + &callback_h_fp, + &callback_c_fp); + + if (!data->error) + { + source_output_interface_and_callbacks_source (data, + interface_h_fp, + interface_c_fp, + callback_h_fp, + callback_c_fp); + } + + /* Now close any files which were opened. */ + if (interface_h_fp) + fclose (interface_h_fp); + if (interface_c_fp) + fclose (interface_c_fp); + if (callback_h_fp) + fclose (callback_h_fp); + if (callback_c_fp) + fclose (callback_c_fp); +} + + +/* Creates the interface.h & interface.c files, and created callbacks.h + & callback.c or opens them for appending as appropriate. */ +static void +source_begin_interface_and_callbacks_files (GbWidgetWriteSourceData * data, + FILE **interface_h_fp, + FILE **interface_c_fp, + FILE **callback_h_fp, + FILE **callback_c_fp) +{ + /* Create the interface.h file and output the standard license. */ + *interface_h_fp = glade_util_fopen (data->interface_h_filename, "w"); + if (*interface_h_fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + data->interface_h_filename); + return; + } + + source_write_interface_h_preamble (data, *interface_h_fp); + if (data->error) + return; + + + /* Create the interface.c file and output the standard license and #include + lines. */ + *interface_c_fp = glade_util_fopen (data->interface_c_filename, "w"); + if (*interface_c_fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + data->interface_c_filename); + return; + } + + source_write_interface_c_preamble (data, *interface_c_fp); + if (data->error) + return; + + + /* If the callback.[hc] files are being created from scratch, create them + and and output the standard license and #include stuff, else just open + them for appending. */ + if (data->creating_callback_files) + { + *callback_h_fp = glade_util_fopen (data->callback_h_filename, "w"); + if (*callback_h_fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + data->callback_h_filename); + return; + } + + source_write_callback_h_preamble (data, *callback_h_fp); + if (data->error) + return; + + + *callback_c_fp = glade_util_fopen (data->callback_c_filename, "w"); + if (*callback_c_fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + data->callback_c_filename); + return; + } + + source_write_callback_c_preamble (data, *callback_c_fp); + if (data->error) + return; + } + else + { + *callback_h_fp = glade_util_fopen (data->callback_h_filename, "a"); + if (*callback_h_fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't append to file:\n %s\n"), + data->callback_h_filename); + return; + } + + *callback_c_fp = glade_util_fopen (data->callback_c_filename, "a"); + if (*callback_c_fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't append to file:\n %s\n"), + data->callback_c_filename); + return; + } + } +} + + +static void +source_output_interface_and_callbacks_source (GbWidgetWriteSourceData * data, + FILE *interface_h_fp, + FILE *interface_c_fp, + FILE *callback_h_fp, + FILE *callback_c_fp) +{ + GladeSourceCallbackData source_data; + + /* This outputs the code to create the components and the signal handler + prototypes. */ + source_data.write_source_data = data; + source_data.interface_h_fp = interface_h_fp; + source_data.interface_c_fp = interface_c_fp; + source_data.callback_h_fp = callback_h_fp; + source_data.callback_c_fp = callback_c_fp; + + /* Iterate through the project components outputting the source code and + declarations. */ + glade_project_foreach_component (data->project, + (GtkCallback) source_write_component, + &source_data); +} + + +/* This outputs the source code for one component (a window, dialog or popup + menu). */ +static void +source_write_component (GtkWidget * component, + GladeSourceCallbackData * source_data) +{ + GbWidgetWriteSourceData * data; + FILE *interface_h_fp, *interface_c_fp; + FILE *callback_h_fp, *callback_c_fp; + + /* Get the data out of the callback data struct. */ + data = source_data->write_source_data; + interface_h_fp = source_data->interface_h_fp; + interface_c_fp = source_data->interface_c_fp; + callback_h_fp = source_data->callback_h_fp; + callback_c_fp = source_data->callback_c_fp; + + /* Reset the GbWidgetWriteSourceData, ready to begin a new component. */ + data->component = component; + data->component_name = source_create_valid_identifier (gtk_widget_get_name (component)); + data->parent = NULL; + data->need_tooltips = FALSE; + data->need_accel_group = FALSE; + data->create_widget = TRUE; + data->write_children = TRUE; + data->focus_widget = NULL; + data->default_widget = NULL; + + /* Clear all the code from the previous component. */ + source_reset_code_buffers (data); + + /* Recursively write the source for all the widgets in the component. */ + gb_widget_write_source (component, data); + + /* + * Output interface.h + */ + + /* Output the declaration of the function to create the component in the + header file. */ + fprintf (interface_h_fp, "GtkWidget* create_%s (void);\n", + data->component_name); + + + /* + * Output interface.c + */ + + /* Output any GnomeUIInfo structs. */ + fprintf (interface_c_fp, "%s", data->source_buffers[GLADE_UIINFO]->str); + + fprintf (interface_c_fp, + "GtkWidget*\n" + "create_%s (void)\n" + "{\n", + data->component_name); + + /* Output the declarations of all the widgets and any temporary variables + needed at the start of the function. */ + fprintf (interface_c_fp, "%s", + data->source_buffers[GLADE_DECLARATIONS]->str); + + /* Output a declaration of accel_group and tooltips if they are needed by + the component. */ + if (data->need_accel_group) + fprintf (interface_c_fp, " GtkAccelGroup *accel_group;\n"); +// if (data->need_tooltips) +// fprintf (interface_c_fp, " GtkTooltips *tooltips;\n"); + + /* Output a blank line between the declarations and the source. */ + fprintf (interface_c_fp, "\n"); + + /* Create the tooltips object if needed. */ +// if (data->need_tooltips) +// fprintf (interface_c_fp, " tooltips = gtk_tooltips_new ();\n\n"); + + /* Create the accel group if needed. */ + if (data->need_accel_group) + fprintf (interface_c_fp, " accel_group = gtk_accel_group_new ();\n\n"); + + /* Output the source code to create the widgets in the component. */ + fprintf (interface_c_fp, "%s", data->source_buffers[GLADE_SOURCE]->str); + + /* Output the source code to connect the signal handlers. */ + if (data->source_buffers[GLADE_SIGNAL_CONNECTIONS]->len > 0) + fprintf (interface_c_fp, "%s\n", + data->source_buffers[GLADE_SIGNAL_CONNECTIONS]->str); + + /* Output the source code to setup the accelerator keys. */ + if (data->source_buffers[GLADE_ACCELERATORS]->len > 0) + fprintf (interface_c_fp, "%s\n", + data->source_buffers[GLADE_ACCELERATORS]->str); + + /* Output the source code to set ATK properties. */ + if (data->source_buffers[GLADE_ATK_SOURCE]->len > 0) + fprintf (interface_c_fp, "%s\n", + data->source_buffers[GLADE_ATK_SOURCE]->str); + + /* Output the source code to set the pointers to the widgets. */ + if (data->source_buffers[GLADE_OBJECT_HOOKUP]->len > 0) + { + fprintf (interface_c_fp, + " /* Store pointers to all widgets, for use by lookup_widget(). */\n"); + fprintf (interface_c_fp, "%s", + data->source_buffers[GLADE_OBJECT_HOOKUP]->str); + } + + /* Store a pointer to the tooltips object, if we used one, so that it can + be accessed in callbacks. */ +// if (data->need_tooltips) +// { +// fprintf (interface_c_fp, +// " GLADE_HOOKUP_OBJECT_NO_REF (%s, tooltips, \"tooltips\");\n", +// data->component_name); +// } + + fprintf (interface_c_fp, "\n"); + + /* Set the focus widget, if there is one. */ + if (data->focus_widget) + { + fprintf (interface_c_fp, " gtk_widget_grab_focus (%s);\n", + data->focus_widget); + g_free (data->focus_widget); + data->focus_widget = NULL; + } + + /* Set the default widget, if there is one. */ + if (data->default_widget) + { + fprintf (interface_c_fp, " gtk_widget_grab_default (%s);\n", + data->default_widget); + g_free (data->default_widget); + data->default_widget = NULL; + } + + /* Add the accel group to the component. */ + if (data->need_accel_group) + { + if (GTK_IS_MENU (data->component)) + fprintf (interface_c_fp, + " gtk_menu_set_accel_group (GTK_MENU (%s), accel_group);\n\n", + data->component_name); + else + fprintf (interface_c_fp, + " gtk_window_add_accel_group (GTK_WINDOW (%s), accel_group);\n\n", + data->component_name); + } + + /* Return the toplevel widget and finish the function. */ + fprintf (interface_c_fp, " return %s;\n}\n\n", data->component_name); + + /* + * Output callbacks.h + */ + + /* Output the signal handler declarations. */ + fprintf (callback_h_fp, "%s", + data->source_buffers[GLADE_CALLBACK_DECLARATIONS]->str); + + /* + * Output callbacks.c + */ + + /* Output the signal handler functions. */ + fprintf (callback_c_fp, "%s", + data->source_buffers[GLADE_CALLBACK_SOURCE]->str); + + + g_free (data->component_name); + data->component_name = NULL; +} + + +/* Outputs the license and and other code needed at the top of the interface.h + header file, before any function declarations are output. */ +static void +source_write_interface_h_preamble (GbWidgetWriteSourceData * data, FILE *fp) +{ + source_write_no_editing_warning (fp); + source_write_preamble (data->project_name, fp); +} + +static void +source_write_include_files (FILE *fp) +{ + fprintf (fp, + "#ifdef HAVE_CONFIG_H\n" + "# include <config.h>\n" + "#endif\n" + "\n" + "#include <sys/types.h>\n" + "#include <sys/stat.h>\n" + "#include <unistd.h>\n" + "#include <string.h>\n" + "#include <stdio.h>\n" + "\n"); +} + +/* Outputs the license and any include files needed at the top of the + interface.c source file, before code for the components is output. */ +static void +source_write_interface_c_preamble (GbWidgetWriteSourceData * data, FILE *fp) +{ + source_write_no_editing_warning (fp); + source_write_preamble (data->project_name, fp); + + source_write_include_files (fp); + + if (glade_project_get_gnome_support (data->project)) + { + fprintf (fp, "#include <bonobo.h>\n"); + fprintf (fp, "#include <gnome.h>\n"); + + if (glade_project_get_gnome_db_support (data->project)) + { + fprintf (fp, "#include <libgnomedb/libgnomedb.h>\n"); + } + } + else + { + fprintf (fp, + "#include <gdk/gdkkeysyms.h>\n" + "#include <gtk/gtk.h>\n"); + } + + fprintf (fp, + "\n" + "#include \"%s\"\n" + "#include \"%s\"\n" + "#include \"%s\"\n\n", + g_basename (data->callback_h_filename), + g_basename (data->interface_h_filename), + glade_project_get_support_header_file (data->project)); + + fprintf (fp, + "#define GLADE_HOOKUP_OBJECT(component,widget,name) \\\n" + " g_object_set_data_full (G_OBJECT (component), name, \\\n" + " g_object_ref(G_OBJECT(widget)), (GDestroyNotify) g_object_unref)\n\n"); + + fprintf (fp, + "#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \\\n" + " g_object_set_data (G_OBJECT (component), name, widget)\n\n"); +} + + +/* Outputs the license and and other code needed at the top of the callback.h + header file, before any signal handler and callback function declarations + are output. */ +static void +source_write_callback_h_preamble (GbWidgetWriteSourceData * data, FILE *fp) +{ + source_write_preamble (data->project_name, fp); + + if (glade_project_get_gnome_support (data->project)) + fprintf (fp, "#include <gnome.h>\n\n"); + else + fprintf (fp, "#include <gtk/gtk.h>\n\n"); + + if (glade_project_get_gnome_db_support (data->project)) + fprintf (fp, "#include <libgnomedb/libgnomedb.h>\n"); +} + + +/* Outputs the license and any include files needed at the top of the + callback.c source file, before code for the signal handlers and callback + functions is output. */ +static void +source_write_callback_c_preamble (GbWidgetWriteSourceData * data, FILE *fp) +{ + source_write_preamble (data->project_name, fp); + + fprintf (fp, + "#ifdef HAVE_CONFIG_H\n" + "# include <config.h>\n" + "#endif\n" + "\n"); + + if (glade_project_get_gnome_support (data->project)) + fprintf (fp, "#include <gnome.h>\n"); + else + fprintf (fp, "#include <gtk/gtk.h>\n"); + + fprintf (fp, + "\n" + "#include \"%s\"\n" + "#include \"%s\"\n" + "#include \"%s\"\n\n", + g_basename (data->callback_h_filename), + g_basename (data->interface_h_filename), + glade_project_get_support_header_file (data->project)); +} + + +/* Output a license at the top of a source or header file. + Note this will eventually be editable in the user interface, with an option + to include a few standard licenses, e.g. GPL, which are then edited by the + user. FIXME: I've taken this out until we support it fully. */ +static void +source_write_preamble (gchar *project_name, FILE * fp) +{ +#if 0 + fprintf (fp, + "/* Note: You are free to use whatever license you want.\n" + " Eventually you will be able to edit it within Glade. */\n" + "\n" + "/* %s\n" + " * Copyright (C) <YEAR> <AUTHORS>\n" + " *\n" + " * This program is free software; you can redistribute it and/or modify\n" + " * it under the terms of the GNU General Public License as published by\n" + " * the Free Software Foundation; either version 2 of the License, or\n" + " * (at your option) any later version.\n" + " *\n" + " * This program is distributed in the hope that it will be useful,\n" + " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + " * GNU General Public License for more details.\n" + " *\n" + " * You should have received a copy of the GNU General Public License\n" + " * along with this program; if not, write to the Free Software\n" + " * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n" + "*/\n\n", project_name); +#endif +} + + +/************************************************************************* + * main.c file. + *************************************************************************/ + +void +source_write_main_c (GbWidgetWriteSourceData * data) +{ + gchar *directory, *source_directory, *filename; + FILE *fp; + + /* See if the main.c file is wanted. */ + if (!glade_project_get_output_main_file (data->project)) + return; + + directory = glade_project_get_directory (data->project); + source_directory = glade_project_get_source_directory (data->project); + + filename = glade_util_make_absolute_path (source_directory, "main.c"); + + /* Return if it already exists. */ + if (glade_util_file_exists (filename)) + { + g_free (filename); + return; + } + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + source_write_preamble (data->project_name, fp); + + fprintf (fp, + "/*\n" + " * Initial main.c file generated by Glade. Edit as required.\n" + " * Glade will not overwrite this file.\n" + " */\n\n"); + + fprintf (fp, + "#ifdef HAVE_CONFIG_H\n" + "# include <config.h>\n" + "#endif\n\n"); + + if (glade_project_get_gnome_support (data->project)) + { + fprintf (fp, "#include <gnome.h>\n\n"); + } + else + { + fprintf (fp, "#include <gtk/gtk.h>\n\n"); + } + + /* Include the interface.h header to get declarations of the functions to + create the components, and support.h so we include libintl.h if needed. */ + fprintf (fp, + "#include \"%s\"\n" + "#include \"%s\"\n\n", + g_basename (data->interface_h_filename), + glade_project_get_support_header_file (data->project)); + + fprintf (fp, + "int\n" + "main (int argc, char *argv[])\n" + "{\n"); + + source_reset_code_buffers (data); + + /* This outputs code in main() to create one of each component, just so that + the user sees something after first building the project. */ + glade_project_foreach_component (data->project, + (GtkCallback) source_write_component_create, + data); + fprintf (fp, "%s\n", data->source_buffers[GLADE_DECLARATIONS]->str); + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, + "#ifdef ENABLE_NLS\n" + " bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);\n" + " bind_textdomain_codeset (GETTEXT_PACKAGE, \"UTF-8\");\n" + " textdomain (GETTEXT_PACKAGE);\n" + "#endif\n" + "\n"); + } + + /* Note that we don't mark the project name as translatable. + I'm not entirely certain we should do that. */ + if (glade_project_get_gnome_support (data->project)) + { + fprintf (fp, + " gnome_program_init (PACKAGE, VERSION, LIBGNOMEUI_MODULE,\n" + " argc, argv,\n" + " GNOME_PARAM_APP_DATADIR, PACKAGE_DATA_DIR,\n" + " NULL);\n"); + } + else + { + fprintf (fp, + " gtk_set_locale ();\n" + " gtk_init (&argc, &argv);\n" + "\n"); + + fprintf (fp, + " add_pixmap_directory (PACKAGE_DATA_DIR \"/\" PACKAGE \"/pixmaps\");\n"); + } + + fprintf (fp, + "\n" + " /*\n" + " * The following code was added by Glade to create one of each component\n" + " * (except popup menus), just so that you see something after building\n" + " * the project. Delete any components that you don't want shown initially.\n" + " */\n"); + fprintf (fp, "%s", data->source_buffers[GLADE_SOURCE]->str); + fprintf (fp, "\n gtk_main ();\n return 0;\n}\n\n"); + + fclose (fp); + + g_free (filename); +} + + +static void +source_write_component_create (GtkWidget * component, + GbWidgetWriteSourceData * data) +{ + gchar *component_name; + + /* Don't output code to show popup menus. */ + if (GTK_IS_MENU (component)) + return; + + component_name = (gchar*) gtk_widget_get_name (component); + component_name = source_create_valid_identifier (component_name); + source_add_decl (data, " GtkWidget *%s;\n", component_name); + source_add (data, + " %s = create_%s ();\n" + " gtk_widget_show (%s);\n", + component_name, component_name, component_name); + g_free (component_name); +} + + +/************************************************************************* + * Build Files. + *************************************************************************/ + +static void +source_write_build_files (GbWidgetWriteSourceData * data) +{ + /* If the build files aren't wanted, return. */ + if (!glade_project_get_output_build_files (data->project)) + return; + + /* Write the support files - Makefile.am, configure.in, etc. */ + if (glade_project_get_gnome_support (data->project)) + { + source_write_gnome_build_files (data); + } + else + { + source_write_gtk_build_files (data); + } + if (data->error) + return; + + source_write_common_build_files (data); + if (data->error) + return; + +#ifdef HAVE_OS2_H + source_write_os2_files(data); +#endif +} + + +/* + * GTK+ build files. + */ + +static void +source_write_gtk_build_files (GbWidgetWriteSourceData * data) +{ + source_write_autogen_sh (data); + if (data->error) + return; + + source_write_gtk_configure_in (data); + if (data->error) + return; + + source_write_gtk_makefile_am (data); +} + + +/* Copies the generic autogen.sh script which runs aclocal, automake + & autoconf to create the Makefiles etc. */ +static void +source_write_autogen_sh (GbWidgetWriteSourceData * data) +{ + static const gchar *data_dir = GLADE_DATADIR "/glade-2"; + gchar *project_dir, *srcbuffer, *destbuffer; + const gchar *filename = "autogen.sh"; + gint old_umask; + + srcbuffer = g_malloc (strlen (data_dir) + 128); + project_dir = glade_project_get_directory (data->project); + destbuffer = g_malloc (strlen (project_dir) + 128); + + sprintf (srcbuffer, "%s/gtk/%s", data_dir, filename); + sprintf (destbuffer, "%s/%s", project_dir, filename); + + if (!glade_util_file_exists (destbuffer)) + { + data->error = glade_util_copy_file (srcbuffer, destbuffer); + } + + /* We need to make the script executable, but we try to honour any umask. */ +#ifndef _WIN32 + old_umask = umask (0666); + chmod (destbuffer, 0777 & ~old_umask); + umask (old_umask); +#endif + + g_free (srcbuffer); + g_free (destbuffer); +} + + +static void +source_write_gtk_configure_in (GbWidgetWriteSourceData * data) +{ + FILE *fp; + gchar *filename, *alt_filename, *source_subdir; + + filename = glade_util_make_absolute_path (glade_project_get_directory (data->project), "configure.in"); + alt_filename = glade_util_make_absolute_path (glade_project_get_directory (data->project), "configure.ac"); + + /* FIXME: If configure.in exists, just leave it, for now. */ + if (glade_util_file_exists (filename) + || glade_util_file_exists (alt_filename)) + { + g_free (filename); + g_free (alt_filename); + return; + } + + g_free (alt_filename); + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + /* FIXME: Using AC_INIT(configure.in) is not really correct - we should be + using a file unique to the project. */ + fprintf (fp, + "dnl Process this file with autoconf to produce a configure script.\n" + "\n" + "AC_INIT(configure.in)\n" + "AM_INIT_AUTOMAKE(%s, 0.1)\n" + "AM_CONFIG_HEADER(config.h)\n" + "AM_MAINTAINER_MODE\n" + "\n" + "AC_ISC_POSIX\n" + "AC_PROG_CC\n" + "AM_PROG_CC_STDC\n" + "AC_HEADER_STDC\n" + "\n", + data->program_name); + + fprintf (fp, + "pkg_modules=\"gtk+-2.0 >= 2.0.0\"\n" + "PKG_CHECK_MODULES(PACKAGE, [$pkg_modules])\n" + "AC_SUBST(PACKAGE_CFLAGS)\n" + "AC_SUBST(PACKAGE_LIBS)\n" + "\n"); + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, + "GETTEXT_PACKAGE=%s\n" + "AC_SUBST(GETTEXT_PACKAGE)\n" + "AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,\"$GETTEXT_PACKAGE\", [Gettext package.])\n\n", + data->program_name); + fprintf (fp, + "dnl Add the languages which your application supports here.\n" + "ALL_LINGUAS=\"\"\n" + "AM_GLIB_GNU_GETTEXT\n" + "\n"); + } + + fprintf (fp, + "AC_OUTPUT([\n" + "Makefile\n"); + + source_subdir = source_get_source_subdirectory (data); + if (source_subdir) + { + fprintf (fp, "%s/Makefile\n", source_subdir); + g_free (source_subdir); + } + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, + "po/Makefile.in\n"); + } + + fprintf (fp, "])\n\n"); + + fclose (fp); + + g_free (filename); +} + + +static void +source_write_gtk_makefile_am (GbWidgetWriteSourceData * data) +{ + FILE *fp; + gchar *directory, *source_directory, *filename, *program_name_as_target; + gboolean is_toplevel; + + directory = glade_project_get_directory (data->project); + source_directory = glade_project_get_source_directory (data->project); + + filename = glade_util_make_absolute_path (source_directory, "Makefile.am"); + + /* FIXME: If Makefile.am exists, just leave it, for now. */ + if (glade_util_file_exists (filename)) + { + g_free (filename); + return; + } + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + fprintf (fp, + "## Process this file with automake to produce Makefile.in\n\n"); + + /* If the project directory is the source directory, we need to output + SUBDIRS here. */ + is_toplevel = glade_util_directories_equivalent (directory, + source_directory); + if (is_toplevel) + { + fprintf (fp, "SUBDIRS ="); + + if (glade_project_get_gettext_support (data->project)) + fprintf (fp, " po"); + + fprintf (fp, "\n\n"); + } + + fprintf (fp, + "INCLUDES = \\\n" + "\t-DPACKAGE_DATA_DIR=\\\"\"$(datadir)\"\\\" \\\n" + "\t-DPACKAGE_LOCALE_DIR=\\\"\"$(prefix)/$(DATADIRNAME)/locale\"\\\" \\\n" +#ifdef GLADE_ADD_DISABLE_DEPRECATED_FLAGS + "\t-DG_DISABLE_DEPRECATED \\\n" + "\t-DGDK_DISABLE_DEPRECATED \\\n" + "\t-DGTK_DISABLE_DEPRECATED \\\n" +#endif + "\t@PACKAGE_CFLAGS@\n" + "\n"); + + program_name_as_target = g_strdup (data->program_name); + g_strdelimit (program_name_as_target, "-", '_'); + + fprintf (fp, + "bin_PROGRAMS = %s\n" + "\n", + data->program_name); + + fprintf (fp, + "%s_SOURCES = \\\n", + program_name_as_target); + + if (glade_project_get_output_main_file (data->project)) + fprintf (fp, "\tmain.c \\\n"); + + if (glade_project_get_output_support_files (data->project)) + { + fprintf (fp, "\t%s %s \\\n", + glade_project_get_support_source_file (data->project), + glade_project_get_support_header_file (data->project)); + } + + fprintf (fp, + "\t%s %s \\\n" + "\t%s %s\n\n", + g_basename (data->interface_c_filename), + g_basename (data->interface_h_filename), + g_basename (data->callback_c_filename), + g_basename (data->callback_h_filename)); + + fprintf (fp, + "%s_LDADD = @PACKAGE_LIBS@", + program_name_as_target); + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, " $(INTLLIBS)"); + } + + fprintf (fp, "\n\n"); + + if (is_toplevel) + source_write_extra_dist (data, source_directory, fp); + + source_write_gtk_makefile_am_pixmaps_targets (data, source_directory, fp); + + fclose (fp); + + g_free (program_name_as_target); + g_free (filename); +} + + +/* This outputs targets to install pixmaps and to include them in the + distribution, if the pixmaps are in a subdirectory of the given directory. +*/ +static void +source_write_gtk_makefile_am_pixmaps_targets (GbWidgetWriteSourceData * data, + gchar * directory, + FILE * fp) +{ + gchar *pixmaps_directory, *subdir; + gint subdir_len; + + pixmaps_directory = glade_project_get_pixmaps_directory (data->project); + + if (!glade_util_directory_contains_file (directory, pixmaps_directory)) + return; + + subdir = glade_util_make_relative_path (directory, pixmaps_directory); + subdir_len = strlen (subdir); + if (subdir_len > 0 && subdir[subdir_len - 1] == G_DIR_SEPARATOR) + subdir[subdir_len - 1] = '\0'; + + /* When installing we simply copy anything in the pixmaps directory into + $(datadir)/pixmaps, if the pixmaps directory exists. + FIXME: The @$(NORMAL_INSTALL) line comes from the GNU Makefile + conventions, in the GNU coding standards info pages (under Releases). + Using DESTDIR also allows installation into 'staging areas'. + To comply fully the install commands should install to a particular file, + rather than a directory, and we should probably also have an uninstall + command. Kepp this in sync with source_write_gnome_makefile_am_... */ + fprintf (fp, + "install-data-local:\n" + "\t@$(NORMAL_INSTALL)\n" + "\tif test -d $(srcdir)/%s; then \\\n" + "\t $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/pixmaps; \\\n" + "\t for pixmap in $(srcdir)/%s/*; do \\\n" + "\t if test -f $$pixmap; then \\\n" + "\t $(INSTALL_DATA) $$pixmap $(DESTDIR)$(pkgdatadir)/pixmaps; \\\n" + "\t fi \\\n" + "\t done \\\n" + "\tfi\n" + "\n", + subdir, subdir); + + /* When building the distribution we simply copy anything in the pixmaps + directory into $(distdir)/subdir, if the pixmaps directory exists. */ + fprintf (fp, + "dist-hook:\n" + "\tif test -d %s; then \\\n" + "\t mkdir $(distdir)/%s; \\\n" + "\t for pixmap in %s/*; do \\\n" + "\t if test -f $$pixmap; then \\\n" + "\t cp -p $$pixmap $(distdir)/%s; \\\n" + "\t fi \\\n" + "\t done \\\n" + "\tfi\n" + "\n", + subdir, subdir, subdir, subdir); + + g_free (subdir); +} + + +/* + * Gnome build files. + */ + +static void +source_write_gnome_build_files (GbWidgetWriteSourceData * data) +{ + source_write_autogen_sh (data); + if (data->error) + return; + + source_write_gnome_configure_in (data); + if (data->error) + return; + + source_write_gnome_makefile_am (data); + if (data->error) + return; +} + + +static void +source_write_gnome_configure_in (GbWidgetWriteSourceData * data) +{ + FILE *fp; + gchar *filename, *alt_filename, *source_subdir; + + filename = glade_util_make_absolute_path (glade_project_get_directory (data->project), "configure.in"); + alt_filename = glade_util_make_absolute_path (glade_project_get_directory (data->project), "configure.ac"); + + /* FIXME: If configure.in exists, just leave it, for now. */ + if (glade_util_file_exists (filename) + || glade_util_file_exists (alt_filename)) + { + g_free (filename); + g_free (alt_filename); + return; + } + + g_free (alt_filename); + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + /* FIXME: Using AC_INIT(configure.in) is not really correct - we should be + using a file unique to the project. */ + fprintf (fp, + "dnl Process this file with autoconf to produce a configure script.\n" + "\n" + "AC_INIT(configure.in)\n" + "AM_INIT_AUTOMAKE(%s, 0.1)\n" + "AM_MAINTAINER_MODE\n" + "AM_CONFIG_HEADER(config.h)\n" + "\n" + "AC_ISC_POSIX\n" + "AC_PROG_CC\n" + "AM_PROG_CC_STDC\n" + "AC_HEADER_STDC\n" + "\n", + data->program_name); + + if (glade_project_get_gnome_db_support (data->project)) + fprintf (fp, "pkg_modules=\"libgnomedb\"\n"); + else + fprintf (fp, "pkg_modules=\"libgnomeui-2.0\"\n"); + + fprintf (fp, + "PKG_CHECK_MODULES(PACKAGE, [$pkg_modules])\n" + "AC_SUBST(PACKAGE_CFLAGS)\n" + "AC_SUBST(PACKAGE_LIBS)\n" + "\n"); + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, + "GETTEXT_PACKAGE=%s\n" + "AC_SUBST(GETTEXT_PACKAGE)\n" + "AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,\"$GETTEXT_PACKAGE\", [Gettext package.])\n\n", + data->program_name); + fprintf (fp, + "dnl Add the languages which your application supports here.\n" + "ALL_LINGUAS=\"\"\n" + "AM_GLIB_GNU_GETTEXT\n" + "\n"); + } + + fprintf (fp, + "AC_OUTPUT([\n" + "Makefile\n"); + + source_subdir = source_get_source_subdirectory (data); + if (source_subdir) + { + fprintf (fp, "%s/Makefile\n", source_subdir); + g_free (source_subdir); + } + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, + "po/Makefile.in\n"); + } + + fprintf (fp, "])\n\n"); + + fclose (fp); + + g_free (filename); +} + + +static void +source_write_gnome_makefile_am (GbWidgetWriteSourceData * data) +{ + FILE *fp; + gchar *directory, *source_directory, *filename, *program_name_as_target; + gboolean is_toplevel; + + directory = glade_project_get_directory (data->project); + source_directory = glade_project_get_source_directory (data->project); + + filename = glade_util_make_absolute_path (source_directory, "Makefile.am"); + + /* FIXME: If Makefile.am exists, just leave it, for now. */ + if (glade_util_file_exists (filename)) + { + g_free (filename); + return; + } + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + fprintf (fp, + "## Process this file with automake to produce Makefile.in\n\n"); + + /* If the project directory is the source directory, we need to output + SUBDIRS here. */ + is_toplevel = glade_util_directories_equivalent (directory, + source_directory); + if (is_toplevel) + { + if (glade_project_get_gettext_support (data->project)) + fprintf (fp, "SUBDIRS = po\n\n"); + } + + fprintf (fp, + "INCLUDES = \\\n" + "\t-DPACKAGE_DATA_DIR=\\\"\"$(datadir)\"\\\" \\\n" + "\t-DPACKAGE_LOCALE_DIR=\\\"\"$(prefix)/$(DATADIRNAME)/locale\"\\\" \\\n" +#ifdef GLADE_ADD_DISABLE_DEPRECATED_FLAGS + "\t-DG_DISABLE_DEPRECATED \\\n" + "\t-DGDK_DISABLE_DEPRECATED \\\n" + "\t-DGTK_DISABLE_DEPRECATED \\\n" + "\t-DGNOME_DISABLE_DEPRECATED \\\n" +#endif + "\t@PACKAGE_CFLAGS@\n" + "\n"); + + program_name_as_target = g_strdup (data->program_name); + g_strdelimit (program_name_as_target, "-", '_'); + + fprintf (fp, + "bin_PROGRAMS = %s\n" + "\n", + data->program_name); + + fprintf (fp, + "%s_SOURCES = \\\n", + program_name_as_target); + + if (glade_project_get_output_main_file (data->project)) + fprintf (fp, "\tmain.c \\\n"); + + if (glade_project_get_output_support_files (data->project)) + { + fprintf (fp, "\t%s %s \\\n", + glade_project_get_support_source_file (data->project), + glade_project_get_support_header_file (data->project)); + } + + fprintf (fp, + "\t%s %s \\\n" + "\t%s %s\n\n", + g_basename (data->interface_c_filename), + g_basename (data->interface_h_filename), + g_basename (data->callback_c_filename), + g_basename (data->callback_h_filename)); + + fprintf (fp, + "%s_LDADD = @PACKAGE_LIBS@", + program_name_as_target); + + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, " $(INTLLIBS)"); + } + + fprintf (fp, "\n\n"); + + if (is_toplevel) + source_write_extra_dist (data, source_directory, fp); + + source_write_gnome_makefile_am_pixmaps_targets (data, source_directory, fp); + + fclose (fp); + + g_free (program_name_as_target); + g_free (filename); +} + + +/* This outputs targets to install pixmaps and to include them in the + distribution, if the pixmaps are in a subdirectory of the given directory. +*/ +static void +source_write_gnome_makefile_am_pixmaps_targets (GbWidgetWriteSourceData * data, + gchar * directory, + FILE * fp) +{ + gchar *pixmaps_directory, *subdir; + gint subdir_len; + + pixmaps_directory = glade_project_get_pixmaps_directory (data->project); + + if (!glade_util_directory_contains_file (directory, pixmaps_directory)) + return; + + subdir = glade_util_make_relative_path (directory, pixmaps_directory); + subdir_len = strlen (subdir); + if (subdir_len > 0 && subdir[subdir_len - 1] == G_DIR_SEPARATOR) + subdir[subdir_len - 1] = '\0'; + + /* When installing we simply copy anything in the pixmaps directory into + $(datadir)/pixmaps, if the pixmaps directory exists. */ + fprintf (fp, + "install-data-local:\n" + "\t@$(NORMAL_INSTALL)\n" + "\tif test -d $(srcdir)/%s; then \\\n" + "\t $(mkinstalldirs) $(DESTDIR)$(datadir)/pixmaps/$(PACKAGE); \\\n" + "\t for pixmap in $(srcdir)/%s/*; do \\\n" + "\t if test -f $$pixmap; then \\\n" + "\t $(INSTALL_DATA) $$pixmap $(DESTDIR)$(datadir)/pixmaps/$(PACKAGE); \\\n" + "\t fi \\\n" + "\t done \\\n" + "\tfi\n" + "\n", + subdir, subdir); + + /* When building the distribution we simply copy anything in the pixmaps + directory into $(distdir)/subdir, if the pixmaps directory exists. */ + fprintf (fp, + "dist-hook:\n" + "\tif test -d %s; then \\\n" + "\t mkdir $(distdir)/%s; \\\n" + "\t for pixmap in %s/*; do \\\n" + "\t if test -f $$pixmap; then \\\n" + "\t cp -p $$pixmap $(distdir)/%s; \\\n" + "\t fi \\\n" + "\t done \\\n" + "\tfi\n" + "\n", + subdir, subdir, subdir, subdir); + + g_free (subdir); +} + + +/* + * Common build files. + */ + +/* This creates the standard files that automake expects you to have, + i.e. NEWS, README, AUTHORS, ChangeLog. + If they already exist, they are left as they are. */ +static void +source_write_common_build_files (GbWidgetWriteSourceData * data) +{ + gchar *directory; + + directory = glade_project_get_directory (data->project); + + source_write_toplevel_makefile_am (data); + if (data->error) + return; + + data->error = source_create_file_if_not_exist (directory, "NEWS", NULL); + if (data->error) + return; + + data->error = source_create_file_if_not_exist (directory, "README", NULL); + if (data->error) + return; + + data->error = source_create_file_if_not_exist (directory, "AUTHORS", NULL); + if (data->error) + return; + + data->error = source_create_file_if_not_exist (directory, "ChangeLog", NULL); + if (data->error) + return; + + data->error = source_create_file_if_not_exist (directory, "stamp-h.in", + "timestamp\n"); + if (data->error) + return; + + source_write_po_files (data); + if (data->error) + return; + +#if 0 + source_write_acconfig_h (data); +#endif +} + + +/* This writes the toplevel Makefile.am, if the source directory is a + subdirectory of the project directory, and the Makefile.am doesn't already + exist. */ +static void +source_write_toplevel_makefile_am (GbWidgetWriteSourceData * data) +{ + FILE *fp; + gchar *directory, *source_subdir, *filename; + + directory = glade_project_get_directory (data->project); + + /* If the source directory isn't a subdirectory of the project directory, + just return. */ + source_subdir = source_get_source_subdirectory (data); + if (!source_subdir) + return; + + filename = glade_util_make_absolute_path (directory, "Makefile.am"); + + /* FIXME: If Makefile.am exists, just leave it, for now. */ + if (glade_util_file_exists (filename)) + { + g_free (source_subdir); + g_free (filename); + return; + } + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (source_subdir); + g_free (filename); + return; + } + + fprintf (fp, + "## Process this file with automake to produce Makefile.in\n" + "\n" + "SUBDIRS = %s", + source_subdir); + + if (glade_project_get_gettext_support (data->project)) + fprintf (fp, " po"); + + fprintf (fp, "\n\n"); + + source_write_extra_dist (data, directory, fp); + + if (glade_project_get_gnome_support (data->project)) + source_write_gnome_makefile_am_pixmaps_targets (data, directory, fp); + else + source_write_gtk_makefile_am_pixmaps_targets (data, directory, fp); + + fclose (fp); + + g_free (source_subdir); + g_free (filename); +} + + +static void +source_write_extra_dist (GbWidgetWriteSourceData * data, + const gchar *directory, + FILE *fp) +{ + gchar *xml_filename; + + xml_filename = glade_project_get_xml_filename (data->project); + + xml_filename = glade_util_make_relative_path (directory, xml_filename); + + fprintf (fp, + "EXTRA_DIST = \\\n" + "\tautogen.sh \\\n" + "\t%s \\\n" + "\t%sp\n\n", + xml_filename, xml_filename); + + g_free (xml_filename); +} + + +/* This creates an empty file in the given directory if it doesn't already + exist. It is used to create the empty NEWS, README, AUTHORS & ChangeLog. */ +static GladeError* +source_create_file_if_not_exist (const gchar *directory, + const gchar *filename, + const gchar *contents) +{ + gchar *pathname; + FILE *fp; + GladeError *error = NULL; + gint bytes_written; + + pathname = glade_util_make_absolute_path (directory, filename); + + if (!glade_util_file_exists (pathname)) + { + fp = glade_util_fopen (pathname, "w"); + if (fp) + { + if (contents) + { + gint contents_len; + + contents_len = strlen (contents); + bytes_written = fwrite (contents, 1, contents_len, fp); + if (bytes_written != contents_len) + { + error = glade_error_new_system (_("Error writing to file:\n %s\n"), pathname); + } + } + + fclose (fp); + } + else + { + error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + pathname); + } + } + g_free (pathname); + return error; +} + + +/* This creates the initial po/POTFILES.in & po/ChangeLog if they don't exist. + If we don't create a ChangeLog the 'make dist' fails. */ +static void +source_write_po_files (GbWidgetWriteSourceData * data) +{ + gchar *dirname = NULL, *filename = NULL, *prefix, *separator; + gint prefix_len; + FILE *fp; + + /* Returns if gettext support isn't wanted. */ + if (!glade_project_get_gettext_support (data->project)) + return; + + dirname = glade_util_make_absolute_path (glade_project_get_directory (data->project), "po"); + + /* Create the po directory if it doesn't exist. */ + data->error = glade_util_ensure_directory_exists (dirname); + if (data->error) + { + g_free (dirname); + return; + } + + /* Create ChangeLog if it doesn't exist. */ + data->error = source_create_file_if_not_exist (dirname, "ChangeLog", NULL); + if (data->error) + { + g_free (dirname); + return; + } + + /* FIXME: If POTFILES.in exists, just leave it, for now. */ + filename = glade_util_make_absolute_path (dirname, "POTFILES.in"); + if (glade_util_file_exists (filename)) + { + g_free (dirname); + g_free (filename); + return; + } + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (dirname); + g_free (filename); + return; + } + + /* We need the relative path from the project directory to the source + directory, to prefix each source file. */ + prefix = glade_util_make_relative_path (glade_project_get_directory (data->project), glade_project_get_source_directory (data->project)); + + /* See if we need a directory separator after the prefix. */ + prefix_len = strlen (prefix); + if (prefix_len == 0 || prefix[prefix_len - 1] == G_DIR_SEPARATOR) + separator = ""; + else + separator = G_DIR_SEPARATOR_S; + + fprintf (fp, "# List of source files containing translatable strings.\n\n"); + + /* Add the main.c file if we are outputting it. */ + if (glade_project_get_output_main_file (data->project)) + fprintf (fp, "%s%s%s\n", prefix, separator, "main.c"); + + /* Add the interface.c & callbacks.c files. */ + fprintf (fp, + "%s%s%s\n" + "%s%s%s\n", + prefix, separator, g_basename (data->interface_c_filename), + prefix, separator, g_basename (data->callback_c_filename)); + + /* Add the support.c file if we are outputting it. */ + if (glade_project_get_output_support_files (data->project)) + fprintf (fp, "%s%s%s\n", prefix, separator, + glade_project_get_support_source_file (data->project)); + + g_free (prefix); + + fclose (fp); + + g_free (dirname); + g_free (filename); +} + + +#if 0 +static void +source_write_acconfig_h (GbWidgetWriteSourceData * data) +{ + FILE *fp; + gchar *filename; + + filename = glade_util_make_absolute_path (glade_project_get_directory (data->project), "acconfig.h"); + + /* FIXME: If acconfig.h exists, just leave it, for now. */ + if (glade_util_file_exists (filename)) + { + g_free (filename); + return; + } + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + /* If we aren't using gettext or Gnome some of these may not be necessary, + but I don't think they cause problems. */ + fprintf (fp, + "#undef ENABLE_NLS\n" + "#undef HAVE_CATGETS\n" + "#undef HAVE_GETTEXT\n" + "#undef GETTEXT_PACKAGE\n" + "#undef HAVE_LC_MESSAGES\n" + "#undef HAVE_STPCPY\n" + "#undef HAVE_LIBSM\n"); + fclose (fp); + + g_free (filename); +} +#endif + + +/************************************************************************* + * Support Files. + *************************************************************************/ + +/* We copy a file containing support functions into the project, and a + corresponding header. + Note that we do overwrite these, in case an old version is currently + being used. */ +static void +source_write_support_files (GbWidgetWriteSourceData * data) +{ + gchar *filename; + FILE *fp; + + /* If the support files aren't wanted, just return. */ + if (!glade_project_get_output_support_files (data->project)) + return; + + /* Create the support header file first. */ + filename = glade_util_make_absolute_path (glade_project_get_source_directory (data->project), glade_project_get_support_header_file (data->project)); + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + source_write_no_editing_warning (fp); + source_write_preamble (data->project_name, fp); + + if (glade_project_get_gnome_support (data->project)) + { + fprintf (fp, + "#ifdef HAVE_CONFIG_H\n" + "# include <config.h>\n" + "#endif\n" + "\n" + "#include <gnome.h>\n\n"); + + if (glade_project_get_gettext_support (data->project)) + { + /* bonobo-i18n.h doesn't include the Q_ macro so we add it here. */ + fprintf (fp, + "#undef Q_\n" + "#ifdef ENABLE_NLS\n" + "# define Q_(String) g_strip_context ((String), gettext (String))\n" + "#else\n" + "# define Q_(String) g_strip_context ((String), (String))\n" + "#endif\n\n\n"); + } + } + else + { + fprintf (fp, + "#ifdef HAVE_CONFIG_H\n" + "# include <config.h>\n" + "#endif\n" + "\n" + "#include <gtk/gtk.h>\n\n"); + + /* For GTK+ apps that want gettext support, we define the standard + macros here. */ + if (glade_project_get_gettext_support (data->project)) + { + fprintf (fp, + "/*\n" + " * Standard gettext macros.\n" + " */\n" + "#ifdef ENABLE_NLS\n" + "# include <libintl.h>\n" + "# undef _\n" + "# define _(String) dgettext (PACKAGE, String)\n" + "# define Q_(String) g_strip_context ((String), gettext (String))\n" + "# ifdef gettext_noop\n" + "# define N_(String) gettext_noop (String)\n" + "# else\n" + "# define N_(String) (String)\n" + "# endif\n" + "#else\n" + "# define textdomain(String) (String)\n" + "# define gettext(String) (String)\n" + "# define dgettext(Domain,Message) (Message)\n" + "# define dcgettext(Domain,Message,Type) (Message)\n" + "# define bindtextdomain(Domain,Directory) (Domain)\n" + "# define _(String) (String)\n" + "# define Q_(String) g_strip_context ((String), (String))\n" + "# define N_(String) (String)\n" + "#endif\n\n\n"); + } + } + + fprintf (fp, + "/*\n" + " * Public Functions.\n" + " */\n" + "\n"); + + if (!data->use_component_struct) + { + fprintf (fp, + "/*\n" + " * This function returns a widget in a component created by Glade.\n" + " * Call it with the toplevel widget in the component (i.e. a window/dialog),\n" + " * or alternatively any widget in the component, and the name of the widget\n" + " * you want returned.\n" + " */\n" + "GtkWidget* lookup_widget (GtkWidget *widget,\n" + " const gchar *widget_name);\n" + "\n" + "\n"); + } + + /* Gnome has its own function for looking for pixmaps, so we don't need this + one. */ + if (!glade_project_get_gnome_support (data->project)) + { + fprintf (fp, + "/* Use this function to set the directory containing installed pixmaps. */\n" + "void add_pixmap_directory (const gchar *directory);\n" + "\n"); + } + + fprintf (fp, + "\n" + "/*\n" + " * Private Functions.\n" + " */\n" + "\n"); + + fprintf (fp, + "/* This is used to create the pixmaps used in the interface. */\n" + "GtkWidget* create_pixmap (GtkWidget *widget,\n" + " const gchar *filename);\n" + "\n"); + + fprintf (fp, + "/* This is used to create the pixbufs used in the interface. */\n" + "GdkPixbuf* create_pixbuf (const gchar *filename);\n" + "\n"); + + fprintf (fp, + "/* This is used to set ATK action descriptions. */\n" + "void glade_set_atk_action_description (AtkAction *action,\n" + " const gchar *action_name,\n" + " const gchar *description);\n" + "\n"); + + fclose (fp); + + g_free (filename); + + + /* Now create the support source file. */ + filename = glade_util_make_absolute_path (glade_project_get_source_directory (data->project), glade_project_get_support_source_file (data->project)); + + fp = glade_util_fopen (filename, "w"); + if (fp == NULL) + { + data->error = glade_error_new_system (_("Couldn't create file:\n %s\n"), + filename); + g_free (filename); + return; + } + + source_write_no_editing_warning (fp); + source_write_preamble (data->project_name, fp); + source_write_include_files (fp); + + if (glade_project_get_gnome_support (data->project)) + fprintf (fp, "#include <gnome.h>\n\n"); + else + fprintf (fp, "#include <gtk/gtk.h>\n\n"); + + fprintf (fp, "#include \"%s\"\n\n", + glade_project_get_support_header_file (data->project)); + + /* Write a function to get a widget from the component's hash. */ + if (!data->use_component_struct) + { + fprintf (fp, + "GtkWidget*\n" + "lookup_widget (GtkWidget *widget,\n" + " const gchar *widget_name)\n" + "{\n" + " GtkWidget *parent, *found_widget;\n" + "\n" + " for (;;)\n" + " {\n" + " if (GTK_IS_MENU (widget))\n" + " parent = gtk_menu_get_attach_widget (GTK_MENU (widget));\n" + " else\n" + " parent = gtk_widget_get_parent (widget);\n" + " if (!parent)\n" + " parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), \"GladeParentKey\");\n" + " if (parent == NULL)\n" + " break;\n" + " widget = parent;\n" + " }\n" + "\n" + " found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget),\n" + " widget_name);\n" + " if (!found_widget)\n" + " g_warning (\"Widget not found: %%s\", widget_name);\n" + " return found_widget;\n" + "}\n\n"); + } + + if (glade_project_get_gnome_support (data->project)) + { + source_write_gnome_create_pixmap_functions (data, fp); + } + else + { + source_write_gtk_create_pixmap_functions (data, fp); + } + + /* Output the support function to set AtkAction descriptions. */ + fprintf (fp, + "/* This is used to set ATK action descriptions. */\n" + "void\n" + "glade_set_atk_action_description (AtkAction *action,\n" + " const gchar *action_name,\n" + " const gchar *description)\n" + "{\n" + " gint n_actions, i;\n" + "\n" + " n_actions = atk_action_get_n_actions (action);\n" + " for (i = 0; i < n_actions; i++)\n" + " {\n" + " if (!strcmp (atk_action_get_name (action, i), action_name))\n" + " atk_action_set_description (action, i, description);\n" + " }\n" + "}\n\n"); + + fclose (fp); + + g_free (filename); +} + + +static void +source_write_gtk_create_pixmap_functions (GbWidgetWriteSourceData * data, + FILE *fp) +{ + /* + * Write a function used to set the installed pixmaps directory. + */ + fprintf (fp, + "static GList *pixmaps_directories = NULL;\n" + "\n" + "/* Use this function to set the directory containing installed pixmaps. */\n" + "void\n" + "add_pixmap_directory (const gchar *directory)\n" + "{\n" + " pixmaps_directories = g_list_prepend (pixmaps_directories,\n" + " g_strdup (directory));\n" + "}\n\n"); + + /* + * Write a function used to find pixmap files. It returns the full pathname + * of the found pixmap file, or NULL if it wasn't found. + */ + fprintf (fp, + "/* This is an internally used function to find pixmap files. */\n" + "static gchar*\n" + "find_pixmap_file (const gchar *filename)\n" + "{\n" + " GList *elem;\n" + "\n"); + + fprintf (fp, + " /* We step through each of the pixmaps directory to find it. */\n" + " elem = pixmaps_directories;\n" + " while (elem)\n" + " {\n" + " gchar *pathname = g_strdup_printf (\"%%s%%s%%s\", (gchar*)elem->data,\n" + " G_DIR_SEPARATOR_S, filename);\n" + " if (g_file_test (pathname, G_FILE_TEST_EXISTS))\n" + " return pathname;\n" + " g_free (pathname);\n" + " elem = elem->next;\n" + " }\n" + " return NULL;\n" + "}\n\n"); + + + + /* + * Write a function used for creating GtkImage widgets from pixmap files. + * It will return an unset GtkImage if the pixmap file isn't found, so the + * application will still run. Though it will output a warning if the + * pixmap filename was set but wasn't found. + */ + fprintf (fp, + "/* This is an internally used function to create pixmaps. */\n" + "GtkWidget*\n" + "create_pixmap (GtkWidget *widget,\n" + " const gchar *filename)\n" + "{\n" + " gchar *pathname = NULL;\n" + " GtkWidget *pixmap;\n" + "\n"); + + /* The developer may not have finished the interface yet, so we handle + the filename not being set. */ + fprintf (fp, + " if (!filename || !filename[0])\n" + " return gtk_image_new ();\n" + "\n"); + + fprintf (fp, + " pathname = find_pixmap_file (filename);\n" + "\n"); + + fprintf (fp, + " if (!pathname)\n" + " {\n" + " g_warning (%s, filename);\n" + " return gtk_image_new ();\n" + " }\n" + "\n", + source_make_string ("Couldn't find pixmap file: %s", + data->use_gettext)); + + fprintf (fp, + " pixmap = gtk_image_new_from_file (pathname);\n" + " g_free (pathname);\n" + " return pixmap;\n" + "}\n\n"); + + + + /* + * Write a function used for creating GdkPixbufs from pixmap files. + * It will return NULL if the pixmap file isn't found, so any calls to this + * function may need to handle that. It will output a warning if the + * pixmap filename was set but wasn't found. + */ + fprintf (fp, + "/* This is an internally used function to create pixmaps. */\n" + "GdkPixbuf*\n" + "create_pixbuf (const gchar *filename)\n" + "{\n" + " gchar *pathname = NULL;\n" + " GdkPixbuf *pixbuf;\n" + " GError *error = NULL;\n" + "\n"); + + /* The developer may not have finished the interface yet, so we handle + the filename not being set. */ + fprintf (fp, + " if (!filename || !filename[0])\n" + " return NULL;\n" + "\n"); + + fprintf (fp, + " pathname = find_pixmap_file (filename);\n" + "\n"); + + fprintf (fp, + " if (!pathname)\n" + " {\n" + " g_warning (%s, filename);\n" + " return NULL;\n" + " }\n" + "\n", + source_make_string ("Couldn't find pixmap file: %s", + data->use_gettext)); + + fprintf (fp, + " pixbuf = gdk_pixbuf_new_from_file (pathname, &error);\n" + " if (!pixbuf)\n" + " {\n" + " fprintf (stderr, \"Failed to load pixbuf file: %%s: %%s\\n\",\n" + " pathname, error->message);\n" + " g_error_free (error);\n" + " }\n" + " g_free (pathname);\n" + " return pixbuf;\n" + "}\n\n"); +} + + +static void +source_write_gnome_create_pixmap_functions (GbWidgetWriteSourceData * data, + FILE *fp) +{ + /* + * Write a function used for creating GtkImage widgets from pixmap files. + * It will return an unset GtkImage if the pixmap file isn't found, so the + * application will still run. Though it will output a warning if the + * pixmap filename was set but wasn't found. + */ + fprintf (fp, + "/* This is an internally used function to create pixmaps. */\n" + "GtkWidget*\n" + "create_pixmap (GtkWidget *widget,\n" + " const gchar *filename)\n" + "{\n" + " GtkWidget *pixmap;\n" + " gchar *pathname;\n" + "\n"); + + /* The developer may not have finished the interface yet, so we handle + the filename not being set. */ + fprintf (fp, + " if (!filename || !filename[0])\n" + " return gtk_image_new ();\n" + "\n"); + + fprintf (fp, + " pathname = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,\n" + " filename, TRUE, NULL);\n" + " if (!pathname)\n" + " {\n" + " g_warning (%s, filename);\n" + " return gtk_image_new ();\n" + " }\n" + "\n", + source_make_string ("Couldn't find pixmap file: %s", + data->use_gettext)); + + fprintf (fp, + " pixmap = gtk_image_new_from_file (pathname);\n" + " g_free (pathname);\n" + " return pixmap;\n" + "}\n" + "\n"); + + + /* + * Write a function used for creating GdkPixbufs from pixmap files. + * It will return NULL if the pixmap file isn't found, so any calls to this + * function may need to handle that. It will output a warning if the + * pixmap filename was set but wasn't found. + */ + fprintf (fp, + "/* This is an internally used function to create pixmaps. */\n" + "GdkPixbuf*\n" + "create_pixbuf (const gchar *filename)\n" + "{\n" + " gchar *pathname = NULL;\n" + " GdkPixbuf *pixbuf;\n" + " GError *error = NULL;\n" + "\n"); + + /* The developer may not have finished the interface yet, so we handle + the filename not being set. */ + fprintf (fp, + " if (!filename || !filename[0])\n" + " return NULL;\n" + "\n"); + + fprintf (fp, + " pathname = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,\n" + " filename, TRUE, NULL);\n" + "\n"); + + fprintf (fp, + " if (!pathname)\n" + " {\n" + " g_warning (%s, filename);\n" + " return NULL;\n" + " }\n" + "\n", + source_make_string ("Couldn't find pixmap file: %s", + data->use_gettext)); + + fprintf (fp, + " pixbuf = gdk_pixbuf_new_from_file (pathname, &error);\n" + " if (!pixbuf)\n" + " {\n" + " fprintf (stderr, \"Failed to load pixbuf file: %%s: %%s\\n\",\n" + " pathname, error->message);\n" + " g_error_free (error);\n" + " }\n" + " g_free (pathname);\n" + " return pixbuf;\n" + "}\n\n"); +} + + +/************************************************************************* + * Public Functions. + *************************************************************************/ + +/* Adds some source code to one of the buffers, using printf-like format + and arguments. */ +void +source_add_to_buffer (GbWidgetWriteSourceData * data, + GladeSourceBuffer buffer, + const gchar *fmt, + ...) +{ + va_list args; + + va_start (args, fmt); + source_add_to_buffer_v (data, buffer, fmt, args); + va_end (args); +} + + +/* A va_list implementation of the above. */ +void +source_add_to_buffer_v (GbWidgetWriteSourceData * data, + GladeSourceBuffer buffer, + const gchar *fmt, + va_list args) +{ + gchar *buf; + + buf = g_strdup_vprintf (fmt, args); + g_string_append (data->source_buffers[buffer], buf); + g_free (buf); +} + + +/* Convenience functions to add to the 2 main source buffers, containing + the code which creates the widgets and the declarations of the widgets. */ +void +source_add (GbWidgetWriteSourceData * data, const gchar * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + source_add_to_buffer_v (data, GLADE_SOURCE, fmt, args); + va_end (args); +} + + +void +source_add_translator_comments (GbWidgetWriteSourceData *data, + gboolean translatable, + const gchar *comments) +{ + /* If the property isn't being translated we don't bother outputting the + translator comments. */ + if (!translatable || !comments || comments[0] == '\0') + return; + + /* We simply output it in a C comment. + FIXME: If the comments contain an end of comment marker it won't + compile. */ + source_add (data, " /* %s */\n", comments); +} + + +void +source_add_translator_comments_to_buffer (GbWidgetWriteSourceData *data, + GladeSourceBuffer buffer, + gboolean translatable, + const gchar *comments) +{ + /* If the property isn't being translated we don't bother outputting the + translator comments. */ + if (!translatable || !comments || comments[0] == '\0') + return; + + /* We simply output it in a C comment. + FIXME: If the comments contain an end of comment marker it won't + compile. */ + source_add_to_buffer (data, buffer, " /* %s */\n", comments); +} + + +void +source_add_decl (GbWidgetWriteSourceData * data, const gchar * fmt, ...) +{ + va_list args; + + va_start (args, fmt); + source_add_to_buffer_v (data, GLADE_DECLARATIONS, fmt, args); + va_end (args); +} + + +/* This ensures that a temporary variable is declared, by adding the given + declaration if it has not already been added. */ +void +source_ensure_decl (GbWidgetWriteSourceData *data, + const gchar *decl) +{ + if (!glade_util_strstr (data->source_buffers[GLADE_DECLARATIONS]->str, decl)) + source_add_decl (data, decl); +} + + +/* This creates a valid C identifier from a given string (usually the name of + a widget or a signal handler function name). Currently all we do is convert + any illegal characters to underscores. + The returned string should be freed when no longer needed. */ +gchar * +source_create_valid_identifier (const gchar * name) +{ + gint name_len, i, j; + gchar *identifier; + + name_len = strlen (name); + /* allocate extra space for _ prefix if the identifier starts with + a number */ + identifier = g_malloc (name_len + 2); + j = 1; + + /* The first character of an identifier must be in [a-zA-Z_] */ + if ((name[0] >= 'a' && name[0] <= 'z') + || (name[0] >= 'A' && name[0] <= 'Z') + || name[0] == '_') + identifier[0] = name[0]; + else + { + if (name[0] >= '0' && name[0] <= '9') + { + /* prepend the _ instead of overwriting, so you'll still have + unique names */ + identifier[0] = '_'; + identifier[1] = name[0]; + j++; + } + else + identifier[0] = '_'; + } + + /* The remaining characters must be in [a-zA-Z0-9_] */ + for (i = 1; i < name_len; i++, j++) + { + if ((name[i] >= 'a' && name[i] <= 'z') + || (name[i] >= 'A' && name[i] <= 'Z') + || (name[i] >= '0' && name[i] <= '9') + || name[i] == '_') + identifier[j] = name[i]; + else + identifier[j] = '_'; + } + + identifier[j] = '\0'; + + return identifier; +} + + +/* This converts a string so that it can be output as part of the C source + * code. It converts non-printable characters to escape codes. + * Note that it uses one dynamically allocated buffer, so the result is only + * valid until the next call to the function. + * + * FIXME: There is a limit to the length of literal strings in ANSI C - ~500 + * chars. What should we do when that is exceeded? + */ +gchar * +source_make_string (const gchar * text, + gboolean translatable) +{ + return source_make_string_internal (text, translatable, FALSE, FALSE); +} + + +gchar * +source_make_string_full (const gchar * text, + gboolean translatable, + gboolean context) +{ + return source_make_string_internal (text, translatable, FALSE, context); +} + + +gchar * +source_make_static_string (const gchar * text, + gboolean translatable) +{ + return source_make_string_internal (text, translatable, TRUE, FALSE); +} + + +/* This converts a string so that it can be output as part of the C source + * code. It converts non-printable characters to escape codes. + * If is_static is TRUE it uses "N_" to mark translatable strings. + * Note that it uses one dynamically allocated buffer, so the result is only + * valid until the next call to the function. + */ +static gchar * +source_make_string_internal (const gchar * text, + gboolean translatable, + gboolean is_static, + gboolean context) +{ + static GString *buffer = NULL; + + gchar escape_buffer[16]; + const gchar *p; + + /* If the text is empty, we return an empty string without the _ macro. */ + if (!text || text[0] == '\0') + return "\"\""; + + /* Create the buffer if it hasn't already been created. */ + if (buffer == NULL) + { + buffer = g_string_sized_new (1024); + } + + /* Clear any previous string. */ + g_string_truncate (buffer, 0); + + /* Output the C code to start the string. */ + if (translatable) + { + /* Static "N_" overrides any context setting, though they shouldn't + really both be passed in as TRUE anyway. */ + if (is_static) + g_string_append (buffer, "N_(\""); + else if (context) + g_string_append (buffer, "Q_(\""); + else + g_string_append (buffer, "_(\""); + } + else + { + g_string_append (buffer, "\""); + } + + /* Step through each character of the given string, adding it to our GString + buffer, converting it so that it is valid in a literal C string. */ + for (p = text; *p; p++) + { + switch (*p) + { + case '\n': + g_string_append (buffer, "\\n"); + break; + case '\r': + g_string_append (buffer, "\\r"); + break; + case '\t': + g_string_append (buffer, "\\t"); + break; + case '\\': + g_string_append (buffer, "\\\\"); + break; + case '"': + g_string_append (buffer, "\\\""); + break; + default: + if (isprint (*p)) + { + g_string_append_c (buffer, *p); + } + else + { + sprintf (escape_buffer, "\\%02o", (guchar) *p); + g_string_append (buffer, escape_buffer); + } + break; + } + } + + /* Output the C code to end the string. */ + g_string_append (buffer, translatable ? "\")" : "\""); + + return buffer->str; +} + + +/* This outputs code to create a GtkImage widget with the given identifier, + and using the given filename (only the basename is used). If filename NULL + or "" an empty GtkImage is created. */ +void +source_create_pixmap (GbWidgetWriteSourceData * data, + const gchar * identifier, + const gchar * filename) +{ + gboolean empty_filename = FALSE; + + if (!filename || filename[0] == '\0') + empty_filename = TRUE; + + /* We use the basename of the pixmap file. The create_pixmap() support + function is responsible for finding the pixmap. + If it can't find the pixmap, it outputs a warning messages and returns + a simple dummy pixmap, so that the app can continue without crashing. */ + if (glade_project_get_gnome_support (data->project)) + { + /* FIXME: Should convert filename to a valid C string? */ + if (!empty_filename) + source_add (data, " %s = create_pixmap (%s, \"%s/%s\");\n", + identifier, data->component_name, + data->program_name, g_basename (filename)); + else + source_add (data, " %s = create_pixmap (%s, NULL);\n", + identifier, data->component_name); + } + else + { + if (!empty_filename) + source_add (data, " %s = create_pixmap (%s, \"%s\");\n", + identifier, data->component_name, g_basename (filename)); + else + source_add (data, " %s = create_pixmap (%s, NULL);\n", + identifier, data->component_name); + } +} + + +/* This outputs code to create a GdkPixbuf with the given identifier, + and using the given filename (only the basename is used). filename must not + be NULL or "". */ +void +source_create_pixbuf (GbWidgetWriteSourceData * data, + const gchar * identifier, + const gchar * filename) +{ + g_return_if_fail (filename && filename[0]); + + /* We use the basename of the pixmap file. The create_pixmap() support + function is responsible for finding the pixmap. + If it can't find the pixmap, it outputs a warning messages and returns + a simple dummy pixmap, so that the app can continue without crashing. */ + if (glade_project_get_gnome_support (data->project)) + { + /* FIXME: Should convert filename to a valid C string? */ + source_add (data, " %s = create_pixbuf (\"%s/%s\");\n", + identifier, data->program_name, g_basename (filename)); + } + else + { + source_add (data, " %s = create_pixbuf (\"%s\");\n", + identifier, g_basename (filename)); + } +} + + +/************************************************************************* + * Utility Functions. + *************************************************************************/ + +/* This empties all the source code buffers, but doesn't free them. */ +static void +source_reset_code_buffers (GbWidgetWriteSourceData * data) +{ + gint i; + + for (i = 0; i < GLADE_NUM_SOURCE_BUFFERS; i++) + g_string_truncate (data->source_buffers[i], 0); +} + + +/* A callback used to free the 'standard widgets' (widgets we use to determine + default property values) when iterating over their GHashTable. */ +static void +source_destroy_standard_widgets_callback (gchar * key, GtkWidget * widget, + gpointer data) +{ + gtk_widget_destroy (widget); +} + + +/* A callback used to free GHashTable keys when iterating over them */ +static void +source_free_hash_keys_callback (gchar * key, gpointer value, gpointer data) +{ + g_free (key); +} + + +/* Checks if the given file is a valid source filename, i.e. not NULL and + not absolute. If not it returns an appropriate error message. */ +static gchar* +source_is_valid_source_filename (const gchar * filename) +{ + if (filename == NULL || filename[0] == '\0') + return _("The filename must be set in the Project Options dialog."); + + if (g_path_is_absolute (filename)) + return _("The filename must be a simple relative filename.\n" + "Use the Project Options dialog to set it."); + + return NULL; +} + + +/* Checks if the given file exists, and if so renames it to file.bak. */ +static GladeError* +source_backup_file_if_exists (const gchar * filename) +{ + GladeError *error = NULL; + gchar *backup_filename; + int status; + + if (glade_util_file_exists (filename)) + { + backup_filename = g_strdup_printf ("%s.bak", filename); +#if defined (__EMX__) || defined (_WIN32) + remove (backup_filename); +#endif + status = rename (filename, backup_filename); + + if (status == -1) + { + error = glade_error_new_system (_("Couldn't rename file:\n %s\nto:\n %s\n"), filename, backup_filename); + } + g_free (backup_filename); + } + return error; +} + + +/* This returns the relative path from the project directory to the source + directory, or NULL if the source directory is the project directory. + The returned string will not have any trailing '/', and should be freed + when no longer needed. */ +static gchar* +source_get_source_subdirectory (GbWidgetWriteSourceData * data) +{ + gchar *directory, *source_directory, *subdir; + gint subdir_len; + + directory = glade_project_get_directory (data->project); + source_directory = glade_project_get_source_directory (data->project); + + /* Check if the source directory is a subdirectory of the project directory. + */ + if (!glade_util_directory_contains_file (directory, source_directory)) + return NULL; + + subdir = glade_util_make_relative_path (directory, source_directory); + subdir_len = strlen (subdir); + if (subdir_len > 0 && subdir[subdir_len - 1] == G_DIR_SEPARATOR) + subdir[subdir_len - 1] = '\0'; + + return subdir; +} + + +/* Outputs a warning to the given file, telling the user not to edit the file + since it is generated by Glade. */ +static void +source_write_no_editing_warning (FILE *fp) +{ + /* + * Output a 3-line comment like this one, makes it easy for translators. + * Actually I've turned translation off now, as UTF-8 may cause problems + * in the C code. See bug #95435. + */ + fprintf (fp, "/*\n * %s\n */\n\n", + "DO NOT EDIT THIS FILE - it is generated by Glade."); +} |