diff options
Diffstat (limited to 'gladeui/glade-project.c')
-rw-r--r-- | gladeui/glade-project.c | 2309 |
1 files changed, 0 insertions, 2309 deletions
diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c deleted file mode 100644 index 31c8ba12..00000000 --- a/gladeui/glade-project.c +++ /dev/null @@ -1,2309 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2001 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Authors: - * Chema Celorio <chema@celorio.com> - */ - -#include <config.h> - -#include <string.h> -#include <stdlib.h> -#include <glib.h> -#include <glib/gi18n-lib.h> -#include <glib/gstdio.h> - -#include "glade.h" -#include "glade-widget.h" -#include "glade-id-allocator.h" -#include "glade-app.h" -#include "glade-marshallers.h" -#include "glade-catalog.h" - -#include "glade-project.h" - -#define GLADE_PROJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GLADE_TYPE_PROJECT, GladeProjectPrivate)) - -enum -{ - ADD_WIDGET, - REMOVE_WIDGET, - WIDGET_NAME_CHANGED, - SELECTION_CHANGED, - CLOSE, - RESOURCE_ADDED, - RESOURCE_REMOVED, - CHANGED, - PARSE_FINISHED, - LAST_SIGNAL -}; - -enum -{ - PROP_0, - PROP_MODIFIED, - PROP_HAS_SELECTION, - PROP_PATH, - PROP_READ_ONLY -}; - -struct _GladeProjectPrivate -{ - gchar *path; /* The full canonical path of the glade file for this project */ - - guint instance_count; /* How many projects with this name */ - - gint unsaved_number; /* A unique number for this project if it is untitled */ - - gboolean readonly; /* A flag that is set if the project is readonly */ - - gboolean loading; /* A flags that is set when the project is loading */ - - gboolean modified; /* A flag that is set when a project has unsaved modifications - * if this flag is not set we don't have to query - * for confirmation after a close or exit is - * requested - */ - - GList *objects; /* A list of #GObjects that make up this project. - * The objects are stored in no particular order. - */ - - GList *selection; /* We need to keep the selection in the project - * because we have multiple projects and when the - * user switchs between them, he will probably - * not want to loose the selection. This is a list - * of #GtkWidget items. - */ - - gboolean has_selection; /* Whether the project has a selection */ - - GList *undo_stack; /* A stack with the last executed commands */ - GList *prev_redo_item; /* Points to the item previous to the redo items */ - GHashTable *widget_names_allocator; /* hash table with the used widget names */ - GHashTable *widget_old_names; /* widget -> old name of the widget */ - GtkTooltips *tooltips; - - GladeCommand *first_modification; /* we record the first modification, so that we - * can set "modification" to FALSE when we - * undo this modification - */ - - GtkAccelGroup *accel_group; - - GHashTable *resources; /* resource filenames & thier associated properties */ - - gchar *comment; /* XML comment, Glade will preserve whatever comment was - * in file, so users can delete or change it. - */ - - time_t mtime; /* last UTC modification time of file, or 0 if it could not be read */ -}; - - -static guint glade_project_signals[LAST_SIGNAL] = {0}; - -static GladeIDAllocator *unsaved_number_allocator = NULL; - -static gboolean glade_project_load_from_interface (GladeProject *project, - GladeInterface *interface, - const gchar *path); - - -G_DEFINE_TYPE (GladeProject, glade_project, G_TYPE_OBJECT) - - -/******************************************************************* - GObjectClass - *******************************************************************/ - -static GladeIDAllocator * -get_unsaved_number_allocator (void) -{ - if (unsaved_number_allocator == NULL) - unsaved_number_allocator = glade_id_allocator_new (); - - return unsaved_number_allocator; -} - -static void -glade_project_list_unref (GList *original_list) -{ - GList *l; - for (l = original_list; l; l = l->next) - g_object_unref (G_OBJECT (l->data)); - - if (original_list != NULL) - g_list_free (original_list); -} - -static void -glade_project_dispose (GObject *object) -{ - GladeProject *project = GLADE_PROJECT (object); - GList *list; - GladeWidget *gwidget; - - /* Emit close signal */ - g_signal_emit (object, glade_project_signals [CLOSE], 0); - - glade_project_selection_clear (project, TRUE); - - glade_project_list_unref (project->priv->undo_stack); - project->priv->undo_stack = NULL; - - /* Unparent all widgets in the heirarchy first - * (Since we are bookkeeping exact reference counts, we - * dont want the hierarchy to just get destroyed) - */ - for (list = project->priv->objects; list; list = list->next) - { - gwidget = glade_widget_get_from_gobject (list->data); - - if (gwidget->parent && - gwidget->internal == NULL && - glade_widget_adaptor_has_child (gwidget->parent->adaptor, - gwidget->parent->object, - gwidget->object)) - glade_widget_remove_child (gwidget->parent, gwidget); - } - - /* Remove objects from the project */ - for (list = project->priv->objects; list; list = list->next) - { - gwidget = glade_widget_get_from_gobject (list->data); - - g_object_unref (G_OBJECT (list->data)); /* Remove the GladeProject reference */ - g_object_unref (G_OBJECT (gwidget)); /* Remove the overall "Glade" reference */ - } - project->priv->objects = NULL; - - gtk_object_destroy (GTK_OBJECT (project->priv->tooltips)); - project->priv->tooltips = NULL; - - G_OBJECT_CLASS (glade_project_parent_class)->dispose (object); -} - -static void -glade_project_finalize (GObject *object) -{ - GladeProject *project = GLADE_PROJECT (object); - - g_free (project->priv->path); - g_free (project->priv->comment); - - if (project->priv->unsaved_number > 0) - glade_id_allocator_release (get_unsaved_number_allocator (), project->priv->unsaved_number); - - g_hash_table_destroy (project->priv->widget_names_allocator); - g_hash_table_destroy (project->priv->widget_old_names); - g_hash_table_destroy (project->priv->resources); - - G_OBJECT_CLASS (glade_project_parent_class)->finalize (object); -} - -static void -glade_project_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GladeProject *project = GLADE_PROJECT (object); - - switch (prop_id) - { - case PROP_MODIFIED: - g_value_set_boolean (value, project->priv->modified); - break; - case PROP_HAS_SELECTION: - g_value_set_boolean (value, project->priv->has_selection); - break; - case PROP_READ_ONLY: - g_value_set_boolean (value, project->priv->readonly); - break; - case PROP_PATH: - g_value_set_string (value, project->priv->path); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/** - * glade_project_set_modified: - * @project: a #GladeProject - * @modified: Whether the project should be set as modified or not - * @modification: The first #GladeCommand which caused the project to have unsaved changes - * - * Set's whether a #GladeProject should be flagged as modified or not. This is useful - * for indicating that a project has unsaved changes. If @modified is #TRUE, then - * @modification will be recorded as the first change which caused the project to - * have unsaved changes. @modified is #FALSE then @modification will be ignored. - * - * If @project is already flagged as modified, then calling this method with - * @modified as #TRUE, will have no effect. Likewise, if @project is unmodified - * then calling this method with @modified as #FALSE, will have no effect. - * - */ -static void -glade_project_set_modified (GladeProject *project, - gboolean modified, - GladeCommand *modification) -{ - GladeProjectPrivate *priv = project->priv; - - if (priv->modified != modified) - { - priv->modified = !priv->modified; - - if (priv->modified) - { - g_assert (priv->first_modification == NULL); - g_assert (modification != NULL); - priv->first_modification = modification; - } - else - { - g_assert (priv->first_modification != NULL); - priv->first_modification = NULL; - } - - g_object_notify (G_OBJECT (project), "modified"); - } -} - -/******************************************************************* - GladeProjectClass - *******************************************************************/ -static void -glade_project_walk_back (GladeProject *project) -{ - if (project->priv->prev_redo_item) - project->priv->prev_redo_item = project->priv->prev_redo_item->prev; -} - -static void -glade_project_walk_forward (GladeProject *project) -{ - if (project->priv->prev_redo_item) - project->priv->prev_redo_item = project->priv->prev_redo_item->next; - else - project->priv->prev_redo_item = project->priv->undo_stack; -} - -static void -glade_project_undo_impl (GladeProject *project) -{ - GladeCommand *cmd, *next_cmd; - - while ((cmd = glade_project_next_undo_item (project)) != NULL) - { - glade_command_undo (cmd); - - glade_project_walk_back (project); - - g_signal_emit (G_OBJECT (project), - glade_project_signals [CHANGED], - 0, cmd, FALSE); - - if ((next_cmd = glade_project_next_undo_item (project)) != NULL && - (next_cmd->group_id == 0 || next_cmd->group_id != cmd->group_id)) - break; - } -} - -static void -glade_project_redo_impl (GladeProject *project) -{ - GladeCommand *cmd, *next_cmd; - - while ((cmd = glade_project_next_redo_item (project)) != NULL) - { - glade_command_execute (cmd); - - glade_project_walk_forward (project); - - g_signal_emit (G_OBJECT (project), - glade_project_signals [CHANGED], - 0, cmd, TRUE); - - if ((next_cmd = glade_project_next_redo_item (project)) != NULL && - (next_cmd->group_id == 0 || next_cmd->group_id != cmd->group_id)) - break; - } -} - -static GladeCommand * -glade_project_next_undo_item_impl (GladeProject *project) -{ - GList *l; - - if ((l = project->priv->prev_redo_item) == NULL) - return NULL; - - return GLADE_COMMAND (l->data); -} - -static GladeCommand * -glade_project_next_redo_item_impl (GladeProject *project) -{ - GList *l; - - if ((l = project->priv->prev_redo_item) == NULL) - return project->priv->undo_stack ? - GLADE_COMMAND (project->priv->undo_stack->data) : NULL; - else - return l->next ? GLADE_COMMAND (l->next->data) : NULL; -} - -static void -glade_project_push_undo_impl (GladeProject *project, GladeCommand *cmd) -{ - GladeProjectPrivate *priv = project->priv; - GList *tmp_redo_item; - - /* If there are no "redo" items, and the last "undo" item unifies with - us, then we collapse the two items in one and we're done */ - if (priv->prev_redo_item != NULL && priv->prev_redo_item->next == NULL) - { - GladeCommand *cmd1 = priv->prev_redo_item->data; - - if (glade_command_unifies (cmd1, cmd)) - { - glade_command_collapse (cmd1, cmd); - g_object_unref (cmd); - - g_signal_emit (G_OBJECT (project), - glade_project_signals [CHANGED], - 0, cmd1, TRUE); - return; - } - } - - /* We should now free all the "redo" items */ - tmp_redo_item = g_list_next (priv->prev_redo_item); - while (tmp_redo_item) - { - g_assert (tmp_redo_item->data); - - /* just for safety, we might not need this */ - if (GLADE_COMMAND (tmp_redo_item->data) == priv->first_modification) - priv->first_modification = NULL; - - g_object_unref (G_OBJECT (tmp_redo_item->data)); - - tmp_redo_item = g_list_next (tmp_redo_item); - } - - if (priv->prev_redo_item) - { - g_list_free (g_list_next (priv->prev_redo_item)); - priv->prev_redo_item->next = NULL; - } - else - { - g_list_free (priv->undo_stack); - priv->undo_stack = NULL; - } - - /* and then push the new undo item */ - priv->undo_stack = g_list_append (priv->undo_stack, cmd); - - if (project->priv->prev_redo_item == NULL) - priv->prev_redo_item = priv->undo_stack; - else - priv->prev_redo_item = g_list_next (priv->prev_redo_item); - - - g_signal_emit (G_OBJECT (project), - glade_project_signals [CHANGED], - 0, cmd, TRUE); -} - -static void -glade_project_changed_impl (GladeProject *project, - GladeCommand *command, - gboolean forward) -{ - if (!project->priv->loading) - { - /* if this command is the first modification to cause the project - * to have unsaved changes, then we can now flag the project as unmodified - */ - if (command == project->priv->first_modification) - glade_project_set_modified (project, FALSE, NULL); - else - glade_project_set_modified (project, TRUE, command); - } - glade_app_update_ui (); -} - -/******************************************************************* - Initializers - *******************************************************************/ -static void -glade_project_init (GladeProject *project) -{ - GladeProjectPrivate *priv; - - project->priv = priv = GLADE_PROJECT_GET_PRIVATE (project); - - priv->path = NULL; - priv->instance_count = 0; - priv->readonly = FALSE; - priv->objects = NULL; - priv->selection = NULL; - priv->has_selection = FALSE; - priv->undo_stack = NULL; - priv->prev_redo_item = NULL; - priv->first_modification = NULL; - priv->widget_names_allocator = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) glade_id_allocator_destroy); - - priv->widget_old_names = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free); - - priv->tooltips = gtk_tooltips_new (); - priv->accel_group = NULL; - - priv->resources = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, g_free); - - priv->unsaved_number = glade_id_allocator_allocate (get_unsaved_number_allocator ()); -} - -static void -glade_project_class_init (GladeProjectClass *klass) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (klass); - - object_class->get_property = glade_project_get_property; - object_class->finalize = glade_project_finalize; - object_class->dispose = glade_project_dispose; - - klass->add_object = NULL; - klass->remove_object = NULL; - klass->undo = glade_project_undo_impl; - klass->redo = glade_project_redo_impl; - klass->next_undo_item = glade_project_next_undo_item_impl; - klass->next_redo_item = glade_project_next_redo_item_impl; - klass->push_undo = glade_project_push_undo_impl; - - klass->widget_name_changed = NULL; - klass->selection_changed = NULL; - klass->close = NULL; - klass->resource_added = NULL; - klass->resource_removed = NULL; - klass->changed = glade_project_changed_impl; - - /** - * GladeProject::add-widget: - * @gladeproject: the #GladeProject which received the signal. - * @arg1: the #GladeWidget that was added to @gladeproject. - * - * Emitted when a widget is added to a project. - */ - glade_project_signals[ADD_WIDGET] = - g_signal_new ("add_widget", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, add_object), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - GLADE_TYPE_WIDGET); - - /** - * GladeProject::remove-widget: - * @gladeproject: the #GladeProject which received the signal. - * @arg1: the #GladeWidget that was removed from @gladeproject. - * - * Emitted when a widget is removed from a project. - */ - glade_project_signals[REMOVE_WIDGET] = - g_signal_new ("remove_widget", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, remove_object), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - GLADE_TYPE_WIDGET); - - - /** - * GladeProject::widget-name-changed: - * @gladeproject: the #GladeProject which received the signal. - * @arg1: the #GladeWidget who's name changed. - * - * Emitted when @gwidget's name changes. - */ - glade_project_signals[WIDGET_NAME_CHANGED] = - g_signal_new ("widget_name_changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, widget_name_changed), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, - 1, - GLADE_TYPE_WIDGET); - - - /** - * GladeProject::selection-changed: - * @gladeproject: the #GladeProject which received the signal. - * - * Emitted when @gladeproject selection list changes. - */ - glade_project_signals[SELECTION_CHANGED] = - g_signal_new ("selection_changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, selection_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - - /** - * GladeProject::close: - * @gladeproject: the #GladeProject which received the signal. - * - * Emitted when a project is closing (a good time to clean up - * any associated resources). - */ - glade_project_signals[CLOSE] = - g_signal_new ("close", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, close), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - - /** - * GladeProject::resource-added: - * @gladeproject: the #GladeProject which received the signal. - * @arg1: the file's basename (in the project path). - * - * Emitted when a resource file required by a #GladeProperty is - * added to @gladeproject - */ - glade_project_signals[RESOURCE_ADDED] = - g_signal_new ("resource-added", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, resource_added), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - /** - * GladeProject::resource-removed: - * @gladeproject: the #GladeProject which received the signal. - * @arg1: the file's basename - * - * Emitted when a resource file is removed from @gladeproject - */ - glade_project_signals[RESOURCE_REMOVED] = - g_signal_new ("resource-removed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GladeProjectClass, resource_removed), - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, - 1, - G_TYPE_STRING); - - - /** - * GladeProject::changed: - * @gladeproject: the #GladeProject which received the signal. - * @arg1: the #GladeCommand that was executed - * @arg2: whether the command was executed or undone. - * - * Emitted when a @gladeproject's state changes via a #GladeCommand. - */ - glade_project_signals[CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GladeProjectClass, changed), - NULL, NULL, - glade_marshal_VOID__OBJECT_BOOLEAN, - G_TYPE_NONE, - 2, - GLADE_TYPE_COMMAND, G_TYPE_BOOLEAN); - - /** - * GladeProject::parse-finished: - * @gladeproject: the #GladeProject which received the signal. - * - * Emitted when @gladeproject parsing has finished. - */ - glade_project_signals[PARSE_FINISHED] = - g_signal_new ("parse-finished", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GladeProjectClass, parse_finished), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); - - g_object_class_install_property (object_class, - PROP_MODIFIED, - g_param_spec_boolean ("modified", - _(""), - _("Whether project has beem modified since it was last saved"), - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_HAS_SELECTION, - g_param_spec_boolean ("has-selection", - _("Has Selection"), - _("Whether project has a selection"), - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_READ_ONLY, - g_param_spec_boolean ("read-only", - _("Read Only"), - _("Whether project is read only or not"), - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_PATH, - g_param_spec_string ("path", - _("Path"), - _("The filesystem path of the project"), - NULL, - G_PARAM_READABLE)); - - g_type_class_add_private (klass, sizeof (GladeProjectPrivate)); -} - -/******************************************************************* - API - *******************************************************************/ - -static void -glade_project_set_readonly (GladeProject *project, gboolean readonly) -{ - g_assert (GLADE_IS_PROJECT (project)); - - if (project->priv->readonly != readonly) - { - project->priv->readonly = readonly; - g_object_notify (G_OBJECT (project), "read-only"); - } -} - -/** - * glade_project_get_readonly: - * @project: a #GladeProject - * - * Gets whether the project is read only or not - * - * Returns: TRUE if project is read only - */ -gboolean -glade_project_get_readonly (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - - return project->priv->readonly; -} - -/** - * glade_project_new: - * - * Creates a new #GladeProject. - * - * Returns: a new #GladeProject - */ -GladeProject * -glade_project_new (void) -{ - return g_object_new (GLADE_TYPE_PROJECT, NULL); -} - -gboolean -glade_project_load_from_file (GladeProject *project, const gchar *path) -{ - GladeInterface *interface; - - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - interface = glade_parser_interface_new_from_file (path, NULL); - - if (interface != NULL) - { - if (!glade_project_load_from_interface (project, interface, path)) - { - glade_parser_interface_destroy (interface); - return FALSE; - } - glade_parser_interface_destroy (interface); - } - else - { - return FALSE; - } - - if (glade_util_file_is_writeable (project->priv->path) == FALSE) - glade_project_set_readonly (project, TRUE); - - project->priv->modified = FALSE; - - project->priv->mtime = glade_util_get_file_mtime (project->priv->path, NULL); - - return TRUE; -} - - -/** - * glade_project_selection_changed: - * @project: a #GladeProject - * - * Causes @project to emit a "selection_changed" signal. - */ -void -glade_project_selection_changed (GladeProject *project) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_signal_emit (G_OBJECT (project), - glade_project_signals [SELECTION_CHANGED], - 0); -} - -static void -glade_project_on_widget_notify (GladeWidget *widget, GParamSpec *arg, GladeProject *project) -{ - g_return_if_fail (GLADE_IS_WIDGET (widget)); - g_return_if_fail (GLADE_IS_PROJECT (project)); - - switch (arg->name[0]) - { - case 'n': - if (strcmp (arg->name, "name") == 0) - { - const char *old_name = g_hash_table_lookup (project->priv->widget_old_names, widget); - glade_project_widget_name_changed (project, widget, old_name); - g_hash_table_insert (project->priv->widget_old_names, widget, g_strdup (glade_widget_get_name (widget))); - } - - case 'p': - if (strcmp (arg->name, "project") == 0) - glade_project_remove_object (project, glade_widget_get_object (widget)); - } -} - - -static void -gp_sync_resources (GladeProject *project, - GladeProject *prev_project, - GladeWidget *gwidget, - gboolean remove) -{ - GList *prop_list, *l; - GladeProperty *property; - gchar *resource, *full_resource; - - prop_list = g_list_copy (gwidget->properties); - prop_list = g_list_concat - (prop_list, g_list_copy (gwidget->packing_properties)); - - for (l = prop_list; l; l = l->next) - { - property = l->data; - if (property->klass->resource) - { - GValue value = { 0, }; - - if (remove) - { - glade_project_set_resource (project, property, NULL); - continue; - } - - glade_property_get_value (property, &value); - - if ((resource = glade_property_class_make_string_from_gvalue - (property->klass, &value)) != NULL) - { - full_resource = glade_project_resource_fullpath - (prev_project ? prev_project : project, resource); - - /* Use a full path here so that the current - * working directory isnt used. - */ - glade_project_set_resource (project, - property, - full_resource); - - g_free (full_resource); - g_free (resource); - } - g_value_unset (&value); - } - } - g_list_free (prop_list); -} - -static void -glade_project_sync_resources_for_widget (GladeProject *project, - GladeProject *prev_project, - GladeWidget *gwidget, - gboolean remove) -{ - GList *children, *l; - GladeWidget *gchild; - - children = glade_widget_adaptor_get_children - (gwidget->adaptor, gwidget->object); - - for (l = children; l; l = l->next) - if ((gchild = - glade_widget_get_from_gobject (l->data)) != NULL) - glade_project_sync_resources_for_widget - (project, prev_project, gchild, remove); - if (children) - g_list_free (children); - - gp_sync_resources (project, prev_project, gwidget, remove); -} - -/** - * glade_project_add_object: - * @project: the #GladeProject the widget is added to - * @old_project: the #GladeProject the widget was previously in - * (or %NULL for the clipboard) - * @object: the #GObject to add - * - * Adds an object to the project. - */ -void -glade_project_add_object (GladeProject *project, - GladeProject *old_project, - GObject *object) -{ - GladeWidget *gwidget; - GList *list, *children; - GtkWindow *transient_parent; - static gint reentrancy_count = 0; - - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - - /* We don't list placeholders */ - if (GLADE_IS_PLACEHOLDER (object)) - return; - - /* Only widgets accounted for in the catalog or widgets declared - * in the plugin with glade_widget_new_for_internal_child () are - * usefull in the project. - */ - if ((gwidget = glade_widget_get_from_gobject (object)) == NULL) - return; - - /* Code body starts here */ - reentrancy_count++; - - if ((children = glade_widget_adaptor_get_children - (gwidget->adaptor, gwidget->object)) != NULL) - { - for (list = children; list && list->data; list = list->next) - glade_project_add_object - (project, old_project, G_OBJECT (list->data)); - g_list_free (children); - } - - glade_widget_set_project (gwidget, (gpointer)project); - g_hash_table_insert (project->priv->widget_old_names, - gwidget, g_strdup (glade_widget_get_name (gwidget))); - - g_signal_connect (G_OBJECT (gwidget), "notify", - (GCallback) glade_project_on_widget_notify, project); - - project->priv->objects = g_list_prepend (project->priv->objects, g_object_ref (object)); - - g_signal_emit (G_OBJECT (project), - glade_project_signals [ADD_WIDGET], - 0, gwidget); - - if (GTK_IS_WINDOW (object) && - (transient_parent = glade_app_get_transient_parent ()) != NULL) - gtk_window_set_transient_for (GTK_WINDOW (object), transient_parent); - - /* Notify widget it was added to the project */ - glade_widget_project_notify (gwidget, project); - - /* Call this once at the end for every recursive call */ - if (--reentrancy_count == 0) - glade_project_sync_resources_for_widget - (project, old_project, gwidget, FALSE); -} - -/** - * glade_project_has_object: - * @project: the #GladeProject the widget is added to - * @object: the #GObject to search - * - * Returns whether this object is in this project. - */ -gboolean -glade_project_has_object (GladeProject *project, GObject *object) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - g_return_val_if_fail (G_IS_OBJECT (object), FALSE); - return (g_list_find (project->priv->objects, object)) != NULL; -} - -/** - * glade_project_release_widget_name: - * @project: a #GladeProject - * @glade_widget: - * @widget_name: - * - * TODO: Write me - */ -static void -glade_project_release_widget_name (GladeProject *project, GladeWidget *glade_widget, const char *widget_name) -{ - const char *first_number = widget_name; - char *end_number; - char *base_widget_name; - GladeIDAllocator *id_allocator; - gunichar ch; - int id; - - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (GLADE_IS_WIDGET (glade_widget)); - - do - { - ch = g_utf8_get_char (first_number); - - if (ch == 0 || g_unichar_isdigit (ch)) - break; - - first_number = g_utf8_next_char (first_number); - } - while (TRUE); - - if (ch == 0) - return; - - base_widget_name = g_strdup (widget_name); - *(base_widget_name + (first_number - widget_name)) = 0; - - id_allocator = g_hash_table_lookup (project->priv->widget_names_allocator, base_widget_name); - if (id_allocator == NULL) - goto lblend; - - id = (int) strtol (first_number, &end_number, 10); - if (*end_number != 0) - goto lblend; - - glade_id_allocator_release (id_allocator, id); - - lblend: - g_hash_table_remove (project->priv->widget_old_names, glade_widget); - g_free (base_widget_name); -} - -/** - * glade_project_remove_object: - * @project: a #GladeProject - * @object: the #GObject to remove - * - * Removes @object from @project. - * - * Note that when removing the #GObject from the project we - * don't change ->project in the associated #GladeWidget; this - * way UNDO can work. - */ -void -glade_project_remove_object (GladeProject *project, GObject *object) -{ - GladeWidget *gwidget; - GList *link, *list, *children; - static gint reentrancy_count = 0; - - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - - if (GLADE_IS_PLACEHOLDER (object)) - return; - - if ((gwidget = glade_widget_get_from_gobject (object)) == NULL) - return; - - /* Code body starts here */ - reentrancy_count++; - - /* Notify widget is being removed from the project */ - glade_widget_project_notify (gwidget, NULL); - - if ((children = - glade_widget_adaptor_get_children (gwidget->adaptor, - gwidget->object)) != NULL) - { - for (list = children; list && list->data; list = list->next) - glade_project_remove_object (project, G_OBJECT (list->data)); - g_list_free (children); - } - - glade_project_selection_remove (project, object, TRUE); - - if ((link = g_list_find (project->priv->objects, object)) != NULL) - { - g_object_unref (object); - glade_project_release_widget_name (project, gwidget, - glade_widget_get_name (gwidget)); - project->priv->objects = g_list_delete_link (project->priv->objects, link); - } - - g_signal_emit (G_OBJECT (project), - glade_project_signals [REMOVE_WIDGET], - 0, - gwidget); - - /* Call this once at the end for every recursive call */ - if (--reentrancy_count == 0) - glade_project_sync_resources_for_widget (project, NULL, gwidget, TRUE); -} - -/** - * glade_project_widget_name_changed: - * @project: a #GladeProject - * @widget: a #GladeWidget - * @old_name: - * - * TODO: write me - */ -void -glade_project_widget_name_changed (GladeProject *project, GladeWidget *widget, const char *old_name) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (GLADE_IS_WIDGET (widget)); - - glade_project_release_widget_name (project, widget, old_name); - - g_signal_emit (G_OBJECT (project), - glade_project_signals [WIDGET_NAME_CHANGED], - 0, - widget); -} - -/** - * glade_project_get_widget_by_name: - * @project: a #GladeProject - * @name: The user visible name of the widget we are looking for - * - * Searches through @project looking for a #GladeWidget named @name. - * - * Returns: a pointer to the widget, %NULL if the widget does not exist - */ -GladeWidget * -glade_project_get_widget_by_name (GladeProject *project, const gchar *name) -{ - GList *list; - - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - g_return_val_if_fail (name != NULL, NULL); - - for (list = project->priv->objects; list; list = list->next) { - GladeWidget *widget; - - widget = glade_widget_get_from_gobject (list->data); - g_return_val_if_fail (widget->name != NULL, NULL); - if (strcmp (widget->name, name) == 0) - return widget; - } - - return NULL; -} - -/** - * glade_project_new_widget_name: - * @project: a #GladeProject - * @base_name: base name of the widget to create - * - * Creates a new name for a widget that doesn't collide with any of the names - * already in @project. This name will start with @base_name. - * - * Returns: a string containing the new name, %NULL if there is not enough - * memory for this string - */ -char * -glade_project_new_widget_name (GladeProject *project, const char *base_name) -{ - GladeIDAllocator *id_allocator; - const gchar *number; - gchar *name = NULL, *freeme = NULL; - guint i = 1; - - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - number = base_name + strlen (base_name); - - while (number > base_name && g_ascii_isdigit (number[-1])) - --number; - - if (*number) - { - freeme = g_strndup (base_name, number - base_name); - base_name = freeme; - } - - id_allocator = g_hash_table_lookup (project->priv->widget_names_allocator, base_name); - - if (id_allocator == NULL) - { - id_allocator = glade_id_allocator_new (); - g_hash_table_insert (project->priv->widget_names_allocator, - g_strdup (base_name), id_allocator); - } - - while (TRUE) - { - i = glade_id_allocator_allocate (id_allocator); - name = g_strdup_printf ("%s%u", base_name, i); - - /* ok, there is no widget with this name, so return the name */ - if (glade_project_get_widget_by_name (project, name) == NULL) - return name; - - /* we already have a widget with this name, so free the name and - * try again with another number */ - g_free (name); - i++; - } - - g_free (freeme); - return name; -} - -static void -glade_project_set_has_selection (GladeProject *project, gboolean has_selection) -{ - g_assert (GLADE_IS_PROJECT (project)); - - if (project->priv->has_selection != has_selection) - { - project->priv->has_selection = has_selection; - g_object_notify (G_OBJECT (project), "has-selection"); - } -} - -/** - * glade_project_get_has_selection: - * @project: a #GladeProject - * - * Returns: whether @project currently has a selection - */ -gboolean -glade_project_get_has_selection (GladeProject *project) -{ - g_assert (GLADE_IS_PROJECT (project)); - - return project->priv->has_selection; -} - -/** - * glade_project_is_selected: - * @project: a #GladeProject - * @object: a #GObject - * - * Returns: whether @object is in @project selection - */ -gboolean -glade_project_is_selected (GladeProject *project, - GObject *object) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - return (g_list_find (project->priv->selection, object)) != NULL; -} - -/** - * glade_project_selection_clear: - * @project: a #GladeProject - * @emit_signal: whether or not to emit a signal indication a selection change - * - * Clears @project's selection chain - * - * If @emit_signal is %TRUE, calls glade_project_selection_changed(). - */ -void -glade_project_selection_clear (GladeProject *project, gboolean emit_signal) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - if (project->priv->selection == NULL) - return; - - glade_util_clear_selection (); - - g_list_free (project->priv->selection); - project->priv->selection = NULL; - glade_project_set_has_selection (project, FALSE); - - if (emit_signal) - glade_project_selection_changed (project); -} - -/** - * glade_project_selection_remove: - * @project: a #GladeProject - * @object: a #GObject in @project - * @emit_signal: whether or not to emit a signal - * indicating a selection change - * - * Removes @object from the selection chain of @project - * - * If @emit_signal is %TRUE, calls glade_project_selection_changed(). - */ -void -glade_project_selection_remove (GladeProject *project, - GObject *object, - gboolean emit_signal) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - - if (glade_project_is_selected (project, object)) - { - if (GTK_IS_WIDGET (object)) - glade_util_remove_selection (GTK_WIDGET (object)); - project->priv->selection = g_list_remove (project->priv->selection, object); - if (project->priv->selection == NULL) - glade_project_set_has_selection (project, FALSE); - if (emit_signal) - glade_project_selection_changed (project); - } -} - -/** - * glade_project_selection_add: - * @project: a #GladeProject - * @object: a #GObject in @project - * @emit_signal: whether or not to emit a signal indicating - * a selection change - * - * Adds @object to the selection chain of @project - * - * If @emit_signal is %TRUE, calls glade_project_selection_changed(). - */ -void -glade_project_selection_add (GladeProject *project, - GObject *object, - gboolean emit_signal) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - g_return_if_fail (g_list_find (project->priv->objects, object) != NULL); - - if (glade_project_is_selected (project, object) == FALSE) - { - if (GTK_IS_WIDGET (object)) - glade_util_add_selection (GTK_WIDGET (object)); - if (project->priv->selection == NULL) - glade_project_set_has_selection (project, TRUE); - project->priv->selection = g_list_prepend (project->priv->selection, object); - if (emit_signal) - glade_project_selection_changed (project); - } -} - -/** - * glade_project_selection_set: - * @project: a #GladeProject - * @object: a #GObject in @project - * @emit_signal: whether or not to emit a signal - * indicating a selection change - * - * Set the selection in @project to @object - * - * If @emit_signal is %TRUE, calls glade_project_selection_changed(). - */ -void -glade_project_selection_set (GladeProject *project, - GObject *object, - gboolean emit_signal) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - - if (g_list_find (project->priv->objects, object) == NULL) - return; - - if (project->priv->selection == NULL) - glade_project_set_has_selection (project, TRUE); - - if (glade_project_is_selected (project, object) == FALSE || - g_list_length (project->priv->selection) != 1) - { - glade_project_selection_clear (project, FALSE); - glade_project_selection_add (project, object, emit_signal); - } -} - -/** - * glade_project_selection_get: - * @project: a #GladeProject - * - * Returns: a #GList containing the #GtkWidget items currently selected in - * @project - */ -GList * -glade_project_selection_get (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - return project->priv->selection; -} - -static GList * -glade_project_required_libs (GladeProject *project) -{ - GList *required = NULL, *l, *ll; - GladeWidget *gwidget; - gboolean listed; - - for (l = project->priv->objects; l; l = l->next) - { - gchar *catalog = NULL; - - gwidget = glade_widget_get_from_gobject (l->data); - g_assert (gwidget); - - g_object_get (gwidget->adaptor, "catalog", &catalog, NULL); - - if (catalog) - { - listed = FALSE; - for (ll = required; ll; ll = ll->next) - if (!strcmp ((gchar *)ll->data, catalog)) - { - listed = TRUE; - break; - } - - if (!listed) - required = g_list_prepend (required, catalog); - } - } - return required; -} - -#define GLADE_XML_COMMENT "Generated with "PACKAGE_NAME - -static gchar * -glade_project_make_comment () -{ - time_t now = time (NULL); - gchar *comment; - comment = g_strdup_printf (GLADE_XML_COMMENT" "PACKAGE_VERSION" on %s", - ctime (&now)); - glade_util_replace (comment, '\n', ' '); - - return comment; -} - -static void -glade_project_update_comment (GladeProject *project) -{ - gchar **lines, **l, *comment = NULL; - - /* If project has no comment -> add the new one */ - if (project->priv->comment == NULL) - { - project->priv->comment = glade_project_make_comment (); - return; - } - - for (lines = l = g_strsplit (project->priv->comment, "\n", 0); *l; l++) - { - gchar *start; - - /* Ignore leading spaces */ - for (start = *l; *start && g_ascii_isspace (*start); start++); - - if (g_str_has_prefix (start, GLADE_XML_COMMENT)) - { - /* This line was generated by glade -> updating... */ - g_free (*l); - *l = comment = glade_project_make_comment (); - } - } - - if (comment) - { - g_free (project->priv->comment); - project->priv->comment = g_strjoinv ("\n", lines); - } - - g_strfreev (lines); -} - -/** - * glade_project_write: - * @project: a #GladeProject - * - * Returns: a libglade's GladeInterface representation of the - * project and its contents - */ -static GladeInterface * -glade_project_write (GladeProject *project) -{ - GladeInterface *interface; - GList *required, *list, *tops = NULL; - gchar **strv = NULL; - guint i; - - interface = glade_parser_interface_new (); - - if ((required = glade_project_required_libs (project)) != NULL) - { - strv = g_malloc0 (g_list_length (required) * sizeof (char *)); - for (i = 0, list = required; list; i++, list = list->next) - strv[i] = list->data; /* list->data is allocated for us */ - - interface->n_requires = g_list_length (required); - interface->requires = strv; - - g_list_free (required); - } - - for (i = 0, list = project->priv->objects; list; list = list->next) - { - GladeWidget *widget; - GladeWidgetInfo *info; - - widget = glade_widget_get_from_gobject (list->data); - - /* - * Append toplevel widgets. Each widget then takes - * care of appending its children. - */ - if (widget->parent == NULL) - { - info = glade_widget_write (widget, interface); - if (!info) - return NULL; - - tops = g_list_prepend (tops, info); - ++i; - } - } - interface->n_toplevels = i; - interface->toplevels = (GladeWidgetInfo **) g_new (GladeWidgetInfo *, i); - for (i = 0, list = tops; list; list = list->next, ++i) - interface->toplevels[i] = list->data; - - g_list_free (tops); - - glade_project_update_comment (project); - interface->comment = g_strdup (project->priv->comment); - - return interface; -} - -static gboolean -loadable_interface (GladeInterface *interface, const gchar *path) -{ - GString *string = g_string_new (NULL); - gboolean loadable = TRUE; - guint i; - - /* Check for required plugins here - */ - for (i = 0; i < interface->n_requires; i++) - if (!glade_catalog_is_loaded (interface->requires[i])) - { - g_string_append (string, interface->requires[i]); - loadable = FALSE; - } - - if (loadable == FALSE) - glade_util_ui_message (glade_app_get_window(), - GLADE_UI_ERROR, - _("Failed to load %s.\n" - "The following required catalogs are unavailable: %s"), - path, string->str); - g_string_free (string, TRUE); - return loadable; -} - -static void -glade_project_fix_object_props (GladeProject *project) -{ - GList *l, *ll; - GValue *value; - GladeWidget *gwidget; - GladeProperty *property; - gchar *txt; - - for (l = project->priv->objects; l; l = l->next) - { - gwidget = glade_widget_get_from_gobject (l->data); - - for (ll = gwidget->properties; ll; ll = ll->next) - { - property = GLADE_PROPERTY (ll->data); - - if (glade_property_class_is_object (property->klass) && - (txt = g_object_get_data (G_OBJECT (property), - "glade-loaded-object")) != NULL) - { - /* Parse the object list and set the property to it - * (this magicly works for both objects & object lists) - */ - value = glade_property_class_make_gvalue_from_string - (property->klass, txt, project); - - glade_property_set_value (property, value); - - g_value_unset (value); - g_free (value); - - g_object_set_data (G_OBJECT (property), - "glade-loaded-object", NULL); - } - } - } -} - -static gboolean -glade_project_load_from_interface (GladeProject *project, - GladeInterface *interface, - const gchar *path) -{ - GladeWidget *widget; - guint i; - - g_return_val_if_fail (project != NULL, FALSE); - g_return_val_if_fail (interface != NULL, FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - if (loadable_interface (interface, path) == FALSE) - return FALSE; - - project->priv->path = glade_util_canonical_path (path); - - project->priv->selection = NULL; - project->priv->objects = NULL; - project->priv->loading = TRUE; - - /* keep a comment */ - if (interface->comment) - project->priv->comment = g_strdup (interface->comment); - - for (i = 0; i < interface->n_toplevels; ++i) - { - widget = glade_widget_read ((gpointer) project, interface->toplevels[i]); - - if (!widget) - { - g_warning ("Failed to read a <widget> tag"); - continue; - } - - glade_project_add_object (project, NULL, widget->object); - } - - /* Reset project status here too so that you get a clean - * slate after calling glade_project_open(). - */ - project->priv->modified = FALSE; - project->priv->loading = FALSE; - - /* Emit "parse-finished" signal */ - g_signal_emit (project, glade_project_signals [PARSE_FINISHED], 0); - - /* Now we have to loop over all the object properties - * and fix'em all ('cause they probably weren't found) - */ - glade_project_fix_object_props (project); - - return TRUE; -} - -/** - * glade_project_load: - * @path: - * - * Opens a project at the given path. - * - * Returns: a new #GladeProject for the opened project on success, %NULL on - * failure - */ -GladeProject * -glade_project_load (const gchar *path) -{ - GladeProject *project; - gboolean retval; - - g_return_val_if_fail (path != NULL, NULL); - - project = glade_project_new (); - - retval = glade_project_load_from_file (project, path); - - if (retval) - { - return project; - } - else - { - g_object_unref (project); - return NULL; - } -} - -static void -glade_project_move_resources (GladeProject *project, - const gchar *old_dir, - const gchar *new_dir) -{ - GList *list, *resources; - gchar *old_name, *new_name; - - if (old_dir == NULL || /* <-- Cant help you :( */ - new_dir == NULL) /* <-- Unlikely */ - return; - - if ((resources = /* Nothing to do here */ - glade_project_list_resources (project)) == NULL) - return; - - for (list = resources; list; list = list->next) - { - old_name = g_build_filename - (old_dir, (gchar *)list->data, NULL); - new_name = g_build_filename - (new_dir, (gchar *)list->data, NULL); - glade_util_copy_file (old_name, new_name); - g_free (old_name); - g_free (new_name); - } - g_list_free (resources); -} - -/** - * glade_project_save: - * @project: a #GladeProject - * @path: location to save glade file - * @error: an error from the G_FILE_ERROR domain. - * - * Saves @project to the given path. - * - * Returns: %TRUE on success, %FALSE on failure - */ -gboolean -glade_project_save (GladeProject *project, const gchar *path, GError **error) -{ - GladeInterface *interface; - gboolean ret; - gchar *canonical_path; - - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - - interface = glade_project_write (project); - if (!interface) - { - g_warning ("Could not write glade document\n"); - return FALSE; - } - - ret = glade_parser_interface_dump (interface, path, error); - glade_parser_interface_destroy (interface); - - canonical_path = glade_util_canonical_path (path); - g_assert (canonical_path); - - if (project->priv->path == NULL || - strcmp (canonical_path, project->priv->path)) - { - gchar *old_dir, *new_dir; - - if (project->priv->path) - { - old_dir = g_path_get_dirname (project->priv->path); - new_dir = g_path_get_dirname (canonical_path); - - glade_project_move_resources (project, - old_dir, - new_dir); - g_free (old_dir); - g_free (new_dir); - } - project->priv->path = (g_free (project->priv->path), - g_strdup (canonical_path)); - } - - glade_project_set_readonly (project, - !glade_util_file_is_writeable (project->priv->path)); - - project->priv->mtime = glade_util_get_file_mtime (project->priv->path, NULL); - - glade_project_set_modified (project, FALSE, NULL); - - if (project->priv->unsaved_number > 0) - { - glade_id_allocator_release (get_unsaved_number_allocator (), project->priv->unsaved_number); - project->priv->unsaved_number = 0; - } - - g_free (canonical_path); - - return ret; -} - - -/** - * glade_project_undo: - * @project: a #GladeProject - * - * Undoes a #GladeCommand in this project. - */ -void -glade_project_undo (GladeProject *project) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - GLADE_PROJECT_GET_CLASS (project)->undo (project); -} - -/** - * glade_project_undo: - * @project: a #GladeProject - * - * Redoes a #GladeCommand in this project. - */ -void -glade_project_redo (GladeProject *project) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - GLADE_PROJECT_GET_CLASS (project)->redo (project); -} - -/** - * glade_project_next_undo_item: - * @project: a #GladeProject - * - * Gets the next undo item on @project's command stack. - * - * Returns: the #GladeCommand - */ -GladeCommand * -glade_project_next_undo_item (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - return GLADE_PROJECT_GET_CLASS (project)->next_undo_item (project); -} - - -/** - * glade_project_next_redo_item: - * @project: a #GladeProject - * - * Gets the next redo item on @project's command stack. - * - * Returns: the #GladeCommand - */ -GladeCommand * -glade_project_next_redo_item (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - return GLADE_PROJECT_GET_CLASS (project)->next_redo_item (project); -} - - - -/** - * glade_project_push_undo: - * @project: a #GladeProject - * @cmd: the #GladeCommand - * - * Pushes a newly created #GladeCommand onto @projects stack. - */ -void -glade_project_push_undo (GladeProject *project, GladeCommand *cmd) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (GLADE_IS_COMMAND (cmd)); - - GLADE_PROJECT_GET_CLASS (project)->push_undo (project, cmd); -} - -static GList * -walk_command (GList *list, gboolean forward) -{ - GladeCommand *cmd = list->data; - GladeCommand *next_cmd; - - if (forward) - list = list->next; - else - list = list->prev; - - next_cmd = list ? list->data : NULL; - - while (list && next_cmd->group_id != 0 && - next_cmd->group_id == cmd->group_id) - { - if (forward) - list = list->next; - else - list = list->prev; - - if (list) - next_cmd = list->data; - } - - return list; -} - -static void -undo_item_activated (GtkMenuItem *item, - GladeProject *project) -{ - gint index, next_index; - - GladeCommand *cmd = g_object_get_data (G_OBJECT (item), "command-data"); - GladeCommand *next_cmd; - - index = g_list_index (project->priv->undo_stack, cmd); - - do - { - next_cmd = glade_project_next_undo_item (project); - next_index = g_list_index (project->priv->undo_stack, next_cmd); - - glade_project_undo (project); - - } while (next_index > index); -} - -static void -redo_item_activated (GtkMenuItem *item, - GladeProject *project) -{ - gint index, next_index; - - GladeCommand *cmd = g_object_get_data (G_OBJECT (item), "command-data"); - GladeCommand *next_cmd; - - index = g_list_index (project->priv->undo_stack, cmd); - - do - { - next_cmd = glade_project_next_redo_item (project); - next_index = g_list_index (project->priv->undo_stack, next_cmd); - - glade_project_redo (project); - - } while (next_index < index); -} - - -/** - * glade_project_undo_items: - * @project: A #GladeProject - * - * Creates a menu of the undo items in the project stack - * - * Returns: A newly created menu - */ -GtkWidget * -glade_project_undo_items (GladeProject *project) -{ - GtkWidget *menu = NULL; - GtkWidget *item; - GladeCommand *cmd; - GList *l; - - for (l = project->priv->prev_redo_item; l; l = walk_command (l, FALSE)) - { - cmd = l->data; - - if (!menu) menu = gtk_menu_new (); - - item = gtk_menu_item_new_with_label (cmd->description); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), GTK_WIDGET (item)); - g_object_set_data (G_OBJECT (item), "command-data", cmd); - - g_signal_connect (G_OBJECT (item), "activate", - G_CALLBACK (undo_item_activated), project); - - } - - return menu; -} - -/** - * glade_project_redo_items: - * @project: A #GladeProject - * - * Creates a menu of the undo items in the project stack - * - * Returns: A newly created menu - */ -GtkWidget * -glade_project_redo_items (GladeProject *project) -{ - GtkWidget *menu = NULL; - GtkWidget *item; - GladeCommand *cmd; - GList *l; - - for (l = project->priv->prev_redo_item ? - project->priv->prev_redo_item->next : - project->priv->undo_stack; - l; l = walk_command (l, TRUE)) - { - cmd = l->data; - - if (!menu) menu = gtk_menu_new (); - - item = gtk_menu_item_new_with_label (cmd->description); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (menu), GTK_WIDGET (item)); - g_object_set_data (G_OBJECT (item), "command-data", cmd); - - g_signal_connect (G_OBJECT (item), "activate", - G_CALLBACK (redo_item_activated), project); - - } - - return menu; -} - - -void -glade_project_reset_path (GladeProject *project) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - project->priv->path = (g_free (project->priv->path), NULL); -} - -/** - * glade_project_get_tooltips: - * @project: a #GladeProject - * - * Returns: a #GtkTooltips object containing all tooltips for @project - */ -GtkTooltips * -glade_project_get_tooltips (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - return project->priv->tooltips; -} - -/** - * glade_project_set_accel_group: - * @project: A #GladeProject - * @accel_group: The @GtkAccelGroup - * - * Set @accel_group to every top level widget in @project. - */ -void -glade_project_set_accel_group (GladeProject *project, GtkAccelGroup *accel_group) -{ - GList *objects; - - g_return_if_fail (GLADE_IS_PROJECT (project) && GTK_IS_ACCEL_GROUP (accel_group)); - - objects = project->priv->objects; - while (objects) - { - if(GTK_IS_WINDOW (objects->data)) - { - if (project->priv->accel_group) - gtk_window_remove_accel_group (GTK_WINDOW (objects->data), project->priv->accel_group); - - gtk_window_add_accel_group (GTK_WINDOW (objects->data), accel_group); - } - - objects = objects->next; - } - - project->priv->accel_group = accel_group; -} - -/** - * glade_project_resource_fullpath: - * @project: The #GladeProject. - * @resource: The resource basename - * - * Returns: A newly allocated string holding the - * full path the the project resource. - */ -gchar * -glade_project_resource_fullpath (GladeProject *project, - const gchar *resource) -{ - gchar *fullpath, *project_dir; - - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - if (project->priv->path == NULL) - return g_strdup (resource); - - project_dir = g_path_get_dirname (project->priv->path); - fullpath = g_build_filename (project_dir, resource, NULL); - g_free (project_dir); - - return fullpath; -} - - -static gboolean -find_resource_by_resource (GladeProperty *key, - const gchar *resource, - const gchar *resource_cmp) -{ - g_assert (resource); - g_assert (resource_cmp); - return (!strcmp (resource, resource_cmp)); -} - - - -/** - * glade_project_set_resource: - * @project: A #GladeProject - * @property: The #GladeProperty this resource is required by - * @resource: The resource file basename to be found in the same - * directory as the glade file. - * - * Adds/Modifies/Removes a resource from a project; any project resources - * will be copied when using "Save As...", when moving widgets across projects - * and will be copied into the project's directory when selected. - */ -void -glade_project_set_resource (GladeProject *project, - GladeProperty *property, - const gchar *resource) -{ - gchar *last_resource, *last_resource_dup = NULL, *base_resource = NULL; - gchar *fullpath, *dirname; - - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (GLADE_IS_PROPERTY (property)); - - if ((last_resource = - g_hash_table_lookup (project->priv->resources, property)) != NULL) - last_resource_dup = g_strdup (last_resource); - - /* Get dependable input */ - if (resource && resource[0] != '\0' && strcmp (resource, ".")) - base_resource = g_path_get_basename (resource); - - /* If the resource has been removed or the base name has changed - * then remove from hash and emit removed. - */ - if (last_resource_dup && - (base_resource == NULL || strcmp (last_resource_dup, base_resource))) - { - g_hash_table_remove (project->priv->resources, property); - - if (g_hash_table_find (project->priv->resources, - (GHRFunc)find_resource_by_resource, - last_resource_dup) == NULL) - g_signal_emit (G_OBJECT (project), - glade_project_signals [RESOURCE_REMOVED], - 0, last_resource_dup); - } - - /* Copy files when importing widgets with resources. - */ - if (project->priv->path) - { - dirname = g_path_get_dirname (project->priv->path); - fullpath = g_build_filename (dirname, base_resource, NULL); - - if (resource && project->priv->path && - g_file_test (resource, G_FILE_TEST_IS_REGULAR) && - strcmp (fullpath, resource)) - { - /* FIXME: In the case of copy/pasting widgets - * across projects we should ask the user about - * copying any resources. - */ - glade_util_copy_file (resource, fullpath); - } - g_free (fullpath); - g_free (dirname); - } - - if (base_resource) - { - - /* If the resource has been added or the base name has - * changed then emit added. - */ - if ((last_resource_dup == NULL || - strcmp (last_resource_dup, base_resource)) && - g_hash_table_find (project->priv->resources, - (GHRFunc)find_resource_by_resource, - base_resource) == NULL) - g_signal_emit (G_OBJECT (project), - glade_project_signals [RESOURCE_ADDED], - 0, base_resource); - - g_hash_table_insert (project->priv->resources, property, base_resource); - - } - g_free (last_resource_dup); -} - -static void -list_resources_accum (GladeProperty *key, - gchar *value, - GList **list) -{ - *list = g_list_prepend (*list, value); -} - - - -/** - * glade_project_list_resources: - * @project: A #GladeProject - * - * Returns: A newly allocated #GList of file basenames - * of resources in this project, note that the - * strings are not allocated and are unsafe to - * use once the projects state changes. - * The returned list should be freed with g_list_free. - */ -GList * -glade_project_list_resources (GladeProject *project) -{ - GList *list = NULL; - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - g_hash_table_foreach (project->priv->resources, - (GHFunc)list_resources_accum, &list); - return list; -} - -const gchar * -glade_project_get_path (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - return project->priv->path; -} - -gchar * -glade_project_get_name (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - if (project->priv->path) - return g_filename_display_basename (project->priv->path); - else - return g_strdup_printf (_("Unsaved %i"), project->priv->unsaved_number); -} - -/** - * glade_project_is_loading: - * @project: A #GladeProject - * - * Returns: Whether the project is being loaded or not - * - */ -gboolean -glade_project_is_loading (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - - return project->priv->loading; -} - -time_t -glade_project_get_file_mtime (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), 0); - - return project->priv->mtime; -} - -const GList * -glade_project_get_objects (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - return project->priv->objects; -} - -guint -glade_project_get_instance_count (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), 0); - - return project->priv->instance_count; -} - -void -glade_project_set_instance_count (GladeProject *project, guint instance_count) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - - project->priv->instance_count = instance_count; -} - -/** - * glade_project_get_modified: - * @project: a #GladeProject - * - * Get's whether the project has been modified since it was last saved. - * - * Returns: #TRUE if the project has been modified since it was last saved - */ -gboolean -glade_project_get_modified (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - - return project->priv->modified; -} - |