Re: low-hanging fruit

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



On Fri, 2007-08-17 at 14:11 -0400, Matthias Clasen wrote:
> 
> Yeah, doing a "lauch preferred browser/mail client/terminal" applet should 
> take ~20 lines of non-boilerplate code...
> 

Turns out it took a bit more than 20 lines, but I ended up taking a
different approach to implementing this anyway. The attached panel patch
does 3 things:

- let the launcher properties dialog handle selected desktop files
  sensibly

- make the panel optionally use file monitoring for desktop files 
  backing launchers

- add some code that maintains preferred-web-browser.desktop and
  preferred-mail-reader.desktop and keeps them in sync with the
  corresponding gconf keys. 

The first two parts make sense independently of preferred apps; 
the third one should maybe live somewhere else, e.g.
gnome-settings-daemon.

Using this patch, we can have nicely updating launchers with correct
icons for the preferred web and mail apps. 


Matthias


diff -up gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.c.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.c
--- gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.c.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.c	2007-08-26 00:44:56.000000000 -0400
@@ -80,6 +80,8 @@ struct _PanelDItemEditorPrivate
 
 	/* the directory of the theme for the icon, see bug #119208 */
 	char *icon_theme_dir;
+
+	char *orig_desktop_file;
 };
 
 /* Time in seconds after which we save the file on the disk */
@@ -367,6 +369,11 @@ panel_ditem_editor_destroy (GtkObject *o
 		g_free (dialog->priv->icon_theme_dir);
 	dialog->priv->icon_theme_dir = NULL;
 
+
+	if (dialog->priv->orig_desktop_file != NULL)
+		g_free (dialog->priv->orig_desktop_file);
+	dialog->priv->orig_desktop_file = NULL;
+	
 	GTK_OBJECT_CLASS (panel_ditem_editor_parent_class)->destroy (object);
 }
 
@@ -833,6 +840,13 @@ panel_ditem_editor_changed (PanelDItemEd
 							   TRUE);
 	}
 
+	/* When the user changes any fields, unset the orig_desktop_file
+ 	 * field since the editor contents are not entirely from a desktop
+ 	 * file anymore.
+ 	 */ 
+	g_free (dialog->priv->orig_desktop_file);
+	dialog->priv->orig_desktop_file = NULL;
+
 	dialog->priv->dirty = TRUE;
 	g_signal_emit (G_OBJECT (dialog), ditem_edit_signals[CHANGED], 0);
 }
@@ -973,6 +987,49 @@ panel_ditem_editor_icon_changed (PanelDI
 	g_free (file);
 }
 
+static gboolean
+update_editor_from_desktop_file (PanelDItemEditor *dialog,
+				 const gchar      *uri)
+{
+	GKeyFile *key_file;
+	char *name;
+	char *comment;
+	char *exec;
+	char *icon;
+
+	key_file = g_key_file_new ();
+	if (g_key_file_load_from_file (key_file, uri, 0, NULL)) {
+		if (panel_util_key_file_get_boolean (key_file, "Terminal", FALSE)) 
+			gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->priv->type_combo), 1);
+		else
+			gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->priv->type_combo), 0);	
+			
+		name = panel_util_key_file_get_locale_string (key_file, "Name");
+		comment = panel_util_key_file_get_locale_string (key_file, "Comment");
+		icon = panel_util_key_file_get_locale_string (key_file, "Icon");
+		exec = panel_util_key_file_get_string (key_file, "Exec");
+
+		gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), name ? name : "");		
+		gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry), comment ? comment : "");		
+		gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), exec ? exec : "");		
+		/* Note: we need to set the icon last, since the editor
+                 * tries to guess the icon when the command is modified
+                 */ 
+		setup_icon_entry (dialog, icon);
+
+		/* We set the orig_desktop_file field to let the
+                 * launcher know that the editor contents are coming
+                 * directly from a desktop file.
+                 */ 
+		g_free (dialog->priv->orig_desktop_file);
+		dialog->priv->orig_desktop_file = g_strdup (uri);
+	
+		return TRUE;	
+	}
+
+	return FALSE;
+}
+
 static void
 command_browse_chooser_response (GtkFileChooser   *chooser,
 				 gint              response_id,
@@ -986,7 +1043,11 @@ command_browse_chooser_response (GtkFile
 		case PANEL_DITEM_EDITOR_TYPE_APPLICATION:
 		case PANEL_DITEM_EDITOR_TYPE_TERMINAL_APPLICATION:
 			text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
-			uri = panel_util_make_exec_uri_for_desktop (text);
+			if (g_str_has_suffix (text, ".desktop") &&
+			    update_editor_from_desktop_file (dialog, text))
+				uri = NULL;
+			else 
+				uri = panel_util_make_exec_uri_for_desktop (text);
 			g_free (text);
 			break;
 		case PANEL_DITEM_EDITOR_TYPE_LINK:
@@ -996,8 +1057,9 @@ command_browse_chooser_response (GtkFile
 			g_assert_not_reached ();
 		}
 
-		gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry),
-				    uri);
+		if (uri)
+			gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry),
+					    uri);
 		g_free (uri);
 	}
 
