Re: [Nautilus-list] [patch] - hierarchical script menus - done?



I fixed a bug in add_directory_to_scripts_directory_list ()  where I was
indexing into a string of unknown size.

Hopefully this is the last time I need to send this patch, tonight
anyway... :)

-- 
The theory of groups is a branch of Mathematics in which one does
something to something and then compares the result with the result
obtained from doing the same thing to something else, or something else to
the same thing.
J. R. Newman
--- ./nautilus-1.0.3-orig/src/file-manager/fm-directory-view.c	Sat May 26 11:02:20 2001
+++ ./nautilus-1.0.3/src/file-manager/fm-directory-view.c	Sun Jun  3 01:10:49 2001
@@ -22,8 +22,9 @@
  *
  * Authors: Ettore Perazzoli,
  *          John Sullivan <sullivan eazel com>,
- *          Darin Adler <darin eazel com>
- *          Pavel Cisler <pavel eazel com>
+ *          Darin Adler <darin eazel com>,
+ *          Pavel Cisler <pavel eazel com>,
+ *          David Emory Watson <dwatson cs ucr edu>
  */
 
 #include <config.h>
@@ -135,6 +136,8 @@
 #define FM_DIRECTORY_VIEW_POPUP_PATH_OPEN_WITH				"/popups/selection/Open Placeholder/Open With"
 #define FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS				"/popups/selection/Open Placeholder/Scripts"
 
+#define MAX_MENU_LEVELS 5
+
 enum {
 	ADD_FILE,
 	BEGIN_ADDING_FILES,
@@ -166,9 +169,16 @@ struct FMDirectoryViewDetails
 	NautilusFile *directory_as_file;
 	BonoboUIComponent *ui;
 
-	NautilusDirectory *scripts_directory;
-	guint scripts_added_handler_id;
-	guint scripts_changed_handler_id;
+	/* The first link in this list is reserved for the top level scripts
+	 * directory.  This means that new links are always appended to the
+	 * list. In addition, lower level script  directories must always occur
+	 * after higher level ones so if you remove a directory, you must remove
+	 * its subdirectories as well.  This is done to preserve the ordering of
+	 * the callbacks when we rebuild the  script menus.
+	 */
+	GList *scripts_directory_list;
+	GList *scripts_uri_list;
+	guint scripts_directory_length;
 
 	guint display_selection_idle_id;
 	guint update_menus_timeout_id;
@@ -287,7 +297,9 @@ static void           schedule_timeout_d
 static void           unschedule_timeout_display_of_pending_files    (FMDirectoryView      *view);
 static void           unschedule_display_of_pending_files            (FMDirectoryView      *view);
 static void           disconnect_model_handlers                      (FMDirectoryView      *view);
-static void           disconnect_script_handlers                     (FMDirectoryView      *view);
+static void           disconnect_scripts_directory_list              (FMDirectoryView      *view);
+static void           disconnect_scripts_directory                   (FMDirectoryView *view,
+								      NautilusDirectory *directory);
 static void           filtering_changed_callback                     (gpointer              callback_data);
 static void           metadata_for_directory_as_file_ready_callback  (NautilusFile         *file,
 								      gpointer              callback_data);
@@ -1138,46 +1150,45 @@ get_scripts_directory (void)
 
 static void
 scripts_added_or_changed_callback (NautilusDirectory *directory,
-		        GList *files,
-		        gpointer callback_data)
+				   GList *files,
+				   gpointer callback_data)
 {
 	FMDirectoryView *view;
 
 	view = FM_DIRECTORY_VIEW (callback_data);
 
-	g_assert (directory == view->details->scripts_directory);
+	g_assert (g_list_find (view->details->scripts_directory_list, directory) != NULL);
 
 	view->details->scripts_invalid = TRUE;
 	schedule_update_menus (view);
 }
 
 static void
