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



Indeed.  I forgot the u and never knew the p.  Thanks!

On 03 Jun 2001 00:20:29 -0700, Seth Nickell wrote:
> Can you please send unified diffs? (cvs diff -up is the preferred
> command, it also tags on function names).
> 
> -Seth

--- ./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	Sat Jun  2 23:35:51 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,153 @@ 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);
+
+	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 +3775,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 +3808,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 +3818,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 +3871,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 +5104,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 +5147,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]