diff options
Diffstat (limited to 'shell/e-shell.c')
-rw-r--r-- | shell/e-shell.c | 2175 |
1 files changed, 1082 insertions, 1093 deletions
diff --git a/shell/e-shell.c b/shell/e-shell.c index 061071efb8..d95853dffa 100644 --- a/shell/e-shell.c +++ b/shell/e-shell.c @@ -1,4 +1,6 @@ /* + * e-shell.c + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -13,1420 +15,1407 @@ * License along with the program; if not, see <http://www.gnu.org/licenses/> * * - * Authors: - * Ettore Perazzoli <ettore@ximian.com> - * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ -#include <config.h> - -#include <string.h> -#include <unistd.h> -#include <sys/types.h> - -#include <gtk/gtk.h> -#include <glib/gstdio.h> - -#ifdef GDK_WINDOWING_X11 -#include <gdk/gdkprivate.h> -#include <gdk/gdkx.h> -#include <X11/Xatom.h> -#include <X11/Xlib.h> -#elif defined (GDK_WINDOWING_WIN32) -/* gdkwin32.h includes <windows.h> which stomps over the namespace */ -#undef DATADIR -#define interface windows_interface -#include <gdk/gdkwin32.h> -#undef interface -#endif +#include "e-shell.h" #include <glib/gi18n.h> - -#include <gconf/gconf-client.h> - -#include <bonobo-activation/bonobo-activation.h> -#include <bonobo/bonobo-exception.h> -#include <bonobo/bonobo-moniker-util.h> - -#include <libedataserver/e-xml-utils.h> #include <libedataserverui/e-passwords.h> -#include "e-util/e-bconf-map.h" -#include "e-util/e-dialog-utils.h" -#include "e-util/e-error.h" -#include "e-util/e-fsutils.h" #include "e-util/e-util.h" +#include "e-util/e-module.h" +#include "widgets/misc/e-preferences-window.h" -#include "Evolution.h" -#include "e-shell-constants.h" -#include "e-shell-settings-dialog.h" -#include "e-shell.h" -#include "e-shell-view.h" -#include "es-event.h" -#include "evolution-listener.h" -#include "evolution-shell-component-utils.h" +#include "e-shell-backend.h" +#include "e-shell-migrate.h" +#include "e-shell-window.h" -static void set_line_status_complete(EvolutionListener *el, void *data); +#define SHUTDOWN_TIMEOUT 500 /* milliseconds */ -#define PARENT_TYPE bonobo_object_get_type () -static BonoboObjectClass *parent_class = NULL; -static gboolean session_started = FALSE; +#define E_SHELL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SHELL, EShellPrivate)) struct _EShellPrivate { - /* IID for registering the object on OAF. */ - char *iid; + GList *watched_windows; + EShellSettings *settings; + GConfClient *gconf_client; + GtkWidget *preferences_window; - GList *windows; + /* Shell Backends */ + GList *loaded_backends; + GHashTable *backends_by_name; + GHashTable *backends_by_scheme; - /* EUriSchemaRegistry *uri_schema_registry; FIXME */ - EComponentRegistry *component_registry; + gpointer preparing_for_line_change; /* weak pointer */ - /* Names for the types of the folders that have maybe crashed. */ - /* FIXME TODO */ - GList *crash_type_names; /* char * */ + guint auto_reconnect : 1; + guint network_available : 1; + guint online : 1; + guint safe_mode : 1; +}; - /* Line status and controllers */ - EShellLineStatus line_status; - int line_status_pending; - EShellLineStatus line_status_working; - EvolutionListener *line_status_listener; +enum { + PROP_0, + PROP_NETWORK_AVAILABLE, + PROP_ONLINE, + PROP_SHELL_SETTINGS +}; - /* Settings Dialog */ - union { - GtkWidget *widget; - gpointer pointer; - } settings_dialog; +enum { + EVENT, + HANDLE_URI, + PREPARE_FOR_OFFLINE, + PREPARE_FOR_ONLINE, + SEND_RECEIVE, + WINDOW_CREATED, + WINDOW_DESTROYED, + LAST_SIGNAL +}; - /* If we're quitting and things are still busy, a timeout handler */ - guint quit_timeout; +enum { + DEBUG_KEY_SETTINGS = 1 << 0 +}; - /* Whether the shell is succesfully initialized. This is needed during - the start-up sequence, to avoid CORBA calls to do make wrong things - to happen while the shell is initializing. */ - unsigned int is_initialized : 1; +static GDebugKey debug_keys[] = { + { "settings", DEBUG_KEY_SETTINGS } +}; - /* Wether the shell is working in "interactive" mode or not. - (Currently, it's interactive IIF there is at least one active - view.) */ - unsigned int is_interactive : 1; +EShell *default_shell = NULL; +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; - /* Whether quit has been requested, and the shell is now waiting for - permissions from all the components to quit. */ - unsigned int preparing_to_quit : 1; +#if NM_SUPPORT +void e_shell_dbus_initialize (EShell *shell); +#endif - /* Whether we are recovering from a crash in the previous session. */ - unsigned int crash_recovery : 1; -}; +static void +shell_parse_debug_string (EShell *shell) +{ + guint flags; + flags = g_parse_debug_string ( + g_getenv ("EVOLUTION_DEBUG"), + debug_keys, G_N_ELEMENTS (debug_keys)); -/* Signals. */ + if (flags & DEBUG_KEY_SETTINGS) + e_shell_settings_enable_debug (shell->priv->settings); +} -enum { - NO_WINDOWS_LEFT, - LINE_STATUS_CHANGED, - NEW_WINDOW_CREATED, - LAST_SIGNAL -}; +static void +shell_notify_online_cb (EShell *shell) +{ + gboolean online; -static guint signals[LAST_SIGNAL] = { 0 }; + online = e_shell_get_online (shell); + e_passwords_set_online (online); +} +static gboolean +shell_window_delete_event_cb (EShell *shell, + GtkWindow *window) +{ + /* If other windows are open we can safely close this one. */ + if (g_list_length (shell->priv->watched_windows) > 1) + return FALSE; -/* Utility functions. */ + /* Otherwise we initiate application shutdown. */ + return !e_shell_quit (shell); +} static gboolean -get_config_start_offline (void) +shell_window_focus_in_event_cb (EShell *shell, + GdkEventFocus *event, + GtkWindow *window) { - GConfClient *client; - gboolean value; + GList *list, *link; - client = gconf_client_get_default (); + /* Keep the watched windows list sorted by most recently focused, + * so the first item in the list should always be the currently + * focused window. */ - value = gconf_client_get_bool (client, "/apps/evolution/shell/start_offline", NULL); + list = shell->priv->watched_windows; + link = g_list_find (list, window); + g_return_val_if_fail (link != NULL, FALSE); - g_object_unref (client); - - return value; -} + if (link != list) { + list = g_list_remove_link (list, link); + list = g_list_concat (link, list); + } + shell->priv->watched_windows = list; -/* Interactivity handling. */ + return FALSE; +} static void -set_interactive (EShell *shell, - gboolean interactive) +shell_window_weak_notify_cb (EShell *shell, + GObject *where_the_object_was) { - GSList *component_list; - GSList *p; - GList *first_element; - int num_windows; - GtkWidget *view; - - g_return_if_fail (E_IS_SHELL (shell)); + GList *list; - shell->priv->is_interactive = interactive; + list = shell->priv->watched_windows; + list = g_list_remove (list, where_the_object_was); + shell->priv->watched_windows = list; - num_windows = g_list_length (shell->priv->windows); + g_signal_emit (shell, signals[WINDOW_DESTROYED], 0); +} - /* We want to send the "interactive" message only when the first - window is created */ - if (num_windows != 1) +static void +shell_ready_for_offline (EShell *shell, + EActivity *activity, + gboolean is_last_ref) +{ + if (!is_last_ref) return; - first_element = g_list_first (shell->priv->windows); - view = GTK_WIDGET (first_element->data); - - component_list = e_component_registry_peek_list (shell->priv->component_registry); + /* Increment the reference count so we can safely emit + * a signal without triggering the toggle reference. */ + g_object_ref (activity); - for (p = component_list; p != NULL; p = p->next) { - EComponentInfo *info = p->data; - CORBA_Environment ev; + e_activity_complete (activity); - CORBA_exception_init (&ev); + g_object_remove_toggle_ref ( + G_OBJECT (activity), (GToggleNotify) + shell_ready_for_offline, shell); -#ifdef GDK_WINDOWING_X11 - GNOME_Evolution_Component_interactive (info->iface, interactive,GPOINTER_TO_INT (GDK_WINDOW_XWINDOW (view->window)), &ev); -#elif defined (GDK_WINDOWING_WIN32) - GNOME_Evolution_Component_interactive (info->iface, interactive,GPOINTER_TO_INT (GDK_WINDOW_HWND (view->window)), &ev); -#else -#error Port this to your windowing system -#endif + /* Finalize the activity. */ + g_object_unref (activity); - /* Ignore errors, the components can decide to not implement - this interface. */ + shell->priv->online = FALSE; + g_object_notify (G_OBJECT (shell), "online"); - CORBA_exception_free (&ev); - } + g_message ("Offline preparations complete."); } +static void +shell_prepare_for_offline (EShell *shell) +{ + /* Are preparations already in progress? */ + if (shell->priv->preparing_for_line_change != NULL) + return; + + g_message ("Preparing for offline mode..."); -/* CORBA interface implementation. */ + shell->priv->preparing_for_line_change = + e_activity_new (_("Preparing to go offline...")); -static gboolean -raise_exception_if_not_ready (PortableServer_Servant servant, - CORBA_Environment *ev) -{ - EShell *shell; + g_object_add_toggle_ref ( + G_OBJECT (shell->priv->preparing_for_line_change), + (GToggleNotify) shell_ready_for_offline, shell); - shell = E_SHELL (bonobo_object_from_servant (servant)); + g_object_add_weak_pointer ( + G_OBJECT (shell->priv->preparing_for_line_change), + &shell->priv->preparing_for_line_change); - if (! shell->priv->is_initialized) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, - ex_GNOME_Evolution_Shell_NotReady, NULL); - return TRUE; - } + g_signal_emit ( + shell, signals[PREPARE_FOR_OFFLINE], 0, + shell->priv->preparing_for_line_change); - return FALSE; + g_object_unref (shell->priv->preparing_for_line_change); } -static GNOME_Evolution_ShellView -impl_Shell_createNewWindow (PortableServer_Servant servant, - const CORBA_char *component_id, - CORBA_Environment *ev) +static void +shell_ready_for_online (EShell *shell, + EActivity *activity, + gboolean is_last_ref) { - BonoboObject *bonobo_object; - EShell *shell; - EShellWindow *shell_window; - EShellView *shell_view; - - if (raise_exception_if_not_ready (servant, ev)) - return CORBA_OBJECT_NIL; + if (!is_last_ref) + return; - bonobo_object = bonobo_object_from_servant (servant); - shell = E_SHELL (bonobo_object); + /* Increment the reference count so we can safely emit + * a signal without triggering the toggle reference. */ + g_object_ref (activity); - if (component_id[0] == '\0') - component_id = NULL; + e_activity_complete (activity); - shell_window = e_shell_create_window (shell, component_id, NULL); - if (shell_window == NULL) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, - ex_GNOME_Evolution_Shell_ComponentNotFound, NULL); - return CORBA_OBJECT_NIL; - } + g_object_remove_toggle_ref ( + G_OBJECT (activity), (GToggleNotify) + shell_ready_for_online, shell); - /* refs?? */ - shell_view = e_shell_view_new(shell_window); + /* Finalize the activity. */ + g_object_unref (activity); - return BONOBO_OBJREF(shell_view); + shell->priv->online = TRUE; + g_object_notify (G_OBJECT (shell), "online"); + g_message ("Online preparations complete."); } static void -impl_Shell_handleURI (PortableServer_Servant servant, - const CORBA_char *uri, - CORBA_Environment *ev) +shell_prepare_for_online (EShell *shell) { - EShell *shell = E_SHELL (bonobo_object_from_servant (servant)); - EComponentInfo *component_info; - char *schema, *p; - int show = FALSE; - - schema = g_alloca(strlen(uri)+1); - strcpy(schema, uri); - p = strchr(schema, ':'); - if (p) - *p = 0; - - component_info = e_component_registry_peek_info(shell->priv->component_registry, ECR_FIELD_SCHEMA, schema); - if (component_info == NULL) { - show = TRUE; - component_info = e_component_registry_peek_info(shell->priv->component_registry, ECR_FIELD_ALIAS, schema); - } - - if (component_info == NULL) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_UnsupportedSchema, NULL); + /* Are preparations already in progress? */ + if (shell->priv->preparing_for_line_change != NULL) return; - } - - if (show) { - GtkWidget *shell_window; - - shell_window = (GtkWidget *)e_shell_create_window (shell, component_info->id, NULL); - if (shell_window == NULL) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_ComponentNotFound, NULL); - return; - } - } - GNOME_Evolution_Component_handleURI (component_info->iface, uri, ev); - /* not an error not to implement it */ - if (ev->_id != NULL && strcmp(ev->_id, ex_CORBA_NO_IMPLEMENT) == 0) - memset(ev, 0, sizeof(*ev)); -} + g_message ("Preparing for online mode..."); -static void -impl_Shell_setLineStatus (PortableServer_Servant servant, - CORBA_boolean online, - CORBA_Environment *ev) -{ - BonoboObject *bonobo_object; - EShell *shell; + shell->priv->preparing_for_line_change = + e_activity_new (_("Preparing to go online...")); - if (raise_exception_if_not_ready (servant, ev)) - return; + g_object_add_toggle_ref ( + G_OBJECT (shell->priv->preparing_for_line_change), + (GToggleNotify) shell_ready_for_online, shell); - bonobo_object = bonobo_object_from_servant (servant); - shell = E_SHELL (bonobo_object); + g_object_add_weak_pointer ( + G_OBJECT (shell->priv->preparing_for_line_change), + &shell->priv->preparing_for_line_change); - /* let the password manager know out online status */ - e_passwords_set_online(online); + g_signal_emit ( + shell, signals[PREPARE_FOR_ONLINE], 0, + shell->priv->preparing_for_line_change); - if (online) - e_shell_set_line_status (shell, GNOME_Evolution_USER_ONLINE); - else - e_shell_set_line_status (shell, GNOME_Evolution_USER_OFFLINE); + g_object_unref (shell->priv->preparing_for_line_change); } -/* -static GNOME_Evolution_Component -impl_Shell_findComponent(PortableServer_Servant servant, - const CORBA_char *id, - CORBA_Environment *ev) -{ - EShell *shell; - EComponentInfo *ci; - - if (raise_exception_if_not_ready (servant, ev)) - return CORBA_OBJECT_NIL; - - shell = (EShell *)bonobo_object_from_servant (servant); - ci = e_component_registry_peek_info(shell->priv->component_registry, ECR_FIELD_ALIAS, id); - if (ci == NULL) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_ComponentNotFound, NULL); - return CORBA_OBJECT_NIL; - } else if (ci->iface == NULL) { - CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_NotReady, NULL); - return CORBA_OBJECT_NIL; - } else { - return ci->iface; - } -} -*/ - -/* EShellWindow handling and bookkeeping. */ -static int -window_delete_event_cb (GtkWidget *widget, - GdkEventAny *ev, - void *data) +static void +shell_load_modules (EShell *shell) { - EShell *shell; + GList *modules; - g_return_val_if_fail (E_IS_SHELL_WINDOW (widget), TRUE); - shell = E_SHELL (data); + /* Load all shared library modules. */ + modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR); - return ! e_shell_request_close_window (shell, E_SHELL_WINDOW (widget)); + while (modules != NULL) { + g_type_module_unuse (G_TYPE_MODULE (modules->data)); + modules = g_list_delete_link (modules, modules); + } } -static gboolean -notify_no_windows_left_idle_cb (void *data) +/* Helper for shell_process_backend() */ +static void +shell_split_and_insert_items (GHashTable *hash_table, + const gchar *items, + EShellBackend *shell_backend) { - EShell *shell; - EShellPrivate *priv; - - shell = E_SHELL (data); - priv = shell->priv; - - set_interactive (shell, FALSE); + gpointer key; + gchar **strv; + gint ii; - g_signal_emit (shell, signals [NO_WINDOWS_LEFT], 0); + strv = g_strsplit_set (items, ":", -1); - if (priv->iid != NULL) - bonobo_activation_active_server_unregister (priv->iid, - bonobo_object_corba_objref (BONOBO_OBJECT (shell))); - bonobo_object_unref (BONOBO_OBJECT (shell)); + for (ii = 0; strv[ii] != NULL; ii++) { + key = (gpointer) g_intern_string (strv[ii]); + g_hash_table_insert (hash_table, key, shell_backend); + } - return FALSE; + g_strfreev (strv); } static void -window_weak_notify (void *data, - GObject *where_the_object_was) +shell_process_backend (EShell *shell, + EShellBackend *shell_backend) { - EShell *shell; - int num_windows; + EShellBackendClass *class; + GHashTable *backends_by_name; + GHashTable *backends_by_scheme; + const gchar *string; - shell = E_SHELL (data); + shell->priv->loaded_backends = g_list_insert_sorted ( + shell->priv->loaded_backends, shell_backend, + (GCompareFunc) e_shell_backend_compare); - num_windows = g_list_length (shell->priv->windows); + /* Bookkeeping */ - /* If this is our last window, save settings now because in the callback - for no_windows_left shell->priv->windows will be NULL and settings won't - be saved because of that. */ - if (num_windows == 1) - e_shell_save_settings (shell); + class = E_SHELL_BACKEND_GET_CLASS (shell_backend); + backends_by_name = shell->priv->backends_by_name; + backends_by_scheme = shell->priv->backends_by_scheme; - shell->priv->windows = g_list_remove (shell->priv->windows, where_the_object_was); + if ((string = class->name) != NULL) + g_hash_table_insert ( + backends_by_name, (gpointer) + g_intern_string (string), shell_backend); - if (shell->priv->windows == NULL) { - bonobo_object_ref (BONOBO_OBJECT (shell)); - g_idle_add (notify_no_windows_left_idle_cb, shell); - } -} + if ((string = class->aliases) != NULL) + shell_split_and_insert_items ( + backends_by_name, string, shell_backend); -/* GObject methods. */ + if ((string = class->schemes) != NULL) + shell_split_and_insert_items ( + backends_by_scheme, string, shell_backend); +} static void -impl_dispose (GObject *object) +shell_create_backends (EShell *shell) { - EShell *shell; - EShellPrivate *priv; - GList *p; + GType *children; + guint ii, n_children; - shell = E_SHELL (object); - priv = shell->priv; + /* Create an instance of each EShellBackend subclass. */ + children = g_type_children (E_TYPE_SHELL_BACKEND, &n_children); - priv->is_initialized = FALSE; + for (ii = 0; ii < n_children; ii++) { + EShellBackend *shell_backend; + GType type = children[ii]; -#if 0 /* FIXME */ - if (priv->uri_schema_registry != NULL) { - g_object_unref (priv->uri_schema_registry); - priv->uri_schema_registry = NULL; + shell_backend = g_object_new (type, "shell", shell, NULL); + shell_process_backend (shell, shell_backend); } -#endif - if (priv->component_registry != NULL) { - g_object_unref (priv->component_registry); - priv->component_registry = NULL; - } + g_free (children); +} - if (priv->quit_timeout) { - g_source_remove(priv->quit_timeout); - priv->quit_timeout = 0; +static gboolean +shell_shutdown_timeout (EShell *shell) +{ + GList *list, *iter; + gboolean proceed = TRUE; + static guint source_id = 0; + static guint message_timer = 1; + + /* Module list is read-only; do not free. */ + list = e_shell_get_shell_backends (shell); + + /* Any backend can defer shutdown if it's still busy. */ + for (iter = list; proceed && iter != NULL; iter = iter->next) { + EShellBackend *shell_backend = iter->data; + proceed = e_shell_backend_shutdown (shell_backend); + + /* Emit a message every few seconds to indicate + * which backend(s) we're still waiting on. */ + if (proceed || message_timer == 0) + continue; + + g_message ( + _("Waiting for the \"%s\" backend to finish..."), + E_SHELL_BACKEND_GET_CLASS (shell_backend)->name); } - for (p = priv->windows; p != NULL; p = p->next) { - EShellWindow *window; - - window = E_SHELL_WINDOW (p->data); + message_timer = (message_timer + 1) % 10; + + /* If we're go for shutdown, destroy all shell windows. Note, + * we iterate over a /copy/ of the active windows list because + * 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->watched_windows); + g_list_foreach (list, (GFunc) gtk_widget_destroy, NULL); + g_list_free (list); + + /* If a backend is still busy, try again after a short delay. */ + } else if (source_id == 0) + source_id = g_timeout_add ( + SHUTDOWN_TIMEOUT, (GSourceFunc) + shell_shutdown_timeout, shell); + + /* Return TRUE to repeat the timeout, FALSE to stop it. This + * may seem backwards if the function was called directly. */ + return !proceed; +} - g_signal_handlers_disconnect_by_func (window, G_CALLBACK (window_delete_event_cb), shell); - g_object_weak_unref (G_OBJECT (window), window_weak_notify, shell); +static void +shell_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_NETWORK_AVAILABLE: + e_shell_set_network_available ( + E_SHELL (object), + g_value_get_boolean (value)); + return; - gtk_object_destroy (GTK_OBJECT (window)); + case PROP_ONLINE: + e_shell_set_online ( + E_SHELL (object), + g_value_get_boolean (value)); + return; } - g_list_free (priv->windows); - priv->windows = NULL; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} - /* No unreffing for these as they are aggregate. */ - /* bonobo_object_unref (BONOBO_OBJECT (priv->corba_storage_registry)); */ +static void +shell_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_NETWORK_AVAILABLE: + g_value_set_boolean ( + value, e_shell_get_network_available ( + E_SHELL (object))); + return; - if (priv->settings_dialog.widget != NULL) { - gtk_widget_destroy (priv->settings_dialog.widget); - priv->settings_dialog.widget = NULL; - } + case PROP_ONLINE: + g_value_set_boolean ( + value, e_shell_get_online ( + E_SHELL (object))); + return; - if (priv->line_status_listener) { - priv->line_status_listener->complete = NULL; - bonobo_object_unref(BONOBO_OBJECT(priv->line_status_listener)); - priv->line_status_listener = NULL; + case PROP_SHELL_SETTINGS: + g_value_set_object ( + value, e_shell_get_shell_settings ( + E_SHELL (object))); + return; } - g_free (priv->iid); - (* G_OBJECT_CLASS (parent_class)->dispose) (object); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -impl_finalize (GObject *object) +shell_dispose (GObject *object) { - EShell *shell; EShellPrivate *priv; - shell = E_SHELL (object); - priv = shell->priv; + priv = E_SHELL_GET_PRIVATE (object); - g_list_foreach (priv->crash_type_names, (GFunc) g_free, NULL); - g_list_free (priv->crash_type_names); - - g_free (priv); + if (priv->settings != NULL) { + g_object_unref (priv->settings); + priv->settings = NULL; + } - (* G_OBJECT_CLASS (parent_class)->finalize) (object); -} + if (priv->gconf_client != NULL) { + g_object_unref (priv->gconf_client); + priv->gconf_client = NULL; + } + if (priv->preferences_window != NULL) { + g_object_unref (priv->preferences_window); + priv->preferences_window = NULL; + } -/* Initialization. */ + g_list_foreach (priv->loaded_backends, (GFunc) g_object_unref, NULL); + g_list_free (priv->loaded_backends); + priv->loaded_backends = NULL; -static void -e_shell_class_init (EShellClass *klass) -{ - GObjectClass *object_class; - POA_GNOME_Evolution_Shell__epv *epv; - - parent_class = g_type_class_ref(PARENT_TYPE); - - object_class = G_OBJECT_CLASS (klass); - object_class->dispose = impl_dispose; - object_class->finalize = impl_finalize; - - signals[NO_WINDOWS_LEFT] = - g_signal_new ("no_windows_left", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EShellClass, no_windows_left), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[LINE_STATUS_CHANGED] = - g_signal_new ("line_status_changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EShellClass, line_status_changed), - NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, - G_TYPE_INT); - - signals[NEW_WINDOW_CREATED] = - g_signal_new ("new_window_created", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (EShellClass, new_window_created), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - - epv = & klass->epv; - epv->createNewWindow = impl_Shell_createNewWindow; - epv->handleURI = impl_Shell_handleURI; - epv->setLineStatus = impl_Shell_setLineStatus; -/* epv->findComponent = impl_Shell_findComponent;*/ + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); } static void -e_shell_init (EShell *shell) +shell_finalize (GObject *object) { EShellPrivate *priv; - priv = g_new0 (EShellPrivate, 1); - priv->line_status = E_SHELL_LINE_STATUS_OFFLINE; - priv->component_registry = e_component_registry_new (); + priv = E_SHELL_GET_PRIVATE (object); + + g_hash_table_destroy (priv->backends_by_name); + g_hash_table_destroy (priv->backends_by_scheme); - shell->priv = priv; + /* Indicates a clean shut down to the next session. */ + if (!unique_app_is_running (UNIQUE_APP (object))) + e_file_lock_destroy (); - priv->line_status_listener = evolution_listener_new(set_line_status_complete, shell); + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); } static void -detect_version (GConfClient *gconf, int *major, int *minor, int *revision) +shell_constructed (GObject *object) { - char *val, *evolution_dir; - struct stat st; - - evolution_dir = g_build_filename (g_get_home_dir (), "evolution", NULL); - - val = gconf_client_get_string(gconf, "/apps/evolution/version", NULL); - if (val) { - /* Since 1.4.0 We've been keeping the version key in gconf */ - sscanf(val, "%d.%d.%d", major, minor, revision); - g_free(val); - } else if (g_lstat (evolution_dir, &st) != 0 || !S_ISDIR (st.st_mode)) { - /* If ~/evolution does not exit or is not a directory it must be a new installation */ - *major = 0; - *minor = 0; - *revision = 0; - } else { - xmlDocPtr config_doc; - xmlNodePtr source; - char *tmp; - - tmp = g_build_filename (evolution_dir, "config.xmldb", NULL); - config_doc = e_xml_parse_file (tmp); - g_free (tmp); - tmp = NULL; - - if (config_doc - && (source = e_bconf_get_path (config_doc, "/Shell")) - && (tmp = e_bconf_get_value (source, "upgrade_from_1_0_to_1_2_performed")) - && tmp[0] == '1' ) { - *major = 1; - *minor = 2; - *revision = 0; - } else { - *major = 1; - *minor = 0; - *revision = 0; - } - g_free (tmp); - if (config_doc) - xmlFreeDoc (config_doc); - } + /* UniqueApp will have by this point determined whether we're + * the only Evolution process running. If so, proceed normally. + * Otherwise we just issue commands to the other process. */ + if (unique_app_is_running (UNIQUE_APP (object))) + return; - g_free (evolution_dir); + e_file_lock_create (); + + shell_load_modules (E_SHELL (object)); + shell_create_backends (E_SHELL (object)); + e_shell_migrate_attempt (E_SHELL (object)); } -/* calls components to perform upgrade */ static gboolean -attempt_upgrade (EShell *shell, int major, int minor, int revision) +shell_message_handle_new (EShell *shell, + UniqueMessageData *data) { - GSList *component_infos, *p; - gboolean success; - int res; - - success = TRUE; - - component_infos = e_component_registry_peek_list (shell->priv->component_registry); - for (p = component_infos; success && p != NULL; p = p->next) { - const EComponentInfo *info = p->data; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - GNOME_Evolution_Component_upgradeFromVersion (info->iface, major, minor, revision, &ev); - - if (BONOBO_EX (&ev)) { - char *exception_text; - CORBA_char *id = CORBA_exception_id(&ev); - - if (strcmp (id, ex_CORBA_NO_IMPLEMENT) == 0) { - /* Ignore components that do not implement this version, it - might just mean that they don't need an upgrade path. */ - } else if (strcmp (id, ex_GNOME_Evolution_Component_UpgradeFailed) == 0) { - GNOME_Evolution_Component_UpgradeFailed *ex = CORBA_exception_value(&ev); - - res = e_error_run(NULL, "shell:upgrade-failed", ex->what, ex->why, NULL); - if (res == GTK_RESPONSE_CANCEL) - success = FALSE; - } else if (strcmp (id, ex_GNOME_Evolution_Component_UnsupportedVersion) == 0) { - /* This is non-fatal */ - /* DO WE CARE??? */ - printf("Upgrade of component failed, unsupported prior version\n"); - } else { - exception_text = bonobo_exception_get_text (&ev); - res = e_error_run(NULL, "shell:upgrade-failed", exception_text, _("Unknown system error."), NULL); - g_free (exception_text); - if (res == GTK_RESPONSE_CANCEL) - success = FALSE; - } - } - CORBA_exception_free (&ev); - } + gchar *view_name; - return success; + view_name = unique_message_data_get_text (data); + e_shell_create_shell_window (shell, view_name); + g_free (view_name); + + return TRUE; } -/** - * e_shell_construct: - * @shell: An EShell object to construct - * @iid: OAFIID for registering the shell into the name server - * @startup_line_mode: How to set up the line mode (online or offline) initally. - * - * Construct @shell so that it uses the specified @corba_object. - * - * Return value: The result of the operation. - **/ -EShellConstructResult -e_shell_construct (EShell *shell, - const char *iid, - EShellStartupLineMode startup_line_mode) +static gboolean +shell_message_handle_open (EShell *shell, + UniqueMessageData *data) { - EShellPrivate *priv; - CORBA_Object corba_object; - gboolean start_online; - GSList *component; - - g_return_val_if_fail (E_IS_SHELL (shell), E_SHELL_CONSTRUCT_RESULT_INVALIDARG); - g_return_val_if_fail (startup_line_mode == E_SHELL_STARTUP_LINE_MODE_CONFIG - || startup_line_mode == E_SHELL_STARTUP_LINE_MODE_ONLINE - || startup_line_mode == E_SHELL_STARTUP_LINE_MODE_OFFLINE, - E_SHELL_CONSTRUCT_RESULT_INVALIDARG); - - priv = shell->priv; - priv->iid = g_strdup (iid); - - /* Now we can register into OAF. Notice that we shouldn't be - registering into OAF until we are sure we can complete. */ - - /* FIXME: Multi-display stuff. */ - corba_object = bonobo_object_corba_objref (BONOBO_OBJECT (shell)); - if (bonobo_activation_active_server_register (iid, corba_object) != Bonobo_ACTIVATION_REG_SUCCESS) - return E_SHELL_CONSTRUCT_RESULT_CANNOTREGISTER; - - while (gtk_events_pending ()) - gtk_main_iteration (); - - /* activate all the components (peek list does this implictly) */ - /* Do we really need to assign the result of this to the list? */ - component = e_component_registry_peek_list (shell->priv->component_registry); - - e_shell_attempt_upgrade(shell); - - priv->is_initialized = TRUE; - - switch (startup_line_mode) { - case E_SHELL_STARTUP_LINE_MODE_CONFIG: - start_online = ! get_config_start_offline (); - break; - case E_SHELL_STARTUP_LINE_MODE_ONLINE: - start_online = TRUE; - break; - case E_SHELL_STARTUP_LINE_MODE_OFFLINE: - start_online = FALSE; - break; - default: - start_online = FALSE; /* Make compiler happy. */ - g_return_val_if_reached(E_SHELL_CONSTRUCT_RESULT_OK); - } + gchar **uris; - e_passwords_set_online(start_online); + uris = unique_message_data_get_uris (data); + e_shell_handle_uris (shell, uris); + g_strfreev (uris); - if (start_online) - e_shell_set_line_status (shell, GNOME_Evolution_USER_ONLINE); - else - e_shell_set_line_status (shell, GNOME_Evolution_USER_OFFLINE); + return TRUE; +} + +static gboolean +shell_message_handle_close (EShell *shell, + UniqueMessageData *data) +{ + e_shell_quit (shell); - return E_SHELL_CONSTRUCT_RESULT_OK; + return TRUE; } -/** - * e_shell_new: - * @start_online: Whether to start in on-line mode or not. - * @construct_result_return: A pointer to an EShellConstructResult variable into - * which the result of the operation will be stored. - * - * Create a new EShell. - * - * Return value: - **/ -EShell * -e_shell_new (EShellStartupLineMode startup_line_mode, - EShellConstructResult *construct_result_return) +static UniqueResponse +shell_message_received (UniqueApp *app, + gint command, + UniqueMessageData *data, + guint time_) { - EShell *new; - EShellConstructResult construct_result; + EShell *shell = E_SHELL (app); - new = g_object_new (e_shell_get_type (), NULL); + switch (command) { + case UNIQUE_ACTIVATE: + break; /* use the default behavior */ - construct_result = e_shell_construct (new, E_SHELL_OAFIID, startup_line_mode); + case UNIQUE_NEW: + if (shell_message_handle_new (shell, data)) + return UNIQUE_RESPONSE_OK; + break; - if (construct_result != E_SHELL_CONSTRUCT_RESULT_OK) { - *construct_result_return = construct_result; - bonobo_object_unref (BONOBO_OBJECT (new)); - return NULL; - } + case UNIQUE_OPEN: + if (shell_message_handle_open (shell, data)) + return UNIQUE_RESPONSE_OK; + break; - *construct_result_return = E_SHELL_CONSTRUCT_RESULT_OK; - return new; -} + case UNIQUE_CLOSE: + if (shell_message_handle_close (shell, data)) + return UNIQUE_RESPONSE_OK; + break; -static int -remove_dir(const char *root, const char *path) -{ - GDir *dir; - const char *dname; - int res = -1; - char *new = NULL; - struct stat st; - - dir = g_dir_open(path, 0, NULL); - if (dir == NULL) - return -1; - - while ( (dname = g_dir_read_name(dir)) ) { - new = g_build_filename(path, dname, NULL); - if (g_stat(new, &st) == -1) - goto fail; - - /* make sure we're really removing something from evolution dir */ - g_return_val_if_fail (strlen(path) >= strlen(root) - && strncmp(root, path, strlen(root)) == 0, -1); - - if (S_ISDIR(st.st_mode)) { - if (remove_dir(root, new) == -1) - goto fail; - } else { - if (g_unlink(new) == -1) - goto fail; - } - g_free(new); - new = NULL; + default: + break; } - res = g_rmdir(path); -fail: - g_free(new); - g_dir_close(dir); - return res; + /* Chain up to parent's message_received() method. */ + return UNIQUE_APP_CLASS (parent_class)-> + message_received (app, command, data, time_); } -/** - * e_shell_attempt_upgrade: - * @shell: - * - * Upgrade config and components from the currently installed version. - * - * Return value: %TRUE If it works. If it fails the application will exit. - **/ -gboolean -e_shell_attempt_upgrade (EShell *shell) +static void +shell_class_init (EShellClass *class) { - GConfClient *gconf_client; - int major = 0, minor = 0, revision = 0; - int lmajor, lminor, lrevision; - int cmajor, cminor, crevision; - char *version_string, *last_version = NULL; - int done_upgrade = FALSE; - char *oldpath; - struct stat st; - ESEvent *ese; - - gconf_client = gconf_client_get_default(); - - oldpath = g_build_filename(g_get_home_dir(), "evolution", NULL); - - g_return_val_if_fail (sscanf(BASE_VERSION, "%u.%u", &cmajor, &cminor) == 2, TRUE); - crevision = atoi(UPGRADE_REVISION); - - detect_version (gconf_client, &major, &minor, &revision); - - if (!(cmajor > major - || (cmajor == major && cminor > minor) - || (cminor == minor && crevision > revision))) - goto check_old; - - /* if upgrading from < 1.5, we need to copy most data from ~/evolution to ~/.evolution */ - if (major == 1 && minor < 5) { - long size, space; - - size = e_fsutils_usage(oldpath); - space = e_fsutils_avail(g_get_home_dir()); - if (size != -1 && space != -1 && space < size) { - char *required = g_strdup_printf(_("%ld KB"), size); - char *have = g_strdup_printf(_("%ld KB"), space); - - e_error_run(NULL, "shell:upgrade-nospace", required, have, NULL); - g_free(required); - g_free(have); - _exit(0); - } - } + GObjectClass *object_class; + UniqueAppClass *unique_app_class; - if (!attempt_upgrade (shell, major, minor, revision)) - _exit(0); - - /* mark as upgraded */ - version_string = g_strdup_printf ("%s.%s", BASE_VERSION, UPGRADE_REVISION); - gconf_client_set_string (gconf_client, "/apps/evolution/version", version_string, NULL); - done_upgrade = TRUE; - -check_old: - /* if the last upgraded version was old, check for stuff to remove */ - if (done_upgrade - || (last_version = gconf_client_get_string (gconf_client, "/apps/evolution/last_version", NULL)) == NULL - || sscanf(last_version, "%d.%d.%d", &lmajor, &lminor, &lrevision) != 3) { - lmajor = major; - lminor = minor; - lrevision = revision; - } - g_free(last_version); - - if (lmajor == 1 && lminor < 5 - && g_stat(oldpath, &st) == 0 - && S_ISDIR(st.st_mode)) { - int res; - - last_version = g_strdup_printf("%d.%d.%d", lmajor, lminor, lrevision); - res = e_error_run(NULL, "shell:upgrade-remove-1-4", last_version, NULL); - g_free(last_version); - - switch (res) { - case GTK_RESPONSE_OK: /* 'delete' */ - if (e_error_run(NULL, "shell:upgrade-remove-1-4-confirm", NULL) == GTK_RESPONSE_OK) - remove_dir(oldpath, oldpath); - else - break; - /* falls through */ - case GTK_RESPONSE_ACCEPT: /* 'keep' */ - lmajor = cmajor; - lminor = cminor; - lrevision = crevision; - break; - default: - /* cancel - noop */ - break; - } - } else { - /* otherwise 'last version' is now the same as current */ - lmajor = cmajor; - lminor = cminor; - lrevision = crevision; - } + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EShellPrivate)); - last_version = g_strdup_printf("%d.%d.%d", lmajor, lminor, lrevision); - gconf_client_set_string (gconf_client, "/apps/evolution/last_version", last_version, NULL); - g_free(last_version); + object_class = G_OBJECT_CLASS (class); + object_class->set_property = shell_set_property; + object_class->get_property = shell_get_property; + object_class->dispose = shell_dispose; + object_class->finalize = shell_finalize; + object_class->constructed = shell_constructed; - g_free(oldpath); - g_object_unref (gconf_client); + unique_app_class = UNIQUE_APP_CLASS (class); + unique_app_class->message_received = shell_message_received; - /** @Event: Shell attempted upgrade - * @Id: upgrade.done - * @Target: ESMenuTargetState + /** + * EShell:network-available * - * This event is emitted whenever the shell successfully attempts an upgrade. + * Whether the network is available. + **/ + g_object_class_install_property ( + object_class, + PROP_NETWORK_AVAILABLE, + g_param_spec_boolean ( + "network-available", + _("Network Available"), + _("Whether the network is available"), + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + /** + * EShell:online * - */ - ese = es_event_peek(); - e_event_emit((EEvent *)ese, "upgrade.done", (EEventTarget *)es_event_target_new_upgrade(ese, cmajor, cminor, crevision)); - - return TRUE; + * Whether the shell is online. + **/ + g_object_class_install_property ( + object_class, + PROP_ONLINE, + g_param_spec_boolean ( + "online", + _("Online"), + _("Whether the shell is online"), + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + /** + * EShell:settings + * + * The #EShellSettings object stores application settings. + **/ + g_object_class_install_property ( + object_class, + PROP_SHELL_SETTINGS, + g_param_spec_object ( + "shell-settings", + _("Shell Settings"), + _("Application-wide settings"), + E_TYPE_SHELL_SETTINGS, + G_PARAM_READABLE)); + + /** + * EShell::event + * @shell: the #EShell which emitted the signal + * @event_data: data associated with the event + * + * This signal is used to broadcast custom events to the entire + * application. The nature of @event_data depends on the event + * being broadcast. The signal's detail denotes the event. + **/ + signals[EVENT] = g_signal_new ( + "event", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION, + 0, NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + /** + * EShell::handle-uri + * @shell: the #EShell which emitted the signal + * @uri: the URI to be handled + * + * Emitted when @shell receives a URI to be handled, usually by + * way of a command-line argument. An #EShellBackend should listen + * for this signal and try to handle the URI, usually by opening an + * editor window for the identified resource. + * + * Returns: %TRUE if the URI could be handled, %FALSE otherwise + **/ + signals[HANDLE_URI] = g_signal_new ( + "handle-uri", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + 0, g_signal_accumulator_true_handled, NULL, + e_marshal_BOOLEAN__STRING, + G_TYPE_BOOLEAN, 1, + G_TYPE_STRING); + + /** + * EShell::prepare-for-offline + * @shell: the #EShell which emitted the signal + * @activity: the #EActivity for offline preparations + * + * Emitted when the user elects to work offline. An #EShellBackend + * should listen for this signal and make preparations for working + * in offline mode. + * + * If preparations for working offline cannot immediately be + * completed (such as when synchronizing with a remote server), + * the #EShellBackend should reference the @activity until + * preparations are complete, and then unreference the @activity. + * This will delay Evolution from actually going to offline mode + * until all backends have unreferenced @activity. + **/ + signals[PREPARE_FOR_OFFLINE] = g_signal_new ( + "prepare-for-offline", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_ACTIVITY); + + /** + * EShell::prepare-for-online + * @shell: the #EShell which emitted the signal + * @activity: the #EActivity for offline preparations + * + * Emitted when the user elects to work online. An #EShellBackend + * should listen for this signal and make preparations for working + * in online mode. + * + * If preparations for working online cannot immediately be + * completed (such as when re-connecting to a remote server), the + * #EShellBackend should reference the @activity until preparations + * are complete, and then unreference the @activity. This will + * delay Evolution from actually going to online mode until all + * backends have unreferenced @activity. + **/ + signals[PREPARE_FOR_ONLINE] = g_signal_new ( + "prepare-for-online", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_ACTIVITY); + + /** + * EShell::send-receive + * @shell: the #EShell which emitted the signal + * @parent: a parent #GtkWindow + * + * Emitted when the user chooses the "Send / Receive" action. + * The parent window can be used for showing transient windows. + **/ + signals[SEND_RECEIVE] = g_signal_new ( + "send-receive", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_WINDOW); + + /** + * EShell::window-created + * @shell: the #EShell which emitted the signal + * @window: the newly created #GtkWindow + * + * Emitted when @shell begins watching a newly created window. + **/ + signals[WINDOW_CREATED] = g_signal_new ( + "window-created", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_WINDOW); + + /** + * EShell::window-destroyed + * @shell: the #EShell which emitted the signal + * + * Emitted when a watched is destroyed. + **/ + signals[WINDOW_DESTROYED] = g_signal_new ( + "window-destroyed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* Install some application-wide settings. */ + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "disable-application-handlers", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "disable-command-line", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "disable-printing", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "disable-print-setup", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_install_property ( + g_param_spec_boolean ( + "disable-save-to-disk", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE)); + + e_shell_settings_install_property ( + g_param_spec_string ( + "file-chooser-folder", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); } -/** - * e_shell_create_window: - * @shell: The shell for which to create a new window. - * @component_id: Id or alias of the component to display in the new window. - * @template_window: Window from which to copy the window settings (can be %NULL). - * - * Create a new window for @uri. - * - * Return value: The new window. - **/ -EShellWindow * -e_shell_create_window (EShell *shell, - const char *component_id, - EShellWindow *template_window) +static void +shell_init (EShell *shell) { - EShellWindow *window; + GHashTable *backends_by_name; + GHashTable *backends_by_scheme; - /* FIXME need to actually copy settings from template_window. */ + shell->priv = E_SHELL_GET_PRIVATE (shell); - g_return_val_if_fail (shell != NULL, NULL); - g_return_val_if_fail (E_IS_SHELL (shell), NULL); + backends_by_name = g_hash_table_new (g_str_hash, g_str_equal); + backends_by_scheme = g_hash_table_new (g_str_hash, g_str_equal); - window = E_SHELL_WINDOW (e_shell_window_new (shell, component_id)); + shell->priv->settings = g_object_new (E_TYPE_SHELL_SETTINGS, NULL); + shell->priv->gconf_client = gconf_client_get_default (); + shell->priv->preferences_window = e_preferences_window_new (); + shell->priv->backends_by_name = backends_by_name; + shell->priv->backends_by_scheme = backends_by_scheme; + shell->priv->safe_mode = e_file_lock_exists (); - g_signal_connect (window, "delete_event", G_CALLBACK (window_delete_event_cb), shell); - g_object_weak_ref (G_OBJECT (window), window_weak_notify, shell); - shell->priv->windows = g_list_prepend (shell->priv->windows, window); + g_object_ref_sink (shell->priv->preferences_window); - g_signal_emit (shell, signals[NEW_WINDOW_CREATED], 0, window); +#if NM_SUPPORT + e_shell_dbus_initialize (shell); +#endif - gtk_widget_show (GTK_WIDGET (window)); + shell_parse_debug_string (shell); - e_error_default_parent((GtkWindow *)window); + g_signal_connect ( + shell, "notify::online", + G_CALLBACK (shell_notify_online_cb), NULL); - set_interactive (shell, TRUE); + e_shell_settings_bind_to_gconf ( + shell->priv->settings, "disable-application-handlers", + "/desktop/gnome/lockdown/disable_application_handlers"); - if (!session_started) { - ESEvent *ese; + e_shell_settings_bind_to_gconf ( + shell->priv->settings, "disable-command-line", + "/desktop/gnome/lockdown/disable_command_line"); - session_started = TRUE; - ese = es_event_peek(); - e_event_emit((EEvent *)ese, "started.done", (EEventTarget *)es_event_target_new_shell(ese, shell)); - } - return window; -} + e_shell_settings_bind_to_gconf ( + shell->priv->settings, "disable-printing", + "/desktop/gnome/lockdown/disable_printing"); -gboolean -e_shell_request_close_window (EShell *shell, - EShellWindow *shell_window) -{ - g_return_val_if_fail (E_IS_SHELL (shell), FALSE); - g_return_val_if_fail (E_IS_SHELL_WINDOW (shell_window), FALSE); + e_shell_settings_bind_to_gconf ( + shell->priv->settings, "disable-print-setup", + "/desktop/gnome/lockdown/disable_print_setup"); - e_shell_save_settings (shell); + e_shell_settings_bind_to_gconf ( + shell->priv->settings, "disable-save-to-disk", + "/desktop/gnome/lockdown/disable_save_to_disk"); +} - if (g_list_length (shell->priv->windows) != 1) { - /* Not the last window. */ - return TRUE; +GType +e_shell_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + const GTypeInfo type_info = { + sizeof (EShellClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) shell_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EShell), + 0, /* n_preallocs */ + (GInstanceInitFunc) shell_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + UNIQUE_TYPE_APP, "EShell", &type_info, 0); } - return e_shell_quit(shell); + return type; } -#if 0 /* FIXME */ /** - * e_shell_peek_uri_schema_registry: - * @shell: An EShell object. + * e_shell_get_default: * - * Get the schema registry associated to @shell. + * Returns the #EShell created by <function>main()</function>. * - * Return value: A pointer to the EUriSchemaRegistry associated to @shell. + * 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 **/ -EUriSchemaRegistry * -e_shell_peek_uri_schema_registry (EShell *shell) +EShell * +e_shell_get_default (void) { - g_return_val_if_fail (E_IS_SHELL (shell), NULL); + /* Emit a warning if we call this too early. */ + g_return_val_if_fail (default_shell != NULL, NULL); - return shell->priv->uri_schema_registry; + return default_shell; } -#endif - /** - * e_shell_peek_component_registry: - * @shell: + * e_shell_get_shell_backends: + * @shell: an #EShell * - * Get the component registry associated to @shell. + * Returns a list of loaded #EShellBackend instances. The list is + * owned by @shell and should not be modified or freed. * - * Return value: + * Returns: a list of loaded #EShellBackend instances **/ -EComponentRegistry * -e_shell_peek_component_registry (EShell *shell) +GList * +e_shell_get_shell_backends (EShell *shell) { g_return_val_if_fail (E_IS_SHELL (shell), NULL); - return shell->priv->component_registry; + return shell->priv->loaded_backends; } - /** - * e_shell_save_settings: - * @shell: + * e_shell_get_canonical_name: + * @shell: an #EShell + * @name: the name or alias of an #EShellBackend * - * Save the settings for this shell. + * Returns the canonical name for the #EShellBackend whose name or alias + * is @name. * - * Return value: %TRUE if it worked, %FALSE otherwise. Even if %FALSE is - * returned, it is possible that at least part of the settings for the windows - * have been saved. + * Returns: the canonical #EShellBackend name **/ -gboolean -e_shell_save_settings (EShell *shell) +const gchar * +e_shell_get_canonical_name (EShell *shell, + const gchar *name) { - GConfClient *client; - gboolean is_offline; + EShellBackend *shell_backend; + + g_return_val_if_fail (E_IS_SHELL (shell), NULL); - is_offline = ( e_shell_get_line_status (shell) == E_SHELL_LINE_STATUS_OFFLINE || e_shell_get_line_status (shell) == E_SHELL_LINE_STATUS_FORCED_OFFLINE); + /* Handle NULL name arguments silently. */ + if (name == NULL) + return NULL; - client = gconf_client_get_default (); - gconf_client_set_bool (client, "/apps/evolution/shell/start_offline", is_offline, NULL); - g_object_unref (client); + shell_backend = e_shell_get_backend_by_name (shell, name); - return TRUE; + if (shell_backend == NULL) + return NULL; + + return E_SHELL_BACKEND_GET_CLASS (shell_backend)->name; } /** - * e_shell_close_all_windows: - * @shell: + * e_shell_get_backend_by_name: + * @shell: an #EShell + * @name: the name or alias of an #EShellBackend + * + * Returns the corresponding #EShellBackend for the given name or alias, + * or %NULL if @name is not recognized. * - * Destroy all the windows in @shell. + * Returns: the #EShellBackend named @name, or %NULL **/ -void -e_shell_close_all_windows (EShell *shell) +EShellBackend * +e_shell_get_backend_by_name (EShell *shell, + const gchar *name) { - EShellPrivate *priv; - GList *p, *pnext; - - g_return_if_fail (shell != NULL); - g_return_if_fail (E_IS_SHELL (shell)); + GHashTable *hash_table; - if (shell->priv->windows) - e_shell_save_settings (shell); + g_return_val_if_fail (E_IS_SHELL (shell), NULL); + g_return_val_if_fail (name != NULL, NULL); - priv = shell->priv; - for (p = priv->windows; p != NULL; p = pnext) { - pnext = p->next; + hash_table = shell->priv->backends_by_name; - /* Note that this will also remove the window from the list... Hence the - need for the pnext variable. */ - gtk_widget_destroy (GTK_WIDGET (p->data)); - } + return g_hash_table_lookup (hash_table, name); } /** - * e_shell_get_line_status: - * @shell: A pointer to an EShell object. + * e_shell_get_backend_by_scheme: + * @shell: an #EShell + * @scheme: a URI scheme * - * Get the line status for @shell. + * Returns the #EShellBackend that implements the given URI scheme, + * or %NULL if @scheme is not recognized. * - * Return value: The current line status for @shell. + * Returns: the #EShellBackend that implements @scheme, or %NULL **/ -EShellLineStatus -e_shell_get_line_status (EShell *shell) +EShellBackend * +e_shell_get_backend_by_scheme (EShell *shell, + const gchar *scheme) { - g_return_val_if_fail (shell != NULL, E_SHELL_LINE_STATUS_OFFLINE); - g_return_val_if_fail (E_IS_SHELL (shell), E_SHELL_LINE_STATUS_OFFLINE); - - return shell->priv->line_status; -} + GHashTable *hash_table; + g_return_val_if_fail (E_IS_SHELL (shell), NULL); + g_return_val_if_fail (scheme != NULL, NULL); -/* Offline/online handling. */ + hash_table = shell->priv->backends_by_scheme; -static void -set_line_status_finished(EShell *shell) + return g_hash_table_lookup (hash_table, scheme); +} +/** + * e_shell_get_shell_settings: + * @shell: an #EShell + * + * Returns the #EShellSettings instance for @shell. + * + * Returns: the #EShellSettings instance for @shell + **/ +EShellSettings * +e_shell_get_shell_settings (EShell *shell) { - EShellPrivate *priv = shell->priv; - ESEvent *ese; - - priv->line_status = priv->line_status_working; - - e_passwords_set_online (priv->line_status == E_SHELL_LINE_STATUS_ONLINE); - g_signal_emit (shell, signals[LINE_STATUS_CHANGED], 0, priv->line_status); + g_return_val_if_fail (E_IS_SHELL (shell), NULL); - /** @Event: Shell online state changed - * @Id: state.changed - * @Target: ESMenuTargetState - * - * This event is emitted whenever the shell online state changes. - * - * Only the online and offline states are emitted. - */ - ese = es_event_peek(); - e_event_emit((EEvent *)ese, "state.changed", (EEventTarget *)es_event_target_new_state(ese, priv->line_status == E_SHELL_LINE_STATUS_ONLINE)); + return shell->priv->settings; } -static void -set_line_status_complete(EvolutionListener *el, void *data) +/** + * e_shell_get_gconf_client: + * @shell: an #EShell + * + * Returns the default #GConfClient. This function is purely for + * convenience. The @shell owns the reference so you don't have to. + * + * Returns: the default #GConfClient + **/ +GConfClient * +e_shell_get_gconf_client (EShell *shell) { - EShell *shell = data; - EShellPrivate *priv = shell->priv; + g_return_val_if_fail (E_IS_SHELL (shell), NULL); - if (priv->line_status_pending > 0) { - priv->line_status_pending--; - if (priv->line_status_pending == 0) - set_line_status_finished(shell); - } + return shell->priv->gconf_client; } -void -e_shell_set_line_status (EShell *shell, - GNOME_Evolution_ShellState shell_state) +/** + * e_shell_create_shell_window: + * @shell: an #EShell + * @view_name: name of the initial shell view, or %NULL + * + * Creates a new #EShellWindow and emits the #EShell::window-created + * signal. Use this function instead of e_shell_window_new() so that + * @shell can track the window. + * + * Returns: a new #EShellWindow + **/ +GtkWidget * +e_shell_create_shell_window (EShell *shell, + const gchar *view_name) { - EShellPrivate *priv; - GSList *component_infos; - GSList *p; - CORBA_Environment ev; - GConfClient *client; - gboolean status; - gboolean forced = FALSE; - - priv = shell->priv; - - if (shell_state == GNOME_Evolution_FORCED_OFFLINE || shell_state == GNOME_Evolution_USER_OFFLINE) { - status = FALSE; - if (shell_state == GNOME_Evolution_FORCED_OFFLINE) - forced = TRUE; - } else - status = TRUE; + GtkWidget *shell_window; + UniqueMessageData *data; + UniqueApp *app; - if ((status && priv->line_status == E_SHELL_LINE_STATUS_ONLINE) - || (!status && priv->line_status == E_SHELL_LINE_STATUS_OFFLINE && !forced)) - return; - - /* we use 'going offline' to mean 'changing status' now */ - priv->line_status = E_SHELL_LINE_STATUS_GOING_OFFLINE; - g_signal_emit (shell, signals[LINE_STATUS_CHANGED], 0, priv->line_status); + g_return_val_if_fail (E_IS_SHELL (shell), NULL); - client = gconf_client_get_default (); - gconf_client_set_bool (client, "/apps/evolution/shell/start_offline", !status, NULL); - g_object_unref (client); + app = UNIQUE_APP (shell); - priv->line_status_working = status?E_SHELL_LINE_STATUS_ONLINE: forced?E_SHELL_LINE_STATUS_FORCED_OFFLINE:E_SHELL_LINE_STATUS_OFFLINE; - /* we start at 2: setLineStatus could recursively call back, we therefore - `need to not complete till we're really complete */ - priv->line_status_pending += 2; + if (unique_app_is_running (app)) + goto unique; - component_infos = e_component_registry_peek_list (priv->component_registry); - for (p = component_infos; p != NULL; p = p->next) { - EComponentInfo *info = p->data; + view_name = e_shell_get_canonical_name (shell, view_name); - CORBA_exception_init (&ev); + /* EShellWindow initializes its active view from a GConf key, + * so set the key ahead of time to control the intial view. */ + if (view_name != NULL) { + GConfClient *client; + const gchar *key; + GError *error = NULL; - GNOME_Evolution_Component_setLineStatus(info->iface, shell_state, bonobo_object_corba_objref((BonoboObject *)priv->line_status_listener), &ev); - if (ev._major == CORBA_NO_EXCEPTION) - priv->line_status_pending++; + client = e_shell_get_gconf_client (shell); + key = "/apps/evolution/shell/view_defaults/component_id"; + gconf_client_set_string (client, key, view_name, &error); - CORBA_exception_free (&ev); + if (error != NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } } - priv->line_status_pending -= 2; - if (priv->line_status_pending == 0) - set_line_status_finished(shell); -} + shell_window = e_shell_window_new (shell, shell->priv->safe_mode); -gboolean -e_shell_get_crash_recovery (EShell *shell) -{ - g_return_val_if_fail (E_IS_SHELL (shell), FALSE); + gtk_widget_show (shell_window); - return shell->priv->crash_recovery; -} + return shell_window; -void -e_shell_set_crash_recovery (EShell *shell, - gboolean crash_recovery) -{ - g_return_if_fail (E_IS_SHELL (shell)); +unique: /* Send a message to the other Evolution process. */ - shell->priv->crash_recovery = crash_recovery; + /* XXX Do something with UniqueResponse? */ + + if (view_name != NULL) { + data = unique_message_data_new (); + unique_message_data_set_text (data, view_name, -1); + unique_app_send_message (app, UNIQUE_NEW, data); + unique_message_data_free (data); + } else + unique_app_send_message (app, UNIQUE_ACTIVATE, NULL); + + return NULL; } -void -e_shell_send_receive (EShell *shell) +/** + * e_shell_handle_uris: + * @shell: an #EShell + * @uris: %NULL-terminated list of URIs + * + * Emits the #EShell::handle-uri signal for each URI. + * + * Returns: the number of URIs successfully handled + **/ +guint +e_shell_handle_uris (EShell *shell, + gchar **uris) { - GSList *component_list; - GSList *p; + UniqueApp *app; + UniqueMessageData *data; + guint n_handled = 0; + gint ii; - g_return_if_fail (E_IS_SHELL (shell)); + g_return_val_if_fail (E_IS_SHELL (shell), FALSE); + g_return_val_if_fail (uris != NULL, FALSE); - component_list = e_component_registry_peek_list (shell->priv->component_registry); + app = UNIQUE_APP (shell); - for (p = component_list; p != NULL; p = p->next) { - EComponentInfo *info = p->data; - CORBA_Environment ev; + if (unique_app_is_running (app)) + goto unique; - CORBA_exception_init (&ev); + for (ii = 0; uris[ii] != NULL; ii++) { + gboolean handled; - GNOME_Evolution_Component_sendAndReceive (info->iface, &ev); + g_signal_emit ( + shell, signals[HANDLE_URI], + 0, uris[ii], &handled); + n_handled += handled ? 1 : 0; + } - /* Ignore errors, the components can decide to not implement - this interface. */ + return n_handled; - CORBA_exception_free (&ev); - } -} +unique: /* Send a message to the other Evolution process. */ + + /* XXX Do something with UniqueResponse? */ + + data = unique_message_data_new (); + 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_show_settings (EShell *shell, - const char *type, - EShellWindow *shell_window) +e_shell_watch_window (EShell *shell, + GtkWindow *window) { - EShellPrivate *priv; + GList *list; - g_return_if_fail (shell != NULL); g_return_if_fail (E_IS_SHELL (shell)); + g_return_if_fail (GTK_IS_WINDOW (window)); - priv = shell->priv; + list = shell->priv->watched_windows; - if (priv->settings_dialog.widget != NULL) { - gdk_window_show (priv->settings_dialog.widget->window); - gtk_widget_grab_focus (priv->settings_dialog.widget); + /* Ignore duplicates. */ + if (g_list_find (list, window) != NULL) return; - } - priv->settings_dialog.widget = e_shell_settings_dialog_new (); + list = g_list_prepend (list, window); + shell->priv->watched_windows = list; - if (type != NULL) - e_shell_settings_dialog_show_type (E_SHELL_SETTINGS_DIALOG (priv->settings_dialog.widget), type); + unique_app_watch_window (UNIQUE_APP (shell), window); - g_object_add_weak_pointer (G_OBJECT (priv->settings_dialog.widget), &priv->settings_dialog.pointer); + g_signal_connect_swapped ( + window, "delete-event", + G_CALLBACK (shell_window_delete_event_cb), shell); - gtk_window_set_transient_for (GTK_WINDOW (priv->settings_dialog.widget), GTK_WINDOW (shell_window)); - gtk_widget_show (priv->settings_dialog.widget); -} + 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); -const char * -e_shell_construct_result_to_string (EShellConstructResult result) -{ - switch (result) { - case E_SHELL_CONSTRUCT_RESULT_OK: - return _("OK"); - case E_SHELL_CONSTRUCT_RESULT_INVALIDARG: - return _("Invalid arguments"); - case E_SHELL_CONSTRUCT_RESULT_CANNOTREGISTER: - return _("Cannot register on OAF"); - case E_SHELL_CONSTRUCT_RESULT_NOCONFIGDB: - return _("Configuration Database not found"); - case E_SHELL_CONSTRUCT_RESULT_GENERICERROR: - return _("Generic error"); - default: - return _("Unknown error"); - } + g_signal_emit (shell, signals[WINDOW_CREATED], 0, window); } -/* timeout handler, so returns TRUE if we can't quit yet */ -static gboolean -es_run_quit(EShell *shell) +/** + * 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) { - EShellPrivate *priv; - GSList *component_infos; - GSList *sp; - CORBA_boolean done_quit; - - g_return_val_if_fail (E_IS_SHELL (shell), FALSE); - - priv = shell->priv; - priv->preparing_to_quit = TRUE; - - component_infos = e_component_registry_peek_list (priv->component_registry); - done_quit = TRUE; - for (sp = component_infos; sp != NULL; sp = sp->next) { - EComponentInfo *info = sp->data; - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - done_quit = GNOME_Evolution_Component_quit(info->iface, &ev); - if (BONOBO_EX (&ev)) { - /* The component might not implement the interface, in which case we assume we can quit. */ - done_quit = TRUE; - } - - CORBA_exception_free (&ev); + g_return_val_if_fail (E_IS_SHELL (shell), NULL); - if (!done_quit) - break; - } + return shell->priv->watched_windows; +} - if (done_quit) { - if (priv->quit_timeout) { - g_source_remove(priv->quit_timeout); - priv->quit_timeout = 0; - } - e_shell_close_all_windows(shell); - } else if (priv->quit_timeout == 0) { - priv->quit_timeout = g_timeout_add(500, (GSourceFunc)es_run_quit, shell); - } +/** + * e_shell_send_receive: + * @shell: an #EShell + * @parent: the parent #GtkWindow + * + * Emits the #EShell::send-receive signal. + **/ +void +e_shell_send_receive (EShell *shell, + GtkWindow *parent) +{ + g_return_if_fail (E_IS_SHELL (shell)); + g_return_if_fail (GTK_IS_WINDOW (parent)); - return !done_quit; + g_signal_emit (shell, signals[SEND_RECEIVE], 0, parent); } +/** + * e_shell_get_network_available: + * @shell: an #EShell + * + * Returns %TRUE if a network is available. + * + * Returns: %TRUE if a network is available + **/ gboolean -e_shell_can_quit (EShell *shell) +e_shell_get_network_available (EShell *shell) { - EShellPrivate *priv; - GSList *component_infos; - GSList *sp; - CORBA_boolean can_quit; - g_return_val_if_fail (E_IS_SHELL (shell), FALSE); - priv = shell->priv; - - if (priv->preparing_to_quit) - return FALSE; - - component_infos = e_component_registry_peek_list (priv->component_registry); - can_quit = TRUE; - for (sp = component_infos; sp != NULL; sp = sp->next) { - EComponentInfo *info = sp->data; - CORBA_Environment ev; - - CORBA_exception_init (&ev); + return shell->priv->network_available; +} - can_quit = GNOME_Evolution_Component_requestQuit (info->iface, &ev); - if (BONOBO_EX (&ev)) { - /* The component might not implement the interface, in which case we assume we can quit. */ - can_quit = TRUE; - } +/** + * e_shell_set_network_available: + * @shell: an #EShell + * @network_available: whether a network is available + * + * Sets whether a network is available. This is usually called in + * response to a status change signal from NetworkManager. If the + * network becomes unavailable while #EShell:online is %TRUE, the + * @shell will force #EShell:online to %FALSE until the network + * becomes available again. + **/ +void +e_shell_set_network_available (EShell *shell, + gboolean network_available) +{ + g_return_if_fail (E_IS_SHELL (shell)); - CORBA_exception_free (&ev); + if (network_available == shell->priv->network_available) + return; - if (! can_quit) - break; + shell->priv->network_available = network_available; + g_object_notify (G_OBJECT (shell), "network-available"); + + /* If we're being forced offline, perhaps due to a network outage, + * reconnect automatically when the network becomes available. */ + if (!network_available && shell->priv->online) { + g_message ("Network disconnected. Forced offline."); + e_shell_set_online (shell, FALSE); + shell->priv->auto_reconnect = TRUE; + } else if (network_available && shell->priv->auto_reconnect) { + g_message ("Connection established. Going online."); + e_shell_set_online (shell, TRUE); + shell->priv->auto_reconnect = FALSE; } - - return can_quit; } +/** + * e_shell_get_online: + * @shell: an #EShell + * + * Returns %TRUE if Evolution is online, %FALSE if Evolution is offline. + * Evolution may be offline because the user elected to work offline, or + * because the network has become unavailable. + * + * Returns: %TRUE if Evolution is online + **/ gboolean -e_shell_do_quit (EShell *shell) +e_shell_get_online (EShell *shell) { - EShellPrivate *priv; - GList *p; - gboolean can_quit; - g_return_val_if_fail (E_IS_SHELL (shell), FALSE); - priv = shell->priv; - - if (priv->preparing_to_quit) - return FALSE; - - for (p = shell->priv->windows; p != NULL; p = p->next) { - gtk_widget_set_sensitive (GTK_WIDGET (p->data), FALSE); - - if (p == shell->priv->windows) - e_shell_window_save_defaults (p->data); - } - - can_quit = !es_run_quit (shell); - - /* Mark a safe quit by destroying the lock. */ - e_file_lock_destroy (); - - return can_quit; + return shell->priv->online; } -gboolean -e_shell_quit (EShell *shell) +/** + * e_shell_set_online: + * @shell: an #EShell + * @online: %TRUE to go online, %FALSE to go offline + * + * Asynchronously places Evolution in online or offline mode. + **/ +void +e_shell_set_online (EShell *shell, + gboolean online) { - return e_shell_can_quit (shell) && e_shell_do_quit (shell); + g_return_if_fail (E_IS_SHELL (shell)); + + if (online == shell->priv->online) + return; + + if (online) + shell_prepare_for_online (shell); + else + shell_prepare_for_offline (shell); } /** - * gboolean (*EMainShellFunc) (EShell *shell, EShellWindow *window, gpointer user_data); - * Function used in @ref e_shell_foreach_shell_window. - * @param shell Pointer to EShell. - * @param window Pointer to EShellWindow. - * @param user_data User's data passed to @ref main_shell_foreach_shell_window. - * @return TRUE if need to go to next window, FALSE when stop looking for next window. + * e_shell_get_preferences_window: + * @shell: an #EShell + * + * Returns the Evolution Preferences window. + * + * Returns: the preferences window **/ +GtkWidget * +e_shell_get_preferences_window (EShell *shell) +{ + g_return_val_if_fail (E_IS_SHELL (shell), NULL); + + return shell->priv->preferences_window; +} /** - * e_shell_foreach_shell_window - * This will call function callback for all known EShellWindow of main shell. - * When there is no shell active, then this will do nothing. - * @param shell EShell instance. - * @param func Function to be called. - * @param user_data User data to pass to func. + * e_shell_event: + * @shell: an #EShell + * @event_name: the name of the event + * @event_data: data associated with the event + * + * The #EShell::event signal acts as a cheap mechanism for broadcasting + * events to the rest of the application, such as new mail arriving. The + * @event_name is used as the signal detail, and @event_data may point to + * an object or data structure associated with the event. **/ void -e_shell_foreach_shell_window (EShell *shell, EMainShellFunc func, gpointer user_data) +e_shell_event (EShell *shell, + const gchar *event_name, + gpointer event_data) { - EShellPrivate *priv; - GList *p; - - if (!shell) - return; + GQuark detail; - priv = shell->priv; + g_return_if_fail (E_IS_SHELL (shell)); + g_return_if_fail (event_name != NULL); - for (p = priv->windows; p != NULL; p = p->next) { - EShellWindow *window; + detail = g_quark_from_string (event_name); + g_signal_emit (shell, signals[EVENT], detail, event_data); +} - window = E_SHELL_WINDOW (p->data); +gboolean +e_shell_is_busy (EShell *shell) +{ + /* FIXME */ + return FALSE; +} - if (window && !func (shell, window, user_data)) - break; - } +gboolean +e_shell_do_quit (EShell *shell) +{ + /* FIXME */ + return TRUE; } -BONOBO_TYPE_FUNC_FULL (EShell, GNOME_Evolution_Shell, PARENT_TYPE, e_shell) +gboolean +e_shell_quit (EShell *shell) +{ + /* FIXME */ + return TRUE; +} |