-connect_script_handlers (FMDirectoryView *view)
+connect_script_handlers (FMDirectoryView *view,
+			 NautilusDirectory *directory)
 {
-	if (view->details->scripts_directory == NULL) {
-		return;
-	}
+	g_assert(directory != NULL);
 
-	nautilus_directory_file_monitor_add (view->details->scripts_directory,
-					     &view->details->scripts_directory,
+	nautilus_directory_file_monitor_add (directory, &view->details->scripts_directory_list,
 					     FALSE, FALSE, NULL);
 
-    	view->details->scripts_added_handler_id = gtk_signal_connect
-		(GTK_OBJECT (view->details->scripts_directory),
-		 "files_added",
-		 scripts_added_or_changed_callback,
-		 view);
+	gtk_signal_connect (GTK_OBJECT (directory),
+			    "files_added",
+			    scripts_added_or_changed_callback,
+			    view);
 
-	view->details->scripts_changed_handler_id = gtk_signal_connect
-		(GTK_OBJECT (view->details->scripts_directory), 
-		 "files_changed",
-		 scripts_added_or_changed_callback,
-		 view);
+	gtk_signal_connect (GTK_OBJECT (directory), 
+			    "files_changed",
+			    scripts_added_or_changed_callback,
+			    view);
 }
 
 static void
 fm_directory_view_initialize (FMDirectoryView *view)
 {
+	NautilusDirectory *scripts_directory;
+	char *scripts_directory_uri;
+
 	view->details = g_new0 (FMDirectoryViewDetails, 1);
 
 	/* We need to have our own X window so that cut, copy, and
@@ -1196,8 +1207,19 @@ fm_directory_view_initialize (FMDirector
 
 	view->details->nautilus_view = nautilus_view_new (GTK_WIDGET (view));
 
-	view->details->scripts_directory = get_scripts_directory ();
-	connect_script_handlers (view);
+	scripts_directory = get_scripts_directory ();
+
+	if (scripts_directory != NULL) {
+		view->details->scripts_directory_list = g_list_append (view->details->scripts_directory_list,
+								       scripts_directory);
+
+		scripts_directory_uri = nautilus_directory_get_uri (scripts_directory);
+		view->details->scripts_uri_list = g_list_append (view->details->scripts_uri_list,
+								 scripts_directory_uri);
+		view->details->scripts_directory_length = strlen (scripts_directory_uri);
+
+		connect_script_handlers (view, scripts_directory);
+	}
 
 	view->details->zoomable = bonobo_zoomable_new ();
 	bonobo_zoomable_set_parameters_full (view->details->zoomable,
@@ -1297,6 +1319,7 @@ static void
 fm_directory_view_destroy (GtkObject *object)
 {
 	FMDirectoryView *view;
+	GList *node1, *node2;
 
 	view = FM_DIRECTORY_VIEW (object);
 
@@ -1314,8 +1337,17 @@ fm_directory_view_destroy (GtkObject *ob
 	fm_directory_view_stop (view);
 	fm_directory_view_clear (view);
 
-	disconnect_script_handlers (view);
-	nautilus_directory_unref (view->details->scripts_directory);
+	disconnect_scripts_directory_list (view);
+
+	for (node1 = view->details->scripts_directory_list, node2 = view->details->scripts_uri_list;
+	     node1 != NULL;
+	     node1 = node1->next, node2 = node2->next) {
+		nautilus_directory_unref (node1->data);
+		g_free (node2->data);
+	}
+
+	g_list_free (view->details->scripts_directory_list);
+	g_list_free (view->details->scripts_uri_list);
 
 	disconnect_model_handlers (view);
 	nautilus_directory_unref (view->details->model);
@@ -3176,7 +3208,24 @@ add_numbered_menu_item (BonoboUIComponen
 		(ui, parent_path, index);	
 	bonobo_ui_component_add_verb_full (ui, verb_name, callback, callback_data, destroy_notify);	   
 	g_free (verb_name);
-}				 
+}
+
+/* FIXME: Allow the icon to be set. */
+static void
+add_menu (BonoboUIComponent *ui,
+	  const char *parent_path,
+	  const char *label)
+{
+	char *escaped_label;
+
+	escaped_label = eel_str_double_underscores (label);
+
+	nautilus_bonobo_add_submenu (ui,
+				     parent_path,
+				     escaped_label);
+
+	g_free (escaped_label);
+}
 
 static void
 add_application_to_bonobo_menu (FMDirectoryView *directory_view,
@@ -3521,12 +3570,14 @@ run_script_callback (BonoboUIComponent *
 	chdir (old_working_dir);		
 	g_free (old_working_dir);
 	g_free (quoted_path);
-}				    
+}
 
 static void
-add_script_to_menus (FMDirectoryView *directory_view,
-		     NautilusFile *file,
-		     int index)
+add_script_to_script_menus (FMDirectoryView *directory_view,
+			  NautilusFile *file,
+			  int index,
+			  const char *menu_path,
+			  const char *popup_path)
 {
 	ScriptLaunchParameters *launch_parameters;
 	char *tip;
@@ -3535,13 +3586,13 @@ add_script_to_menus (FMDirectoryView *di
 
 	name = nautilus_file_get_name (file);
 	tip = g_strdup_printf (_("Run \"%s\" on any selected items"), name);
-	
+
 	launch_parameters = script_launch_parameters_new (file, directory_view);
 	pixbuf = nautilus_icon_factory_get_pixbuf_for_file 
 		(file, NULL, NAUTILUS_ICON_SIZE_FOR_MENUS, TRUE);
 
- 	add_numbered_menu_item (directory_view->details->ui, 
-				FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER,
+	add_numbered_menu_item (directory_view->details->ui, 
+				menu_path,
 				name,
 				tip,
 				index,
@@ -3553,8 +3604,8 @@ add_script_to_menus (FMDirectoryView *di
 	/* Use same launch parameters and no DestroyNotify for popup item, which has same
 	 * lifetime as the item in the File menu in the menu bar.
 	 */
- 	add_numbered_menu_item (directory_view->details->ui, 
-				FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER,
+	add_numbered_menu_item (directory_view->details->ui,
+				popup_path,
 				name,
 				tip,
 				index,
@@ -3569,17 +3620,158 @@ add_script_to_menus (FMDirectoryView *di
 }
 
 static void
+add_menu_to_script_menus (FMDirectoryView *directory_view,
+			  NautilusFile *file,
+			  const char *menu_path,
+			  const char *popup_path)
+{
+	ScriptLaunchParameters *launch_parameters;
+	char *tip;
+	char *name;
+	GdkPixbuf *pixbuf;
+
+	name = nautilus_file_get_name (file);
+	tip = g_strdup_printf (_("Run \"%s\" on any selected items"), name);
+
+	launch_parameters = script_launch_parameters_new (file, directory_view);
+	pixbuf = nautilus_icon_factory_get_pixbuf_for_file 
+		(file, NULL, NAUTILUS_ICON_SIZE_FOR_MENUS, TRUE);
+
+	add_menu (directory_view->details->ui, 
+		  menu_path,
+		  name);
+
+	add_menu (directory_view->details->ui,
+		  popup_path,
+		  name);
+
+	gdk_pixbuf_unref (pixbuf);
+	g_free (name);
+	g_free (tip);
+}
+
+static gboolean
+add_directory_to_scripts_directory_list (FMDirectoryView *view, NautilusFile *file)
+{
+	char *uri;
+	NautilusDirectory *directory;
+	int num_levels;
+	int i;
+
+	uri = nautilus_file_get_uri (file);
+
+	if (strlen (uri) < view->details->scripts_directory_length) {
+		g_free (uri);
+		return FALSE;
+	}
+
+	num_levels = 0;
+	for (i = view->details->scripts_directory_length; uri[i] != '\0'; i++) {
+		if (uri[i] == '/') {
+			num_levels++;
+		}
+	}
+
+	if (num_levels > MAX_MENU_LEVELS) {
+		g_free (uri);
+		return FALSE;
+	}
+
+	directory = nautilus_directory_get (uri);
+
+	if (g_list_find (view->details->scripts_directory_list, directory) == NULL) {
+		connect_script_handlers (view, directory);
+		
+		view->details->scripts_directory_list = g_list_append (view->details->scripts_directory_list,
+								       directory);
+		view->details->scripts_uri_list = g_list_append (view->details->scripts_uri_list,
+								 uri);
+	} else {
+		nautilus_directory_unref (directory);
+		g_free (uri);
+	}
+
+	return TRUE;
+}
+
+/**
+ * clean_scripts_directory_list
+ *
+ * Verify that a given directory has not changed.  If it has, remove references to
+ * it from both scripts_directory_list and scripts_uri_list.
+ * @view: FMDirectoryView of interest.
+ * @directory: The directory that we are verifying.
+ * 
+ * Return value: True if the directory was removed, false otherwise.
+ * 
+ **/
+static gboolean
+clean_scripts_directory_list (FMDirectoryView *view, NautilusDirectory *directory)
+{
+	GList *node1, *node2;
+	char *uri;
+
+	uri = nautilus_directory_get_uri (directory);
+
+	for (node1 = view->details->scripts_directory_list, node2 = view->details->scripts_uri_list;
+	     node1 != NULL;
+	     node1 = node1->next, node2 = node2->next) {
+		if (directory == node1->data) {
+			if (strcmp (uri, node2->data) == 0) {
+				g_free (uri);
+				return FALSE;
+			} else {
+				view->details->scripts_directory_list = g_list_remove_link (view->details->scripts_directory_list, node1);
+				view->details->scripts_uri_list = g_list_remove_link (view->details->scripts_uri_list, node2);
+
+				disconnect_scripts_directory (view, node1->data);
+				nautilus_directory_unref (node1->data);
+				g_free (node2->data);
+
+				g_list_free (node1);
+				g_list_free (node2);
+
+				g_free (uri);
+				return TRUE;
+			}
+		}
+	}
+
+	/* This should never happen. */
+	g_free (uri);
+	return TRUE;
+}
+
+static void
 reset_scripts_menu (FMDirectoryView *view, GList *all_files)
 {
 	GList *node;
 	NautilusFile *file;
 	int index;
 	gboolean any_scripts;
-	
-	nautilus_bonobo_remove_menu_items_and_commands
-		(view->details->ui, FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER);
-	nautilus_bonobo_remove_menu_items_and_commands 
-		(view->details->ui, FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER);
+	char *parent_path;
+	char *menu_path, *popup_path;
+
+	if (all_files == NULL) {
+		view->details->scripts_invalid = FALSE;
+		return;
+	}
+
+	parent_path = nautilus_file_get_parent_uri (all_files->data);
+
+	menu_path = g_strdup_printf ("%s%s", FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER,
+				     parent_path + view->details->scripts_directory_length);
+	popup_path = g_strdup_printf ("%s%s", FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER,
+				      parent_path + view->details->scripts_directory_length);
+
+	/* We rebuild the entire scripts menu each time so we only need to remove the
+	   top level menu since it contains all other menus and is rebuilt first. */
+	if (strcmp(parent_path, view->details->scripts_uri_list->data) == 0) {
+		nautilus_bonobo_remove_menu_items_and_commands (view->details->ui, menu_path);
+		nautilus_bonobo_remove_menu_items_and_commands (view->details->ui, popup_path);
+	}
+
+	g_free (parent_path);
 
 	all_files = nautilus_file_list_sort_by_name (all_files);
 
@@ -3588,11 +3780,19 @@ reset_scripts_menu (FMDirectoryView *vie
 		file = node->data;
 
 		if (file_is_launchable (file)) {
-			add_script_to_menus (view, file, index);
+			add_script_to_script_menus (view, file, index, menu_path, popup_path);
 			any_scripts = TRUE;
+		} else if (nautilus_file_is_directory (file)) {
+			if (add_directory_to_scripts_directory_list (view, file)) {
+				add_menu_to_script_menus (view, file, menu_path, popup_path);
+				any_scripts = TRUE;
+			}
 		}
 	}
 
+	g_free (popup_path);
+	g_free (menu_path);
+
 	nautilus_bonobo_set_hidden (view->details->ui, 
 				    FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_SEPARATOR, 
 				    !any_scripts);
@@ -3613,7 +3813,7 @@ reset_scripts_menu_callback (NautilusDir
 	view = callback_data;
 
 	g_assert (FM_IS_DIRECTORY_VIEW (view));
-	g_assert (view->details->scripts_directory == directory);
+	g_assert (g_list_find (view->details->scripts_directory_list, directory) != NULL);
 
 	reset_scripts_menu (view, files);
 }
@@ -3623,23 +3823,40 @@ call_when_ready_on_scripts_directory (FM
 				      NautilusDirectoryCallback scripts_directory_callback)
 {
 	GList *attributes;
+	GList *node, *next_node;
 
-	if (view->details->scripts_directory == NULL) {
+	if (view->details->scripts_directory_list == NULL) {
 		return;
 	}
 
-	nautilus_directory_cancel_callback (view->details->scripts_directory,
-				    	    scripts_directory_callback,
-				    	    view);
+	for (node = view->details->scripts_directory_list; node != NULL; node = node->next) {
+		nautilus_directory_cancel_callback (node->data,
+						    scripts_directory_callback,
+						    view);
+	}
 
 	/* Later we may want to add more attributes here to get icon, etc. */
 	attributes = nautilus_icon_factory_get_required_file_attributes ();
 	attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_CAPABILITIES);
 	attributes = g_list_prepend (attributes, NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT);
-	nautilus_directory_call_when_ready (view->details->scripts_directory,
-					    attributes,
-					    scripts_directory_callback,
-					    view);
+
+	node = view->details->scripts_directory_list;
+	while (node != NULL) {
+
+		/* It is important that we remember the next link in the list because
+		   clean_scripts_directory_list() may remove links from underneath us. */
+		next_node = node->next;
+
+		if (!clean_scripts_directory_list (view, node->data)) {
+			nautilus_directory_call_when_ready (node->data,
+							    attributes,
+							    scripts_directory_callback,
+							    view);
+		}
+
+		node = next_node;
+	}
+
 	g_list_free (attributes);
 }
 
@@ -3659,8 +3876,8 @@ open_scripts_folder_callback (BonoboUICo
 
 	view = FM_DIRECTORY_VIEW (callback_data);          
 
-	if (view->details->scripts_directory != NULL) {
-		uri = nautilus_directory_get_uri (view->details->scripts_directory);
+	if (view->details->scripts_directory_list != NULL) {
+		uri = nautilus_directory_get_uri (view->details->scripts_directory_list->data);
 		open_location (view, uri, RESPECT_PREFERENCE);
 		g_free (uri);
 
@@ -4892,9 +5109,17 @@ disconnect_directory_handler (FMDirector
 }
 
 static void
-disconnect_scripts_directory_handler (FMDirectoryView *view, int *id)
+disconnect_scripts_directory (FMDirectoryView *view, NautilusDirectory *directory)
 {
-	disconnect_handler (GTK_OBJECT (view->details->scripts_directory), id);
+	gtk_signal_disconnect_by_func (GTK_OBJECT (directory),
+				       scripts_added_or_changed_callback,
+				       view);
+
+	nautilus_directory_file_monitor_remove (directory, &view->details->scripts_directory_list);
+	
+	nautilus_directory_cancel_callback (directory,
+					    reset_scripts_menu_callback,
+					    view);
 }
 
 static void
@@ -4927,21 +5152,13 @@ disconnect_model_handlers (FMDirectoryVi
 }
 
 static void
-disconnect_script_handlers (FMDirectoryView *view)
+disconnect_scripts_directory_list (FMDirectoryView *view)
 {
-	if (view->details->scripts_directory == NULL) {
-		return;
-	}
-
-	disconnect_scripts_directory_handler (view, &view->details->scripts_added_handler_id);
-	disconnect_scripts_directory_handler (view, &view->details->scripts_changed_handler_id);
-
-	nautilus_directory_file_monitor_remove (view->details->scripts_directory,
-						&view->details->scripts_directory);
+	GList *node;
 
-	nautilus_directory_cancel_callback (view->details->scripts_directory,
-					    reset_scripts_menu_callback,
-					    view);
+	for (node = view->details->scripts_directory_list; node != NULL; node = node->next) {
+		disconnect_scripts_directory (view, node->data);
+	}
 }
 
 /**


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]