From 70fce0bbb0712dc70a15c481c0b65d68a98a4ff7 Mon Sep 17 00:00:00 2001
From: Matthew Barnes <mbarnes@src.gnome.org>
Date: Wed, 28 Jan 2009 22:28:57 +0000
Subject: When invoking Evolution with URIs on the command-line (e.g. mailto:),
 terminate after all the windows for those URIs have been closed.

svn path=/branches/kill-bonobo/; revision=37157
---
 shell/e-shell-window-private.c   |   2 +
 shell/e-shell-window.c           |  19 +++--
 shell/e-shell.c                  | 167 +++++++++++++++++++++++----------------
 shell/e-shell.h                  |   6 +-
 shell/main.c                     |  15 ++--
 shell/test/e-test-shell-module.c |  11 ++-
 6 files changed, 128 insertions(+), 92 deletions(-)

(limited to 'shell')

diff --git a/shell/e-shell-window-private.c b/shell/e-shell-window-private.c
index 53f7c28c64..02aac6cf10 100644
--- a/shell/e-shell-window-private.c
+++ b/shell/e-shell-window-private.c
@@ -340,6 +340,8 @@ e_shell_window_private_constructed (EShellWindow *shell_window)
 	shell = e_shell_window_get_shell (shell_window);
 	shell_settings = e_shell_get_shell_settings (shell);
 
+	e_shell_watch_window (shell, GTK_WINDOW (shell_window));
+
 	/* Create the switcher actions before we set the initial
 	 * shell view, because the shell view relies on them for
 	 * default settings during construction. */
diff --git a/shell/e-shell-window.c b/shell/e-shell-window.c
index 82727fecdd..ecc30cb9b5 100644
--- a/shell/e-shell-window.c
+++ b/shell/e-shell-window.c
@@ -132,17 +132,22 @@ static void
 shell_window_update_close_action_cb (EShellWindow *shell_window)
 {
 	EShell *shell;
-	GList *shell_windows;
-	gboolean sensitive;
+	GList *watched_windows;
+	gint n_shell_windows = 0;
 
 	shell = e_shell_window_get_shell (shell_window);
-	shell_windows = e_shell_get_shell_windows (shell);
-	g_return_if_fail (shell_windows != NULL);
+	watched_windows = e_shell_get_watched_windows (shell);
 
-	/* Disable Close Window if there's only one window.
+	/* Count the shell windows. */
+	while (watched_windows != NULL) {
+		if (E_IS_SHELL_WINDOW (watched_windows->data))
+			n_shell_windows++;
+		watched_windows = g_list_next (watched_windows);
+	}
+
+	/* Disable Close Window if there's only one shell window.
 	 * Helps prevent users from accidentally quitting. */
-	sensitive = (g_list_length (shell_windows) > 1);
-	gtk_action_set_sensitive (ACTION (CLOSE), sensitive);
+	gtk_action_set_sensitive (ACTION (CLOSE), n_shell_windows > 1);
 }
 
 static void
diff --git a/shell/e-shell.c b/shell/e-shell.c
index 160d637d54..e2d7761965 100644
--- a/shell/e-shell.c
+++ b/shell/e-shell.c
@@ -38,7 +38,7 @@
 	((obj), E_TYPE_SHELL, EShellPrivate))
 
 struct _EShellPrivate {
-	GList *active_windows;
+	GList *watched_windows;
 	EShellSettings *settings;
 
 	/* Shell Modules */
@@ -101,12 +101,21 @@ shell_parse_debug_string (EShell *shell)
 		e_shell_settings_enable_debug (shell->priv->settings);
 }
 
