[PATCH]: Application chooser for the Open With dialog
- From: Fernando Herrera <fernando herrera tecsidel es>
- To: nautilus-list gnome org
- Subject: [PATCH]: Application chooser for the Open With dialog
- Date: Tue, 18 Jan 2005 18:04:25 +0100
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]