diff options
Diffstat (limited to 'tools/glade/glade/glade_project.c')
-rw-r--r-- | tools/glade/glade/glade_project.c | 1992 |
1 files changed, 1992 insertions, 0 deletions
diff --git a/tools/glade/glade/glade_project.c b/tools/glade/glade/glade_project.c new file mode 100644 index 00000000..26714933 --- /dev/null +++ b/tools/glade/glade/glade_project.c @@ -0,0 +1,1992 @@ +/* Gtk+ User Interface Builder + * Copyright (C) 1998-1999 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/stat.h> +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <errno.h> + +#include <dirent.h> + +#include <libxml/parser.h> + +#include <gtk/gtkmenu.h> +#include <gtk/gtk.h> +#include "gladeconfig.h" + +#include "editor.h" +#include "glade_menu_editor.h" +#include "glade_project.h" +#include "load.h" +#include "property.h" +#include "save.h" +#include "source.h" +#include "tree.h" +#include "utils.h" + +/* FIXME: This is the current project. We only support one open at present. */ +GladeProject *current_project = NULL; + + +/* The order must match the GladeLanguageType enum in glade_project.h. */ +gchar *GladeLanguages[] = { "C", "C++" , "Ada 95", "Perl", "Eiffel" }; +#if 1 +gint GladeNumLanguages = 3; /* Only C, C++ and Ada ported to GTK+ 2. */ +#else +gint GladeNumLanguages = sizeof (GladeLanguages) / sizeof (GladeLanguages[0]); +#endif + +static void glade_project_class_init (GladeProjectClass * klass); +static void glade_project_init (GladeProject *project); +static void glade_project_destroy (GtkObject *object); + +static GladeError* glade_project_write_c_source (GladeProject *project); +static GladeError* glade_project_write_cxx_source (GladeProject *project); +static GladeError* glade_project_write_ada95_source (GladeProject *project); +static GladeError* glade_project_write_perl_source (GladeProject *project); +static GladeError* glade_project_write_eiffel_source (GladeProject *project); + +static void free_key (gchar *key, + gchar *value, + gpointer data); + +static gchar* glade_project_find_id (GladeProject *project, + const gchar *name); +static void glade_project_real_ensure_widgets_named (GtkWidget *widget, + GladeProject *project); + +enum +{ + ADD_COMPONENT, + REMOVE_COMPONENT, + COMPONENT_CHANGED, + LAST_SIGNAL +}; + +static guint glade_project_signals[LAST_SIGNAL] = {0}; + +static GtkObjectClass *parent_class = NULL; + + +GType +glade_project_get_type (void) +{ + static GType glade_project_type = 0; + + if (!glade_project_type) + { + GtkTypeInfo glade_project_info = + { + "GladeProject", + sizeof (GladeProject), + sizeof (GladeProjectClass), + (GtkClassInitFunc) glade_project_class_init, + (GtkObjectInitFunc) glade_project_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + glade_project_type = gtk_type_unique (gtk_object_get_type (), + &glade_project_info); + } + + return glade_project_type; +} + +static void +glade_project_class_init (GladeProjectClass * klass) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass *) klass; + + parent_class = gtk_type_class (gtk_object_get_type ()); + + glade_project_signals[ADD_COMPONENT] = + gtk_signal_new ("add_component", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GladeProjectClass, add_component), + gtk_marshal_VOID__OBJECT, + GTK_TYPE_NONE, 1, GTK_TYPE_OBJECT); + glade_project_signals[REMOVE_COMPONENT] = + gtk_signal_new ("remove_component", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GladeProjectClass, remove_component), + gtk_marshal_VOID__OBJECT, + GTK_TYPE_NONE, 1, GTK_TYPE_OBJECT); + glade_project_signals[COMPONENT_CHANGED] = + gtk_signal_new ("component_changed", + GTK_RUN_LAST, + GTK_CLASS_TYPE (object_class), + GTK_SIGNAL_OFFSET (GladeProjectClass, component_changed), + gtk_marshal_VOID__OBJECT, + GTK_TYPE_NONE, 1, GTK_TYPE_OBJECT); + + klass->add_component = NULL; + klass->remove_component = NULL; + klass->component_changed = NULL; + + object_class->destroy = glade_project_destroy; +} + + +static void +glade_project_init (GladeProject * project) +{ + project->xml_filename = NULL; + + /* The default project name, used when new projects are created. */ + project->name = NULL; + project->program_name = NULL; + project->directory = NULL; + project->source_directory = NULL; + project->pixmaps_directory = NULL; + + project->language = GLADE_LANGUAGE_C; + property_show_lang_specific_page (project->language); + + project->changed = FALSE; + + project->unique_id_hash = g_hash_table_new (g_str_hash, g_str_equal); + + project->components = NULL; + + project->pixmap_filenames = NULL; + + project->current_directory = NULL; + + project->gettext_support = TRUE; + +#ifdef USE_GNOME + project->gnome_support = TRUE; +#else + project->gnome_support = FALSE; +#endif + + project->gnome_db_support = FALSE; + project->gnome_help_support = FALSE; + + project->use_widget_names = FALSE; + project->output_main_file = TRUE; + project->output_support_files = TRUE; + project->output_build_files = TRUE; + project->backup_source_files = TRUE; + + project->main_source_file = g_strdup ("interface.c"); + project->main_header_file = g_strdup ("interface.h"); + project->handler_source_file = g_strdup ("callbacks.c"); + project->handler_header_file = g_strdup ("callbacks.h"); + + project->support_source_file = g_strdup ("support.c"); + project->support_header_file = g_strdup ("support.h"); + + project->output_translatable_strings = FALSE; + project->translatable_strings_file = NULL; +} + + +GladeProject* +glade_project_new (void) +{ + /* FIXME: Currently we only support one project open, so we have to destroy + any existing project, and reset everything. */ + if (current_project) + { + property_set_widget (NULL); + editor_clear_selection (NULL); + gtk_object_destroy (GTK_OBJECT (current_project)); + /* Delete all GbStyles and reset hash table. Do this after all widgets + are destroyed. */ +#ifdef GLADE_STYLE_SUPPORT + gb_widget_reset_gb_styles (); +#endif + } + + current_project = GLADE_PROJECT (gtk_type_new (glade_project_get_type())); + return current_project; +} + + +static void +glade_project_destroy (GtkObject *object) +{ + GladeProject *project; + GList *tmp_list; + + project = GLADE_PROJECT (object); + + g_free (project->xml_filename); + project->xml_filename = NULL; + + g_free (project->name); + project->name = NULL; + + g_free (project->program_name); + project->program_name = NULL; + + g_free (project->directory); + project->directory = NULL; + + g_free (project->source_directory); + project->source_directory = NULL; + + g_free (project->pixmaps_directory); + project->pixmaps_directory = NULL; + + /* Destroy all project components. */ + tmp_list = project->components; + while (tmp_list) + { + gtk_widget_destroy (GTK_WIDGET (tmp_list->data)); + tmp_list = tmp_list->next; + } + g_list_free (project->components); + project->components = NULL; + + /* Empty the tree. Do this after the widgets are destroyed as it is a bit + safer since the widgets won't ever have invalid pointers to the tree + nodes. */ + tree_clear (); + + /* Destroy the unique id hash. */ + if (project->unique_id_hash) + { + g_hash_table_foreach (project->unique_id_hash, (GHFunc) free_key, NULL); + g_hash_table_destroy (project->unique_id_hash); + project->unique_id_hash = NULL; + } + + /* Free all pixmap filenames. */ + tmp_list = project->pixmap_filenames; + while (tmp_list) + { + g_free (tmp_list->data); + tmp_list = tmp_list->next; + } + g_list_free (project->pixmap_filenames); + project->pixmap_filenames = NULL; + + g_free (project->current_directory); + project->current_directory = NULL; + + g_free (project->main_source_file); + project->main_source_file = NULL; + + g_free (project->main_header_file); + project->main_header_file = NULL; + + g_free (project->handler_source_file); + project->handler_source_file = NULL; + + g_free (project->handler_header_file); + project->handler_header_file = NULL; + + g_free (project->support_source_file); + project->support_source_file = NULL; + + g_free (project->support_header_file); + project->support_header_file = NULL; +} + + +/* This is called for each id in the unique id hash. We have to free the + id strings, since they were g_strdup'ed. */ +static void +free_key (gchar *key, + gchar *value, + gpointer data) +{ + g_free (key); +} + + +gboolean +glade_project_open (const gchar *xml_filename, + GladeProject **project_return) +{ + gboolean status; + GladeProject *project; + + project = glade_project_new (); + project->xml_filename = g_strdup (xml_filename); + + tree_freeze (); + + status = load_project_file (project); + + tree_thaw (); + + if (!status) + { + gtk_object_destroy (GTK_OBJECT (project)); + current_project = NULL; + } + else + { + *project_return = project; + } + return status; +} + + +GladeError* +glade_project_save (GladeProject *project) +{ + GladeError *error; + + error = glade_util_ensure_directory_exists (project->directory); + if (error) + return error; + + /* Copy any pixmaps to the project's pixmaps directory. */ + error = glade_project_copy_all_pixmaps (project); + if (error) + return error; + + error = save_project_file (project); + if (error == NULL) + glade_project_set_changed (project, FALSE); + + return error; +} + + +GladeError* +glade_project_write_source (GladeProject *project) +{ + GladeError *error; + + /* First we check that we have a project directory, a source directory, + and a pixmaps directory, and that the source directory is the same as + the project directory or is a subdirectory of it. */ + if (project->directory == NULL) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("The project directory is not set.\n" + "Please set it using the Project Options dialog.\n")); + } + + if (project->source_directory == NULL) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("The source directory is not set.\n" + "Please set it using the Project Options dialog.\n")); + } + + if (!glade_util_directories_equivalent (project->directory, + project->source_directory) + && !glade_util_directory_contains_file (project->directory, + project->source_directory)) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Invalid source directory:\n\n" + "The source directory must be the project directory\n" + "or a subdirectory of the project directory.\n")); + } + + if (project->pixmaps_directory == NULL) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("The pixmaps directory is not set.\n" + "Please set it using the Project Options dialog.\n")); + } + + /* Copy any pixmaps to the project's pixmaps directory. */ + error = glade_project_copy_all_pixmaps (project); + if (error) + return error; + + /* We call a function according to the project's language. */ + switch (project->language) + { + case GLADE_LANGUAGE_C: + return glade_project_write_c_source (project); + case GLADE_LANGUAGE_CPP: + return glade_project_write_cxx_source (project); + case GLADE_LANGUAGE_ADA95: + return glade_project_write_ada95_source (project); + case GLADE_LANGUAGE_PERL: + return glade_project_write_perl_source (project); + case GLADE_LANGUAGE_EIFFEL: + return glade_project_write_eiffel_source (project); + default: + break; + } + + /* Shouldn't get here. */ + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Sorry - generating source for %s is not implemented yet"), + GladeLanguages[project->language]); +} + + +/* C source code output is built in to Glade, so we just call the main + C source code generation function here. */ +static GladeError* +glade_project_write_c_source (GladeProject *project) +{ + return source_write (project); +} + +/* + * Iterate through the list of widgets to ensure we're not using any + * deprecated widgets in the project when emitting C++ code. + */ +static gboolean +check_deprecated_widget (GList* list) +{ + GtkWidget* widget; + GList* tmpList; + gboolean status; + + for (; list != NULL; list = list->next ) + { + widget = GTK_WIDGET (list->data); + + if (GTK_IS_CONTAINER (widget)) + { + tmpList = gtk_container_get_children (GTK_CONTAINER (widget)); + status = check_deprecated_widget (tmpList); + g_list_free (tmpList); + + if (!status) + { + return FALSE; + } + } + + /* Check if any deprecated widgets are being used. Note that we only + check for widgets created by Glade, since GtkCombo uses a GtkList + internally and we don't want to disallow that. */ + if (GB_IS_GB_WIDGET (widget) + && (GTK_IS_CLIST (widget) || + GTK_IS_CTREE (widget) || + GTK_IS_LIST (widget) || + GTK_IS_PIXMAP (widget) || + GTK_IS_PREVIEW (widget) )) + { + return FALSE; + } + } + + return TRUE; +} + +/* Use system() to run glade-- on the XML file to generate C++ source code. */ +static GladeError* +glade_project_write_cxx_source (GladeProject *project) +{ + gchar *command_buffer; + gint status; + + /* + * Ensure that our project does not have deprecated widgets + * that gtkmm-2 doesn't support. + */ + if ( !check_deprecated_widget (project->components)) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Your project uses deprecated widgets that Gtkmm-2\n" + "does not support. Check your project for these\n" + "widgets, and use their replacements.")); + } + + command_buffer = g_strdup_printf ("glade-- %s", project->xml_filename); + status = system (command_buffer); + g_free (command_buffer); + + if (status != 0) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Error running glade-- to generate the C++ source code.\n" + "Check that you have glade-- installed and that it is in your PATH.\n" + "Then try running 'glade-- <project_file.glade>' in a terminal.")); + } + + return NULL; +} + + +/* Use system() to run gate on the XML file to generate Ada95 source code. */ +static GladeError* +glade_project_write_ada95_source (GladeProject *project) +{ + gchar *command_buffer; + gint status; + +#ifdef _WIN32 + chdir(project->directory); +#endif + + command_buffer = g_strdup_printf ("gate %s", project->xml_filename); + status = system (command_buffer); + g_free (command_buffer); + + if (status != 0) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Error running gate to generate the Ada95 source code.\n" + "Check that you have gate installed and that it is in your PATH.\n" + "Then try running 'gate <project_file.glade>' in a terminal.")); + } + + return NULL; +} + + +/* Use system() to run gate on the XML file to generate Ada95 source code. */ +static GladeError* +glade_project_write_perl_source (GladeProject *project) +{ + gchar *command_buffer; + gint status; + + command_buffer = g_strdup_printf ("glade2perl %s", project->xml_filename); + status = system (command_buffer); + g_free (command_buffer); + + if (status != 0) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Error running glade2perl to generate the Perl source code.\n" + "Check that you have glade2perl installed and that it is in your PATH.\n" + "Then try running 'glade2perl <project_file.glade>' in a terminal.")); + } + + return NULL; +} + + +/* Use system() to run eglade on the XML file to generate Eiffel source code. */ +static GladeError* +glade_project_write_eiffel_source (GladeProject *project) +{ + gchar *command_buffer; + gint status; + + command_buffer = g_strdup_printf ("eglade %s", project->xml_filename); + status = system (command_buffer); + g_free (command_buffer); + + if (status != 0) + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("Error running eglade to generate the Eiffel source code.\n" + "Check that you have eglade installed and that it is in your PATH.\n" + "Then try running 'eglade <project_file.glade>' in a terminal.")); + } + + return NULL; +} + + +/* This should be called by anything which changes the project - adding or + removing widgets, changing properties, setting project options etc. + FIXME: It's not used throughout Glade yet. */ +void +glade_project_set_changed (GladeProject *project, + gboolean changed) +{ + project->changed = changed; +} + + +gchar* +glade_project_get_name (GladeProject *project) +{ + return project->name; +} + + +void +glade_project_set_name (GladeProject *project, + const gchar *name) +{ + if (glade_util_strings_equivalent (project->name, name)) + return; + g_free (project->name); + project->name = g_strdup (name); + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_program_name (GladeProject *project) +{ + return project->program_name; +} + + +void +glade_project_set_program_name (GladeProject *project, + const gchar *program_name) +{ + if (glade_util_strings_equivalent (project->program_name, program_name)) + return; + g_free (project->program_name); + project->program_name = g_strdup (program_name); + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_xml_filename (GladeProject *project) +{ + return project->xml_filename; +} + + +void +glade_project_set_xml_filename (GladeProject *project, + const gchar *filename) +{ + gchar *xml_directory; + + if (glade_util_strings_equivalent (project->xml_filename, filename)) + return; + g_free (project->xml_filename); + project->xml_filename = g_strdup (filename); + xml_directory = glade_util_dirname (filename); + + /* If the project directories are not set, set them to defaults based on + the directory the XML file is in. */ + if (!project->directory || project->directory[0] == '\0') + { + project->directory = g_strdup (xml_directory); + } + + if (!project->source_directory || project->source_directory[0] == '\0') + { + project->source_directory = glade_util_make_path (project->directory, + "src"); + } + + if (!project->pixmaps_directory || project->pixmaps_directory[0] == '\0') + { + project->pixmaps_directory = glade_util_make_path (project->directory, + "pixmaps"); + } + + g_free (xml_directory); + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_directory (GladeProject *project) +{ + return project->directory; +} + + +void +glade_project_set_directory (GladeProject *project, + const gchar *directory) +{ + if (glade_util_strings_equivalent (project->directory, directory)) + return; + g_free (project->directory); + project->directory = g_strdup (directory); + glade_project_set_changed (project, TRUE); + + /* If the pixmaps directory is not set, set it to defaults based on + the source directory. */ + if (directory && directory[0] != '\0') + { + if (!project->pixmaps_directory || project->pixmaps_directory[0] == '\0') + { + project->pixmaps_directory = glade_util_make_path (directory, + "pixmaps"); + } + } +} + + +gchar* +glade_project_get_source_directory (GladeProject *project) +{ + return project->source_directory; +} + + +void +glade_project_set_source_directory (GladeProject *project, + const gchar *directory) +{ + if (glade_util_strings_equivalent (project->source_directory, directory)) + return; + g_free (project->source_directory); + project->source_directory = g_strdup (directory); + + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_pixmaps_directory (GladeProject *project) +{ + return project->pixmaps_directory; +} + + +void +glade_project_set_pixmaps_directory (GladeProject *project, + const gchar *directory) +{ + if (glade_util_strings_equivalent (project->pixmaps_directory, directory)) + return; + g_free (project->pixmaps_directory); + project->pixmaps_directory = g_strdup (directory); + glade_project_set_changed (project, TRUE); +} + + +gint +glade_project_get_language (GladeProject *project) +{ + return project->language; +} + + +void +glade_project_set_language (GladeProject *project, + GladeLanguageType language) +{ + if (project->language == language) + return; + project->language = language; + property_show_lang_specific_page (language); + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_set_language_name (GladeProject *project, + const gchar *language_name) +{ + gint language; + + if (language_name == NULL || language_name[0] == '\0') + return FALSE; + + for (language = 0; language < GladeNumLanguages; language++) + { + if (!strcmp (language_name, GladeLanguages[language])) + { + glade_project_set_language (project, language); + return TRUE; + } + } + return FALSE; +} + + +void +glade_project_add_component (GladeProject *project, + GtkWidget *component) +{ + project->components = g_list_append (project->components, component); + tree_add_widget (component); + gtk_signal_emit (GTK_OBJECT (project), + glade_project_signals[ADD_COMPONENT], component); + glade_project_set_changed (project, TRUE); +} + + +void +glade_project_show_component (GladeProject *project, + GtkWidget *component) +{ + /* Popup menus are shown in the menu editor. */ + if (GTK_IS_MENU (component)) + { + GtkWidget *menued; + + menued = glade_menu_editor_new (project, GTK_MENU_SHELL (component)); + gtk_widget_show (menued); + } + else if (GTK_IS_WINDOW (component)) + { + gtk_widget_show (component); + /* This maps the window, which de-iconifies it according to the ICCCM. */ + gdk_window_show (component->window); + /* This raises is to the top, in case it was hidden. */ + gdk_window_raise (component->window); + } + else + g_warning ("Don't know how to show component."); +} + + +void +glade_project_component_changed (GladeProject *project, + GtkWidget *component) +{ + gtk_signal_emit (GTK_OBJECT (project), + glade_project_signals[COMPONENT_CHANGED], component); + glade_project_set_changed (project, TRUE); +} + + +void +glade_project_foreach_component (GladeProject *project, + GtkCallback callback, + gpointer callback_data) +{ + GList *tmp_list; + + g_return_if_fail (project != NULL); + + tmp_list = project->components; + while (tmp_list) + { + (*callback) (GTK_WIDGET (tmp_list->data), callback_data); + tmp_list = tmp_list->next; + } +} + + +void +glade_project_remove_component (GladeProject *project, + GtkWidget *component) +{ + project->components = g_list_remove (project->components, component); + gtk_signal_emit (GTK_OBJECT (project), + glade_project_signals[REMOVE_COMPONENT], component); + + /* FIXME: These could be better. */ + property_set_widget (NULL); + editor_clear_selection (NULL); + + gtk_widget_destroy (component); + glade_project_set_changed (project, TRUE); +} + + +/* These add/remove pixmaps to the project. The same filename can appear + more than once in the project's list of pixmaps, so refcounting isn't + needed. The filename is copied. */ +void +glade_project_add_pixmap (GladeProject *project, + const gchar *filename) +{ + if (filename && filename[0]) + { +#if 0 + g_print ("Adding pixmap: %s\n", filename); +#endif + project->pixmap_filenames = g_list_prepend (project->pixmap_filenames, + g_strdup (filename)); + glade_project_set_changed (project, TRUE); + } +} + + +/* This removes the given pixmap from the project. If the pixmap isn't in + the project it is ignored. */ +void +glade_project_remove_pixmap (GladeProject *project, + const gchar *filename) +{ + GList *element; + + if (!filename) + return; + +#if 0 + g_print ("Removing pixmap: %s\n", filename); +#endif + + element = project->pixmap_filenames; + while (element) + { + if (!strcmp (element->data, filename)) + { + project->pixmap_filenames = g_list_remove_link (project->pixmap_filenames, element); + g_free (element->data); + g_list_free (element); + + glade_project_set_changed (project, TRUE); + break; + } + element = element->next; + } +} + + +/* This ensures that all pixmaps are in the project's pixmaps directory, + by copying them if necessary. Note that it doesn't change the pixmap + filenames in the list. They will be updated the next time the project is + opened. */ +GladeError* +glade_project_copy_all_pixmaps (GladeProject *project) +{ + gchar *pixmaps_dir, *filename, *new_filename; + gint pixmaps_dir_len; + GList *element; + GladeError *error = NULL; + gboolean checked_pixmaps_dir = FALSE; + + pixmaps_dir = glade_project_get_pixmaps_directory (project); + if (!pixmaps_dir || pixmaps_dir[0] == '\0') + { + return glade_error_new_general (GLADE_STATUS_ERROR, + _("The pixmap directory is not set.\n" + "Please set it using the Project Options dialog.\n")); + } + + pixmaps_dir_len = strlen (pixmaps_dir); + + element = project->pixmap_filenames; + while (element) + { + filename = (gchar*) element->data; + + /* If the start of the pixmap filename doesn't match the pixmaps + directory, we make sure it is copied there. */ + if (!glade_util_directory_contains_file (pixmaps_dir, filename)) + { + new_filename = glade_util_make_path (pixmaps_dir, + g_basename (filename)); + + /* Check if it already exists, and copy if it doesn't. */ + if (!glade_util_file_exists (new_filename)) + { + /* We only want to do this once. */ + if (!checked_pixmaps_dir) + { + checked_pixmaps_dir = TRUE; + error = glade_util_ensure_directory_exists (project->pixmaps_directory); + if (error) + { + g_free (new_filename); + break; + } + } + + error = glade_util_copy_file (filename, new_filename); + g_free (new_filename); + if (error) + break; + } + } + element = element->next; + } + + return error; +} + + +gchar* +glade_project_get_current_directory (GladeProject *project) +{ + return project->current_directory; +} + + +void +glade_project_set_current_directory (GladeProject *project, + const gchar *directory) +{ + g_free (project->current_directory); + project->current_directory = g_strdup (directory); +} + + +gboolean +glade_project_get_gnome_support (GladeProject *project) +{ + return project->gnome_support; +} + + +void +glade_project_set_gnome_support (GladeProject *project, + gboolean gnome_support) +{ + if (project->gnome_support == gnome_support) + return; + + /* If we don't have Gnome support compiled-in, we can't build a Gnome app. */ +#ifndef USE_GNOME + if (gnome_support == TRUE) + return; +#endif + + project->gnome_support = gnome_support; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_gnome_db_support (GladeProject *project) +{ + return project->gnome_db_support; +} + + +void +glade_project_set_gnome_db_support (GladeProject *project, + gboolean gnome_db_support) +{ + if (project->gnome_db_support == gnome_db_support) + return; + + project->gnome_db_support = gnome_db_support; + glade_project_set_changed (project, TRUE); +} + + +/* + * C Output options. + */ +gboolean +glade_project_get_gettext_support (GladeProject *project) +{ + return project->gettext_support; +} + + +void +glade_project_set_gettext_support (GladeProject *project, + gboolean gettext_support) +{ + if (project->gettext_support == gettext_support) + return; + project->gettext_support = gettext_support; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_use_widget_names (GladeProject *project) +{ + return project->use_widget_names; +} + + +void +glade_project_set_use_widget_names (GladeProject *project, + gboolean use_widget_names) +{ + if (project->use_widget_names == use_widget_names) + return; + project->use_widget_names = use_widget_names; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_output_main_file (GladeProject *project) +{ + return project->output_main_file; +} + + +void +glade_project_set_output_main_file (GladeProject *project, + gboolean output_main_file) +{ + if (project->output_main_file == output_main_file) + return; + project->output_main_file = output_main_file; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_output_support_files (GladeProject *project) +{ + return project->output_support_files; +} + + +void +glade_project_set_output_support_files (GladeProject *project, + gboolean output_support_files) +{ + if (project->output_support_files == output_support_files) + return; + project->output_support_files = output_support_files; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_output_build_files (GladeProject *project) +{ + return project->output_build_files; +} + + +void +glade_project_set_output_build_files (GladeProject *project, + gboolean output_build_files) +{ + if (project->output_build_files == output_build_files) + return; + project->output_build_files = output_build_files; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_backup_source_files (GladeProject *project) +{ + return project->backup_source_files; +} + + +void +glade_project_set_backup_source_files (GladeProject *project, + gboolean backup_source_files) +{ + if (project->backup_source_files == backup_source_files) + return; + project->backup_source_files = backup_source_files; + glade_project_set_changed (project, TRUE); +} + + +gboolean +glade_project_get_gnome_help_support (GladeProject *project) +{ + return project->gnome_help_support; +} + + +void +glade_project_set_gnome_help_support (GladeProject *project, + gboolean gnome_help_support) +{ + if (project->gnome_help_support == gnome_help_support) + return; + + project->gnome_help_support = gnome_help_support; + glade_project_set_changed (project, TRUE); +} + + +/* FIXME: These will be removed when the source code output is improved. */ +void +glade_project_get_source_files (GladeProject *project, + gchar **main_source_file, + gchar **main_header_file, + gchar **handler_source_file, + gchar **handler_header_file) +{ + *main_source_file = project->main_source_file; + *main_header_file = project->main_header_file; + *handler_source_file = project->handler_source_file; + *handler_header_file = project->handler_header_file; +} + + +void +glade_project_set_source_files (GladeProject *project, + const gchar *main_source_file, + const gchar *main_header_file, + const gchar *handler_source_file, + const gchar *handler_header_file) +{ + if (glade_util_strings_equivalent (project->main_source_file, + main_source_file) + && glade_util_strings_equivalent (project->main_header_file, + main_header_file) + && glade_util_strings_equivalent (project->handler_source_file, + handler_source_file) + && glade_util_strings_equivalent (project->handler_header_file, + handler_header_file)) + return; + + g_free (project->main_source_file); + g_free (project->main_header_file); + g_free (project->handler_source_file); + g_free (project->handler_header_file); + project->main_source_file = g_strdup (main_source_file); + project->main_header_file = g_strdup (main_header_file); + project->handler_source_file = g_strdup (handler_source_file); + project->handler_header_file = g_strdup (handler_header_file); + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_support_source_file (GladeProject *project) +{ + return project->support_source_file; +} + + +void +glade_project_set_support_source_file (GladeProject *project, + const gchar* support_source_file) +{ + if (glade_util_strings_equivalent (project->support_source_file, + support_source_file)) + return; + g_free (project->support_source_file); + project->support_source_file = g_strdup (support_source_file); + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_support_header_file (GladeProject *project) +{ + return project->support_header_file; +} + + +void +glade_project_set_support_header_file (GladeProject *project, + const gchar* support_header_file) +{ + if (glade_util_strings_equivalent (project->support_header_file, + support_header_file)) + return; + g_free (project->support_header_file); + project->support_header_file = g_strdup (support_header_file); + glade_project_set_changed (project, TRUE); +} + + +/* + * libglade options. + */ + +gboolean +glade_project_get_output_translatable_strings (GladeProject *project) +{ + return project->output_translatable_strings; +} + + +void +glade_project_set_output_translatable_strings (GladeProject *project, + gboolean output_translatable_strings) +{ + if (project->output_translatable_strings == output_translatable_strings) + return; + project->output_translatable_strings = output_translatable_strings; + glade_project_set_changed (project, TRUE); +} + + +gchar* +glade_project_get_translatable_strings_file (GladeProject *project) +{ + return project->translatable_strings_file; +} + + +void +glade_project_set_translatable_strings_file (GladeProject *project, + const gchar* file) +{ + if (glade_util_strings_equivalent (project->translatable_strings_file, file)) + return; + g_free (project->translatable_strings_file); + project->translatable_strings_file = g_strdup (file); + glade_project_set_changed (project, TRUE); +} + + +/* + * Loading Project Options. + */ +typedef enum { + PARSER_OPTION, + PARSER_UNKNOWN +} OptionsParserState; + +typedef struct _GladeOptionsParseState GladeOptionsParseState; +struct _GladeOptionsParseState { + OptionsParserState state; + + GladeProject *project; + + gchar *base_directory; + + GString *option_name; + GString *option_value; +}; + + +/* This sets one project option, as it is loaded by the SAX parser. */ +void +glade_project_load_option (GladeOptionsParseState *state) +{ + GladeProject *project; + gchar *base, *option_name, *option_value; + + project = state->project; + base = state->base_directory; + option_name = state->option_name->str; + option_value = state->option_value->str; + +#if 0 + g_print ("Setting Option:'%s' to '%s'\n", option_name, option_value); +#endif + + if (!strcmp (option_name, "name")) + { + g_free (project->name); + project->name = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "program_name")) + { + g_free (project->program_name); + project->program_name = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "directory") && base) + { + g_free (project->directory); + project->directory = glade_util_make_absolute_path (base, option_value); + } + else if (!strcmp (option_name, "source_directory") && base) + { + g_free (project->source_directory); + project->source_directory = glade_util_make_absolute_path (base, + option_value); + } + else if (!strcmp (option_name, "pixmaps_directory") && base) + { + g_free (project->pixmaps_directory); + project->pixmaps_directory = glade_util_make_absolute_path (base, + option_value); + } + else if (!strcmp (option_name, "language")) + { + if (!glade_project_set_language_name (project, option_value)) + g_warning ("Invalid source language"); + } + else if (!strcmp (option_name, "gnome_support")) + { + gboolean gnome_support = load_parse_bool (NULL, option_value); +#ifndef USE_GNOME + if (gnome_support == TRUE) + { + g_warning ("Glade has been compiled without support for Gnome."); + } +#endif + project->gnome_support = gnome_support; + } + else if (!strcmp (option_name, "gnome_db_support")) + { + gboolean gnome_db_support = load_parse_bool (NULL, option_value); +#ifndef USE_GNOME_DB + if (gnome_db_support == TRUE) + { + g_warning ("Glade has been compiled without support for Gnome DB."); + } +#endif + project->gnome_db_support = gnome_db_support; + } + else if (!strcmp (option_name, "gettext_support")) + { + project->gettext_support = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "use_widget_names")) + { + project->use_widget_names = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "output_main_file")) + { + project->output_main_file = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "output_support_files")) + { + project->output_support_files = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "output_build_files")) + { + project->output_build_files = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "backup_source_files")) + { + project->backup_source_files = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "gnome_help_support")) + { + project->gnome_help_support = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "main_source_file")) + { + g_free (project->main_source_file); + project->main_source_file = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "main_header_file")) + { + g_free (project->main_header_file); + project->main_header_file = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "handler_source_file")) + { + g_free (project->handler_source_file); + project->handler_source_file = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "handler_header_file")) + { + g_free (project->handler_header_file); + project->handler_header_file = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "support_source_file")) + { + g_free (project->support_source_file); + project->support_source_file = glade_util_copy_string (option_value); + } + else if (!strcmp (option_name, "support_header_file")) + { + g_free (project->support_header_file); + project->support_header_file = glade_util_copy_string (option_value); + } + + else if (!strcmp (option_name, "output_translatable_strings")) + { + project->output_translatable_strings = load_parse_bool (NULL, option_value); + } + else if (!strcmp (option_name, "translatable_strings_file") && base) + { + g_free (project->translatable_strings_file); + project->translatable_strings_file = glade_util_make_absolute_path (base, option_value); + } + + else + { + g_warning ("Unknown project option: %s\n", option_name); + } +} + + +static xmlEntityPtr +glade_options_parser_get_entity(GladeOptionsParseState *state, const xmlChar *name) +{ + return xmlGetPredefinedEntity(name); +} + +static void +glade_options_parser_warning(GladeOptionsParseState *state, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + g_logv("XML", G_LOG_LEVEL_WARNING, msg, args); + va_end(args); +} + +static void +glade_options_parser_error(GladeOptionsParseState *state, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + g_logv("XML", G_LOG_LEVEL_CRITICAL, msg, args); + va_end(args); +} + +static void +glade_options_parser_fatal_error(GladeOptionsParseState *state, const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + g_logv("XML", G_LOG_LEVEL_ERROR, msg, args); + va_end(args); +} + +static void +glade_options_parser_start_document(GladeOptionsParseState *state) +{ + state->state = PARSER_UNKNOWN; + + state->option_name = g_string_sized_new (128); + state->option_value = g_string_sized_new (1024); +} + +static void +glade_options_parser_end_document(GladeOptionsParseState *state) +{ + g_string_free (state->option_name, TRUE); + g_string_free (state->option_value, TRUE); +} + +static void +glade_options_parser_start_element(GladeOptionsParseState *state, + const xmlChar *name, const xmlChar **attrs) +{ + g_string_assign (state->option_name, name); + g_string_truncate (state->option_value, 0); + + state->state = PARSER_OPTION; +} + +static void +glade_options_parser_end_element(GladeOptionsParseState *state, const xmlChar *name) +{ + if (state->state != PARSER_OPTION) + return; + + glade_project_load_option (state); + + state->state = PARSER_UNKNOWN; +} + +static void +glade_options_parser_characters(GladeOptionsParseState *state, const xmlChar *chars, int len) +{ + switch (state->state) { + case PARSER_OPTION: + g_string_append_len (state->option_value, chars, len); + break; + default: + /* don't care about content in any other states */ + break; + } +} + +static xmlSAXHandler glade_options_parser = { + 0, /* internalSubset */ + 0, /* isStandalone */ + 0, /* hasInternalSubset */ + 0, /* hasExternalSubset */ + 0, /* resolveEntity */ + (getEntitySAXFunc)glade_options_parser_get_entity, /* getEntity */ + 0, /* entityDecl */ + 0, /* notationDecl */ + 0, /* attributeDecl */ + 0, /* elementDecl */ + 0, /* unparsedEntityDecl */ + 0, /* setDocumentLocator */ + (startDocumentSAXFunc)glade_options_parser_start_document, /* startDocument */ + (endDocumentSAXFunc)glade_options_parser_end_document, /* endDocument */ + (startElementSAXFunc)glade_options_parser_start_element, /* startElement */ + (endElementSAXFunc)glade_options_parser_end_element, /* endElement */ + 0, /* reference */ + (charactersSAXFunc)glade_options_parser_characters, /* characters */ + 0, /* ignorableWhitespace */ + 0, /* processingInstruction */ + (commentSAXFunc)0, /* comment */ + (warningSAXFunc)glade_options_parser_warning, /* warning */ + (errorSAXFunc)glade_options_parser_error, /* error */ + (fatalErrorSAXFunc)glade_options_parser_fatal_error, /* fatalError */ +}; + + + + +/* Returns TRUE if the options file was found and loaded OK. */ +gboolean +glade_project_load_options (GladeProject *project) +{ + gchar *filename, *base; + gboolean retval = FALSE; + + /* Check if the options file exists. If it doesn't we use defaults for + everything. */ + filename = g_strdup_printf ("%sp", GladeSessionFile ? GladeSessionFile : project->xml_filename); + base = project->xml_filename ? g_dirname (project->xml_filename) : NULL; + + if (glade_util_file_exists (filename)) + { + GladeOptionsParseState state = { 0 }; + + state.project = project; + state.base_directory = base; + + if (xmlSAXUserParseFile (&glade_options_parser, &state, filename) < 0) + { + g_warning("document not well formed!"); + } + else + { + retval = TRUE; + } + } + + /* Check that the directory options are set to defaults, but only if we + have a project filename set. We may not have one when loading a session + file. */ + if (base) + { + if (project->directory == NULL) + project->directory = g_strdup (base); + if (project->source_directory == NULL) + project->source_directory = glade_util_make_absolute_path (base, "src"); + if (project->pixmaps_directory == NULL) + project->pixmaps_directory = glade_util_make_absolute_path (base, + "pixmaps"); + } + + g_free (filename); + g_free (base); + + return retval; +} + + +/* + * Saving Project Options. + */ +static void +save_option (GString *buffer, gint indent, gchar *tag_name, gchar *tag_value) +{ + gint i; + + for (i = 0; i < indent; i++) + g_string_append (buffer, " "); + + g_string_append_printf (buffer, "<%s>%s</%s>\n", + tag_name, tag_value ? tag_value : "", tag_name); +} + + +static void +save_bool_option (GString *buffer, gint indent, gchar *tag_name, + gboolean tag_value) +{ + save_option (buffer, indent, tag_name, tag_value ? "TRUE" : "FALSE"); +} + + +GladeError* +glade_project_save_options (GladeProject *project, + FILE *fp) +{ + GladeError *error = NULL; + GString *buffer; + gchar *base_dir, *dir, *file; + gint indent = 1, bytes_written; + + buffer = g_string_sized_new (1024); + + g_string_append (buffer, "<glade-project>\n"); + + save_option (buffer, indent, "name", project->name); + save_option (buffer, indent, "program_name", project->program_name); + + /* All directories are saved relative to the xml file's directory. */ + base_dir = glade_util_dirname (project->xml_filename); + + /* We use defaults for most properties so only a few properties need to be + saved. */ + + dir = glade_util_make_relative_path (base_dir, project->directory); + if (strcmp (dir, "")) + save_option (buffer, indent, "directory", dir); + g_free (dir); + + dir = glade_util_make_relative_path (base_dir, project->source_directory); + if (strcmp (dir, "src")) + save_option (buffer, indent, "source_directory", dir); + g_free (dir); + + dir = glade_util_make_relative_path (base_dir, project->pixmaps_directory); + if (strcmp (dir, "pixmaps")) + save_option (buffer, indent, "pixmaps_directory", dir); + g_free (dir); + + if (project->language != GLADE_LANGUAGE_C) + save_option (buffer, indent, "language", GladeLanguages[project->language]); + if (!project->gnome_support) + save_bool_option (buffer, indent, "gnome_support", project->gnome_support); + + if (project->gnome_db_support) + save_bool_option (buffer, indent, "gnome_db_support", project->gnome_db_support); + + + /* + * C Options. + */ + if (!project->gettext_support) + save_bool_option (buffer, indent, "gettext_support", project->gettext_support); + if (project->use_widget_names) + save_bool_option (buffer, indent, "use_widget_names", project->use_widget_names); + if (!project->output_main_file) + save_bool_option (buffer, indent, "output_main_file", project->output_main_file); + if (!project->output_support_files) + save_bool_option (buffer, indent, "output_support_files", project->output_support_files); + if (!project->output_build_files) + save_bool_option (buffer, indent, "output_build_files", project->output_build_files); + if (!project->backup_source_files) + save_bool_option (buffer, indent, "backup_source_files", project->backup_source_files); + if (project->gnome_help_support) + save_bool_option (buffer, indent, "gnome_help_support", project->gnome_help_support); + + if (!project->main_source_file + || strcmp (project->main_source_file, "interface.c")) + save_option (buffer, indent, "main_source_file", project->main_source_file); + if (!project->main_header_file + || strcmp (project->main_header_file, "interface.h")) + save_option (buffer, indent, "main_header_file", project->main_header_file); + if (!project->handler_source_file + || strcmp (project->handler_source_file, "callbacks.c")) + save_option (buffer, indent, "handler_source_file", project->handler_source_file); + if (!project->handler_header_file + || strcmp (project->handler_header_file, "callbacks.h")) + save_option (buffer, indent, "handler_header_file", project->handler_header_file); + if (!project->support_source_file + || strcmp (project->support_source_file, "support.c")) + save_option (buffer, indent, "support_source_file", project->support_source_file); + if (!project->support_header_file + || strcmp (project->support_header_file, "support.h")) + save_option (buffer, indent, "support_header_file", project->support_header_file); + + if (project->output_translatable_strings) + save_bool_option (buffer, indent, "output_translatable_strings", TRUE); + if (project->translatable_strings_file + && project->translatable_strings_file[0]) + { + file = glade_util_make_relative_path (base_dir, project->translatable_strings_file); + if (strcmp (file, "")) + save_option (buffer, indent, "translatable_strings_file", file); + g_free (file); + } + + g_free (base_dir); + + g_string_append (buffer, "</glade-project>\n"); + + bytes_written = fwrite (buffer->str, sizeof (gchar), buffer->len, fp); + if (bytes_written != buffer->len) + error = glade_error_new_system (_("Error writing project XML file\n")); + + g_string_free (buffer, TRUE); + + return error; +} + + +/* + * Functions to do with ensuring widget names are unique. + */ + +/* This returns a unique name for a widget using the given base name, often + a widget class name. If the base name starts with 'Gtk' or 'Gnome' then + that is taken off. The base name is converted to lower case and a number is + added on to the end of it to ensure that no other widget in the project + uses the same name. */ +gchar* +glade_project_new_widget_name (GladeProject *project, + const gchar *base_name) +{ + char new_widget_name[128], *id_start; + gint widget_id, i, new_widget_name_len; + + /* Check we won't overflow the buffer. */ + g_return_val_if_fail (strlen (base_name) < 100, g_strdup (base_name)); + + /* Skip 'Gtk' or 'Gnome' at the start of all class names. */ + if (!strncmp (base_name, "Gtk", 3)) + base_name += 3; + else if (!strncmp (base_name, "Gnome", 5)) + base_name += 5; + + strcpy (new_widget_name, base_name); + + /* Remove any id number at the end of the name. */ + id_start = glade_project_find_id (project, new_widget_name); + if (id_start) + *id_start = '\0'; + + /* convert name to lower case (only normal ASCII chars) */ + new_widget_name_len = strlen (new_widget_name); + for (i = 0; i < new_widget_name_len; i++) + { + if ((new_widget_name[i] >= 'A') && (new_widget_name[i] <= 'Z')) + new_widget_name[i] += 'a' - 'A'; + } + + widget_id = GPOINTER_TO_INT (g_hash_table_lookup (project->unique_id_hash, + new_widget_name)); + if (widget_id == 0) + { + widget_id = 1; + g_hash_table_insert (project->unique_id_hash, g_strdup (new_widget_name), + GINT_TO_POINTER (widget_id)); + } + else + { + widget_id++; + /* We don't need to g_strdup new_widget_name since it is already in the + hash. */ + g_hash_table_insert (project->unique_id_hash, new_widget_name, + GINT_TO_POINTER (widget_id)); + } + + /* Add the ID onto the end of the name. */ + sprintf (new_widget_name + strlen (new_widget_name), "%i", widget_id); + + MSG1 ("Allocating new widget name: %s", new_widget_name); + return g_strdup (new_widget_name); +} + + +/* This releases a widget name, so that it can possibly be used again. + It will only be reused if it is the last ID with the same prefix. This is + still useful as it means that if a name is generated based on something + the user edits (e.g. a menuitem label), only the final name is reserved - + all the intermediate names get released so we don't waste IDs. */ +void +glade_project_release_widget_name (GladeProject *project, + const gchar *name) +{ + gchar buffer[128]; + const gchar *id_start; + gint id = 0, current_id; + gpointer hash_key, hash_value; + gboolean found; + + id_start = glade_project_find_id (project, name); + if (id_start == NULL) + return; + + /* Make sure we won't overflow the buffer. */ + g_return_if_fail (id_start - name < 127); + + id = atoi (id_start); + if (id == 0) + return; + + strncpy (buffer, name, id_start - name); + buffer[id_start - name] = '\0'; + + found = g_hash_table_lookup_extended (project->unique_id_hash, buffer, + &hash_key, &hash_value); + if (found) + { + current_id = GPOINTER_TO_INT (hash_value); + /* We only release the ID if it is the last one. */ + if (current_id == id) + { + /* If the current ID is 1, remove it from the hash completely. */ + if (current_id == 1) + { + g_hash_table_remove (project->unique_id_hash, buffer); + g_free (hash_key); + } + else + { + /* We don't need to g_strdup buffer since it is already in the + hash. */ + g_hash_table_insert (project->unique_id_hash, buffer, + GINT_TO_POINTER (id - 1)); + } + } + } +} + + +/* Check if the name has a trailing ID number, and if so compare it to the + current maximum in the ID hash and update if necessary. */ +void +glade_project_reserve_name (GladeProject *project, + const gchar *name) +{ + gchar buffer[128]; + const gchar *id_start; + gint id = 0, current_id; + + id_start = glade_project_find_id (project, name); + if (id_start == NULL) + return; + + /* Make sure we won't overflow the buffer. */ + g_return_if_fail (id_start - name < 127); + + id = atoi (id_start); + if (id == 0) + return; + + strncpy (buffer, name, id_start - name); + buffer[id_start - name] = '\0'; + + current_id = GPOINTER_TO_INT (g_hash_table_lookup (project->unique_id_hash, + buffer)); + if (current_id == 0) + { + g_hash_table_insert (project->unique_id_hash, g_strdup (buffer), + GINT_TO_POINTER (id)); + } + else if (id > current_id) + { + /* We don't need to g_strdup buffer since it is already in the hash. */ + g_hash_table_insert (project->unique_id_hash, buffer, + GINT_TO_POINTER (id)); + } +} + + +/* This returns the start of the ID number at the end of the widget name, + or NULL if there is no number. */ +static gchar* +glade_project_find_id (GladeProject *project, + const gchar *name) +{ + gint pos; + + pos = strlen (name) - 1; + if (pos <= 0) + return NULL; + + /* Step back from the end of the string to find the first digit. */ + while (pos > 0 && name[pos] >= '0' && name[pos] <= '9') + pos--; + + /* We may have gone too far, so check. */ + if (!(name[pos] >= '0' && name[pos] <= '9')) + pos++; + + /* If there is no trailing number return NULL. */ + if (name[pos] == '\0') + return NULL; + + return (gchar*) &name[pos]; +} + + +/* This recursively descends a widget tree, ensuring that all widgets have + names. If any names are NULL, default names are created for them. */ +void +glade_project_ensure_widgets_named (GladeProject *project, + GtkWidget *widget) +{ + /* We have to switch the arguments for the recursive calls to work. */ + glade_project_real_ensure_widgets_named (widget, project); +} + + +static void +glade_project_real_ensure_widgets_named (GtkWidget *widget, + GladeProject *project) +{ + if (GB_IS_GB_WIDGET (widget) && widget->name == NULL) + { + gchar *class = gb_widget_get_class_id (widget); + gtk_widget_set_name (widget, + glade_project_new_widget_name (project, class)); + MSG1 ("set default name for widget: %s\n", gtk_widget_get_name (widget)); + } + + gb_widget_children_foreach (widget, (GtkCallback) glade_project_real_ensure_widgets_named, project); +} |