@@ -1785,3 +1847,12 @@ panel_ditem_register_save_uri_func (Pane
 	dialog->priv->save_uri = save_uri;
 	dialog->priv->save_uri_data = data;
 }
+
+G_CONST_RETURN char *
+panel_ditem_editor_get_orig_desktop_file (PanelDItemEditor *dialog)
+{
+	g_return_val_if_fail (PANEL_IS_DITEM_EDITOR (dialog), NULL);
+
+	return dialog->priv->orig_desktop_file;
+}
+
diff -up gnome-panel-2.19.6/gnome-panel/launcher.h.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/launcher.h
--- gnome-panel-2.19.6/gnome-panel/launcher.h.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/launcher.h	2007-08-25 22:28:39.000000000 -0400
@@ -13,6 +13,7 @@
 
 #include "applet.h"
 #include "panel-widget.h"
+#include <libgnomevfs/gnome-vfs.h>
 
 G_BEGIN_DECLS
 
@@ -29,6 +30,8 @@ typedef struct {
 	GSList 		  *error_dialogs;
 
 	gulong             destroy_handler;
+
+	GnomeVFSMonitorHandle *monitor; 
 } Launcher;
 
 void panel_launcher_create           (PanelToplevel *toplevel,
@@ -73,6 +76,8 @@ void            panel_launcher_set_dnd_e
 						 gboolean  dnd_enabled);
 
 
+void            panel_preferred_apps_init       (void);
+
 G_END_DECLS
 
 #endif
diff -up gnome-panel-2.19.6/gnome-panel/panel-run-dialog.c.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/panel-run-dialog.c
--- gnome-panel-2.19.6/gnome-panel/panel-run-dialog.c.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/panel-run-dialog.c	2007-08-25 22:28:39.000000000 -0400
@@ -813,7 +813,7 @@ get_all_applications_from_dir (GMenuTree
 	return list;
 }
 
-static GSList *
+GSList *
 get_all_applications (void)
 {
 	GMenuTree          *tree;
diff -up gnome-panel-2.19.6/gnome-panel/launcher.c.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/launcher.c
--- gnome-panel-2.19.6/gnome-panel/launcher.c.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/launcher.c	2007-08-26 00:44:48.000000000 -0400
@@ -22,6 +22,7 @@
 #include <libgnomeui/gnome-url.h>
 #include <libgnomevfs/gnome-vfs.h>
 #include <gdk/gdkx.h>
+#include <gmenu-tree.h>
 
 #include "launcher.h"
 
@@ -39,6 +40,7 @@
 #include "panel-compatibility.h"
 #include "panel-ditem-editor.h"
 #include "panel-icon-names.h"
+#include "panel-run-dialog.h"
 
 static GdkScreen *
 launcher_get_screen (Launcher *launcher)
@@ -285,6 +287,8 @@ static void
 destroy_launcher (GtkWidget *widget,
 		  Launcher  *launcher)
 {
+	if (launcher->monitor)
+		gnome_vfs_monitor_cancel (launcher->monitor);
 	launcher_properties_destroy (launcher);
 	launcher_widget_destroy_open_dialogs (launcher);
 }
@@ -458,6 +462,37 @@ drag_data_get_cb (GtkWidget        *widg
 
 }
 
+static void setup_button (Launcher *launcher);
+
+static void
+desktop_file_changed (GnomeVFSMonitorHandle    *handle,
+		      const gchar              *monitor_uri,
+		      const gchar  	       *info_uri,
+		      GnomeVFSMonitorEventType  event_type,
+		      gpointer       		user_data)
+{
+	Launcher *launcher = user_data;
+	GKeyFile *key_file;
+
+	if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ||
+	    event_type == GNOME_VFS_MONITOR_EVENT_CREATED) {
+		gchar *path;
+
+		path = gnome_vfs_get_local_path_from_uri (monitor_uri);
+
+		key_file = g_key_file_new ();
+ 		if (g_key_file_load_from_file (key_file, path,
+                                               G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
+                                               NULL)) {
+			g_key_file_free (launcher->key_file);
+			launcher->key_file = key_file;
+			setup_button (launcher);	
+		}
+
+		g_free (path);
+	}
+}
+
 static Launcher *
 create_launcher (const char *location)
 {
@@ -523,17 +558,34 @@ create_launcher (const char *location)
 		return NULL; /*button is null*/
 	}
 
