changeset 4428:b5669740e34c

[gaim-migrate @ 4703] Two patches from Ray Strode to clean up browser launching (esp. Netscape and Mozilla) and to show only available browsers in the prefs drop down list. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Sun, 26 Jan 2003 19:39:43 +0000
parents 88920ce27a9f
children bf770f11132b
files src/browser.c src/prefs.c
diffstat 2 files changed, 280 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/src/browser.c	Sun Jan 26 14:10:49 2003 +0000
+++ b/src/browser.c	Sun Jan 26 19:39:43 2003 +0000
@@ -34,6 +34,9 @@
 #include <gdk/gdkwin32.h>
 #else
 #include <unistd.h>
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 255
+#endif
 #include <gdk/gdkx.h>
 #endif
 #include <stdio.h>
@@ -69,6 +72,10 @@
 static GdkAtom GDKA_MOZILLA_COMMAND = 0;
 static GdkAtom GDKA_MOZILLA_RESPONSE = 0;
 
+static char *window_check_mozilla_version(Window);
+static const char *get_lock_data();
+static GdkFilterReturn netscape_response_cb(XEvent *, GdkEvent *, GdkWindow *);
+static gboolean netscape_command(const char *);
 
 static int netscape_lock;
 
@@ -202,6 +209,33 @@
 		GDKA_MOZILLA_RESPONSE = gdk_atom_intern(MOZILLA_RESPONSE_PROP, 0);
 }
 
