Hello Tristan

Have written code for the management of all the GtkAction sensitivities
in the user interface.


Overview
--------

1. Separated all the menu actions into two action groups:

   The actions in the 'static_actions' group are always sensitive,
   since the user should be able activate them even if
   no project is open.

   The actions in the 'project_actions' group are desensitized when
   all projects have been closed.

2. Detection of read-only files. Can now preempt users from trying to
   save a read-only project file.

3. Detection of correct state so that Cut/Copy/Paste/Delete actions
   can be carried out.

4. Hacked on the Undo/Redo code. No more "Undo: " labels if there
   is no command description.


ChangeLog Entry
---------------

2006-04-07  Vincent Geddes <[EMAIL PROTECTED]>

        * src/glade-project-window.c: 
           o Manage the sensitivities of all the ui actions.
           o Fix some issues with the Undo & Redo actions.

        * src/glade-project.[ch]: 
          o Detect if project file is read-only. New property
            "read-only"
          o Detect if project has a selection. New property
            "has-selection"

        * src/glade-clipboard.[ch]: 
          o Detect if clipboard has selected items to paste. 
            New property "has-selection"

        * src/glade-utils.h: New function: 
          glade_util_file_is_writeable ().



Cheers

Vincent Geddes



Index: src/glade-utils.c
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-utils.c,v
retrieving revision 1.73
diff -u -p -r1.73 glade-utils.c
--- src/glade-utils.c	5 Apr 2006 19:40:44 -0000	1.73
+++ src/glade-utils.c	9 Apr 2006 12:14:29 -0000
@@ -29,6 +29,7 @@
 #include <gmodule.h>
 #include <glib/gi18n-lib.h>
 #include <glib/gstdio.h>
+#include <unistd.h>
 #include <errno.h>
 
 #include "glade.h"
@@ -1476,4 +1477,20 @@ glade_util_load_library (const gchar *li
 	}
 
 	return module;
+}
+
+/**
+ * glade_util_file_is_writeable:
+ * @path:  the path to the file
+ *
+ * Checks whether the file at @path is writeable
+ *
+ * Returns: TRUE if file is writeable
+ */
+gboolean
+glade_util_file_is_writeable (const gchar *path)
+{
+	g_assert (path != NULL);
+
+	return g_access (path, W_OK) == 0;
 }
Index: src/glade-utils.h
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-utils.h,v
retrieving revision 1.44
diff -u -p -r1.44 glade-utils.h
--- src/glade-utils.h	3 Apr 2006 19:56:02 -0000	1.44
+++ src/glade-utils.h	9 Apr 2006 12:14:29 -0000
@@ -118,6 +118,9 @@ gboolean          glade_util_copy_file  
 LIBGLADEUI_API
 GModule          *glade_util_load_library          (const gchar  *library_name);
 
+LIBGLADEUI_API
+gboolean          glade_util_file_is_writeable     (const gchar *path);
+
 G_END_DECLS
 
 #endif /* __GLADE_UTILS_H__ */
Index: src/glade-project.c
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-project.c,v
retrieving revision 1.96
diff -u -p -r1.96 glade-project.c
--- src/glade-project.c	29 Mar 2006 22:15:41 -0000	1.96
+++ src/glade-project.c	9 Apr 2006 12:14:53 -0000
@@ -63,7 +63,9 @@ enum
 enum
 {
 	PROP_0,
-	PROP_HAS_UNSAVED_CHANGES
+	PROP_HAS_UNSAVED_CHANGES,
+	PROP_HAS_SELECTION,
+	PROP_READ_ONLY
 };
 
 static guint glade_project_signals[LAST_SIGNAL] = {0};
