[patch] context-sensitive save and export actions

Douglas Burke <[email protected]>
Newsgroups gmane.editors.conglomerate.devel
Message-ID <Pine.GSO.4.58.0410181840080.24469@lagado>
This is a re-worked version of my previous patch that makes the Save
action (ie menu item and toolbar icon) insensitive if Conglomerate thinks
the document is unchanged. Improvements are:

 - the state is changed via a function call now rather than a macro.
   The functions are cong_primary_window_action_set_sensitive() and
   cong_primary_window_action_set_label(). I ended up with these names
   since they are sent a CongPrimaryWindow as the first argument, but
   I also thought about them being called cong_menus* or cong_ui*

 - The "Export" action is now also made inactive if the document does
   not have any registered documents. This meant I could remove an
   error dialog from cong-file-export.c

I thought about checking for whether there are any importers register and,
if not, making the Import item insensitive. However, aren't we know
guaranteed there will be at least one importer plugin (as Conglomerate
comes with several), so we can always assume the Import action is valid?

Doug
save2.patch (text/plain, 17.5 KB)
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/conglomerate/src/ChangeLog,v
retrieving revision 1.736
diff -u -r1.736 ChangeLog
--- ChangeLog	15 Oct 2004 16:01:40 -0000	1.736
+++ ChangeLog	18 Oct 2004 22:38:56 -0000
@@ -1,3 +1,30 @@
+2004-10-18  Douglas Burke  <[email protected]>
+
+	* cong-primary-window.c, cong-primary-window.h:
+
+	Added cong_primary_window_action_set_sensitive(), and
+	cong_primary_window_action_set_label().
+
+	* cong-menus.c:
+
+	Updated to use cong_primary_window_action_set_sensitive() and
+	cong_primary_window_action_set_label() functions rather than the
+	SET_ACTION_LABEL and SET_ACTION_SENSITIVE macros. The Export
+	action is now insensitive if there are no exporters registered for
+	the document.
+	
+	* cong-document.c, cong-document.h, cong-file-open.c:
+
+	The "Save" menu and toolbar items are now inactive when the file
+	has not changed. It's not 100% foolproof (add a space and then delete
+	it and it still thinks the file has changed).
+	Added cong_document_can_export() function.
+	
+	* cong-file-export.c:
+
+	Removed the dialog indicating that no exporters are available since
+	this condition can no longer be met.
+	
 2004-10-15  Douglas Burke  <[email protected]>
 
 	* cong-attribute-editor-cdata.c, cong-attribute-editor-cdata.h, cong-attribute-editor-enumeration.c, cong-attribute-editor-lang.c, cong-attribute-editor-lang.h, cong-attribute-editor-nmtoken.c, cong-attribute-editor-nmtoken.h, cong-attribute-editor.c, cong-attribute-editor.h, cong-attribute-wrapper-check-button.c, cong-attribute-wrapper-check-button.h, cong-attribute-wrapper-radio-button.c, cong-attribute-wrapper-radio-button.h, cong-attribute-wrapper.c, cong-attribute-wrapper.h, cong-glade.c:
Index: cong-primary-window.c
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-primary-window.c,v
retrieving revision 1.64
diff -u -r1.64 cong-primary-window.c
--- cong-primary-window.c	14 Oct 2004 20:04:54 -0000	1.64
+++ cong-primary-window.c	18 Oct 2004 22:38:56 -0000
@@ -785,3 +785,57 @@
 
 	return primary_window->action_group[action_group];
 }