+static char *window_check_mozilla_version(Window window) {
+
+	Atom type;
+	int format;
+	unsigned long nitems, bytesafter;
+	unsigned char *version = 0;
+	gchar *retval = NULL;
+
+	if (XGetWindowProperty(gdk_display, window, 
+					gdk_x11_atom_to_xatom(GDKA_MOZILLA_VERSION),
+					0, (65536 / sizeof(long)),
+					False, XA_STRING,
+					&type, &format, &nitems, &bytesafter,
+					&version) != Success) {	
+		return NULL;
+	}
+
+	if (!version) {
+		return NULL;
+	}
+
+	retval = g_strdup(version);
+	XFree(version);
+
+	return retval;
+}
+
 static GdkWindow *mozilla_remote_find_window()
 {
 	int i;
@@ -210,7 +244,21 @@
 	unsigned int nkids;
 	Window result = 0;
 	Window tenative = 0;
-	unsigned char *tenative_version = 0;
+	unsigned char *tenative_version = 0, *version = 0;
+	static GdkWindow *remote_window = NULL;
+
+	if (remote_window != NULL) {
+		version = window_check_mozilla_version(GDK_WINDOW_XID(remote_window));
+
+		if (version != NULL) {
+			g_free(version);
+			return remote_window;
+		}
+		g_free(version);
+
+		gdk_window_destroy(remote_window);
+		remote_window = NULL;
+	}
 
 	if (!XQueryTree(gdk_display, root, &root2, &parent, &kids, &nkids)) {
 		debug_printf("%s: XQueryTree failed on display %s\n", progname,
@@ -227,32 +275,25 @@
 	}
 
 	for (i = nkids - 1; i >= 0; i--) {
-		Atom type;
-		int format;
-		unsigned long nitems, bytesafter;
-		unsigned char *version = 0;
 		Window w = GClientWindow(gdk_display, kids[i]);
-		int status = XGetWindowProperty(gdk_display, w, 
-						gdk_x11_atom_to_xatom(GDKA_MOZILLA_VERSION),
-						0, (65536 / sizeof(long)),
-						False, XA_STRING,
-						&type, &format, &nitems, &bytesafter,
-						&version);
+
+        version = window_check_mozilla_version(w);
 
-		if (!version)
+		if (version == NULL) {
 			continue;
+		}
 
 		if (strcmp((char *)version, expected_mozilla_version) && !tenative) {
 			tenative = w;
 			tenative_version = version;
 			continue;
 		}
-		XFree(version);
-		if (status == Success && type != None) {
+
+		g_free(version);
+
 			result = w;
 			break;
 		}
-	}
 
 	XFree(kids);
 
@@ -279,35 +320,41 @@
 }
 
 
-static char *lock_data = 0;
-
-static void mozilla_remote_obtain_lock(GdkWindow * window)
-{
-	Bool locked = False;
+static const char *get_lock_data() {
+	static char *lock_data = NULL;
 
-	if (!lock_data) {
-		lock_data = (char *)g_malloc(255);
-		sprintf(lock_data, "pid%d@", getpid());
-		if (gethostname(lock_data + strlen(lock_data), 100)) {
-			return;
+	if (lock_data == NULL) {
+		char hostname[HOST_NAME_MAX + 1] = {0}; 
+
+		if (gethostname(hostname, HOST_NAME_MAX + 1) == 0) {
+			lock_data = g_strdup_printf("pid%d@%s", getpid(), hostname);
+		} else {
+			lock_data = g_strdup_printf("pid%d", getpid());
 		}
 	}
 
-	do {
+	return lock_data;
+}
+
+static gboolean mozilla_remote_obtain_lock(GdkWindow * window)
+{
+	gboolean locked = False;
+	const char *lock_data = get_lock_data();
 		int result;
 		GdkAtom actual_type;
 		gint actual_format;
 		gint nitems;
 		unsigned char *data = 0;
 
-		result = gdk_property_get(window, GDKA_MOZILLA_LOCK,
+	gdk_x11_grab_server();
+	if (!gdk_property_get(window, GDKA_MOZILLA_LOCK,
 					  gdk_x11_xatom_to_atom (XA_STRING), 0,
 					  (65536 / sizeof(long)), 0,
-					  &actual_type, &actual_format, &nitems, &data);
-		if (result != Success || actual_type == None) {
+				  &actual_type, &actual_format, &nitems, &data)) {
+
 			/* It's not now locked - lock it. */
-			debug_printf("%s: (writing " MOZILLA_LOCK_PROP
-				     " \"%s\" to 0x%x)\n", progname, lock_data, (unsigned int)window);
+		debug_printf("%s: (writing " MOZILLA_LOCK_PROP " \"%s\" to 0x%x)\n", 
+					 progname, lock_data, (unsigned int) window);
 
 			gdk_property_change(window, GDKA_MOZILLA_LOCK, 
 					    gdk_x11_xatom_to_atom (XA_STRING),
@@ -316,15 +363,9 @@
 			locked = True;
 		}
 
-		if (!locked) {
-			/* Then just fuck it. */
 			if (data)
 				g_free(data);
-			return;
-		}
-		if (data)
-			g_free(data);
-	} while (!locked);
+	gdk_x11_ungrab_server();
 }
 
 
@@ -332,9 +373,10 @@
 {
 	int result = 0;
 	GdkAtom actual_type;
-	gint actual_format;
+	gint actual_format; 
 	gint nitems;
 	unsigned char *data = 0;
+	const char *lock_data = get_lock_data();
 
 	debug_printf("%s: (deleting " MOZILLA_LOCK_PROP
 		     " \"%s\" from 0x%x)\n", progname, lock_data, (unsigned int)window);
@@ -343,7 +385,8 @@
 				  gdk_x11_xatom_to_atom (XA_STRING),
 				  0, (65536 / sizeof(long)),
 				  1, &actual_type, &actual_format, &nitems, &data);
-	if (result != Success) {
+
+	if (result != TRUE) {
 		debug_printf("%s: unable to read and delete " MOZILLA_LOCK_PROP " property\n", progname);
 		return;
 	} else if (!data || !*data) {
@@ -356,12 +399,67 @@
 		return;
 	}
 
-	if (data)
-		g_free(data);
+	XFree(data);
 }
 
+static GdkFilterReturn netscape_response_cb(XEvent *event, GdkEvent *translated, GdkWindow *window)
+{
+	Atom actual_type, mozilla_response;
+	Window xid;
+	int actual_format;
+	unsigned long nitems, bytes_after;
+	unsigned char *data = 0;
+	char *error = NULL;
 
-static int mozilla_remote_command(GdkWindow * window, const char *command, Bool raise_p)
+	if (window == NULL || GDK_WINDOW_OBJECT(window)->destroyed) {
+		do_error_dialog(_("Communication with the browser failed.  Please close all "
+					      "windows and try again."), NULL, GAIM_ERROR);
+		debug_printf("netscape_response_cb called with NULL window.\n");
+		return GDK_FILTER_CONTINUE;
+	}
+
+	mozilla_response = gdk_x11_atom_to_xatom(GDKA_MOZILLA_RESPONSE);
+    xid = GDK_WINDOW_XID(window);
+
+	/* If the event isn't what we want then let gtk handle it */
+	if (event->xany.type != PropertyNotify ||
+		event->xproperty.state != PropertyNewValue ||
+		event->xproperty.window != xid ||
+		event->xproperty.atom != mozilla_response) {
+		return GDK_FILTER_CONTINUE;
+	}
+
+	if (XGetWindowProperty (gdk_display, xid, mozilla_response,
+							 0, (65536 / sizeof (long)),
+							 True, 
+							 XA_STRING, 
+							 &actual_type, &actual_format,
+							 &nitems, &bytes_after,
+							 &data) != Success 
+		|| data == NULL || (data[0] != '1' && data[0] != '2')) {
+
+		do_error_dialog(_("Communication with the browser failed.  Please close all "
+					      "windows and try again."), NULL, GAIM_ERROR);
+	} 
+
+    if (data[0] == '1') {
+		/* Netscape isn't ready yet */
+		debug_printf("Remote Netscape window isn't ready yet.\n");
+		return GDK_FILTER_REMOVE;
+	} 
+	
+	if (data[0] == '2') {
+		/* Yay! It worked */ 
+		debug_printf("Successfully sent command to remote Netscape window.\n");
+	}
+
+	gdk_window_remove_filter(window, (GdkFilterFunc) netscape_response_cb, window); 
+	mozilla_remote_free_lock(window);
+	netscape_lock = 0;
+	return GDK_FILTER_REMOVE;
+}
+
+static void mozilla_remote_command(GdkWindow * window, const char *command, Bool raise_p)
 {
 	int result = 0;
 	Bool done = False;
--- a/src/prefs.c	Sun Jan 26 14:10:49 2003 +0000
+++ b/src/prefs.c	Sun Jan 26 19:39:43 2003 +0000
@@ -65,9 +65,11 @@
 GtkWidget *gaim_button(const char *, guint *, int, GtkWidget *);
 GtkWidget *gaim_labeled_spin_button(GtkWidget *, const gchar *, int*, int, int, GtkSizeGroup *);
 static GtkWidget *gaim_dropdown(GtkWidget *, const gchar *, int *, int, ...);
+static GtkWidget *gaim_dropdown_from_list(GtkWidget *, const gchar *, int *, int, GList *); 
 static GtkWidget *show_color_pref(GtkWidget *, gboolean);
 static void delete_prefs(GtkWidget *, void *);
 void set_default_away(GtkWidget *, gpointer);
+static gboolean program_is_valid(const char *);
 
 struct debug_window *dw = NULL;
 GtkWidget *prefs = NULL;
@@ -781,11 +783,57 @@
 }
 
 #ifndef _WIN32
-static void browser_print_option(GtkEntry *entry, void *nullish) {
-	g_snprintf(web_command, sizeof(web_command), "%s", gtk_entry_get_text(entry));
+static void manual_browser_set(GtkButton *button, GtkEntry *entry) {
+
+	const char *program = gtk_entry_get_text(entry);
+	if (!program_is_valid(program)) {
+		char *error = g_strdup_printf(_("The entered manual browser "
+					                    "'%s' is not valid. Hyperlinks will "
+										"not work."), program); 
+		do_error_dialog(error, NULL, GAIM_WARNING);
+	}
+
+	g_strlcpy(web_command, program, sizeof(web_command));
+}
+
+static void manual_browser_reset(GtkButton *button, GtkEntry *entry) {
+	gtk_entry_set_text(entry, web_command);
 }
 #endif
 
+static GList *get_available_browsers() 
+{
+	struct browser {
+		char *name;
+        char *command;
+		int id; 
+	};
+
+	static struct browser possible_browsers[] = {
+		{N_("Konqueror"), "kfmclient", BROWSER_KONQ},
+		{N_("Opera"), "opera", BROWSER_OPERA}, 
+		{N_("Galeon"), "galeon", BROWSER_GALEON},
+		{N_("Netscape"), "netscape", BROWSER_NETSCAPE},
+		{N_("Mozilla"), "mozilla", BROWSER_MOZILLA},
+	};
+	static const int num_possible_browsers = 5;
+
+	GList *browsers = NULL;
+	int i = 0;
+
+	browsers = g_list_prepend(browsers, GINT_TO_POINTER(BROWSER_MANUAL));
+	browsers = g_list_prepend(browsers, _("Manual"));
+    for (i = 0; i < num_possible_browsers; i++) {
+		if (program_is_valid(possible_browsers[i].command)) {
+			browsers = g_list_prepend(browsers, 
+									  GINT_TO_POINTER(possible_browsers[i].id));
+			browsers = g_list_prepend(browsers, possible_browsers[i].name);
+		}
+	}
+
+	return browsers;
+}
+
 GtkWidget *browser_page() {
 	GtkWidget *ret;
 	GtkWidget *vbox;
@@ -794,6 +842,7 @@
 #endif
 	GtkWidget *label;
 	GtkSizeGroup *sg;
+	GList *browsers = NULL;
 
 	ret = gtk_vbox_new(FALSE, 18);
 	gtk_container_set_border_width (GTK_CONTAINER (ret), 12);
@@ -802,17 +851,14 @@
 #ifndef _WIN32
 	/* Registered default browser is used by Windows */
 	vbox = make_frame (ret, _("Browser Selection"));
-	label = gaim_dropdown(vbox, _("_Browser"), &web_browser, -1,
-			      "Netscape", BROWSER_NETSCAPE,
-			      "Konqueror", BROWSER_KONQ,
-			      "Mozilla", BROWSER_MOZILLA,
-			      _("Manual"), BROWSER_MANUAL,
-/* fixme: GNOME binary helper
-			      _("GNOME URL Handler"), BROWSER_GNOME, */
-			      "Galeon", BROWSER_GALEON,
-			      "Opera", BROWSER_OPERA, NULL);
+
+	browsers = get_available_browsers();
+	if (browsers != NULL) {
+		label = gaim_dropdown_from_list(vbox,_("_Browser"), &web_browser, -1, 
+				                        browsers);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 	gtk_size_group_add_widget(sg, label);
+	}
 
 	hbox = gtk_hbox_new(FALSE, 5);
 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
@@ -823,15 +869,27 @@
 	browser_entry = gtk_entry_new();
 	gtk_label_set_mnemonic_widget(GTK_LABEL(label), browser_entry);
 	if (web_browser != BROWSER_MANUAL)
-		gtk_widget_set_sensitive(browser_entry, FALSE);
+		gtk_widget_set_sensitive(hbox, FALSE);
 	gtk_box_pack_start (GTK_BOX (hbox), browser_entry, FALSE, FALSE, 0);
 
 	gtk_entry_set_text(GTK_ENTRY(browser_entry), web_command);
-	g_signal_connect(GTK_OBJECT(browser_entry), "changed",
-			   G_CALLBACK(browser_print_option), NULL);
+	g_signal_connect_swapped(GTK_OBJECT(browser_entry), "activate",
+					   G_CALLBACK(manual_browser_set), NULL);
+	label = gtk_button_new_with_label(_("Set"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(label), "clicked", 
+					   G_CALLBACK(manual_browser_set), browser_entry);
+	label = gtk_button_new_with_label(_("Reset"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	g_signal_connect(GTK_OBJECT(label), "clicked", 
+					   G_CALLBACK(manual_browser_reset), browser_entry);
+
 #endif /* end !_WIN32 */
+	if (browsers != NULL) {
 	vbox = make_frame (ret, _("Browser Options"));
+
 	label = gaim_button(_("Open new _window by default"), &misc_options, OPT_MISC_BROWSER_POPUP, vbox);
+	}
 #ifdef _WIN32
 	/* Until I figure out how to implement this on windows */
 	gtk_widget_set_sensitive(label, FALSE);
@@ -2119,6 +2177,32 @@
 		default_away = g_slist_nth_data(away_messages, (int)i);
 }
 
+static gboolean program_is_valid(const char *program) 
+{
+	GError *error = NULL;
+	char **argv; 
+	gboolean is_valid = FALSE;
+
+	if (program == NULL || *program == '\0') {
+		return FALSE;
+	}
+
+	if (!g_shell_parse_argv(program, NULL, &argv, &error)) {
+		debug_printf("Could not parse program '%s': ", error->message);
+		return FALSE;
+	}
+
+	if (argv == NULL) {
+		return FALSE;
+	}
+
+	is_valid = g_find_program_in_path(argv[0]) != NULL;
+
+	g_strfreev(argv);
+
+	return is_valid;
+}
+
 static void update_spin_value(GtkWidget *w, GtkWidget *spin)
 {
 	int *value = gtk_object_get_user_data(GTK_OBJECT(spin));
@@ -2179,9 +2263,9 @@
 			gtk_widget_set_sensitive(prefs_proxy_frame, TRUE);
 	} else if (option == &web_browser) {
 		if (opt == BROWSER_MANUAL)
-			gtk_widget_set_sensitive(browser_entry, TRUE);
+			gtk_widget_set_sensitive(gtk_widget_get_parent(browser_entry), TRUE);
 		else
-			gtk_widget_set_sensitive(browser_entry, FALSE);
+			gtk_widget_set_sensitive(gtk_widget_get_parent(browser_entry), FALSE);
 	} else if (option == (int*)&sound_options) {
 		if (opt == OPT_SOUND_CMD)
 			gtk_widget_set_sensitive(sndcmd, TRUE);
@@ -2206,7 +2290,36 @@
 
 static GtkWidget *gaim_dropdown(GtkWidget *box, const gchar *title, int *option, int clear, ...)
 {
-	va_list menuitems;
+	va_list ap;
+	GList *menuitems = NULL;
+	GtkWidget *dropdown = NULL;
+	char *name;
+	int id;
+
+	va_start(ap, clear);
+	while ((name = va_arg(ap, char *)) != NULL) {
+		id = va_arg(ap, int);
+
+		menuitems = g_list_prepend(menuitems, name);
+		menuitems = g_list_prepend(menuitems, GINT_TO_POINTER(id));
+	}
+	va_end(ap);
+
+	if (menuitems == NULL) {
+		return;
+	}
+
+	menuitems = g_list_reverse(menuitems);
+
+	dropdown = gaim_dropdown_from_list(box, title, option, clear, menuitems);
+
+	g_list_free(menuitems);
+
+	return dropdown;
+}
+
+static GtkWidget *gaim_dropdown_from_list(GtkWidget *box, const gchar *title, int *option, int clear, GList *menuitems)
+{
 	GtkWidget *dropdown, *opt, *menu;
 	GtkWidget *label;
 	gchar     *text;
@@ -2214,6 +2327,8 @@
 	int       o = 0;
 	GtkWidget *hbox;
 
+	g_return_val_if_fail(menuitems != NULL, NULL);
+
 	hbox = gtk_hbox_new(FALSE, 5);
 	gtk_container_add (GTK_CONTAINER (box), hbox);
 	gtk_widget_show(hbox);
@@ -2222,19 +2337,17 @@
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 	gtk_widget_show(label);
 
-	va_start(menuitems, clear);
-	text = va_arg(menuitems, gchar *);
-
-	if (!text)
-		return NULL;
-
 	dropdown = gtk_option_menu_new();
 	menu = gtk_menu_new();
 
 	gtk_label_set_mnemonic_widget(GTK_LABEL(label), dropdown);
 
-	while (text) {
-		value = va_arg(menuitems, int);
+	while (menuitems != NULL && (text = (char *) menuitems->data) != NULL) {
+		menuitems = g_list_next(menuitems);
+		g_return_val_if_fail(menuitems != NULL, NULL);
+		value = GPOINTER_TO_INT(menuitems->data);
+		menuitems = g_list_next(menuitems);
+
 		opt = gtk_menu_item_new_with_label(text);
 		gtk_object_set_user_data(GTK_OBJECT(opt), (void *)value);
 		gtk_object_set_data(GTK_OBJECT(opt), "clear", (void *)clear);
@@ -2246,12 +2359,10 @@
 		if (((clear > -1) && ((*option & value) == value)) || *option == value) {
 			gtk_menu_set_active(GTK_MENU(menu), o);
 		}
-		text = va_arg(menuitems, gchar *);
 		o++;
+
 	}
 
-	va_end(menuitems);
-
 	gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu);
 	gtk_box_pack_start(GTK_BOX(hbox), dropdown, FALSE, FALSE, 0);
 	gtk_widget_show(dropdown);