@@ -82,7 +84,13 @@ glade_project_get_property (GObject    *
 	{
 		case PROP_HAS_UNSAVED_CHANGES:
 			g_value_set_boolean (value, project->changed);
+			break;
+		case PROP_HAS_SELECTION:
+			g_value_set_boolean (value, project->has_selection);
 			break;			
+		case PROP_READ_ONLY:
+			g_value_set_boolean (value, project->readonly);
+			break;		
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;			
@@ -261,6 +269,22 @@ glade_project_class_init (GladeProjectCl
 							       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));
+
 
 
 	object_class->finalize = glade_project_finalize;
@@ -282,8 +306,10 @@ glade_project_init (GladeProject *projec
 	project->name = NULL;
 	project->instance = 0;
 	project->untitled_number = 0;
+	project->readonly = FALSE;
 	project->objects = NULL;
 	project->selection = NULL;
+	project->has_selection = FALSE;
 	project->undo_stack = NULL;
 	project->prev_redo_item = NULL;
 	project->widget_names_allocator = 
@@ -332,6 +358,34 @@ glade_project_release_untitled_number (g
 	g_hash_table_remove (allocated_untitled_numbers, GINT_TO_POINTER (n));
 }
 
+static void
+glade_project_set_readonly (GladeProject *project, gboolean readonly)
+{
+	g_assert (GLADE_IS_PROJECT (project));
+	
+	if (project->readonly != readonly)
+	{
+		project->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_assert (project != NULL);
+
+	return project->readonly;
+}
+
 /**
  * glade_project_new:
  * @untitled: Whether or not this project is untitled
@@ -832,6 +886,32 @@ glade_project_new_widget_name (GladeProj
 	return NULL;
 }
 
+void
+glade_project_set_has_selection (GladeProject *project, gboolean has_selection)
+{
+	g_assert (GLADE_IS_PROJECT (project));
+
+	if (project->has_selection != has_selection)
+	{
+		project->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->has_selection;
+}
+
 /**
  * glade_project_is_selected:
  * @project: a #GladeProject
@@ -867,6 +947,7 @@ glade_project_selection_clear (GladeProj
 
 	g_list_free (project->selection);
 	project->selection = NULL;
+	glade_project_set_has_selection (project, FALSE);
 
 	if (emit_signal)
 		glade_project_selection_changed (project);
@@ -896,6 +977,8 @@ glade_project_selection_remove (GladePro
 		if (GTK_IS_WIDGET (object))
 			glade_util_remove_selection (GTK_WIDGET (object));
 		project->selection = g_list_remove (project->selection, object);
+		if (project->selection == NULL)
+			glade_project_set_has_selection (project, FALSE);
 		if (emit_signal)
 			glade_project_selection_changed (project);
 	}
@@ -925,6 +1008,8 @@ glade_project_selection_add (GladeProjec
 	{
 		if (GTK_IS_WIDGET (object))
 			glade_util_add_selection (GTK_WIDGET (object));
+		if (project->selection == NULL)
+			glade_project_set_has_selection (project, TRUE);
 		project->selection = g_list_prepend (project->selection, object);
 		if (emit_signal)
 			glade_project_selection_changed (project);
@@ -953,6 +1038,9 @@ glade_project_selection_set (GladeProjec
 	if (g_list_find (project->objects, object) == NULL)
 		return;
 
+	if (project->selection == NULL)
+		glade_project_set_has_selection (project, TRUE);
+
 	if (glade_project_is_selected (project, object) == FALSE ||
 	    g_list_length (project->selection) != 1)
 	{
@@ -1218,6 +1306,9 @@ glade_project_open (const gchar *path)
 	
 	}
 
+	if (glade_util_file_is_writeable (project->path) == FALSE)
+		glade_project_set_readonly (project, TRUE);
+
 	if (project) project->changed = FALSE;
 
 	return project;
@@ -1308,6 +1399,9 @@ glade_project_save (GladeProject *projec
 				 g_path_get_basename (project->path));
 
 	}
+
+	glade_project_set_readonly (project, 
+				    !glade_util_file_is_writeable (project->path));
 
 	if (project->changed)
 	{
Index: src/glade-project.h
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-project.h,v
retrieving revision 1.49
diff -u -p -r1.49 glade-project.h
--- src/glade-project.h	20 Mar 2006 17:03:08 -0000	1.49
+++ src/glade-project.h	9 Apr 2006 12:14:55 -0000
@@ -30,6 +30,8 @@ struct _GladeProject
 
 	gint   untitled_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 changed;    /* A flag that is set when a project has changes
@@ -49,6 +51,8 @@ struct _GladeProject
 			   * 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 */
@@ -93,6 +97,8 @@ gboolean      glade_project_save        
 LIBGLADEUI_API
 void          glade_project_reset_path          (GladeProject *project);
 LIBGLADEUI_API
+gboolean      glade_project_get_readonly        (GladeProject *project);
+LIBGLADEUI_API
 void          glade_project_add_object          (GladeProject *project, 
 						 GladeProject *old_project,
 						 GObject      *object);
@@ -136,7 +142,8 @@ LIBGLADEUI_API
 void          glade_project_selection_changed   (GladeProject *project);
 LIBGLADEUI_API
 GList        *glade_project_selection_get       (GladeProject *project);
-
+LIBGLADEUI_API
+gboolean      glade_project_get_has_selection   (GladeProject *project);
 LIBGLADEUI_API
 void          glade_project_set_accel_group     (GladeProject  *project, 
 						 GtkAccelGroup *accel_group);
Index: src/glade-project-window.c
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-project-window.c,v
retrieving revision 1.138
diff -u -p -r1.138 glade-project-window.c
--- src/glade-project-window.c	5 Apr 2006 20:48:55 -0000	1.138
+++ src/glade-project-window.c	9 Apr 2006 12:15:07 -0000
@@ -37,9 +37,10 @@
 #define CONFIG_RECENT_PROJECTS     "Recent Projects"
 #define CONFIG_RECENT_PROJECTS_MAX "max_recent_projects"
 
-#define GLADE_ACTION_GROUP_MENU "GladeMenu"
+#define GLADE_ACTION_GROUP_STATIC "GladeStatic"
 #define GLADE_ACTION_GROUP_PROJECT "GladeProject"
 #define GLADE_ACTION_GROUP_RECENT "GladeRecent"
+#define GLADE_ACTION_GROUP_PROJECTS_LIST_MENU "GladeProjectsList"
 
 struct _GladeProjectWindowPriv {
 	/* Application widgets */
@@ -53,17 +54,16 @@ struct _GladeProjectWindowPriv {
 	GtkUIManager *ui;		/* The UIManager */
 	guint projects_list_menu_ui_id; /* Merge id for projects list menu */
 
-	GtkActionGroup *menu_actions;	/* All the static actions */
-	GtkActionGroup *project_actions;/* Projects actions */
+	GtkActionGroup *static_actions;	/* All the static actions */
+	GtkActionGroup *project_actions;/* All the project actions */
 	GtkActionGroup *recent_actions;	/* Recent projects actions */
+	GtkActionGroup *projects_list_menu_actions;/* Projects list menu actions */
 
 	GQueue *recent_projects;	/* A GtkAction queue */
 	gint rp_max;			/* Maximun Recent Projects entries */
 
 	GtkWindow *palette_window;     /* The window that will contain the palette */
 	GtkWindow *editor_window;      /* The window that will contain the editor */
-	GtkWidget *toolbar_undo;       /* undo item on the toolbar */
-	GtkWidget *toolbar_redo;       /* redo item on the toolbar */
 };
 
 #define      WINDOW_TITLE                   (_("Glade-3 GUI Builder"))
@@ -73,7 +73,7 @@ const gint   GLADE_PALETTE_DEFAULT_HEIGH
 
 static gpointer parent_class = NULL;
 
-static void glade_project_window_refresh_undo_redo (GladeProjectWindow *gpw);
+static void gpw_refresh_undo_redo (GladeProjectWindow *gpw);
 
 static void
 gpw_refresh_title (GladeProjectWindow *gpw)
@@ -261,8 +261,73 @@ gpw_refresh_projects_list_item (GladePro
 static void
 gpw_project_notify_handler_cb (GladeProject *project, GParamSpec *spec, GladeProjectWindow *gpw)
 {
-	gpw_refresh_title (gpw);
-	gpw_refresh_projects_list_item (gpw, project);
+	GtkAction *action;
+
+	if (strcmp (spec->name, "has-unsaved-changes") == 0)	
+	{
+		gpw_refresh_title (gpw);
+		gpw_refresh_projects_list_item (gpw, project);
+	}
+	else if (strcmp (spec->name, "read-only") == 0)	
+	{
+		action = gtk_action_group_get_action (gpw->priv->project_actions, "Save");
+		gtk_action_set_sensitive (action,
+				  	  !glade_project_get_readonly (project));
+	}
+	else if (strcmp (spec->name, "has-selection") == 0)	
+	{
+		action = gtk_action_group_get_action (gpw->priv->project_actions, "Cut");
+		gtk_action_set_sensitive (action,
+				  	  glade_project_get_has_selection (project));
+
+		action = gtk_action_group_get_action (gpw->priv->project_actions, "Copy");
+		gtk_action_set_sensitive (action,
+				  	  glade_project_get_has_selection (project));
+
+		action = gtk_action_group_get_action (gpw->priv->project_actions, "Delete");
+		gtk_action_set_sensitive (action,
+				 	  glade_project_get_has_selection (project));
+	}
+}
+
+static void
+gpw_clipboard_notify_handler_cb (GladeClipboard *clipboard, GParamSpec *spec, GladeProjectWindow *gpw)
+{
+	GtkAction *action;
+
+	if (strcmp (spec->name, "has-selection") == 0)	
+	{
+		action = gtk_action_group_get_action (gpw->priv->project_actions, "Paste");
+		gtk_action_set_sensitive (action,
+				 	  glade_clipboard_get_has_selection (clipboard));
+	}
+}
+
+static void
+gpw_set_sensitivity_according_to_project (GladeProjectWindow *gpw, GladeProject *project)
+{
+	GtkAction *action;
+
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Save");
+	gtk_action_set_sensitive (action,
+			          !glade_project_get_readonly (project));
+
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Cut");
+	gtk_action_set_sensitive (action,
+				  glade_project_get_has_selection (project));
+
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Copy");
+	gtk_action_set_sensitive (action,
+				  glade_project_get_has_selection (project));
+
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Paste");
+	gtk_action_set_sensitive (action,
+				  glade_clipboard_get_has_selection
+					(glade_app_get_clipboard (GLADE_APP (gpw))));
+
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Delete");
+	gtk_action_set_sensitive (action,
+				  glade_project_get_has_selection (project));
 }
 
 static void
@@ -275,6 +340,8 @@ gpw_projects_list_menu_activate_cb (GtkA
 
 	glade_app_set_project (GLADE_APP (gpw), project);
 
+	gpw_set_sensitivity_according_to_project (gpw, project);
+
 	gpw_refresh_title (gpw);
 }
 
@@ -291,13 +358,13 @@ gpw_refresh_projects_list_menu (GladePro
 		gtk_ui_manager_remove_ui (p->ui, p->projects_list_menu_ui_id);
 
 	/* Remove all current actions */
-	actions = gtk_action_group_list_actions (p->project_actions);
+	actions = gtk_action_group_list_actions (p->projects_list_menu_actions);
 	for (l = actions; l != NULL; l = l->next)
 	{
 		g_signal_handlers_disconnect_by_func 
 			(GTK_ACTION (l->data),
 			 G_CALLBACK (gpw_projects_list_menu_activate_cb), gpw);
- 		gtk_action_group_remove_action (p->project_actions, GTK_ACTION (l->data));
+ 		gtk_action_group_remove_action (p->projects_list_menu_actions, GTK_ACTION (l->data));
 	}
 	g_list_free (actions);
 
@@ -329,7 +396,10 @@ gpw_refresh_projects_list_menu (GladePro
 
 		group = gtk_radio_action_get_group (action);
 
-		gtk_action_group_add_action (p->project_actions, GTK_ACTION (action));
+		gtk_action_group_add_action (p->projects_list_menu_actions, GTK_ACTION (action));
+
+		if (project == glade_app_get_active_project (GLADE_APP (gpw)))
+			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
 
 		g_signal_connect (action, "activate",
 				  G_CALLBACK (gpw_projects_list_menu_activate_cb),
@@ -341,9 +411,6 @@ gpw_refresh_projects_list_menu (GladePro
 				       GTK_UI_MANAGER_MENUITEM,
 				       FALSE);
 
-		if (project == glade_app_get_active_project (GLADE_APP (gpw)))
-			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
-
 		g_object_unref (action);
 
 		g_free (action_name);
@@ -675,7 +742,9 @@ gpw_confirm_close_project (GladeProjectW
 
 static void
 do_close (GladeProjectWindow *gpw, GladeProject *project)
-{	
+{
+	GladeProject *active_project;
+
 	g_signal_handlers_disconnect_by_func (G_OBJECT (project),
 					      G_CALLBACK (gpw_project_notify_handler_cb),
 					      gpw);	
@@ -685,6 +754,12 @@ do_close (GladeProjectWindow *gpw, Glade
 	gpw_refresh_projects_list_menu (gpw);
 
 	gpw_refresh_title (gpw);
+
+	active_project = glade_app_get_active_project (GLADE_APP (gpw));
+	if (active_project != NULL)
+		gpw_set_sensitivity_according_to_project (gpw, active_project);
+	else
+		gtk_action_group_set_sensitive (gpw->priv->project_actions, FALSE);	
 }
 
 static void
@@ -1213,7 +1288,7 @@ static const gchar *ui_info =
 "  </toolbar>"
 "</ui>\n";
 
-static GtkActionEntry entries[] = {
+static GtkActionEntry static_entries[] = {
 	{ "FileMenu", NULL, "_File" },
 	{ "EditMenu", NULL, "_Edit" },
 	{ "ViewMenu", NULL, "_View" },
@@ -1231,7 +1306,19 @@ static GtkActionEntry entries[] = {
 	
 	{ "ClearRecents", GTK_STOCK_CLEAR, "Clear Recent Projects", NULL,
 	NULL, G_CALLBACK (gpw_recent_project_clear_cb) },
-	
+
+	{ "Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q",
+	"Quit the program", G_CALLBACK (gpw_quit_cb) },
+
+	/* HelpMenu */
+	{ "About", GTK_STOCK_ABOUT, "_About", NULL,
+	"Shows the About Dialog", G_CALLBACK (gpw_about_cb) }
+};
+static guint n_static_entries = G_N_ELEMENTS (static_entries);
+
+static GtkActionEntry project_entries[] = {
+
+	/* FileMenu */
 	{ "Save", GTK_STOCK_SAVE, "_Save","<control>S",
 	"Save the current project file", G_CALLBACK (gpw_save_cb) },
 	
@@ -1240,9 +1327,6 @@ static GtkActionEntry entries[] = {
 	
 	{ "Close", GTK_STOCK_CLOSE, "_Close", "<control>W",
 	"Close the current project file", G_CALLBACK (gpw_close_cb) },
-	
-	{ "Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q",
-	"Quit the program", G_CALLBACK (gpw_quit_cb) },
 
 	/* EditMenu */	
 	{ "Undo", GTK_STOCK_UNDO, "_Undo", "<control>Z",
@@ -1261,13 +1345,9 @@ static GtkActionEntry entries[] = {
 	"Paste the clipboard", G_CALLBACK (gpw_paste_cb) },
 	
 	{ "Delete", GTK_STOCK_DELETE, "_Delete", "Delete",
-	"Delete the selection", G_CALLBACK (gpw_delete_cb) },
-	
-	/* HelpMenu */
-	{ "About", GTK_STOCK_ABOUT, "_About", NULL,
-	"Shows the About Dialog", G_CALLBACK (gpw_about_cb) }
+	"Delete the selection", G_CALLBACK (gpw_delete_cb) }
 };
-static guint n_entries = G_N_ELEMENTS (entries);
+static guint n_project_entries = G_N_ELEMENTS (project_entries);
 
 static GtkToggleActionEntry view_entries[] = {
 	/* ViewMenu */
@@ -1341,32 +1421,41 @@ gpw_ui_disconnect_proxy_cb (GtkUIManager
 	}
 }
 
-
 static GtkWidget *
 gpw_construct_menu (GladeProjectWindow *gpw)
 {
 	GError *error = NULL;
 	
-	gpw->priv->menu_actions = gtk_action_group_new (GLADE_ACTION_GROUP_MENU);
-	gtk_action_group_add_actions (gpw->priv->menu_actions, entries, n_entries, gpw);
-	gtk_action_group_add_toggle_actions (gpw->priv->menu_actions, view_entries,
-						n_view_entries, gpw);
+	gpw->priv->static_actions = gtk_action_group_new (GLADE_ACTION_GROUP_STATIC);
+	gtk_action_group_add_actions (gpw->priv->static_actions,
+				      static_entries,
+				      n_static_entries,
+				      gpw);
+	gtk_action_group_add_toggle_actions (gpw->priv->static_actions, 
+					     view_entries,
+					     n_view_entries, 
+					     gpw);
 
 	gpw->priv->project_actions = gtk_action_group_new (GLADE_ACTION_GROUP_PROJECT);
+	gtk_action_group_add_actions (gpw->priv->project_actions,
+				      project_entries,
+				      n_project_entries,
+				      gpw);
 
+	gpw->priv->projects_list_menu_actions = gtk_action_group_new (GLADE_ACTION_GROUP_PROJECTS_LIST_MENU);
 	gpw->priv->recent_actions = gtk_action_group_new (GLADE_ACTION_GROUP_RECENT);
 	
 	gpw->priv->ui = gtk_ui_manager_new ();
 
 	g_signal_connect(G_OBJECT(gpw->priv->ui), "connect-proxy",
 			 G_CALLBACK (gpw_ui_connect_proxy_cb), gpw);
-
 	g_signal_connect(G_OBJECT(gpw->priv->ui), "disconnect-proxy",
 			 G_CALLBACK (gpw_ui_disconnect_proxy_cb), gpw);
 
-	gtk_ui_manager_insert_action_group (gpw->priv->ui, gpw->priv->menu_actions, 0);
+	gtk_ui_manager_insert_action_group (gpw->priv->ui, gpw->priv->static_actions, 0);
 	gtk_ui_manager_insert_action_group (gpw->priv->ui, gpw->priv->project_actions, 1);
 	gtk_ui_manager_insert_action_group (gpw->priv->ui, gpw->priv->recent_actions, 2);
+	gtk_ui_manager_insert_action_group (gpw->priv->ui, gpw->priv->projects_list_menu_actions, 3);
 	
 	gtk_window_add_accel_group (GTK_WINDOW (gpw->priv->window), 
 				  gtk_ui_manager_get_accel_group (gpw->priv->ui));
@@ -1376,7 +1465,7 @@ gpw_construct_menu (GladeProjectWindow *
 		g_message ("Building menus failed: %s", error->message);
 		g_error_free (error);
 	}
-	
+
 	gtk_widget_set_sensitive (gtk_ui_manager_get_widget 
 				  (gpw->priv->ui, "/MenuBar/FileMenu/Recents"),
 				  FALSE);
@@ -1384,7 +1473,6 @@ gpw_construct_menu (GladeProjectWindow *
 	return gtk_ui_manager_get_widget (gpw->priv->ui, "/MenuBar");
 }
 
-
 static GtkWidget *
 gpw_construct_statusbar (GladeProjectWindow *gpw)
 {
@@ -1481,16 +1569,11 @@ glade_project_window_create (GladeProjec
 	project_view = gpw_create_widget_tree_contents (gpw);
 	statusbar = gpw_construct_statusbar (gpw);
 
-	gpw->priv->toolbar_undo = gtk_ui_manager_get_widget (gpw->priv->ui, "/ToolBar/Undo");
-	gpw->priv->toolbar_redo = gtk_ui_manager_get_widget (gpw->priv->ui, "/ToolBar/Redo");
-
 	gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, TRUE, 0);
 	gtk_box_pack_start (GTK_BOX (vbox), project_view, TRUE, TRUE, 0);
 	gtk_box_pack_end (GTK_BOX (vbox), statusbar, FALSE, TRUE, 0);
 
-	glade_project_window_refresh_undo_redo (gpw);
-
 	gpw_recent_project_config_load (gpw);
 	
 	/* support for opening a file by dragging onto the project window */
@@ -1513,16 +1596,25 @@ glade_project_window_add_project (GladeP
 	
 	glade_app_add_project (GLADE_APP (gpw), project);
 
-	/* Connect callback handler for notify signal emitted by projects */
+	/* Connect callback handler for notify signals emitted by projects */
 	g_signal_connect (G_OBJECT (project), "notify::has-unsaved-changes",
 			  G_CALLBACK (gpw_project_notify_handler_cb),
 			  gpw);
+	g_signal_connect (G_OBJECT (project), "notify::has-selection",
+			  G_CALLBACK (gpw_project_notify_handler_cb),
+			  gpw);
+	g_signal_connect (G_OBJECT (project), "notify::read-only",
+			  G_CALLBACK (gpw_project_notify_handler_cb),
+			  gpw);
+
+	gpw_set_sensitivity_according_to_project (gpw, project);
 
-	/* Refresh projects list menu */
 	gpw_refresh_projects_list_menu (gpw);
 
-	/* Refresh window title */
 	gpw_refresh_title (gpw);
+
+	if (gtk_action_group_get_sensitive (gpw->priv->project_actions) == FALSE)
+		gtk_action_group_set_sensitive (gpw->priv->project_actions, TRUE);
 }
 
 void
@@ -1571,77 +1663,70 @@ glade_project_window_open_project (Glade
 	glade_project_window_add_project (gpw, project);
 }
 
-void
+static void
 glade_project_window_change_menu_label (GladeProjectWindow *gpw,
 					const gchar *path,
-					const gchar *prefix,
-					const gchar *suffix)
+					const gchar *action_label,
+					const gchar *action_description)
 {
-	gboolean sensitive = TRUE;
 	GtkBin *bin;
 	GtkLabel *label;
 	gchar *text;
-	
-	bin = GTK_BIN (gtk_ui_manager_get_widget (gpw->priv->ui, path));
-	label = GTK_LABEL (gtk_bin_get_child (bin));
-	
-	if (prefix == NULL)
-	{
-		gtk_label_set_text_with_mnemonic (label, suffix);
-                return;
-	}
 
-	if (suffix == NULL)
-	{
-		suffix = _("Nothing");
-		sensitive = FALSE;
-	}
+	g_assert (GLADE_IS_PROJECT_WINDOW (gpw));
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (action_label != NULL);
 
-	text = g_strconcat (prefix, suffix, NULL);
+	bin = GTK_BIN (gtk_ui_manager_get_widget (gpw->priv->ui, path));
+	label = GTK_LABEL (gtk_bin_get_child (bin));
 
+	if (action_description == NULL)
+		text = g_strdup (action_label);
+	else
+		text = g_strdup_printf ("%s: %s", action_label, action_description);
+	
 	gtk_label_set_text_with_mnemonic (label, text);
-	gtk_widget_set_sensitive (GTK_WIDGET (bin), sensitive);
 
 	g_free (text);
 }
 
-void
-glade_project_window_refresh_undo_redo (GladeProjectWindow *gpw)
+static void
+gpw_refresh_undo_redo (GladeProjectWindow *gpw)
 {
 	GladeCommand *undo = NULL, *redo = NULL;
 	GladeProject *project;
 	GtkAction    *action;
-	gchar        *desc;
+	gchar        *tooltip;
 
-	if ((project = glade_app_get_active_project (GLADE_APP (gpw))) != NULL)
+	project = glade_app_get_active_project (GLADE_APP (gpw));
+
+	if (project != NULL)
 	{
 		undo = glade_command_next_undo_item (project);
 		redo = glade_command_next_redo_item (project);
 	}
 
-	/* Change label in menu */
-	glade_project_window_change_menu_label
-		(gpw, "/MenuBar/EditMenu/Undo", _("_Undo: "), undo ? undo->description : " ");
+	/* Refresh Undo */
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Undo");
+	gtk_action_set_sensitive (action, undo != NULL);
 
 	glade_project_window_change_menu_label
-		(gpw, "/MenuBar/EditMenu/Redo", _("_Redo: "), redo ? redo->description : " ");
+		(gpw, "/MenuBar/EditMenu/Undo", _("_Undo"), undo ? undo->description : NULL);
 
+	tooltip = g_strdup_printf (_("Undo: %s"), undo ? undo->description : _("the last action"));
+	g_object_set (action, "tooltip", tooltip, NULL);
+	g_free (tooltip);
 	
-	/* Change tooltips on the toolbar */
-	desc = g_strdup_printf (_("Undo: %s"), undo ? undo->description : _("the last action"));
-	action = gtk_ui_manager_get_action (gpw->priv->ui, "/ToolBar/Undo");
-	g_object_set (G_OBJECT (action), "tooltip", desc, NULL);
-	g_free (desc);
-
-	desc = g_strdup_printf (_("Redo: %s"), redo ? redo->description : _("the last action"));
-	action = gtk_ui_manager_get_action (gpw->priv->ui, "/ToolBar/Redo");
-	g_object_set (G_OBJECT (action), "tooltip", desc, NULL);
-	g_free (desc);
-
-	/* Set sensitivity on the toolbar */
-	gtk_widget_set_sensitive (gpw->priv->toolbar_undo, undo != NULL);
-	gtk_widget_set_sensitive (gpw->priv->toolbar_redo, redo != NULL);
+	/* Refresh Redo */
+	action = gtk_action_group_get_action (gpw->priv->project_actions, "Redo");
+	gtk_action_set_sensitive (action, redo != NULL);
+
+	glade_project_window_change_menu_label
+		(gpw, "/MenuBar/EditMenu/Redo", _("_Redo"), redo ? redo->description : NULL);
 
+	tooltip = g_strdup_printf (_("Redo: %s"), redo ? redo->description : _("the last action"));
+	g_object_set (action, "tooltip", tooltip, NULL);
+	g_free (tooltip);
 }
 
 static void
@@ -1652,7 +1737,7 @@ glade_project_window_update_ui (GladeApp
 	/* Chain Up */
 	GLADE_APP_CLASS (parent_class)->update_ui_signal (app);
 	
-	glade_project_window_refresh_undo_redo (gpw);
+	gpw_refresh_undo_redo (gpw);
 }
 
 void
@@ -1749,6 +1834,11 @@ glade_project_window_new (void)
 	gtk_window_add_accel_group(gpw->priv->palette_window, accel_group);
 	gtk_window_add_accel_group(gpw->priv->editor_window, accel_group);
 	gtk_window_add_accel_group(GTK_WINDOW (glade_app_get_clipboard_view (GLADE_APP (gpw))), accel_group);
+
+	/* Connect callback handler for notify signals emitted by clipboard */
+	g_signal_connect (G_OBJECT (glade_app_get_clipboard (GLADE_APP (gpw))), "notify::has-selection",
+			  G_CALLBACK (gpw_clipboard_notify_handler_cb),
+			  gpw);
 
 	return gpw;
 }
Index: src/glade-clipboard.c
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-clipboard.c,v
retrieving revision 1.19
diff -u -p -r1.19 glade-clipboard.c
--- src/glade-clipboard.c	10 Feb 2006 03:41:21 -0000	1.19
+++ src/glade-clipboard.c	9 Apr 2006 12:15:08 -0000
@@ -31,10 +31,47 @@
 #include "glade-placeholder.h"
 #include "glade-project.h"
 
+enum
+{
+	PROP_0,
+	PROP_HAS_SELECTION
+};
+
+static void
+glade_project_get_property (GObject    *object,
+			    guint       prop_id,
+			    GValue     *value,
+			    GParamSpec *pspec)
+{
+	GladeClipboard *clipboard = GLADE_CLIPBOARD (object);
+
+	switch (prop_id)
+	{
+		case PROP_HAS_SELECTION:
+			g_value_set_boolean (value, clipboard->has_selection);
+			break;				
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;			
+	}
+}
+
 static void
 glade_clipboard_class_init (GladeClipboardClass *klass)
 {
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
 
+	object_class->get_property = glade_project_get_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_HAS_SELECTION,
+					 g_param_spec_boolean ("has-selection",
+							       "Has Selection",
+							       "Whether clipboard has a selection of items to paste",
+							       FALSE,
+							       G_PARAM_READABLE));
 }
 
 static void
@@ -43,6 +80,7 @@ glade_clipboard_init (GladeClipboard *cl
 	clipboard->widgets   = NULL;
 	clipboard->view      = NULL;
 	clipboard->selection = NULL;
+	clipboard->has_selection = FALSE;
 }
 
 GType
@@ -70,6 +108,34 @@ glade_clipboard_get_type (void)
 	return type;
 }
 
+static void
+glade_clipboard_set_has_selection  (GladeClipboard *clipboard, gboolean has_selection)
+{
+	g_assert (GLADE_IS_CLIPBOARD (clipboard));
+
+	if (clipboard->has_selection != has_selection)
+	{	
+		clipboard->has_selection = has_selection;
+		g_object_notify (G_OBJECT (clipboard), "has-selection");
+	}
+
+}
+
+/**
+ * glade_clipboard_get_has_selection:
+ * @clipboard: a #GladeClipboard
+ * 
+ * Returns: TRUE if this clipboard has selected items to paste.
+ */
+gboolean
+glade_clipboard_get_has_selection  (GladeClipboard *clipboard)
+{
+	g_assert (GLADE_IS_CLIPBOARD (clipboard));
+
+	return clipboard->has_selection;
+}
+
+
 /**
  * glade_clipboard_new:
  * 
@@ -176,6 +242,8 @@ glade_clipboard_selection_add (GladeClip
 	g_return_if_fail (GLADE_IS_WIDGET    (widget));
 	clipboard->selection =
 		g_list_prepend (clipboard->selection, widget);
+
+	glade_clipboard_set_has_selection (clipboard, TRUE);
 }
 
 void
@@ -186,6 +254,9 @@ glade_clipboard_selection_remove (GladeC
 	g_return_if_fail (GLADE_IS_WIDGET    (widget));
 	clipboard->selection = 
 		g_list_remove (clipboard->selection, widget);
+
+	if (g_list_length (clipboard->selection) == 0)
+		glade_clipboard_set_has_selection (clipboard, FALSE);
 }
 
 void
@@ -194,5 +265,6 @@ glade_clipboard_selection_clear (GladeCl
 	g_return_if_fail (GLADE_IS_CLIPBOARD (clipboard));
 	clipboard->selection = 
 		(g_list_free (clipboard->selection), NULL);
-}
 
+	glade_clipboard_set_has_selection (clipboard, FALSE);
+}
Index: src/glade-clipboard.h
===================================================================
RCS file: /cvs/gnome/glade3/src/glade-clipboard.h,v
retrieving revision 1.9
diff -u -p -r1.9 glade-clipboard.h
--- src/glade-clipboard.h	7 Feb 2006 05:24:07 -0000	1.9
+++ src/glade-clipboard.h	9 Apr 2006 12:15:08 -0000
@@ -18,6 +18,7 @@ struct _GladeClipboard {
 
 	GList     *widgets;     /* A list of GladeWidget's on the clipboard */
 	GList     *selection;   /* Selection list of GladeWidget's */
+	gboolean   has_selection; /* TRUE if clipboard has selection */
 	GtkWidget *view;        /* see glade-clipboard-view.c */
 };
 
@@ -46,6 +47,8 @@ void            glade_clipboard_selectio
 						  GladeWidget    *widget);
 LIBGLADEUI_API
 void            glade_clipboard_selection_clear  (GladeClipboard *clipboard);
+LIBGLADEUI_API
+gboolean        glade_clipboard_get_has_selection  (GladeClipboard *clipboard);
 
 
 G_END_DECLS
_______________________________________________
Glade-devel maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/glade-devel

Reply via email to