+
+/**
+ * cong_primary_window_action_set_sensitive:
+ * @action_name: name of Action item
+ * @state: TRUE for sensitive, FALSE for insensitive
+ * @primary_window: the primary window containing the action
+ *
+ * Makes the given Action (@action_name) sensitive (@state=TRUE)
+ * or insensitive (@state=FALSE) for the primary window
+ * @primary_window.
+ */
+void
+cong_primary_window_action_set_sensitive (CongPrimaryWindow *primary_window,
+					  gchar *action_name,
+					  gboolean state) {
+	GtkActionGroup *action_group;
+	GtkAction *action;
+
+	action_group = cong_primary_window_get_action_group (primary_window, CONG_ACTION_GROUP_DOCUMENT);
+	g_assert (action_group);
+
+	action = gtk_action_group_get_action (action_group, action_name);
+	g_assert (action);
+
+	g_object_set (G_OBJECT (action), "sensitive", state, NULL);
+}
+
+/**
+ * cong_primary_window_action_set_label:
+ * @action_name: name of Action item
+ * @label: label for the Action
+ * @primary_window: the primary window containing the action
+ *
+ * Sets the label for the Action (@action_name) whose
+ * primary window is @primary_window.
+ *
+ * NOTE: should we make a copy of label?
+ */
+void
+cong_primary_window_action_set_label (CongPrimaryWindow *primary_window,
+				      gchar *action_name,
+				      gchar *label) {
+	GtkActionGroup *action_group;
+	GtkAction *action;
+
+	action_group = cong_primary_window_get_action_group (primary_window, CONG_ACTION_GROUP_DOCUMENT);
+	g_assert (action_group);
+
+	action = gtk_action_group_get_action (action_group, action_name);
+	g_assert (action);
+
+	g_object_set (G_OBJECT (action), "label", (gpointer)label, NULL);
+}
+
Index: cong-primary-window.h
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-primary-window.h,v
retrieving revision 1.7
diff -u -r1.7 cong-primary-window.h
--- cong-primary-window.h	23 Sep 2004 03:21:49 -0000	1.7
+++ cong-primary-window.h	18 Oct 2004 22:38:56 -0000
@@ -96,6 +96,17 @@
 cong_menus_setup_document_action_group (CongPrimaryWindow *primary_window);
 
 
+/* deal with action items */
+void
+cong_primary_window_action_set_sensitive (CongPrimaryWindow *primary_window,
+					  gchar *action_name,
+					  gboolean state);
+
+void
+cong_primary_window_action_set_label (CongPrimaryWindow *primary_window,
+				      gchar *action_name,
+				      gchar *label);
+
 G_END_DECLS
 
 #endif
Index: cong-document.c
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-document.c,v
retrieving revision 1.111
diff -u -r1.111 cong-document.c
--- cong-document.c	13 Oct 2004 20:15:21 -0000	1.111
+++ cong-document.c	18 Oct 2004 22:38:56 -0000
@@ -18,10 +18,11 @@
 #include "cong-document-traversal.h"
 #include "cong-edit-find-and-replace.h"
 #include "cong-dispspec-registry.h"
+#include "cong-service-exporter.h"
+#include "cong-plugin-manager.h"
 
 #if ENABLE_PRINTING
 #include "cong-service-print-method.h"
-#include "cong-plugin-manager.h"
 #endif 
 
 /* Internal functions: */
@@ -374,6 +375,7 @@
 	PRIVATE(doc)->url = g_strdup(url);
 
 	g_get_current_time(&PRIVATE(doc)->time_of_last_save);
