[PATCH]: Application chooser for the Open With dialog




		Hello

	I wrote the attached patch as a live example during a mini gnome-love
session.

	It adds a chooser of known applications (got from libmenu) to the open
with dialog for the nautilus file properties page. Most of the code is
ripped from the Exec dialog from gnome-panel.

	I didn't add the expander because I think most users would select it
from there, and if the desired application is not present, or the user
prefers to type it, that piece of UI is not very disturbing.

What do you think about it?

Thanks.

Salu2

? eel-2.0-uninstalled.pc
? mkinstalldirs
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/eel/ChangeLog,v
retrieving revision 1.664
diff -u -r1.664 ChangeLog
--- ChangeLog	25 Nov 2004 17:07:56 -0000	1.664
+++ ChangeLog	5 Jan 2005 15:57:24 -0000
@@ -1,3 +1,16 @@
+2005-01-05  Fernando Herrera  <fherrera onirica com>
+
+	* configure.in:
+	* eel/Makefile.am:
+	* eel/eel-open-with-dialog.c: (eel_open_with_dialog_add_icon_idle),
+	(compare_applications), (get_all_applications_from_dir),
+	(get_all_applications), (eel_open_with_dialog_add_items_idle),
+	(remove_parameters), (program_list_selection_changed),
+	(program_list_selection_activated),
+	(eel_open_with_dialog_instance_init):
+	* eel/eel-open-with-dialog.h: Add an application selector showing all
+	available applications from the .dekstop files.
+
 2004-11-25  Marco Pesenti Gritti  <marco gnome org>
 
 	reviewed by: Alexander Larsson  <alexl redhat com>
Index: configure.in
===================================================================
RCS file: /cvs/gnome/eel/configure.in,v
retrieving revision 1.181
diff -u -r1.181 configure.in
--- configure.in	24 Nov 2004 12:13:39 -0000	1.181
+++ configure.in	5 Jan 2005 15:57:24 -0000
@@ -14,6 +14,8 @@
 XML_REQUIRED=2.4.7
 GAIL_REQUIRED=0.16
 LIBGLADE_REQUIRED=2.0.0
+LIBGNOME_DESKTOP_REQUIRED=2.1.4
+LIBGNOME_MENU_REQUIRED=2.9.1
 
 AC_SUBST(ART_REQUIRED)
 AC_SUBST(GCONF_REQUIRED)
@@ -82,6 +84,8 @@
 	libgnome-2.0		>= $GNOME_REQUIRED
 	libgnomeui-2.0		>= $GNOME_UI_REQUIRED
 	libxml-2.0		>= $XML_REQUIRED
+	libgnome-menu           >= $LIBGNOME_MENU_REQUIRED
+	gnome-desktop-2.0	>= $LIBGNOME_DESKTOP_REQUIRED
 ])
 AC_SUBST(EEL_CFLAGS)
 AC_SUBST(EEL_LIBS)
Index: eel/Makefile.am
===================================================================
RCS file: /cvs/gnome/eel/eel/Makefile.am,v
retrieving revision 1.82
diff -u -r1.82 Makefile.am
--- eel/Makefile.am	22 Jul 2004 03:55:49 -0000	1.82
+++ eel/Makefile.am	5 Jan 2005 15:57:24 -0000
@@ -12,6 +12,7 @@
 	-DG_DISABLE_DEPRECATED				\
 	-DGDK_DISABLE_DEPRECATED			\
 	-DGDK_PIXBUF_DISABLE_DEPRECATED			\
+	-DMENU_I_KNOW_THIS_IS_UNSTABLE			\
 	$(NULL)
 
 # Disable this for now due to gtk 2.4 deprecation
Index: eel/eel-open-with-dialog.c
===================================================================
RCS file: /cvs/gnome/eel/eel/eel-open-with-dialog.c,v
retrieving revision 1.3
diff -u -r1.3 eel-open-with-dialog.c
--- eel/eel-open-with-dialog.c	23 Jul 2004 16:20:18 -0000	1.3
+++ eel/eel-open-with-dialog.c	5 Jan 2005 15:57:24 -0000
@@ -42,10 +42,17 @@
 #include <gtk/gtkimage.h>
 #include <gtk/gtkiconfactory.h>
 #include <gtk/gtklabel.h>
+#include <gtk/gtkscrolledwindow.h>
 #include <gtk/gtkstock.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkcellrendererpixbuf.h>
 #include <gtk/gtkvbox.h>
+#include <libgnome/gnome-desktop-item.h>
 #include <libgnomevfs/gnome-vfs-mime-handlers.h>
 #include <libgnomevfs/gnome-vfs-uri.h>