-	if (!new_location)
+	if (!new_location) 
 		new_location = g_strdup (location);
 
 	launcher = g_new0 (Launcher, 1);
 
 	launcher->info = NULL;
 	launcher->button = NULL;
-	launcher->location = new_location;
 	launcher->key_file = key_file;
 	launcher->prop_dialog = NULL;
 	launcher->destroy_handler = 0;
+	launcher->monitor = NULL;
+	launcher->location = new_location;
+
+	/* Watch for changes to the desktop file. Since we don't want 
+         * to do this for every launcher, we only do it if the desktop 
+         * file contains a X-Panel-Monitor entry.
+         */
+	if (g_key_file_get_boolean (key_file, "Desktop Entry", "X-Panel-Monitor", NULL)) {
+		gchar *uri;
+
+		uri = g_strdup_printf ("file:%s", new_location);
+		gnome_vfs_monitor_add (&launcher->monitor, 
+				       uri,
+				       GNOME_VFS_MONITOR_FILE,
+				       desktop_file_changed,
+				       launcher);
+		g_free (uri);
+	}
 
 	/* Icon will be setup later */
 	launcher->button = button_widget_new (NULL /* icon */,
@@ -662,6 +714,15 @@ static void
 launcher_changed (PanelDItemEditor *dialog,
 		  Launcher         *launcher)
 {
+	/* If the user manually changes launcher properties,
+         * we stop monitoring the desktop file to not overwrite
+         * user changes.
+         */ 
+	if (launcher->monitor) {
+		gnome_vfs_monitor_cancel (launcher->monitor);
+		launcher->monitor = NULL;
+	}
+
 	/* Setup the button look */
 	setup_button (launcher);
 }
@@ -737,14 +798,44 @@ static void
 launcher_saved (GtkWidget *dialog,
 		Launcher  *launcher)
 {
-	const char  *uri;
+	const char  *uri, *path;
 	GConfClient *client;
 	const char  *key;
+	const char *entry;
+	GKeyFile *key_file;
 
 	uri = panel_ditem_editor_get_uri (PANEL_DITEM_EDITOR (dialog));
 	if (panel_launcher_get_filename (uri) != NULL)
-		uri = panel_launcher_get_filename (uri);
+		path = panel_launcher_get_filename (uri);
+	else
+		path = uri;
 
+	/* When the user selects a desktop file in the ditem editor,
+         * start monitoring the desktop file for changes.
+         */
+	entry = panel_ditem_editor_get_orig_desktop_file (PANEL_DITEM_EDITOR (dialog));
+	if (entry) {
+		key_file = g_key_file_new ();
+		if (g_key_file_load_from_file (key_file, entry, 0, NULL) &&
+                    panel_util_key_file_get_boolean (key_file, "X-Panel-Monitor", FALSE)) { 	
+			gchar *s;
+
+			s = g_strdup_printf ("file:%s", entry);
+			if (launcher->monitor)
+				gnome_vfs_monitor_cancel (launcher->monitor);
+			gnome_vfs_monitor_add (&launcher->monitor, 
+					       s,
+					       GNOME_VFS_MONITOR_FILE,
+				       	       desktop_file_changed,
+				       	       launcher);
+			g_free (s);
+		}
+		g_key_file_free (key_file);
+		uri = entry;
+	}
+	else
+		uri = path;
+	
 	if (uri && launcher->location && strcmp (uri, launcher->location)) {
 		client = panel_gconf_get_client ();
 
@@ -1174,3 +1265,170 @@ panel_launcher_set_dnd_enabled (Launcher
 	} else
 		gtk_drag_source_unset (launcher->button);
 }
+
+
+static gchar *
+find_desktop_file_from_exec (const gchar *exec)
+{
+        GSList *all_applications, *l;
+        gchar *path = NULL;
+        gchar **tokens, **tokens2;
+        gint i, match = 0;
+
+        /* FIXME no need to construct a humongous list here */
+        all_applications = get_all_applications ();
+
+        for (l = all_applications; l; l = l->next) {
+                GMenuTreeEntry *entry = l->data;
+                const char     *entry_exec;
+
+                entry_exec = gmenu_tree_entry_get_exec (entry);
+
+                if (strcmp (exec, entry_exec) == 0) {
+                        path = gmenu_tree_entry_get_desktop_file_path (entry);
+                        break;
+                }
+
+                tokens = g_strsplit (exec, " ", -1);
+                tokens2 = g_strsplit (entry_exec, " ", -1);
+
+                for (i = 0; tokens[i] && tokens2[i]; i++) {
+                        if (strcmp (tokens[i], tokens2[i]) != 0)
+                                break;
+                }
+                if (i > match) {
+                        match = i;
+                        path = gmenu_tree_entry_get_desktop_file_path (entry);
+                }
+
+                g_strfreev (tokens);
+                g_strfreev (tokens2);
+        }
+
+        path = g_strdup (path);
+
+        g_slist_foreach (all_applications, (GFunc) gmenu_tree_item_unref, NULL);
+        g_slist_free (all_applications);
+
+        return path;
+}
+
+
+static void
+update_preferred_app (const gchar *filename,
+		      const gchar *key,
+		      const gchar *exec)
+{
+	gchar *location;
+	GKeyFile *key_file;
+	GError *error = NULL;
+	gchar *data;
+	gsize len;
+	gboolean needs_terminal;
+
+	location = find_desktop_file_from_exec (exec);
+	key_file = g_key_file_new ();
+	if (!panel_util_key_file_load_from_uri (key_file, location,
+						G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS,
+						NULL)) {
+		/* FIXME would be much better if preferred apps were backed by desktop files */
+		g_key_file_set_string (key_file, "Desktop Entry", "Version", "1.0");
+		g_key_file_set_string (key_file, "Desktop Entry", "Encoding", "UTF-8");
+		g_key_file_set_string (key_file, "Desktop Entry", "Type", "Application");
+		g_key_file_set_string (key_file, "Desktop Entry", "Exec", exec);
+		if (strstr (key, "http")) {
+			g_key_file_set_string (key_file, "Desktop Entry", "Name", "Preferred Web Browser");
+			g_key_file_set_string (key_file, "Desktop Entry", "GenericName", "Web Browser");
+			g_key_file_set_string (key_file, "Desktop Entry", "Comment", "Browse the Web");
+			g_key_file_set_string (key_file, "Desktop Entry", "Icon", "redhat-web-browser.png");
+		}
+		else if (strstr (key, "mailto")) {
+			g_key_file_set_string (key_file, "Desktop Entry", "Name", "Preferred Mail Reader");
+			g_key_file_set_string (key_file, "Desktop Entry", "GenericName", "Mail Reader");
+			g_key_file_set_string (key_file, "Desktop Entry", "Comment", "Send email");
+			g_key_file_set_string (key_file, "Desktop Entry", "Icon", "redhat-email.png");
+		}
+		if (g_str_has_suffix (key, "command")) {
+			int len;
+			char *key2;
+
+			len = strlen (key);
+			key2 = g_new (char, len - strlen ("command") + strlen ("needs_terminal") + 1);
+			strncpy (key2, key,  len - strlen ("command"));
+			strcpy (key2 + len - strlen ("command"), "needs_terminal");
+			needs_terminal = gconf_client_get_bool (panel_gconf_get_client (),
+								key2,
+								NULL);
+			g_free (key2);
+		}
+		else
+			needs_terminal = FALSE;
+		g_key_file_set_boolean (key_file, "Desktop Entry", "Terminal", needs_terminal);
+	}
+
+	g_free (location);
+	
+	g_key_file_set_boolean (key_file, "Desktop Entry", "X-Panel-Monitor", TRUE);
+	g_key_file_set_boolean (key_file, "Desktop Entry", "NoDisplay", TRUE);
+
+	data = g_key_file_to_data (key_file, &len, &error);
+	if (error) {
+		g_printerr (_("Failed to convert data for '%s': %s"), 
+			filename, error->message);
+		g_error_free (error);
+		g_key_file_free (key_file);
+		
+		return;	
+	}
+	if (!g_file_set_contents (filename, data, len, &error)) {
+		g_printerr (_("Failed to save '%s': %s"), 
+			filename, error->message);
+		g_error_free (error);
+	}
+
+	g_key_file_free (key_file);
+	g_free (data);
+}
+
+static void
+preferred_app_changed (GConfClient *client,
+		       gint	    notify_id,
+		       GConfEntry  *entry,
+		       const gchar  *filename)
+{
+	update_preferred_app (filename, 
+			      gconf_entry_get_key (entry), 
+			      gconf_value_get_string (entry->value));
+}
+
+void
+panel_preferred_apps_init (void)
+{
+	GConfClient *client;
+	gchar *filename, *exec;
+	gint i;
+
+	const gchar *keys[] = {
+		"/desktop/gnome/url-handlers/http/command",
+		"/desktop/gnome/url-handlers/mailto/command", 
+		NULL };
+	const gchar *files[] = {
+		"preferred-web-browser.desktop",
+		"preferred-mail-reader.desktop",
+		NULL };	
+
+	client = panel_gconf_get_client ();
+
+	for (i = 0; keys[i]; i++) {
+		filename = g_build_filename (g_get_user_data_dir (), 
+				  	     "applications", files[i], NULL);
+		if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+			exec = gconf_client_get_string (client, keys[i], NULL);
+			update_preferred_app (filename, keys[i], exec);
+			g_free (exec);
+		}
+		gconf_client_notify_add (client, keys[i],
+					 (GConfClientNotifyFunc) preferred_app_changed,
+					 filename, g_free, NULL);
+	}
+}
diff -up gnome-panel-2.19.6/gnome-panel/main.c.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/main.c
--- gnome-panel-2.19.6/gnome-panel/main.c.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/main.c	2007-08-25 22:28:39.000000000 -0400
@@ -26,6 +26,7 @@
 #include "panel-action-protocol.h"
 #include "panel-lockdown.h"
 #include "panel-icon-names.h"
+#include "launcher.h"
 #include "xstuff.h"
 
 #include "nothing.cP"
@@ -81,6 +82,8 @@ main (int argc, char **argv)
 			      GCONF_CLIENT_PRELOAD_NONE,
 			      NULL);
 