+	PRIVATE(doc)->modified = FALSE;
 
 	#if TEST_VIEW
 	{
@@ -825,18 +827,23 @@
  *
  * TODO: Write me
  */
+
 void
 cong_document_set_modified(CongDocument *doc, gboolean modified)
 {
 	g_return_if_fail(doc);
 
 	if (PRIVATE(doc)->modified != modified) {
+		CongPrimaryWindow *primary_window = PRIVATE(doc)->primary_window;
 
 		PRIVATE(doc)->modified = modified;
 
-		/* get at primary window; set title */
-		if (PRIVATE(doc)->primary_window) {
-			cong_primary_window_update_title(PRIVATE(doc)->primary_window);
+		if (primary_window) {
+			/* set title */
+			cong_primary_window_update_title (primary_window);
+
+			/* change the menu/toolbar Save action */
+			cong_primary_window_action_set_sensitive (primary_window, "Save", modified);
 		}
 	}
 }
@@ -2267,6 +2274,50 @@
 	/* FIXMEPCS: conditions? */
 }
 
+/*
+ * NOTE: can probably abstract this routine a bit since we probably want to
+ * do similar things for other properties/plugins. This is especially true since
+ * this is just a copy of the code used for calculating whether you can print
+ * a document! Then again it's pretty simple as is, so maybe we don't need it
+ */
+struct can_export_data
+{
+	CongDocument *doc;
+	gint num_export_methods;
+};
+
+static void
+callback_can_export (CongServiceExporter *exporter, 
+		     gpointer user_data)
+{
+	struct can_export_data *export_data = (struct can_export_data*)user_data;
+
+	if (cong_exporter_supports_document (exporter, export_data->doc)) {
+		export_data->num_export_methods++;
+	}
+}
+
+/**
+ * cong_document_can_export:
+ * @doc:
+ *
+ * Returns: TRUE if there are any exporters registered for the document.
+ */
+gboolean
+cong_document_can_export (CongDocument *doc)
+{
+	struct can_export_data export_data;
+	g_assert (doc);
+
+	export_data.doc = doc;
+	export_data.num_export_methods = 0;
+	cong_plugin_manager_for_each_exporter (cong_app_get_plugin_manager (cong_app_singleton()),
+					       callback_can_export,
+					       &export_data);
+
+	return export_data.num_export_methods>0;
+}
+
 #if ENABLE_PRINTING
 struct can_print_data
 {
Index: cong-document.h
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-document.h,v
retrieving revision 1.52
diff -u -r1.52 cong-document.h
--- cong-document.h	10 Oct 2004 19:31:26 -0000	1.52
+++ cong-document.h	18 Oct 2004 22:38:56 -0000
@@ -324,6 +324,9 @@
 gboolean
 cong_document_can_paste (CongDocument *doc);
 
+gboolean
+cong_document_can_export (CongDocument *doc);
+
 #if ENABLE_PRINTING
 gboolean
 cong_document_can_print (CongDocument *doc);
Index: cong-menus.c
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-menus.c,v
retrieving revision 1.76
diff -u -r1.76 cong-menus.c
--- cong-menus.c	14 Oct 2004 20:04:54 -0000	1.76
+++ cong-menus.c	18 Oct 2004 22:38:56 -0000
@@ -206,7 +206,11 @@
 action_callback_file_import (GtkAction *action,
 			     CongPrimaryWindow *primary_window)
 {
-	/* FIXME: this option should be disabled if there are no importers installed */
+	/*
+	 * FIXME: this option should be disabled if there are no importers installed
+	 *  - or we could assume that there is always going to be an importer, since
+	 *    some already come with Conglomerate.
+	 */
 
 	cong_ui_hook_file_import (cong_primary_window_get_toplevel (primary_window));
 }
@@ -217,8 +221,6 @@
 {
 	CongDocument *doc = cong_primary_window_get_document(primary_window);
 
-	/* FIXME: this option should be disabled if there are no exporters installed that are appropriate for this FPI */
-
 	cong_ui_hook_file_export (doc,
 				  cong_primary_window_get_toplevel (primary_window));
 }
@@ -230,8 +232,6 @@
 {
 	CongDocument *doc = cong_primary_window_get_document(primary_window);
 
-	/* FIXME: this option should be disabled if there are no print routines installed that are appropriate for this FPI */
-
 	cong_ui_hook_file_print_preview (doc,
 					 cong_primary_window_get_toplevel (primary_window));
 }
@@ -242,8 +242,6 @@
 {
 	CongDocument *doc = cong_primary_window_get_document(primary_window);
 
-	/* FIXME: this option should be disabled if there are no print routines installed that are appropriate for this FPI */
-
 	cong_ui_hook_file_print (doc,
 				 cong_primary_window_get_toplevel (primary_window));
 }
@@ -1745,10 +1743,13 @@
 	}
 }
 
-#define SET_ACTION_SENSITIVE(action_name, sens) \
-			g_object_set (G_OBJECT (gtk_action_group_get_action (cong_primary_window_get_action_group (primary_window, CONG_ACTION_GROUP_DOCUMENT), action_name)), "sensitive", sens, NULL)
+#if OLD_SKOOL
 #define SET_ACTION_LABEL(action_name, label) \
 			g_object_set (G_OBJECT (gtk_action_group_get_action (cong_primary_window_get_action_group (primary_window, CONG_ACTION_GROUP_DOCUMENT), action_name)), "label", label, NULL)
