/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* e-shell.c * * Copyright (C) 2000, 2001 Ximian, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Ettore Perazzoli */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include /* (For the displayName stuff.) */ #include #include #include #include #include #include #include "Evolution.h" #include "e-activity-handler.h" #include "e-component-registry.h" #include "e-corba-shortcuts.h" #include "e-corba-storage-registry.h" #include "e-folder-type-registry.h" #include "e-local-storage.h" #include "e-shell-constants.h" #include "e-shell-folder-selection-dialog.h" #include "e-shell-offline-handler.h" #include "e-shell-startup-wizard.h" #include "e-shell-view.h" #include "e-shortcuts.h" #include "e-storage-set.h" #include "e-splash.h" #include "e-summary-storage.h" #include "e-uri-schema-registry.h" #include "evolution-storage-set-view-factory.h" #include "e-shell.h" #include "importer/intelligent.h" #define PARENT_TYPE bonobo_x_object_get_type () static BonoboXObjectClass *parent_class = NULL; struct _EShellPrivate { /* IID for registering the object on OAF. */ char *iid; char *local_directory; GList *views; EStorageSet *storage_set; ELocalStorage *local_storage; ESummaryStorage *summary_storage; EShortcuts *shortcuts; EFolderTypeRegistry *folder_type_registry; EUriSchemaRegistry *uri_schema_registry; EComponentRegistry *component_registry; EShellUserCreatableItemsHandler *user_creatable_items_handler; /* ::StorageRegistry interface handler. */ ECorbaStorageRegistry *corba_storage_registry; /* */ /* ::Activity interface handler. */ EActivityHandler *activity_handler; /* */ /* ::Shortcuts interface handler. */ ECorbaShortcuts *corba_shortcuts; /* */ /* This object handles going off-line. If the pointer is not NULL, it means we have a going-off-line process in progress. */ EShellOfflineHandler *offline_handler; /* Names for the types of the folders that have maybe crashed. */ GList *crash_type_names; /* char * */ /* Line status. */ EShellLineStatus line_status; /* Configuration Database */ Bonobo_ConfigDatabase db; /* 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. */ gboolean is_initialized; }; /* Constants. */ /* FIXME: We need a component repository instead. */ #define SHORTCUTS_FILE_NAME "shortcuts.xml" #define LOCAL_STORAGE_DIRECTORY "local" enum { NO_VIEWS_LEFT, LINE_STATUS_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /* Callback for the folder selection dialog. */ static void folder_selection_dialog_cancelled_cb (EShellFolderSelectionDialog *folder_selection_dialog, void *data) { GNOME_Evolution_FolderSelectionListener listener; CORBA_Environment ev; listener = gtk_object_get_data (GTK_OBJECT (folder_selection_dialog), "corba_listener"); CORBA_exception_init (&ev); GNOME_Evolution_FolderSelectionListener_notifyCanceled (listener, &ev); CORBA_exception_free (&ev); gtk_widget_destroy (GTK_WIDGET (folder_selection_dialog)); } static void folder_selection_dialog_folder_selected_cb (EShellFolderSelectionDialog *folder_selection_dialog, const char *path, void *data) { CORBA_Environment ev; EShell *shell; GNOME_Evolution_FolderSelectionListener listener; EStorageSet *storage_set; EFolder *folder; char *uri; const char *physical_uri; shell = E_SHELL (data); listener = gtk_object_get_data (GTK_OBJECT (folder_selection_dialog), "corba_listener"); CORBA_exception_init (&ev); storage_set = e_shell_get_storage_set (shell); folder = e_storage_set_get_folder (storage_set, path); uri = g_strconcat (E_SHELL_URI_PREFIX, path, NULL); if (folder == NULL) physical_uri = ""; else physical_uri = e_folder_get_physical_uri (folder); GNOME_Evolution_FolderSelectionListener_notifySelected (listener, uri, physical_uri, &ev); g_free (uri); CORBA_exception_free (&ev); gtk_widget_destroy (GTK_WIDGET (folder_selection_dialog)); } /* CORBA interface implementation. */ static CORBA_char * impl_Shell__get_displayName (PortableServer_Servant servant, CORBA_Environment *ev) { char *display_string; CORBA_char *retval; display_string = DisplayString (gdk_display); if (display_string == NULL) return CORBA_string_dup (""); retval = CORBA_string_dup (display_string); XFree (display_string); return retval; } static GNOME_Evolution_ShellComponent impl_Shell_getComponentByType (PortableServer_Servant servant, const CORBA_char *type, CORBA_Environment *ev) { BonoboObject *bonobo_object; EvolutionShellComponentClient *handler; EFolderTypeRegistry *folder_type_registry; GNOME_Evolution_ShellComponent corba_component; EShell *shell; bonobo_object = bonobo_object_from_servant (servant); shell = E_SHELL (bonobo_object); folder_type_registry = shell->priv->folder_type_registry; handler = e_folder_type_registry_get_handler_for_type (folder_type_registry, type); if (handler == NULL) { CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_NotFound, NULL); return CORBA_OBJECT_NIL; } corba_component = bonobo_object_corba_objref (BONOBO_OBJECT (handler)); Bonobo_Unknown_ref (corba_component, ev); return CORBA_Object_duplicate (corba_component, ev); } static GNOME_Evolution_ShellView impl_Shell_createNewView (PortableServer_Servant servant, const CORBA_char *uri, CORBA_Environment *ev) { BonoboObject *bonobo_object; EShell *shell; EShellView *shell_view; GNOME_Evolution_ShellView shell_view_interface; bonobo_object = bonobo_object_from_servant (servant); shell = E_SHELL (bonobo_object); if (strncmp (uri, E_SHELL_URI_PREFIX, E_SHELL_URI_PREFIX_LEN) != 0) { CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_UnsupportedSchema, NULL); return CORBA_OBJECT_NIL; } shell_view = e_shell_create_view (shell, uri); if (shell_view == NULL) { CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_NotFound, NULL); return CORBA_OBJECT_NIL; } gtk_widget_show (GTK_WIDGET (shell_view)); shell_view_interface = e_shell_view_get_corba_interface (shell_view); if (shell_view_interface == CORBA_OBJECT_NIL) { CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_InternalError, NULL); return CORBA_OBJECT_NIL; } Bonobo_Unknown_ref (shell_view_interface, ev); return CORBA_Object_duplicate ((CORBA_Object) shell_view_interface, ev); } static void impl_Shell_handleURI (PortableServer_Servant servant, const CORBA_char *uri, CORBA_Environment *ev) { EvolutionShellComponentClient *schema_handler; EShell *shell; EShellPrivate *priv; const char *colon_p; char *schema; shell = E_SHELL (bonobo_object_from_servant (servant)); priv = shell->priv; if (strncmp (uri, E_SHELL_URI_PREFIX, E_SHELL_URI_PREFIX_LEN) == 0) { GNOME_Evolution_Shell_createNewView (bonobo_object_corba_objref (BONOBO_OBJECT (shell)), uri, ev); return; } /* Extract the schema. */ colon_p = strchr (uri, ':'); if (colon_p == NULL || colon_p == uri) { CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_InvalidURI, NULL); return; } schema = g_strndup (uri, colon_p - uri); schema_handler = e_uri_schema_registry_get_handler_for_schema (priv->uri_schema_registry, schema); g_free (schema); if (schema_handler == NULL) { CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_UnsupportedSchema, NULL); return; } if (! evolution_shell_component_client_handle_external_uri (schema_handler, uri)) { /* FIXME: Just a wild guess here. */ CORBA_exception_set (ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Shell_NotFound, NULL); return; } } static void corba_listener_destroy_notify (void *data) { CORBA_Environment ev; GNOME_Evolution_FolderSelectionListener listener_interface; listener_interface = (GNOME_Evolution_FolderSelectionListener) data; CORBA_exception_init (&ev); CORBA_Object_release (listener_interface, &ev); CORBA_exception_free (&ev); } static void impl_Shell_selectUserFolder (PortableServer_Servant servant, const GNOME_Evolution_FolderSelectionListener listener, const CORBA_char *title, const CORBA_char *default_folder, const GNOME_Evolution_Shell_FolderTypeNameList *corba_allowed_type_names, CORBA_Environment *ev) { GtkWidget *folder_selection_dialog; BonoboObject *bonobo_object; GNOME_Evolution_FolderSelectionListener listener_duplicate; EShell *shell; const char **allowed_type_names; int i; bonobo_object = bonobo_object_from_servant (servant); shell = E_SHELL (bonobo_object); allowed_type_names = alloca (sizeof (allowed_type_names[0]) * (corba_allowed_type_names->_length + 1)); for (i = 0; i < corba_allowed_type_names->_length; i++) allowed_type_names[i] = corba_allowed_type_names->_buffer[i]; allowed_type_names[corba_allowed_type_names->_length] = NULL; /* CORBA doesn't allow you to pass a NULL pointer. */ if (!*default_folder) default_folder = NULL; folder_selection_dialog = e_shell_folder_selection_dialog_new (shell, title, NULL, default_folder, allowed_type_names); listener_duplicate = CORBA_Object_duplicate (listener, ev); gtk_object_set_data_full (GTK_OBJECT (folder_selection_dialog), "corba_listener", listener_duplicate, corba_listener_destroy_notify); gtk_signal_connect (GTK_OBJECT (folder_selection_dialog), "folder_selected", GTK_SIGNAL_FUNC (folder_selection_dialog_folder_selected_cb), shell); gtk_signal_connect (GTK_OBJECT (folder_selection_dialog), "cancelled", GTK_SIGNAL_FUNC (folder_selection_dialog_cancelled_cb), shell); gtk_widget_show (folder_selection_dialog); } static GNOME_Evolution_Storage impl_Shell_getLocalStorage (PortableServer_Servant servant, CORBA_Environment *ev) { BonoboObject *bonobo_object; GNOME_Evolution_Storage local_storage_interface; EShell *shell; EShellPrivate *priv; bonobo_object = bonobo_object_from_servant (servant); shell = E_SHELL (bonobo_object); priv = shell->priv; local_storage_interface = e_local_storage_get_corba_interface (priv->local_storage); bonobo_object_dup_ref (local_storage_interface, ev); return local_storage_interface; } static Bonobo_Control impl_Shell_createStorageSetView (PortableServer_Servant servant, CORBA_Environment *ev) { BonoboObject *bonobo_object; EShell *shell; BonoboControl *control; bonobo_object = bonobo_object_from_servant (servant); shell = E_SHELL (bonobo_object); control = evolution_storage_set_view_factory_new_view (shell); return bonobo_object_corba_objref (BONOBO_OBJECT (control)); } static void impl_Shell_setLineStatus (PortableServer_Servant servant, CORBA_boolean online, CORBA_Environment *ev) { BonoboObject *bonobo_object; EShell *shell; bonobo_object = bonobo_object_from_servant (servant); shell = E_SHELL (bonobo_object); if (online) e_shell_go_online (shell, NULL); else e_shell_go_offline (shell, NULL); } /* Set up the ::Activity interface. */ static void setup_activity_interface (EShell *shell) { EActivityHandler *activity_handler; EShellPrivate *priv; priv = shell->priv; activity_handler = e_activity_handler_new (); bonobo_object_add_interface (BONOBO_OBJECT (shell), BONOBO_OBJECT (activity_handler)); priv->activity_handler = activity_handler; } /* Set up the ::Shortcuts interface. */ static void setup_shortcuts_interface (EShell *shell) { ECorbaShortcuts *corba_shortcuts; EShellPrivate *priv; priv = shell->priv; g_assert (priv->shortcuts != NULL); corba_shortcuts = e_corba_shortcuts_new (priv->shortcuts); bonobo_object_add_interface (BONOBO_OBJECT (shell), BONOBO_OBJECT (corba_shortcuts)); priv->corba_shortcuts = corba_shortcuts; } /* Initialization of the storages. */ static gboolean setup_corba_storages (EShell *shell) { EShellPrivate *priv; ECorbaStorageRegistry *corba_storage_registry; priv = shell->priv; g_assert (priv->storage_set != NULL); corba_storage_registry = e_corba_storage_registry_new (priv->storage_set); if (corba_storage_registry == NULL) return FALSE; bonobo_object_add_interface (BONOBO_OBJECT (shell), BONOBO_OBJECT (corba_storage_registry)); priv->corba_storage_registry = corba_storage_registry; return TRUE; } static gboolean setup_local_storage (EShell *shell) { EStorage *local_storage; EShellPrivate *priv; gchar *local_storage_path; priv = shell->priv; g_assert (priv->folder_type_registry != NULL); g_assert (priv->local_storage == NULL); local_storage_path = g_concat_dir_and_file (priv->local_directory, LOCAL_STORAGE_DIRECTORY); local_storage = e_local_storage_open (priv->folder_type_registry, local_storage_path); if (local_storage == NULL) { g_warning (_("Cannot set up local storage -- %s"), local_storage_path); g_free (local_storage_path); return FALSE; } g_free (local_storage_path); e_storage_set_add_storage (priv->storage_set, local_storage); priv->local_storage = E_LOCAL_STORAGE (local_storage); priv->summary_storage = E_SUMMARY_STORAGE (e_summary_storage_new ()); e_storage_set_add_storage (priv->storage_set, E_STORAGE (priv->summary_storage)); return TRUE; } /* Initialization of the components. */ static char * get_icon_path_for_component_info (const OAF_ServerInfo *info) { OAF_Property *property; const char *shell_component_icon_value; /* FIXME: liboaf is not const-safe. */ property = oaf_server_info_prop_find ((OAF_ServerInfo *) info, "evolution:shell-component-icon"); if (property == NULL || property->v._d != OAF_P_STRING) return gnome_pixmap_file ("gnome-question.png"); shell_component_icon_value = property->v._u.value_string; if (g_path_is_absolute (shell_component_icon_value)) return g_strdup (shell_component_icon_value); else return g_concat_dir_and_file (EVOLUTION_IMAGES, shell_component_icon_value); } static void setup_components (EShell *shell, ESplash *splash) { EShellPrivate *priv; OAF_ServerInfoList *info_list; CORBA_Environment ev; int i; CORBA_exception_init (&ev); priv = shell->priv; priv->component_registry = e_component_registry_new (shell); info_list = oaf_query ("repo_ids.has ('IDL:GNOME/Evolution/ShellComponent:1.0')", NULL, &ev); if (ev._major != CORBA_NO_EXCEPTION) g_error ("Eeek! Cannot perform OAF query for Evolution components."); if (info_list->_length == 0) g_warning ("No Evolution components installed."); for (i = 0; i < info_list->_length; i++) { const OAF_ServerInfo *info; GdkPixbuf *icon_pixbuf; char *icon_path; info = info_list->_buffer + i; icon_path = get_icon_path_for_component_info (info); icon_pixbuf = gdk_pixbuf_new_from_file (icon_path); if (splash != NULL) e_splash_add_icon (splash, icon_pixbuf); gdk_pixbuf_unref (icon_pixbuf); g_free (icon_path); } while (gtk_events_pending ()) gtk_main_iteration (); for (i = 0; i < info_list->_length; i++) { const OAF_ServerInfo *info; info = info_list->_buffer + i; if (! e_component_registry_register_component (priv->component_registry, info->iid)) { g_warning ("Cannot activate Evolution component -- %s", info->iid); } else { e_shell_user_creatable_items_handler_add_component (priv->user_creatable_items_handler, e_component_registry_get_component_by_id (priv->component_registry, info->iid)); } if (splash != NULL) e_splash_set_icon_highlight (splash, i, TRUE); while (gtk_events_pending ()) gtk_main_iteration (); } CORBA_free (info_list); CORBA_exception_free (&ev); } /* FIXME what if anything fails here? */ static void set_owner_on_components (EShell *shell) { GNOME_Evolution_Shell corba_shell; EShellPrivate *priv; const char *local_directory; GList *id_list; GList *p; priv = shell->priv; local_directory = e_shell_get_local_directory (shell); corba_shell = bonobo_object_corba_objref (BONOBO_OBJECT (shell)); id_list = e_component_registry_get_id_list (priv->component_registry); for (p = id_list; p != NULL; p = p->next) { EvolutionShellComponentClient *component_client; const char *id; id = (const char *) p->data; component_client = e_component_registry_get_component_by_id (priv->component_registry, id); evolution_shell_component_client_set_owner (component_client, corba_shell, local_directory); } e_free_string_list (id_list); } /* EShellView destruction callback. */ static int view_delete_event_cb (GtkWidget *widget, GdkEventAny *ev, void *data) { EShell *shell; g_assert (E_IS_SHELL_VIEW (widget)); shell = E_SHELL (data); e_shell_save_settings (shell); /* Destroy it */ return FALSE; } static void view_destroy_cb (GtkObject *object, void *data) { EShell *shell; int num_views; g_assert (E_IS_SHELL_VIEW (object)); shell = E_SHELL (data); num_views = g_list_length (shell->priv->views); /* If this is our last view, save settings now because in the callback for no_views_left shell->priv->views will be NULL and settings won't be saved because of that */ if (num_views - 1 == 0) e_shell_save_settings (shell); shell->priv->views = g_list_remove (shell->priv->views, object); if (shell->priv->views == NULL) { bonobo_object_ref (BONOBO_OBJECT (shell)); gtk_signal_emit (GTK_OBJECT (shell), signals [NO_VIEWS_LEFT]); bonobo_object_unref (BONOBO_OBJECT (shell)); } } /* GtkObject methods. */ static void destroy (GtkObject *object) { EShell *shell; EShellPrivate *priv; GList *p; shell = E_SHELL (object); priv = shell->priv; if (shell->priv->db != CORBA_OBJECT_NIL) { bonobo_object_release_unref (shell->priv->db, NULL); shell->priv->db = CORBA_OBJECT_NIL; } if (priv->iid != NULL) oaf_active_server_unregister (priv->iid, bonobo_object_corba_objref (BONOBO_OBJECT (shell))); g_free (priv->local_directory); if (priv->storage_set != NULL) gtk_object_unref (GTK_OBJECT (priv->storage_set)); if (priv->local_storage != NULL) gtk_object_unref (GTK_OBJECT (priv->local_storage)); if (priv->summary_storage != NULL) gtk_object_unref (GTK_OBJECT (priv->summary_storage)); if (priv->shortcuts != NULL) gtk_object_unref (GTK_OBJECT (priv->shortcuts)); if (priv->folder_type_registry != NULL) gtk_object_unref (GTK_OBJECT (priv->folder_type_registry)); if (priv->uri_schema_registry != NULL) gtk_object_unref (GTK_OBJECT (priv->uri_schema_registry)); if (priv->component_registry != NULL) gtk_object_unref (GTK_OBJECT (priv->component_registry)); if (priv->user_creatable_items_handler != NULL) gtk_object_unref (GTK_OBJECT (priv->user_creatable_items_handler)); for (p = priv->views; p != NULL; p = p->next) { EShellView *view; view = E_SHELL_VIEW (p->data); gtk_signal_disconnect_by_func (GTK_OBJECT (view), GTK_SIGNAL_FUNC (view_delete_event_cb), shell); gtk_signal_disconnect_by_func (GTK_OBJECT (view), GTK_SIGNAL_FUNC (view_destroy_cb), shell); gtk_object_destroy (GTK_OBJECT (view)); } g_list_free (priv->views); /* No unreffing for these as they are aggregate. */ /* bonobo_object_unref (BONOBO_OBJECT (priv->corba_storage_registry)); */ /* bonobo_object_unref (BONOBO_OBJECT (priv->activity_handler)); */ /* bonobo_object_unref (BONOBO_OBJECT (priv->corba_shortcuts)); */ /* FIXME. Maybe we should do something special here. */ if (priv->offline_handler != NULL) gtk_object_unref (GTK_OBJECT (priv->offline_handler)); e_free_string_list (priv->crash_type_names); g_free (priv); (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } /* Initialization. */ static void class_init (EShellClass *klass) { GtkObjectClass *object_class; POA_GNOME_Evolution_Shell__epv *epv; parent_class = gtk_type_class (PARENT_TYPE); object_class = GTK_OBJECT_CLASS (klass); object_class->destroy = destroy; signals[NO_VIEWS_LEFT] = gtk_signal_new ("no_views_left", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (EShellClass, no_views_left), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); signals[LINE_STATUS_CHANGED] = gtk_signal_new ("line_status_changed", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (EShellClass, line_status_changed), gtk_marshal_NONE__ENUM, GTK_TYPE_NONE, 1, GTK_TYPE_ENUM); gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL); epv = & klass->epv; epv->_get_displayName = impl_Shell__get_displayName; epv->getComponentByType = impl_Shell_getComponentByType; epv->createNewView = impl_Shell_createNewView; epv->handleURI = impl_Shell_handleURI; epv->selectUserFolder = impl_Shell_selectUserFolder; epv->getLocalStorage = impl_Shell_getLocalStorage; epv->createStorageSetView = impl_Shell_createStorageSetView; epv->setLineStatus = impl_Shell_setLineStatus; } static void init (EShell *shell) { EShellPrivate *priv; priv = g_new (EShellPrivate, 1); priv->views = NULL; priv->iid = NULL; priv->local_directory = NULL; priv->storage_set = NULL; priv->local_storage = NULL; priv->summary_storage = NULL; priv->shortcuts = NULL; priv->component_registry = NULL; priv->user_creatable_items_handler = NULL; priv->folder_type_registry = NULL; priv->uri_schema_registry = NULL; priv->corba_storage_registry = NULL; priv->activity_handler = NULL; priv->corba_shortcuts = NULL; priv->offline_handler = NULL; priv->crash_type_names = NULL; priv->line_status = E_SHELL_LINE_STATUS_ONLINE; priv->db = CORBA_OBJECT_NIL; priv->is_initialized = FALSE; shell->priv = priv; } /** * e_shell_construct: * @shell: An EShell object to construct * @iid: OAFIID for registering the shell into the name server * @local_directory: Local directory for storing local information and folders * @show_splash: Whether to display a splash screen. * * Construct @shell so that it uses the specified @local_directory and * @corba_object. * * Return value: The result of the operation. **/ EShellConstructResult e_shell_construct (EShell *shell, const char *iid, const char *local_directory, gboolean show_splash) { GtkWidget *splash; EShellPrivate *priv; CORBA_Object corba_object; CORBA_Environment ev; gchar *shortcut_path; g_return_val_if_fail (shell != NULL, E_SHELL_CONSTRUCT_RESULT_INVALIDARG); g_return_val_if_fail (E_IS_SHELL (shell), E_SHELL_CONSTRUCT_RESULT_INVALIDARG); g_return_val_if_fail (local_directory != NULL, E_SHELL_CONSTRUCT_RESULT_INVALIDARG); g_return_val_if_fail (g_path_is_absolute (local_directory), E_SHELL_CONSTRUCT_RESULT_INVALIDARG); priv = shell->priv; priv->iid = g_strdup (iid); priv->local_directory = g_strdup (local_directory); priv->folder_type_registry = e_folder_type_registry_new (); priv->uri_schema_registry = e_uri_schema_registry_new (); priv->storage_set = e_storage_set_new (priv->folder_type_registry); /* CORBA storages must be set up before the components, because otherwise components cannot register their own storages. */ if (! setup_corba_storages (shell)) return FALSE; CORBA_exception_init (&ev); priv->db = bonobo_get_object ("wombat:", "Bonobo/ConfigDatabase", &ev); if (BONOBO_EX (&ev) || priv->db == CORBA_OBJECT_NIL) { g_warning ("Cannot access Bonobo/ConfigDatabase on wombat:"); /* Make sure the DB object is NIL so we don't mess up (`bonobo_get_object()' might return an undefined value in the case of an exception). */ priv->db = CORBA_OBJECT_NIL; CORBA_exception_free (&ev); return E_SHELL_CONSTRUCT_RESULT_NOCONFIGDB; } CORBA_exception_free (&ev); if (! show_splash) { splash = NULL; } else { splash = e_splash_new (); gtk_signal_connect (GTK_OBJECT (splash), "delete_event", GTK_SIGNAL_FUNC (gtk_widget_hide_on_delete), NULL); gtk_widget_show (splash); } while (gtk_events_pending ()) gtk_main_iteration (); priv->user_creatable_items_handler = e_shell_user_creatable_items_handler_new (); if (show_splash) setup_components (shell, E_SPLASH (splash)); else setup_components (shell, NULL); /* Set up the shortcuts. */ shortcut_path = g_concat_dir_and_file (local_directory, "shortcuts.xml"); priv->shortcuts = e_shortcuts_new (priv->storage_set, priv->folder_type_registry, shortcut_path); g_assert (priv->shortcuts != NULL); if (e_shortcuts_get_num_groups (priv->shortcuts) == 0) e_shortcuts_add_default_group (priv->shortcuts); g_free (shortcut_path); /* The local storage depends on the component registry. */ setup_local_storage (shell); /* Set up the ::Activity interface. This must be done before we notify the components, as they might want to use it. */ setup_activity_interface (shell); /* Set up the shortcuts interface. This has to be done after the shortcuts are actually initialized. */ setup_shortcuts_interface (shell); /* Now that we have a local storage and all the interfaces set up, we can tell the components we are here. */ set_owner_on_components (shell); if (show_splash) { gtk_widget_destroy (splash); } if (e_shell_startup_wizard_create () == FALSE) { e_shell_unregister_all (shell); bonobo_object_unref (BONOBO_OBJECT (shell)); exit (0); } shortcut_path = g_concat_dir_and_file (local_directory, "shortcuts.xml"); priv->shortcuts = e_shortcuts_new (priv->storage_set, priv->folder_type_registry, shortcut_path); g_assert (priv->shortcuts != NULL); if (e_shortcuts_get_num_groups (priv->shortcuts) == 0) e_shortcuts_add_default_group (priv->shortcuts); g_free (shortcut_path); /* Now we can register into OAF. Notice that we shouldn't be registering into OAF until we are initialized. */ /* FIXME: Multi-display stuff. */ corba_object = bonobo_object_corba_objref (BONOBO_OBJECT (shell)); if (oaf_active_server_register (iid, corba_object) != OAF_REG_SUCCESS) { CORBA_exception_free (&ev); return E_SHELL_CONSTRUCT_RESULT_CANNOTREGISTER; } return E_SHELL_CONSTRUCT_RESULT_OK; } /** * e_shell_new: * @local_directory: Local directory for storing local information and folders. * @show_splash: Whether to display a splash screen. * @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 (const char *local_directory, gboolean show_splash, EShellConstructResult *construct_result_return) { EShell *new; EShellPrivate *priv; EShellConstructResult construct_result; g_return_val_if_fail (local_directory != NULL, NULL); g_return_val_if_fail (*local_directory != '\0', NULL); new = gtk_type_new (e_shell_get_type ()); construct_result = e_shell_construct (new, E_SHELL_OAFIID, local_directory, show_splash); if (construct_result != E_SHELL_CONSTRUCT_RESULT_OK) { *construct_result_return = construct_result; bonobo_object_unref (BONOBO_OBJECT (new)); return NULL; } priv = new->priv; if (priv->shortcuts == NULL || priv->storage_set == NULL) { /* FIXME? */ *construct_result_return = E_SHELL_CONSTRUCT_RESULT_GENERICERROR; bonobo_object_unref (BONOBO_OBJECT (new)); return NULL; } *construct_result_return = E_SHELL_CONSTRUCT_RESULT_OK; return new; } /** * e_shell_create_view: * @shell: The shell for which to create a new view. * @uri: URI for the new view. * * Create a new view for @uri. * * Return value: The new view. **/ EShellView * e_shell_create_view (EShell *shell, const char *uri) { EShellView *view; EShellPrivate *priv; ETaskBar *task_bar; g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); priv = shell->priv; view = e_shell_view_new (shell); gtk_signal_connect (GTK_OBJECT (view), "delete_event", GTK_SIGNAL_FUNC (view_delete_event_cb), shell); gtk_signal_connect (GTK_OBJECT (view), "destroy", GTK_SIGNAL_FUNC (view_destroy_cb), shell); if (uri != NULL) if (!e_shell_view_display_uri (E_SHELL_VIEW (view), uri)) /* FIXME: Consider popping a dialog box up about how the provided URI does not exist/could not be displayed */ e_shell_view_display_uri (E_SHELL_VIEW (view), E_SHELL_VIEW_DEFAULT_URI); shell->priv->views = g_list_prepend (shell->priv->views, view); task_bar = e_shell_view_get_task_bar (view); e_activity_handler_attach_task_bar (priv->activity_handler, task_bar); return view; } /** * e_shell_get_local_directory: * @shell: An EShell object. * * Get the local directory associated with @shell. * * Return value: A pointer to the path of the local directory. **/ const char * e_shell_get_local_directory (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->local_directory; } /** * e_shell_get_shortcuts: * @shell: An EShell object. * * Get the shortcuts associated to @shell. * * Return value: A pointer to the EShortcuts associated to @shell. **/ EShortcuts * e_shell_get_shortcuts (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->shortcuts; } /** * e_shell_get_storage_set: * @shell: An EShell object. * * Get the storage set associated to @shell. * * Return value: A pointer to the EStorageSet associated to @shell. **/ EStorageSet * e_shell_get_storage_set (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->storage_set; } /** * e_shell_get_folder_type_registry: * @shell: An EShell object. * * Get the folder type registry associated to @shell. * * Return value: A pointer to the EFolderTypeRegistry associated to @shell. **/ EFolderTypeRegistry * e_shell_get_folder_type_registry (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->folder_type_registry; } /** * e_shell_get_uri_schema_registry: * @shell: An EShell object. * * Get the schema registry associated to @shell. * * Return value: A pointer to the EUriSchemaRegistry associated to @shell. **/ EUriSchemaRegistry * e_shell_get_uri_schema_registry (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->uri_schema_registry; } /** * e_shell_get_local_storage: * @shell: An EShell object. * * Get the local storage associated to @shell. * * Return value: A pointer to the ELocalStorage associated to @shell. **/ ELocalStorage * e_shell_get_local_storage (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->local_storage; } static gboolean save_settings_for_views (EShell *shell) { CORBA_Environment ev; EShellPrivate *priv; GList *p; gboolean retval; int i; priv = shell->priv; retval = TRUE; for (p = priv->views, i = 0; p != NULL; p = p->next, i++) { EShellView *view; view = E_SHELL_VIEW (p->data); if (! e_shell_view_save_settings (view, i)) { g_warning ("Cannot save settings for view -- %d", i); retval = FALSE; } } bonobo_config_set_long (priv->db, "/Shell/Views/NumberOfViews", g_list_length (priv->views), NULL); CORBA_exception_init (&ev); Bonobo_ConfigDatabase_sync (priv->db, &ev); CORBA_exception_free (&ev); return TRUE; } static gboolean save_settings_for_component (EShell *shell, const char *id, EvolutionShellComponentClient *client) { Bonobo_Unknown unknown_interface; GNOME_Evolution_Session session_interface; CORBA_Environment ev; char *prefix; gboolean retval; unknown_interface = bonobo_object_corba_objref (BONOBO_OBJECT (client)); g_assert (unknown_interface != CORBA_OBJECT_NIL); CORBA_exception_init (&ev); session_interface = Bonobo_Unknown_queryInterface (unknown_interface, "IDL:GNOME/Evolution/Session:1.0", &ev); if (ev._major != CORBA_NO_EXCEPTION || CORBA_Object_is_nil (session_interface, &ev)) { CORBA_exception_free (&ev); return TRUE; } prefix = g_strconcat ("/apps/Evolution/Shell/Components/", id, NULL); GNOME_Evolution_Session_saveConfiguration (session_interface, prefix, &ev); if (ev._major == CORBA_NO_EXCEPTION) retval = TRUE; else retval = FALSE; g_free (prefix); CORBA_exception_free (&ev); return retval; } static gboolean save_settings_for_components (EShell *shell) { EShellPrivate *priv; GList *component_ids; GList *p; gboolean retval; priv = shell->priv; g_assert (priv->component_registry); component_ids = e_component_registry_get_id_list (priv->component_registry); retval = TRUE; for (p = component_ids; p != NULL; p = p->next) { EvolutionShellComponentClient *client; const char *id; id = p->data; client = e_component_registry_get_component_by_id (priv->component_registry, id); if (! save_settings_for_component (shell, id, client)) retval = FALSE; } e_free_string_list (component_ids); return retval; } /** * e_shell_save_settings: * @shell: * * Save the settings for this shell. * * 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 views * have been saved. **/ gboolean e_shell_save_settings (EShell *shell) { gboolean views_saved; gboolean components_saved; g_return_val_if_fail (shell != NULL, FALSE); g_return_val_if_fail (E_IS_SHELL (shell), FALSE); views_saved = save_settings_for_views (shell); components_saved = save_settings_for_components (shell); return views_saved && components_saved; } /** * e_shell_restore_from_settings: * @shell: An EShell object. * * Restore the existing views from the saved configuration. The shell must * have no views for this to work. * * Return value: %FALSE if the shell has some open views or there is no saved * configuration. %TRUE if the configuration could be restored successfully. **/ gboolean e_shell_restore_from_settings (EShell *shell) { EShellPrivate *priv; gboolean retval; int num_views; int i; g_return_val_if_fail (shell != NULL, FALSE); g_return_val_if_fail (E_IS_SHELL (shell), FALSE); g_return_val_if_fail (shell->priv->views == NULL, FALSE); priv = shell->priv; num_views = bonobo_config_get_long_with_default (priv->db, "/Shell/Views/NumberOfViews", 0, NULL); if (num_views == 0) return FALSE; retval = TRUE; for (i = 0; i < num_views; i++) { EShellView *view; /* FIXME: restore the URI here. There should be an e_shell_create_view_from_configuration() thingie. */ view = e_shell_create_view (shell, NULL); if (! e_shell_view_load_settings (view, i)) retval = FALSE; /* This needs to be done after loading the settings as the default size is in the settings as well. */ gtk_widget_show (GTK_WIDGET (view)); } return retval; } /** * e_shell_destroy_all_views: * @shell: * * Destroy all the views in @shell. **/ void e_shell_destroy_all_views (EShell *shell) { EShellPrivate *priv; GList *p, *pnext; g_return_if_fail (shell != NULL); g_return_if_fail (E_IS_SHELL (shell)); if (shell->priv->views) e_shell_save_settings (shell); priv = shell->priv; for (p = priv->views; p != NULL; p = pnext) { EShellView *shell_view; pnext = p->next; shell_view = E_SHELL_VIEW (p->data); gtk_widget_destroy (GTK_WIDGET (shell_view)); } } /** * e_shell_component_maybe_crashed: * @shell: A pointer to an EShell object * @uri: URI that caused the crash * @type_name: The type of the folder that caused the crash * @shell_view: Pointer to the EShellView over which we want the modal dialog * to appear. * * Report that a maybe crash happened when trying to display a folder of type * @type_name. The shell will pop up a crash dialog whose parent will be the * @shell_view. **/ void e_shell_component_maybe_crashed (EShell *shell, const char *uri, const char *type_name, EShellView *shell_view) { EShellPrivate *priv; GtkWindow *parent_window; GList *p; g_return_if_fail (shell != NULL); g_return_if_fail (E_IS_SHELL (shell)); g_return_if_fail (type_name != NULL); g_return_if_fail (shell_view != NULL); g_return_if_fail (E_IS_SHELL_VIEW (shell_view)); priv = shell->priv; /* See if that type has caused a crash already. */ for (p = priv->crash_type_names; p != NULL; p = p->next) { const char *crash_type_name; crash_type_name = (const char *) p->data; if (strcmp (type_name, crash_type_name) == 0) { /* This type caused a crash already. */ return; } } /* New crash. */ priv->crash_type_names = g_list_prepend (priv->crash_type_names, g_strdup (type_name)); if (shell_view == NULL) parent_window = NULL; else parent_window = GTK_WINDOW (shell_view); e_notice (parent_window, GNOME_MESSAGE_BOX_ERROR, _("Ooops! The views for `%s' have died unexpectedly. :-(\n" "This probably means that the %s component has crashed."), uri, type_name); if (shell_view) bonobo_window_deregister_dead_components (BONOBO_WINDOW (shell_view)); /* FIXME: we should probably re-start the component here */ } /* Offline/online handling. */ static void offline_procedure_started_cb (EShellOfflineHandler *offline_handler, void *data) { EShell *shell; EShellPrivate *priv; shell = E_SHELL (data); priv = shell->priv; priv->line_status = E_SHELL_LINE_STATUS_GOING_OFFLINE; gtk_signal_emit (GTK_OBJECT (shell), signals[LINE_STATUS_CHANGED], priv->line_status); } static void offline_procedure_finished_cb (EShellOfflineHandler *offline_handler, gboolean now_offline, void *data) { EShell *shell; EShellPrivate *priv; shell = E_SHELL (data); priv = shell->priv; if (now_offline) priv->line_status = E_SHELL_LINE_STATUS_OFFLINE; else priv->line_status = E_SHELL_LINE_STATUS_ONLINE; gtk_object_unref (GTK_OBJECT (priv->offline_handler)); priv->offline_handler = NULL; gtk_signal_emit (GTK_OBJECT (shell), signals[LINE_STATUS_CHANGED], priv->line_status); } /** * e_shell_get_line_status: * @shell: A pointer to an EShell object. * * Get the line status for @shell. * * Return value: The current line status for @shell. **/ EShellLineStatus e_shell_get_line_status (EShell *shell) { 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; } /** * e_shell_go_offline: * @shell: * @action_view: * * Make the shell go into off-line mode. **/ void e_shell_go_offline (EShell *shell, EShellView *action_view) { EShellPrivate *priv; g_return_if_fail (shell != NULL); g_return_if_fail (E_IS_SHELL (shell)); g_return_if_fail (action_view != NULL); g_return_if_fail (action_view == NULL || E_IS_SHELL_VIEW (action_view)); priv = shell->priv; if (priv->line_status != E_SHELL_LINE_STATUS_ONLINE) return; g_assert (priv->offline_handler == NULL); priv->offline_handler = e_shell_offline_handler_new (priv->component_registry); gtk_signal_connect (GTK_OBJECT (priv->offline_handler), "offline_procedure_started", GTK_SIGNAL_FUNC (offline_procedure_started_cb), shell); gtk_signal_connect (GTK_OBJECT (priv->offline_handler), "offline_procedure_finished", GTK_SIGNAL_FUNC (offline_procedure_finished_cb), shell); e_shell_offline_handler_put_components_offline (priv->offline_handler, action_view); } /** * e_shell_go_online: * @shell: * @action_view: * * Make the shell go into on-line mode. **/ void e_shell_go_online (EShell *shell, EShellView *action_view) { EShellPrivate *priv; GList *component_ids; GList *p; g_return_if_fail (shell != NULL); g_return_if_fail (E_IS_SHELL (shell)); g_return_if_fail (action_view == NULL || E_IS_SHELL_VIEW (action_view)); priv = shell->priv; component_ids = e_component_registry_get_id_list (priv->component_registry); for (p = component_ids; p != NULL; p = p->next) { CORBA_Environment ev; EvolutionShellComponentClient *client; GNOME_Evolution_Offline offline_interface; const char *id; id = (const char *) p->data; client = e_component_registry_get_component_by_id (priv->component_registry, id); CORBA_exception_init (&ev); offline_interface = evolution_shell_component_client_get_offline_interface (client); if (CORBA_Object_is_nil (offline_interface, &ev) || ev._major != CORBA_NO_EXCEPTION) { CORBA_exception_free (&ev); continue; } GNOME_Evolution_Offline_goOnline (offline_interface, &ev); if (ev._major != CORBA_NO_EXCEPTION) g_warning ("Error putting component `%s' online.", id); CORBA_exception_free (&ev); } e_free_string_list (component_ids); priv->line_status = E_SHELL_LINE_STATUS_ONLINE; gtk_signal_emit (GTK_OBJECT (shell), signals[LINE_STATUS_CHANGED], priv->line_status); } Bonobo_ConfigDatabase e_shell_get_config_db (EShell *shell) { g_return_val_if_fail (shell != NULL, CORBA_OBJECT_NIL); return shell->priv->db; } EComponentRegistry * e_shell_get_component_registry (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->component_registry; } EShellUserCreatableItemsHandler * e_shell_get_user_creatable_items_handler (EShell *shell) { g_return_val_if_fail (shell != NULL, NULL); g_return_val_if_fail (E_IS_SHELL (shell), NULL); return shell->priv->user_creatable_items_handler; } void e_shell_unregister_all (EShell *shell) { EShellPrivate *priv; g_return_if_fail (E_IS_SHELL (shell)); /* FIXME: This really really sucks. */ priv = shell->priv; gtk_object_unref (GTK_OBJECT (priv->component_registry)); priv->component_registry = NULL; } 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"); } } E_MAKE_X_TYPE (e_shell, "EShell", EShell, class_init, init, PARENT_TYPE, POA_GNOME_Evolution_Shell__init, GTK_STRUCT_OFFSET (EShellClass, epv));