+	panel_preferred_apps_init ();
+
 	panel_global_config_load ();
 	panel_lockdown_init ();
 	panel_profile_load ();
diff -up gnome-panel-2.19.6/gnome-panel/panel-run-dialog.h.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/panel-run-dialog.h
--- gnome-panel-2.19.6/gnome-panel/panel-run-dialog.h.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/panel-run-dialog.h	2007-08-25 22:28:39.000000000 -0400
@@ -32,6 +32,8 @@ G_BEGIN_DECLS
 void panel_run_dialog_present           (GdkScreen  *screen,
 					 guint32    activate_time);
 
+GSList *get_all_applications 		(void);
+
 G_END_DECLS
 
 #endif /* __PANEL_RUN_DIALOG_H__ */
diff -up gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.h.launcher-desktop-file-improvements gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.h
--- gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.h.launcher-desktop-file-improvements	2007-08-13 19:36:51.000000000 -0400
+++ gnome-panel-2.19.6/gnome-panel/panel-ditem-editor.h	2007-08-25 22:28:39.000000000 -0400
@@ -101,6 +101,7 @@ void panel_ditem_editor_set_uri (PanelDI
 				 const char       *uri);
 
 G_CONST_RETURN char *panel_ditem_editor_get_uri (PanelDItemEditor *dialog);
+G_CONST_RETURN char *panel_ditem_editor_get_orig_desktop_file (PanelDItemEditor *dialog);
 
 void panel_ditem_register_save_uri_func (PanelDItemEditor  *dialog,
 					 PanelDitemSaveUri  save_uri,
-- 
Fedora-desktop-list mailing list
Fedora-desktop-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/fedora-desktop-list

[Index of Archives]     [Fedora Users]     [Fedora KDE]     [Fedora Announce]     [Fedora Docs]     [Fedora Config]     [PAM]     [Red Hat Development]     [Red Hat 9]     [Gimp]     [Yosemite News]

  Powered by Linux