+#define SET_ACTION_SENSITIVE(action_name, sens) \
+			g_object_set (G_OBJECT (gtk_action_group_get_action (cong_primary_window_get_action_group (primary_window, CONG_ACTION_GROUP_DOCUMENT), action_name)), "sensitive", sens, NULL)
+
+#endif
 
 static void 
 on_history_changed (CongCommandHistory *history,
@@ -1758,24 +1759,24 @@
 	gboolean can_undo = cong_command_history_can_undo (history);
 	gboolean can_redo = cong_command_history_can_redo (history);
 
-	SET_ACTION_SENSITIVE("Undo", can_undo);
+	cong_primary_window_action_set_sensitive (primary_window, "Undo", can_undo);
 	if (can_undo) {
 		CongCommand *command = cong_command_history_get_next_undo_command (history);
 		gchar *label = g_strdup_printf (_("_Undo: %s"), cong_command_get_description (command));
-		SET_ACTION_LABEL("Undo", label);
+		cong_primary_window_action_set_label (primary_window, "Undo", label);
 		g_free (label);
 	} else {
-		SET_ACTION_LABEL("Undo", _("_Undo"));
+		cong_primary_window_action_set_label (primary_window, "Undo", _("_Undo"));
 	}
 
-	SET_ACTION_SENSITIVE("Redo", cong_command_history_can_redo (history));
+	cong_primary_window_action_set_sensitive (primary_window, "Redo", cong_command_history_can_redo (history));
 	if (can_redo) {
 		CongCommand *command = cong_command_history_get_next_redo_command (history);
 		gchar *label = g_strdup_printf (_("_Redo: %s"), cong_command_get_description (command));
-		SET_ACTION_LABEL("Redo", label);
+		cong_primary_window_action_set_label (primary_window, "Redo", label);
 		g_free (label);
 	} else {
-		SET_ACTION_LABEL("Redo", _("_Redo"));
+		cong_primary_window_action_set_label (primary_window, "Redo", _("_Redo"));
 	}
 }
 
@@ -1786,8 +1787,8 @@
 	/* Update sensitivity of cut and copy actions: */
 	CongSelection *selection = cong_document_get_selection(document);
 	CongRange *range = cong_selection_get_ordered_range(selection);
-	SET_ACTION_SENSITIVE("Cut", cong_range_can_be_cut (range));
-	SET_ACTION_SENSITIVE("Copy", cong_range_can_be_copied (range));
+	cong_primary_window_action_set_sensitive (primary_window, "Cut", cong_range_can_be_cut (range));
+	cong_primary_window_action_set_sensitive (primary_window, "Copy", cong_range_can_be_copied (range));
 }
 
 void
@@ -1814,6 +1815,10 @@
 				      primary_window_application_action_entries, 
 				      G_N_ELEMENTS (primary_window_application_action_entries), 
 				      primary_window);
+	/*
+	 * FIXME: make the Import menu item insensitive if there are no importers
+	 *  (or do we assume there is always an importer available?)
+	 */
 	gtk_action_group_add_actions (cong_primary_window_get_action_group (primary_window, CONG_ACTION_GROUP_DOCUMENT), 
 				      primary_window_document_action_entries, 
 				      G_N_ELEMENTS (primary_window_document_action_entries), 
@@ -1851,15 +1856,32 @@
 			  G_CALLBACK(on_selection_changed),
 			  primary_window);
 	
-	SET_ACTION_SENSITIVE("Undo", FALSE);
-	SET_ACTION_SENSITIVE("Redo", FALSE);
-	SET_ACTION_SENSITIVE("Cut", FALSE);
-	SET_ACTION_SENSITIVE("Copy", FALSE);
-	SET_ACTION_SENSITIVE("Paste", cong_document_can_paste(doc));
-	
+	cong_primary_window_action_set_sensitive (primary_window, "Undo", FALSE);
+	cong_primary_window_action_set_sensitive (primary_window, "Redo", FALSE);
+	cong_primary_window_action_set_sensitive (primary_window, "Cut", FALSE);
+	cong_primary_window_action_set_sensitive (primary_window, "Copy", FALSE);
+
+	/*
+	 * set sensitivity for those menu items that depend on the
+	 * document or plugins
+	 */
+	cong_primary_window_action_set_sensitive (primary_window,
+						  "Paste",
+						  cong_document_can_paste(doc));
+	cong_primary_window_action_set_sensitive (primary_window,
+						  "Export",
+						  cong_document_can_export(doc));
+
 #if ENABLE_PRINTING