+#include <menu-tree.h>
 
 struct _EelOpenWithDialogDetails {
 	char *uri;
@@ -59,8 +66,27 @@
 	GtkWidget *label;
 	GtkWidget *entry;
 
+	GtkWidget *desc_label;
+
 	GtkWidget *open_label;
 	GtkWidget *open_image;
+
+	gboolean       use_program_list;
+	GtkWidget     *program_list;
+	GtkListStore  *program_list_store;
+	GSList	      *add_icon_paths;
+	gint	       add_items_idle_id;
+	gint	       add_icons_idle_id;
+};
+
+enum {
+        COLUMN_ICON,
+        COLUMN_ICON_FILE,
+        COLUMN_NAME,
+        COLUMN_COMMENT,
+        COLUMN_PATH,
+        COLUMN_EXEC,
+        NUM_COLUMNS
 };
 
 enum {
@@ -423,6 +449,320 @@
 	return image;
 }
 
+
+static gboolean
+eel_open_with_dialog_add_icon_idle (EelOpenWithDialog *dialog)
+{
+	GtkTreeIter   iter;
+	GtkTreePath  *path;
+	GdkPixbuf    *pixbuf;
+	char         *file;
+	gboolean      long_operation = FALSE;
+
+	do {
+		if (!dialog->details->add_icon_paths) {
+			dialog->details->add_icons_idle_id = 0;
+			return FALSE;
+		}
+
+		path = dialog->details->add_icon_paths->data;
+		dialog->details->add_icon_paths->data = NULL;
+		dialog->details->add_icon_paths = g_slist_delete_link (dialog->details->add_icon_paths,
+						              dialog->details->add_icon_paths);
+
+		if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->details->program_list_store),
+					      &iter,
+					      path)) {
+			gtk_tree_path_free (path);
+			continue;
+		}
+		
+		gtk_tree_path_free (path);
+
+		gtk_tree_model_get (GTK_TREE_MODEL (dialog->details->program_list_store), &iter,
+				    COLUMN_ICON_FILE, &file, -1);
+
+		if (g_path_is_absolute (file)) {
+			pixbuf = gdk_pixbuf_new_from_file_at_size (file, 24, 24, 0);
+			long_operation = TRUE;
+		} else {
+			char *icon_no_extension;
+			char *p;
+
+			icon_no_extension = g_strdup (file);
+			p = strrchr (icon_no_extension, '.');
+			if (p &&
+			    (strcmp (p, ".png") == 0 ||
+			     strcmp (p, ".xpm") == 0 ||
+			     strcmp (p, ".svg") == 0)) {
+				*p = 0;
+			}
+			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+							   icon_no_extension, 24, 0, NULL);
+			g_free (icon_no_extension);
+		}
+		if (pixbuf) {
+			gtk_list_store_set (dialog->details->program_list_store, &iter, COLUMN_ICON, pixbuf, -1);
+			g_object_unref (pixbuf);
+		}
+		g_free (file);
+		
+	/* don't go back into the main loop if this wasn't very hard to do */
+	} while (!long_operation);
+
+	return TRUE;
+}
+
+static int
+compare_applications (MenuTreeEntry *a,
+		      MenuTreeEntry *b)
+{
+	return g_utf8_collate (menu_tree_entry_get_name (a),
+			       menu_tree_entry_get_name (b));
+}
+
+static GSList *
+get_all_applications_from_dir (MenuTreeDirectory *directory,
+			       GSList            *list)
+{
+	GSList *subdirs;
+	GSList *l;
+
+	list = g_slist_concat (list,
+	menu_tree_directory_get_entries (directory));
+
+	subdirs = menu_tree_directory_get_subdirs (directory);
+	for (l = subdirs; l; l = l->next) {
+		MenuTreeDirectory *subdir = l->data;
+
+		list = get_all_applications_from_dir (subdir, list);
+
+		menu_tree_directory_unref (subdir);
+	}
+	g_slist_free (subdirs);
+
+	return list;
+}
+
+
+static GSList *
+get_all_applications (void)
+{
+	MenuTree          *tree;
+	MenuTreeDirectory *root;
+	GSList            *retval;
+
+	tree = menu_tree_lookup ("applications.menu");
+
+	root = menu_tree_get_root_directory (tree);
+
+	retval = get_all_applications_from_dir (root, NULL);
+
+	menu_tree_directory_unref (root);
+	menu_tree_unref (tree);
+
+	retval = g_slist_sort (retval,
+			       (GCompareFunc) compare_applications);
+
+	return retval;
+}
+
+
+static gboolean
+eel_open_with_dialog_add_items_idle (EelOpenWithDialog *dialog)
+{
+	GtkCellRenderer   *renderer;
+	GtkTreeViewColumn *column;
+	GSList            *all_applications;
+	GSList            *l;
+	GSList            *next;
+	const char        *prev_name;
+
+	/* create list store */
+	dialog->details->program_list_store = gtk_list_store_new (NUM_COLUMNS,
+								  GDK_TYPE_PIXBUF,
+								  G_TYPE_STRING,
+								  G_TYPE_STRING,
+								  G_TYPE_STRING,
+								  G_TYPE_STRING,
+								  G_TYPE_STRING);
+
+	all_applications = get_all_applications ();
+	
+	/* Strip duplicates */
+	prev_name = NULL;
+	for (l = all_applications; l; l = next) {
+		MenuTreeEntry *entry = l->data;
+
+		next = l->next;
+
+		if (prev_name && strcmp (menu_tree_entry_get_name (entry), prev_name) == 0) {
+			menu_tree_entry_unref (entry);
+
+			all_applications = g_slist_delete_link (all_applications, l);
+		} else {
+			prev_name = menu_tree_entry_get_name (entry);
+		}
+	}
+
+	for (l = all_applications; l; l = l->next) {
+		MenuTreeEntry *entry = l->data;
+		GtkTreeIter    iter;
+		GtkTreePath   *path;
+
+		gtk_list_store_append (dialog->details->program_list_store, &iter);
+		gtk_list_store_set (dialog->details->program_list_store, &iter,
+				    COLUMN_ICON,      NULL,
+				    COLUMN_ICON_FILE, menu_tree_entry_get_icon (entry),
+				    COLUMN_NAME,      menu_tree_entry_get_name (entry),
+				    COLUMN_COMMENT,   menu_tree_entry_get_comment (entry),
+				    COLUMN_PATH,      menu_tree_entry_get_desktop_file_path (entry),
+				    -1);
+
+		path = gtk_tree_model_get_path (GTK_TREE_MODEL (dialog->details->program_list_store), &iter);
+		if (path != NULL)
+			dialog->details->add_icon_paths = g_slist_prepend (dialog->details->add_icon_paths, path);
+
+		menu_tree_entry_unref (entry);
+	}
+	g_slist_free (all_applications);
+
+	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->details->program_list), 
+				 GTK_TREE_MODEL (dialog->details->program_list_store));
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	column = gtk_tree_view_column_new ();
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+                                             "pixbuf", COLUMN_ICON,
+                                             NULL);
+        
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+                                             "text", COLUMN_NAME,
+                                             NULL);
+					          
+	gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->details->program_list), column);
+
+	dialog->details->add_icon_paths = g_slist_reverse (dialog->details->add_icon_paths);
+
+	if (!dialog->details->add_icons_idle_id)
+		dialog->details->add_icons_idle_id =
+			g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) eel_open_with_dialog_add_icon_idle,
+					 dialog, NULL);
+
+	dialog->details->add_items_idle_id = 0;					 
+	return FALSE;
+}
+
+
+static char *
+remove_parameters (const char *exec)
+{
+	GString *str;
+	char    *retval, *p;
+
+	str = g_string_new (exec);
+
+	while ((p = strstr (str->str, "%"))) {
+		switch (p [1]) {
+		case '%':
+			g_string_erase (str, p - str->str, 1);
+			break;
+		case 'U':
+		case 'F':
+		case 'N':
+		case 'D':
+		case 'f':
+		case 'u':
+		case 'd':
+		case 'n':
+		case 'm':
+		case 'i':
+		case 'c':
+		case 'k':
+		case 'v':
+			g_string_erase (str, p - str->str, 2);
+			break;
+		default:
+			break;
+		}
+	}
+
+	retval = str->str;
+	g_string_free (str, FALSE);
+
+	return retval;
+}
+
+static void
+program_list_selection_changed (GtkTreeSelection  *selection,
+				EelOpenWithDialog *dialog)
+{
+	GnomeDesktopItem *ditem;
+	GtkTreeModel     *model;
+	GtkTreeIter       iter;
+	const char       *temp;
+	char             *path, *stripped;
+		
+	if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+		return;
+
+	path = NULL;
+	gtk_tree_model_get (model, &iter,
+			    COLUMN_PATH, &path,
+			    -1);
+				  
+	if (path) {
+		ditem = gnome_desktop_item_new_from_file (path,
+							  GNOME_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS,
+							  NULL /* error */);
+		if (ditem) {
+			dialog->details->use_program_list = TRUE;
+			
+			/* Order is important here. We have to set the text first so that the
+			 * drag source is enabled, otherwise the drag icon can't be set by
+			 * panel_run_dialog_set_icon.
+			 */
+			temp = gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_EXEC);
+			if (temp) {
+				stripped = remove_parameters (temp);
+				gtk_entry_set_text (GTK_ENTRY (dialog->details->entry), stripped);
+				g_free (stripped);
+			} else {
+				temp = gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_URL);
+				gtk_entry_set_text (GTK_ENTRY (dialog->details->entry), sure_string (temp));
+			}
+
+			temp = gnome_desktop_item_get_string (ditem, GNOME_DESKTOP_ITEM_ICON);
+			
+			temp = gnome_desktop_item_get_localestring (ditem, GNOME_DESKTOP_ITEM_COMMENT);
+			gtk_label_set_text (GTK_LABEL (dialog->details->desc_label), sure_string (temp));
+			
+			gnome_desktop_item_unref (ditem);
+                }
+
+		g_free (path);
+        }
+}
+
+static void
+program_list_selection_activated (GtkTreeView       *view,
+				  GtkTreePath       *path,
+				  GtkTreeViewColumn *column,
+				  EelOpenWithDialog *dialog)
+{
+	GtkTreeSelection *selection;
+
+	/* update the entry with the info from the selection */
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->details->program_list));	
+	program_list_selection_changed (selection, dialog);
+	
+	gtk_dialog_response (GTK_DIALOG (&dialog->parent), RESPONSE_OPEN);
+}
+
+
 static void
 eel_open_with_dialog_instance_init (EelOpenWithDialog *dialog)
 {
@@ -432,19 +772,20 @@
 	GtkWidget *image;
 	GtkWidget *label;
 	GtkWidget *align;
+	GtkWidget *scrolled_window;
+	GtkTreeSelection *selection;
 
 	dialog->details = g_new0 (EelOpenWithDialogDetails, 1);
 
 	gtk_window_set_title (GTK_WINDOW (dialog), _("Open With"));
-	gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
+	gtk_window_set_default_size (GTK_WINDOW (dialog), 400, -1);
 	gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
 	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
 	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
 	gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
 
-	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 12);
-	hbox = gtk_hbox_new (FALSE, 12);
-	gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
+	hbox = gtk_hbox_new (FALSE, 0);
 
 	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
 			    TRUE, TRUE, 0);
