Index: panel-run-dialog.c
===================================================================
RCS file: /cvs/gnome/gnome-panel/gnome-panel/panel-run-dialog.c,v
retrieving revision 1.28
diff -u -p -r1.28 panel-run-dialog.c
--- panel-run-dialog.c	28 May 2004 09:59:57 -0000	1.28
+++ panel-run-dialog.c	14 Jun 2004 01:18:29 -0000
@@ -75,6 +75,7 @@ typedef struct {
 	
 	GtkListStore     *program_list_store;
 
+	GList		 *possible_executables;
 	GList		 *completion_items;
 	GCompletion      *completion;
 	
@@ -133,6 +134,11 @@ panel_run_dialog_destroy (PanelRunDialog
 					    dialog->content_notify_id);
 	dialog->content_notify_id = 0;
 
+	for (l = dialog->possible_executables; l; l = l->next)
+		g_free (l->data);
+	g_list_free (dialog->possible_executables);
+	dialog->possible_executables = NULL;
+	
 	for (l = dialog->completion_items; l; l = l->next)
 		g_free (l->data);
 	g_list_free (dialog->completion_items);
@@ -1073,23 +1079,28 @@ panel_run_dialog_setup_file_button (Pane
 }
 
 static GList *
-fill_files_from (GList      *list,
-		 const char *dirname,
-		 const char *prefix,
+fill_files_from (const char *dirname,
+		 const char *dirprefix,
+		 char        prefix,
 		 GList      *existing_items)
 {
+	GList         *list;
 	struct dirent *dent;
 	DIR           *dir;
 	
+	list = NULL;
 	dir = opendir (dirname);
 	
 	if (!dir)
 		return list;
 	
 	while ((dent = readdir (dir))) {
-		char          *file;
-		char          *item;
-		const char    *suffix;
+		char       *file;
+		char       *item;
+		const char *suffix;
+		
+		if (dent->d_name [0] != prefix)
+			continue;
 		
 		file = g_build_filename (dirname, dent->d_name, NULL);
 		
@@ -1099,7 +1110,7 @@ fill_files_from (GList      *list,
 		
 		g_free (file);
 		
-		item = g_strconcat (prefix, dent->d_name, suffix, NULL);
+		item = g_strconcat (dirprefix, dent->d_name, suffix, NULL);
 		
 		if (g_list_find_custom (existing_items, item, (GCompareFunc) strcmp)) {
 			g_free (item);
@@ -1116,100 +1127,134 @@ fill_files_from (GList      *list,
 }	
 
 static GList *
-fill_executables_from (GList      *list,
-		       const char *dirname)
+fill_possible_executables ()
 {
-	struct dirent *dent;
-	DIR           *dir;
-	char          *file;
-	struct stat    file_stat;
-
-	dir = opendir (dirname);
-
-	if (!dir)
-		return list;
-
-	while ((dent = readdir (dir))) {
-		file = g_build_filename (dirname, dent->d_name, NULL);
-
-		if (!access (file, X_OK) &&
-		    !stat (file, &file_stat) && !S_ISDIR (file_stat.st_mode))
-			list = g_list_prepend (list, g_strdup (dent->d_name));
-		
-		g_free (file);
-	}
-
-	closedir (dir);
+	GList         *list;
+	const char    *path;
+	char         **pathv;
+	int            i;
 	
-	return list;
-}
-
-static GList *
-fill_executables (GList *list)
-{
-	const char  *path;
-	char       **pathv;
-	int          i;
-
+	list = NULL;
 	path = g_getenv ("PATH");
 
 	if (!path || !path [0])
 		return list;
 
 	pathv = g_strsplit (path, ":", 0);
+	
+	for (i = 0; pathv [i]; i++) {
+		char          *file;
+		struct dirent *dent;
+		DIR           *dir;
+
+		dir = opendir (pathv [i]);
+
+		if (!dir)
+			continue;
 
-	for (i = 0; pathv [i]; i++)
-		list = fill_executables_from (list, pathv [i]);
+		while ((dent = readdir (dir))) {
+			file = g_build_filename (pathv [i], dent->d_name, NULL);
+			list = g_list_prepend (list, file);
+		}
+
+		closedir (dir);
+	}
 	
 	g_strfreev (pathv);
 	
 	return list;
 }
 
-static void
-panel_run_dialog_ensure_completion (PanelRunDialog *dialog)
+static GList *
+fill_executables(GList *possible_executables,
+		 GList *existing_items,
+		 char   prefix)
 {
-	if (!dialog->completion) {
-		dialog->completion = g_completion_new (NULL);
-		
-		dialog->completion_items = fill_executables (NULL);
-		dialog->completion_items = fill_files_from  (dialog->completion_items, "/", "/", NULL);
-		dialog->completion_items = fill_files_from  (dialog->completion_items, g_get_home_dir(), "", NULL);
-		
-		g_completion_add_items (dialog->completion,
-					dialog->completion_items);
+	GList *list;
+	GList *l;
+	
+	list = NULL;	
+	
+	for (l = possible_executables; l; l = l->next) {
+		const char *filename;
+		char       *basename;
+			
+		filename = l->data;
+		basename = g_path_get_basename (filename);
+			
+		if (basename [0] == prefix &&
+		    g_file_test (filename, G_FILE_TEST_IS_REGULAR) &&
+		    g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE)) {
+			    
+			if (g_list_find_custom (existing_items, basename,
+						(GCompareFunc) strcmp)) {
+				g_free (basename);
+				return NULL;
+			}
+
+			list = g_list_prepend (list, basename);
+		 } else {
+			g_free (basename);
+		 }
 	}
+	
+	return list;
 }
 
 static void
 panel_run_dialog_update_completion (PanelRunDialog *dialog,
 				    const char     *text)
 {
-	g_assert (dialog->completion != NULL);
+	GList *list;
+	char   prefix;
+
+	list = NULL;
+	prefix = text [strlen (text) - 1];
 	
-	if (text [strlen (text) - 1] == '/') {
-		const char *dirname;
-		char       *freeme;
-		GList      *list;
+	if (!dialog->completion) {
+		dialog->completion = g_completion_new (NULL);
+		dialog->possible_executables = fill_possible_executables ();
+	}
+	
+	if (text [0] == '/') {
+		/* complete against absolute path */
+		char *dirname;
 		
-		dirname = text;
-		freeme = NULL;
+		dirname = g_strndup (text, strlen (text) - 1);
 		
-		if (dirname [0] != '/')
-			dirname = freeme = g_build_filename(g_get_home_dir(), text, NULL);
+		list = fill_files_from (dirname, dirname, prefix, dialog->completion_items);
 		
-		list = fill_files_from (NULL, dirname, text, dialog->completion_items);
+		g_free (dirname);
+	} else {
+		/* complete against relative path and executable name */
+		GList *executables;
+		char *dirname;
+		char *dirprefix;
 		
-		if (freeme)
-			g_free (freeme);
+		executables = NULL;
+		if (!strchr (text, '/'))
+			executables = fill_executables (dialog->possible_executables,
+							dialog->completion_items,
+							prefix);
 		
-		if (list == NULL)
-			return;
+		dirname = g_build_filename (g_get_home_dir (), text, NULL);
+		dirname [strlen (dirname) - 1] = '\0';
 		
-		g_completion_add_items (dialog->completion, list);
+		dirprefix = g_strndup (text, strlen (text) - 1);
+
+		list = fill_files_from (dirname, dirprefix, prefix, dialog->completion_items);
+		list = g_list_concat (list, executables);
 		
-		dialog->completion_items = g_list_concat (dialog->completion_items, list);
+		g_free (dirname);
+		g_free (dirprefix);
 	}
+
+	if (list == NULL)
+		return;
+		
+	g_completion_add_items (dialog->completion, list);
+		
+	dialog->completion_items = g_list_concat (dialog->completion_items, list);	
 }
 
 static gboolean
@@ -1237,21 +1282,17 @@ entry_event (GtkEditable    *entry,
 		    tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
 	    		gtk_editable_select_region (entry, 0, 0);		
 			gtk_editable_set_position (entry, -1);
-
-			panel_run_dialog_update_completion (dialog,
-			    				    gtk_entry_get_text (GTK_ENTRY (entry)));
 			
 			return TRUE;
 		}
 	} else if (event->type == GDK_KEY_PRESS &&
 		   event->length > 0) {
-		panel_run_dialog_ensure_completion (dialog);
-
+			   
 		gtk_editable_get_selection_bounds (entry, &pos, &tmp);
 
 		if (dialog->completion_started &&
 		    pos != tmp &&
-		    pos != 1 &&
+		    pos != 0 &&
 		    tmp == strlen (gtk_entry_get_text (GTK_ENTRY (entry)))) {
 			temp = gtk_editable_get_chars (entry, 0, pos);
 			prefix = g_strconcat (temp, event->string, NULL);
@@ -1264,6 +1305,15 @@ entry_event (GtkEditable    *entry,
 			return FALSE;
 		}
 		
+		if ((strlen (prefix) == 1 && !strchr (prefix, '/')) ||
+		    (strchr (prefix, '\0') - strrchr (prefix, '/') == 2))
+			    panel_run_dialog_update_completion (dialog, prefix);
+		
+		if (!dialog->completion) {
+			g_free (prefix);
+			return FALSE;
+		}
+		
 		pos = strlen (prefix);
 		nprefix = NULL;
 
@@ -1276,17 +1326,11 @@ entry_event (GtkEditable    *entry,
 			
 			dialog->completion_started = TRUE;
 
-			if (strcmp(event->string, "/") == 0)
-				panel_run_dialog_update_completion (dialog, nprefix);
-			
 			g_free (nprefix);
 			g_free (prefix);
 			
 			return TRUE;
 		}
-
-		if (strcmp(event->string, "/") == 0)
-			panel_run_dialog_update_completion (dialog, prefix);
 		
 		g_free (prefix);
 	}