+static void
+shell_notify_online_mode_cb (EShell *shell)
+{
+	gboolean online;
+
+	online = e_shell_get_online_mode (shell);
+	e_passwords_set_online (online);
+}
+
 static gboolean
 shell_window_delete_event_cb (EShell *shell,
-                              EShellWindow *shell_window)
+                              GtkWindow *window)
 {
 	/* If other windows are open we can safely close this one. */
-	if (g_list_length (shell->priv->active_windows) > 1)
+	if (g_list_length (shell->priv->watched_windows) > 1)
 		return FALSE;
 
 	/* Otherwise we initiate application shutdown. */
@@ -116,16 +125,16 @@ shell_window_delete_event_cb (EShell *shell,
 static gboolean
 shell_window_focus_in_event_cb (EShell *shell,
                                 GdkEventFocus *event,
-                                EShellWindow *shell_window)
+                                GtkWindow *window)
 {
 	GList *list, *link;
 
-	/* Keep the active windows list sorted by most recently focused,
+	/* Keep the watched windows list sorted by most recently focused,
 	 * so the first item in the list should always be the currently
-	 * focused shell window. */
+	 * focused window. */
 
-	list = shell->priv->active_windows;
-	link = g_list_find (list, shell_window);
+	list = shell->priv->watched_windows;
+	link = g_list_find (list, window);
 	g_return_val_if_fail (link != NULL, FALSE);
 
 	if (link != list) {
@@ -133,29 +142,20 @@ shell_window_focus_in_event_cb (EShell *shell,
 		list = g_list_concat (link, list);
 	}
 
-	shell->priv->active_windows = list;
+	shell->priv->watched_windows = list;
 
 	return FALSE;
 }
 
-static void
-shell_notify_online_mode_cb (EShell *shell)
-{
-	gboolean online;
-
-	online = e_shell_get_online_mode (shell);
-	e_passwords_set_online (online);
-}
-
 static void
 shell_window_weak_notify_cb (EShell *shell,
                              GObject *where_the_object_was)
 {
-	GList *active_windows;
+	GList *list;
 
-	active_windows = shell->priv->active_windows;
-	active_windows = g_list_remove (active_windows, where_the_object_was);
-	shell->priv->active_windows = active_windows;
+	list = shell->priv->watched_windows;
+	list = g_list_remove (list, where_the_object_was);
+	shell->priv->watched_windows = list;
 
 	g_signal_emit (shell, signals[WINDOW_DESTROYED], 0);
 }
@@ -380,7 +380,7 @@ shell_shutdown_timeout (EShell *shell)
 	 * the act of destroying a shell window will modify the active
 	 * windows list, which would otherwise derail the iteration. */
 	if (proceed) {
-		list = g_list_copy (shell->priv->active_windows);
+		list = g_list_copy (shell->priv->watched_windows);
 		g_list_foreach (list, (GFunc) gtk_widget_destroy, NULL);
 		g_list_free (list);
 
@@ -748,9 +748,9 @@ shell_class_init (EShellClass *class)
 	/**
 	 * EShell::window-created
 	 * @shell: the #EShell which emitted the signal
-	 * @shell_window: the newly created #EShellWindow
+	 * @window: the newly created #GtkWindow
 	 *
-	 * Emitted when a new #EShellWindow is created.
+	 * Emitted when @shell begins watching a newly created window.
 	 **/
 	signals[WINDOW_CREATED] = g_signal_new (
 		"window-created",
@@ -759,7 +759,7 @@ shell_class_init (EShellClass *class)
 		0, NULL, NULL,
 		g_cclosure_marshal_VOID__OBJECT,
 		G_TYPE_NONE, 1,
-		E_TYPE_SHELL_WINDOW);
+		GTK_TYPE_WINDOW);
 
 	/**
 	 * EShell::window-destroyed
@@ -896,6 +896,9 @@ e_shell_get_type (void)
  *
  * Returns the #EShell created by <function>main()</function>.
  *
+ * Try to obtain the #EShell from elsewhere if you can.  This function
+ * is intended as a temporary workaround for when that proves difficult.
+ *
  * Returns: the #EShell singleton
  **/
 EShell *
@@ -924,26 +927,6 @@ e_shell_get_shell_modules (EShell *shell)
 	return shell->priv->loaded_modules;
 }
 
-/**
- * e_shell_get_shell_windows:
- * @shell: an #EShell
- *
- * Returns a list of active #EShellWindow instances that were created by
- * e_shell_create_shell_window().  The list is sorted by the most recently
- * focused window, such that the first instance is the currently focused
- * window.  (Useful for choosing a parent for a transient window.)  The
- * list is owned by @shell and should not be modified or freed.
- *
- * Returns: a list of active #EShellWindow instances
- **/
-GList *
-e_shell_get_shell_windows (EShell *shell)
-{
-	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
-
-	return shell->priv->active_windows;
-}
-
 /**
  * e_shell_get_canonical_name:
  * @shell: an #EShell
@@ -1052,7 +1035,6 @@ GtkWidget *
 e_shell_create_shell_window (EShell *shell,
                              const gchar *view_name)
 {
-	GList *active_windows;
 	GtkWidget *shell_window;
 	UniqueMessageData *data;
 	UniqueApp *app;
@@ -1085,25 +1067,6 @@ e_shell_create_shell_window (EShell *shell,
 	}
 
 	shell_window = e_shell_window_new (shell, shell->priv->safe_mode);
-	unique_app_watch_window (app, GTK_WINDOW (shell_window));
-
-	active_windows = shell->priv->active_windows;
-	active_windows = g_list_prepend (active_windows, shell_window);
-	shell->priv->active_windows = active_windows;
-
-	g_signal_connect_swapped (
-		shell_window, "delete-event",
-		G_CALLBACK (shell_window_delete_event_cb), shell);
-
-	g_signal_connect_swapped (
-		shell_window, "focus-in-event",
-		G_CALLBACK (shell_window_focus_in_event_cb), shell);
-
-	g_object_weak_ref (
-		G_OBJECT (shell_window), (GWeakNotify)
-		shell_window_weak_notify_cb, shell);
-
-	g_signal_emit (shell, signals[WINDOW_CREATED], 0, shell_window);
 
 	gtk_widget_show (shell_window);
 
@@ -1135,7 +1098,7 @@ unique:  /* Send a message to the other Evolution process. */
  **/
 guint
 e_shell_handle_uris (EShell *shell,
-                     const gchar **uris)
+                     gchar **uris)
 {
 	UniqueApp *app;
 	UniqueMessageData *data;
@@ -1163,17 +1126,81 @@ e_shell_handle_uris (EShell *shell,
 
 unique:  /* Send a message to the other Evolution process. */
 
-	/* XXX Do something with UniqueResponse?
-	 * XXX set_uris() should take a "const" URI list. */
+	/* XXX Do something with UniqueResponse? */
 
 	data = unique_message_data_new ();
-	unique_message_data_set_uris (data, (gchar **) uris);
+	unique_message_data_set_uris (data, uris);
 	unique_app_send_message (app, UNIQUE_OPEN, data);
 	unique_message_data_free (data);
 
 	return 0;
 }
 
+/**
+ * e_shell_watch_window:
+ * @shell: an #EShell
+ * @window: a #GtkWindow
+ *
+ * Makes @shell "watch" a newly created toplevel window, and emits the
+ * #EShell::window-created signal.  All #EShellWindow<!-- -->s should be
+ * watched, along with any editor or viewer windows that may be shown in
+ * response to e_shell_handle_uris().  When the last watched window is
+ * closed, Evolution terminates.
+ **/
+void
+e_shell_watch_window (EShell *shell,
+                      GtkWindow *window)
+{
+	GList *list;
+
+	g_return_if_fail (E_IS_SHELL (shell));
+	g_return_if_fail (GTK_IS_WINDOW (window));
+
+	list = shell->priv->watched_windows;
+
+	/* Ignore duplicates. */
+	if (g_list_find (list, window) != NULL)
+		return;
+
+	list = g_list_prepend (list, window);
+	shell->priv->watched_windows = list;
+
+	unique_app_watch_window (UNIQUE_APP (shell), window);
+
+	g_signal_connect_swapped (
+		window, "delete-event",
+		G_CALLBACK (shell_window_delete_event_cb), shell);
+
+	g_signal_connect_swapped (
+		window, "focus-in-event",
+		G_CALLBACK (shell_window_focus_in_event_cb), shell);
+
+	g_object_weak_ref (
+		G_OBJECT (window), (GWeakNotify)
+		shell_window_weak_notify_cb, shell);
+
+	g_signal_emit (shell, signals[WINDOW_CREATED], 0, window);
+}
+
+/**
+ * e_shell_get_watched_windows:
+ * @shell: an #EShell
+ *
+ * Returns a list of windows being watched by @shell.  The list is sorted
+ * by the most recently focused window, such that the first instance is the
+ * currently focused window.  (Useful for choosing a parent for a transient
+ * window.)  The list is owned by @shell and should not be modified or freed.
+ *
+ * Returns: a list of watched windows
+ **/
+GList *
+e_shell_get_watched_windows (EShell *shell)
+{
+	g_return_val_if_fail (E_IS_SHELL (shell), NULL);
+
+	return shell->priv->watched_windows;
+}
+
 /**
  * e_shell_send_receive:
  * @shell: an #EShell
diff --git a/shell/e-shell.h b/shell/e-shell.h
index 9e30550283..3f23779ee7 100644
--- a/shell/e-shell.h
+++ b/shell/e-shell.h
@@ -78,7 +78,6 @@ struct _EShellClass {
 GType		e_shell_get_type		(void);
 EShell *	e_shell_get_default		(void);
 GList *		e_shell_get_shell_modules	(EShell *shell);
-GList *		e_shell_get_shell_windows	(EShell *shell);
 const gchar *	e_shell_get_canonical_name	(EShell *shell,
 						 const gchar *name);
 EShellModule *	e_shell_get_module_by_name	(EShell *shell,
@@ -89,7 +88,10 @@ EShellSettings *e_shell_get_shell_settings	(EShell *shell);
 GtkWidget *	e_shell_create_shell_window	(EShell *shell,
 						 const gchar *view_name);
 guint		e_shell_handle_uris		(EShell *shell,
-                                                 const gchar **uris);
+						 gchar **uris);
+void		e_shell_watch_window		(EShell *shell,
+						 GtkWindow *window);
+GList *		e_shell_get_watched_windows	(EShell *shell);
 void		e_shell_send_receive		(EShell *shell,
 						 GtkWindow *parent);
 gboolean	e_shell_get_network_available	(EShell *shell);
diff --git a/shell/main.c b/shell/main.c
index 670b24d1a2..ce1b467754 100644
--- a/shell/main.c
+++ b/shell/main.c
@@ -306,7 +306,6 @@ static gboolean
 idle_cb (gchar **uris)
 {
 	EShell *shell;
-	guint ii;
 
 #ifdef KILL_PROCESS_CMD
 	kill_old_dataserver ();
@@ -314,23 +313,21 @@ idle_cb (gchar **uris)
 
 	shell = e_shell_get_default ();
 
-	/* These calls do the right thing when there's another
-	 * Evolution process running. */
+	/* These calls do the right thing when another Evolution
+	 * process is running. */
 	if (uris != NULL && *uris != NULL)
 		e_shell_handle_uris (shell, uris);
 	else
 		e_shell_create_shell_window (shell, requested_view);
 
-	if (unique_app_is_running (UNIQUE_APP (shell))) {
+	/* If another Evolution process is running, we're done. */
+	if (unique_app_is_running (UNIQUE_APP (shell)))
 		gtk_main_quit ();
-		return FALSE;
-	}
 
 	/* This must be done after EShell has loaded all the modules.
 	 * For example the mail module makes the global variable `session`
 	 * which is being used by several EPlugins */
-
-	if (uris == NULL && !disable_eplugin)
+	else if (uris == NULL && !disable_eplugin)
 		e_plugin_load_plugins_with_missing_symbols ();
 
 	return FALSE;
@@ -471,7 +468,7 @@ set_paths (void)
 static void
 shell_window_destroyed_cb (EShell *shell)
 {
-	if (e_shell_get_shell_windows (shell) == NULL)
+	if (e_shell_get_watched_windows (shell) == NULL)
 		gtk_main_quit ();
 }
 
diff --git a/shell/test/e-test-shell-module.c b/shell/test/e-test-shell-module.c
index fc489fa3b0..a7d2389fb7 100644
--- a/shell/test/e-test-shell-module.c
+++ b/shell/test/e-test-shell-module.c
@@ -115,20 +115,23 @@ test_module_send_receive_cb (EShellModule *shell_module,
 
 static void
 test_module_window_created_cb (EShellModule *shell_module,
-                               EShellWindow *shell_window)
+                               GtkWindow *window)
 {
 	const gchar *module_name;
 
-	g_debug ("%s (window=%p)", G_STRFUNC, shell_window);
+	g_debug ("%s (%s)", G_STRFUNC, G_OBJECT_TYPE_NAME (window));
+
+	if (!E_IS_SHELL_WINDOW (window))
+		return;
 
 	module_name = G_TYPE_MODULE (shell_module)->name;
 
 	e_shell_window_register_new_item_actions (
-		shell_window, module_name,
+		E_SHELL_WINDOW (window), module_name,
 		item_entries, G_N_ELEMENTS (item_entries));
 
 	e_shell_window_register_new_source_actions (
-		shell_window, module_name,
+		E_SHELL_WINDOW (window), module_name,
 		source_entries, G_N_ELEMENTS (source_entries));
 }
 
-- 
cgit