@@ -526,6 +867,47 @@
 
 	gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
 				      button, RESPONSE_OPEN);
+
+	vbox = gtk_vbox_new (FALSE, 6);
+
+	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+	
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+					     GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	dialog->details->program_list = gtk_tree_view_new ();
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->details->program_list),
+					   FALSE);
+	gtk_container_add (GTK_CONTAINER (scrolled_window), dialog->details->program_list);
+	
+	gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+
+	dialog->details->desc_label = gtk_label_new (_("Select an application to view its description."));
+	gtk_misc_set_alignment (GTK_MISC (dialog->details->desc_label), 0.0, 0.5);
+	gtk_label_set_justify (GTK_LABEL (dialog->details->desc_label), GTK_JUSTIFY_LEFT);
+	gtk_label_set_line_wrap (GTK_LABEL (dialog->details->desc_label), TRUE);
+	gtk_label_set_single_line_mode (GTK_LABEL (dialog->details->desc_label), FALSE);
+	gtk_box_pack_start (GTK_BOX (vbox), dialog->details->desc_label, TRUE, TRUE, 0);
+	
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->details->program_list));
+	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+	g_signal_connect (selection, "changed",
+			  G_CALLBACK (program_list_selection_changed),
+			  dialog);
+	g_signal_connect (dialog->details->program_list, "row-activated",
+			  G_CALLBACK (program_list_selection_activated),
+			  dialog);
+
+	dialog->details->add_items_idle_id = g_idle_add_full (G_PRIORITY_LOW,
+						     (GSourceFunc) eel_open_with_dialog_add_items_idle,
+						      dialog, NULL);
+
+
+	
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, TRUE, TRUE, 0);
+	gtk_widget_show_all (vbox);
 
 	gtk_dialog_set_default_response (GTK_DIALOG (dialog),
 					 RESPONSE_OPEN);
Index: eel/eel-open-with-dialog.h
===================================================================
RCS file: /cvs/gnome/eel/eel/eel-open-with-dialog.h,v
retrieving revision 1.2
diff -u -r1.2 eel-open-with-dialog.h
--- eel/eel-open-with-dialog.h	22 Jul 2004 03:55:49 -0000	1.2
+++ eel/eel-open-with-dialog.h	5 Jan 2005 15:57:24 -0000
@@ -29,6 +29,8 @@
 #include <gtk/gtkdialog.h>
 #include <libgnomevfs/gnome-vfs-mime-handlers.h>
 
+#define sure_string(s)                    ((const char *)((s)!=NULL?(s):""))
+
 #define EEL_TYPE_OPEN_WITH_DIALOG         (eel_open_with_dialog_get_type ())
 #define EEL_OPEN_WITH_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_OPEN_WITH_DIALOG, EelOpenWithDialog))
 #define EEL_OPEN_WITH_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEL_TYPE_OPEN_WITH_DIALOG, EelOpenWithDialogClass))


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