-	SET_ACTION_SENSITIVE("PrintPreview", cong_document_can_print(doc));
-	SET_ACTION_SENSITIVE("Print", cong_document_can_print(doc));
+	{
+		gboolean can_print = cong_document_can_print(doc);
+		cong_primary_window_action_set_sensitive (primary_window,
+							  "PrintPreview",
+							  can_print);
+		cong_primary_window_action_set_sensitive (primary_window,
+							  "Print",
+							  can_print);
+	}
 #endif
 	
 	/* Now add any plugin tools below the "Tools" menu: */
Index: cong-file-open.c
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-file-open.c,v
retrieving revision 1.22
diff -u -r1.22 cong-file-open.c
--- cong-file-open.c	16 Jul 2004 03:31:34 -0000	1.22
+++ cong-file-open.c	18 Oct 2004 22:38:56 -0000
@@ -190,6 +190,14 @@
 	g_assert(cong_doc);
 
 	cong_primary_window_new(cong_doc);
+
+	/* now we have a primary window we can set the "Save" menu item */
+	{
+		CongPrimaryWindow *primary_window = cong_document_get_primary_window(cong_doc);
+		g_assert (primary_window);
+		cong_primary_window_action_set_sensitive (primary_window, "Save", FALSE);
+	}
+
 	g_object_unref( G_OBJECT(cong_doc));
 
 }
Index: cong-file-export.c
===================================================================
RCS file: /cvs/gnome/conglomerate/src/cong-file-export.c,v
retrieving revision 1.23
diff -u -r1.23 cong-file-export.c
--- cong-file-export.c	14 Oct 2004 20:54:22 -0000	1.23
+++ cong-file-export.c	18 Oct 2004 22:38:56 -0000
@@ -290,7 +290,7 @@
 						       dialog_details);
 
 		gtk_combo_box_set_active (GTK_COMBO_BOX(dialog_details->combo_box), 0);
-
+		g_assert (dialog_details->got_any_exporters);
 	}
 
 	dialog_details->description = GTK_LABEL(gtk_label_new(NULL));
@@ -325,11 +325,9 @@
 						     dialog_details->option_holder,
 						     TRUE);
 
-	if (dialog_details->got_any_exporters) {
-		monitor_exporter(dialog_details);
-		setup_description(dialog_details);
-		setup_options(dialog_details);
-	}
+	monitor_exporter(dialog_details);
+	setup_description(dialog_details);
+	setup_options(dialog_details);
 
 	g_signal_connect(dialog_details->combo_box,
 			 "changed",
@@ -386,12 +384,6 @@
  * how to inform the user of what is going on, and
  * whether we allow the user to stop an exporter whilst it is
  * processing.
- *
- * At the moment the "Export" menu item can be called when there are
- * no exporters registered for the document. We really should make the
- * menu item insensitive in this situation to avoid this situation.
- * (this is already pointed out in action_callback_file_export in
- *  cong-menus.c)
  */
 void
 cong_ui_hook_file_export (CongDocument *doc,
@@ -412,29 +404,6 @@
 					    "dialog_details");
 	g_assert(dialog_details);
 
-	/*
-	 * Temporary until we disable the Export menu item for
-	 * documents with no registered exporterd
-	 */
-	if (dialog_details->got_any_exporters==FALSE) {
-		GtkDialog* error_dialog;
-		gchar *what_failed;
-
-		cong_document_export_dialog_delete (dialog);
-
-		what_failed = g_strdup_printf (_("Conglomerate cannot export \"%s\""),
-					       cong_document_get_filename(doc));
-		error_dialog = cong_error_dialog_new (toplevel_window,
-						      what_failed, 
-						      _("None of Conglomerate's plugins know how to export files of this type."),
-						      "");
-
-		gtk_dialog_run (error_dialog);
-		gtk_widget_destroy (GTK_WIDGET(error_dialog));
-		g_free (what_failed);
-		return;
-	}
-
 	gtk_widget_show_all (dialog);
 
 	/*
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.