diff options
author | Ettore Perazzoli <ettore@src.gnome.org> | 2001-05-03 09:10:46 +0800 |
---|---|---|
committer | Ettore Perazzoli <ettore@src.gnome.org> | 2001-05-03 09:10:46 +0800 |
commit | 483ebae5399d6aa7d63e471ea6000021a4be94fb (patch) | |
tree | 8de0a4611e800febfffa59c25d3b9d806d380757 /shell/e-shell-offline-handler.c | |
parent | 0ac06ee5b0cf27803e037b8bb5f3998898200bd1 (diff) | |
download | gsoc2013-evolution-483ebae5399d6aa7d63e471ea6000021a4be94fb.tar.gz gsoc2013-evolution-483ebae5399d6aa7d63e471ea6000021a4be94fb.tar.zst gsoc2013-evolution-483ebae5399d6aa7d63e471ea6000021a4be94fb.zip |
Add interfaces to the shell to handle off-line operation, as well as a
first (unfinished/untested) implementation for them.
svn path=/trunk/; revision=9653
Diffstat (limited to 'shell/e-shell-offline-handler.c')
-rw-r--r-- | shell/e-shell-offline-handler.c | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/shell/e-shell-offline-handler.c b/shell/e-shell-offline-handler.c new file mode 100644 index 0000000000..1967041a7e --- /dev/null +++ b/shell/e-shell-offline-handler.c @@ -0,0 +1,646 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-shell-offline-handler.c + * + * Copyright (C) 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 <ettore@ximian.com> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtktypeutils.h> +#include <gtk/gtksignal.h> +#include <gtk/gtkwidget.h> + +#include <gal/util/e-util.h> + +#include <bonobo/bonobo-main.h> + +#include "e-shell-offline-handler.h" + + +#define PARENT_TYPE GTK_TYPE_OBJECT +static GtkObjectClass *parent_class = NULL; + + +/* Private part. */ + +struct _OfflineProgressListenerServant { + POA_GNOME_Evolution_OfflineProgressListener servant; + EShellOfflineHandler *offline_handler; + char *component_id; +}; +typedef struct _OfflineProgressListenerServant OfflineProgressListenerServant; + +struct _ComponentInfo { + /* Component ID. */ + char *id; + + /* The `Evolution::Offline' interface for this component (cached just + to avoid going through the EComponentRegistry all the time). */ + GNOME_Evolution_Offline offline_interface; + + /* The interface and servant for the + `Evolution::OfflineProgressListener' we have to implement to get + notifications about progress of the off-line process. */ + GNOME_Evolution_OfflineProgressListener progress_listener_interface; + OfflineProgressListenerServant *progress_listener_servant; + + /* The current active connections for this component. This is updated + by the component itself through the `::ProgressListener' interface; + when the count reaches zero, the off-line process is considered to + be complete. */ + GNOME_Evolution_ConnectionList *active_connection_list; +}; +typedef struct _ComponentInfo ComponentInfo; + +struct _EShellOfflineHandlerPrivate { + EComponentRegistry *component_registry; + + EShellView *parent_shell_view; + + int num_total_connections; + GHashTable *id_to_component_info; + + gboolean procedure_in_progress : 1; +}; + + +/* Signals. */ + +enum { + OFFLINE_PROCEDURE_STARTED, + OFFLINE_PROCEDURE_FINISHED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +/* Implementation for the OfflineProgressListener interface. */ + +static PortableServer_ServantBase__epv OfflineProgressListener_base_epv; +static POA_GNOME_Evolution_OfflineProgressListener__epv OfflineProgressListener_epv; +static POA_GNOME_Evolution_OfflineProgressListener__vepv OfflineProgressListener_vepv; + +static OfflineProgressListenerServant * +progress_listener_servant_new (EShellOfflineHandler *offline_handler, + const char *id) +{ + OfflineProgressListenerServant *servant; + + servant = g_new0 (OfflineProgressListenerServant, 1); + + servant->servant.vepv = &OfflineProgressListener_vepv; + servant->offline_handler = offline_handler; + servant->component_id = g_strdup (id); + + return servant; +} + +static void +progress_listener_servant_free (OfflineProgressListenerServant *servant) +{ + g_free (servant->component_id); + g_free (servant); +} + +static GNOME_Evolution_ConnectionList * +duplicate_connection_list (const GNOME_Evolution_ConnectionList *source) +{ + GNOME_Evolution_ConnectionList *copy; + int i; + + copy = GNOME_Evolution_ConnectionList__alloc (); + + copy->_length = source->_length; + copy->_maximum = source->_length; + + copy->_buffer = CORBA_sequence_GNOME_Evolution_Connection_allocbuf (copy->_maximum); + + for (i = 0; i < source->_length; i++) { + copy->_buffer[i].hostName = CORBA_string_dup (source->_buffer[i].hostName); + copy->_buffer[i].portNumber = source->_buffer[i].portNumber; + } + + return copy; +} + +static void +impl_OfflineProgressListener_updateProgress (PortableServer_Servant servant, + const GNOME_Evolution_ConnectionList *current_active_connections, + CORBA_Environment *ev) +{ + EShellOfflineHandler *offline_handler; + EShellOfflineHandlerPrivate *priv; + ComponentInfo *component_info; + int connection_delta; + const char *component_id; + + component_id = ((OfflineProgressListenerServant *) servant)->component_id; + + offline_handler = ((OfflineProgressListenerServant *) servant)->offline_handler; + priv = offline_handler->priv; + + component_info = g_hash_table_lookup (priv->id_to_component_info, component_id); + g_assert (component_info != NULL); + + connection_delta = component_info->active_connection_list->_length - current_active_connections->_length; + if (connection_delta < 0) { + /* FIXME: Should raise an exception or something? */ + g_warning ("Weird, buggy component increased number of connection when going off-line -- %s", + component_id); + } + + g_assert (priv->num_total_connections >= connection_delta); + priv->num_total_connections -= connection_delta; + + CORBA_free (component_info->active_connection_list); + component_info->active_connection_list = duplicate_connection_list (current_active_connections); + + if (priv->num_total_connections == 0) + gtk_signal_emit (GTK_OBJECT (offline_handler), signals[OFFLINE_PROCEDURE_FINISHED], TRUE); + + /* TODO: update the dialog. */ +} + +static gboolean +create_progress_listener (EShellOfflineHandler *offline_handler, + const char *component_id, + GNOME_Evolution_OfflineProgressListener *objref_return, + OfflineProgressListenerServant **servant_return) +{ + OfflineProgressListenerServant *servant; + CORBA_Environment ev; + + *servant_return = NULL; + *objref_return = CORBA_OBJECT_NIL; + + OfflineProgressListener_base_epv._private = NULL; + OfflineProgressListener_base_epv.finalize = NULL; + OfflineProgressListener_base_epv.default_POA = NULL; + + OfflineProgressListener_epv.updateProgress = impl_OfflineProgressListener_updateProgress; + + OfflineProgressListener_vepv._base_epv = &OfflineProgressListener_base_epv; + OfflineProgressListener_vepv.GNOME_Evolution_OfflineProgressListener_epv = &OfflineProgressListener_epv; + + servant = progress_listener_servant_new (offline_handler, component_id); + + CORBA_exception_init (&ev); + + POA_GNOME_Evolution_OfflineProgressListener__init ((PortableServer_Servant) servant, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot initialize GNOME::Evolution::Offline::ProgressListener"); + progress_listener_servant_free (servant); + CORBA_exception_free (&ev); + return FALSE; + } + + CORBA_free (PortableServer_POA_activate_object (bonobo_poa (), servant, &ev)); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot activate GNOME::Evolution::Offline::ProgressListener"); + progress_listener_servant_free (servant); + CORBA_exception_free (&ev); + return FALSE; + } + + *servant_return = servant; + *objref_return = PortableServer_POA_servant_to_reference (bonobo_poa (), servant, &ev); + + CORBA_exception_free (&ev); + + return TRUE; +} + + +/* ComponentInfo handling. */ + +static ComponentInfo * +component_info_new (const char *id, + const GNOME_Evolution_Offline offline_interface, + GNOME_Evolution_OfflineProgressListener progress_listener_interface, + OfflineProgressListenerServant *progress_listener_servant, + GNOME_Evolution_ConnectionList *active_connection_list) +{ + ComponentInfo *new; + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + new = g_new (ComponentInfo, 1); + new->id = g_strdup (id); + new->offline_interface = CORBA_Object_duplicate (offline_interface, &ev); + new->progress_listener_interface = progress_listener_interface; + new->progress_listener_servant = progress_listener_servant; + new->active_connection_list = active_connection_list; + + CORBA_exception_free (&ev); + + return new; +} + +static void +component_info_free (ComponentInfo *component_info) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + g_free (component_info->id); + + CORBA_Object_release (component_info->offline_interface, &ev); + + POA_GNOME_Evolution_OfflineProgressListener__fini + ((POA_GNOME_Evolution_OfflineProgressListener *) component_info->progress_listener_servant, &ev); + progress_listener_servant_free (component_info->progress_listener_servant); + + CORBA_Object_release (component_info->progress_listener_interface, &ev); + + CORBA_free (component_info->active_connection_list); + + g_free (component_info); + + CORBA_exception_free (&ev); +} + + +/* Utility functions. */ + +static void +hash_foreach_free_component_info (void *key, + void *value, + void *user_data) +{ + ComponentInfo *component_info; + + component_info = (ComponentInfo *) value; + component_info_free (component_info); +} + + +/* Cancelling the off-line procedure. */ + +static void +cancel_offline (EShellOfflineHandler *offline_handler) +{ + EShellOfflineHandlerPrivate *priv; + GList *component_ids; + GList *p; + + priv = offline_handler->priv; + + component_ids = e_component_registry_get_id_list (priv->component_registry); + + for (p = component_ids; p != NULL; p = p->next) { + EvolutionShellComponentClient *shell_component_client; + GNOME_Evolution_Offline offline_interface; + CORBA_Environment ev; + const char *id; + + id = (const char *) p->data; + shell_component_client = e_component_registry_get_component_by_id (priv->component_registry, id); + + offline_interface = evolution_shell_component_client_get_offline_interface (shell_component_client); + if (offline_interface == CORBA_OBJECT_NIL) + continue; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Offline_goOnline (offline_interface, &ev); + if (ev._major != CORBA_NO_EXCEPTION) + g_warning ("Error putting component `%s' on-line.", id); + + CORBA_exception_free (&ev); + } + + e_free_string_list (component_ids); + + priv->num_total_connections = 0; +} + + +/* Preparing the off-line procedure. */ + +static gboolean +prepare_for_offline (EShellOfflineHandler *offline_handler) +{ + EShellOfflineHandlerPrivate *priv; + GList *component_ids; + GList *p; + gboolean error; + + priv = offline_handler->priv; + + component_ids = e_component_registry_get_id_list (priv->component_registry); + + error = FALSE; + for (p = component_ids; p != NULL; p = p->next) { + EvolutionShellComponentClient *shell_component_client; + GNOME_Evolution_Offline offline_interface; + GNOME_Evolution_OfflineProgressListener progress_listener_interface; + GNOME_Evolution_ConnectionList *active_connection_list; + OfflineProgressListenerServant *progress_listener_servant; + ComponentInfo *component_info; + CORBA_Environment ev; + const char *id; + + id = (const char *) p->data; + shell_component_client = e_component_registry_get_component_by_id (priv->component_registry, id); + offline_interface = evolution_shell_component_client_get_offline_interface (shell_component_client); + if (offline_interface == CORBA_OBJECT_NIL) + continue; + + if (! create_progress_listener (offline_handler, id, + &progress_listener_interface, + &progress_listener_servant)) { + g_warning ("Cannot create the Evolution::OfflineProgressListener interface for `%s'", id); + continue; + } + + CORBA_exception_init (&ev); + + GNOME_Evolution_Offline_prepareForOffline (offline_interface, &active_connection_list, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot prepare component component to go offline -- %s", id); + + POA_GNOME_Evolution_OfflineProgressListener__fini + ((POA_GNOME_Evolution_OfflineProgressListener *) progress_listener_servant, &ev); + progress_listener_servant_free (progress_listener_servant); + + CORBA_Object_release (progress_listener_interface, &ev); + + CORBA_exception_free (&ev); + + error = TRUE; + break; + } + + CORBA_exception_free (&ev); + + priv->num_total_connections += active_connection_list->_length; + + component_info = component_info_new (id, + offline_interface, + progress_listener_interface, + progress_listener_servant, + active_connection_list); + + g_assert (g_hash_table_lookup (priv->id_to_component_info, component_info->id) == NULL); + g_hash_table_insert (priv->id_to_component_info, component_info->id, component_info); + } + + /* If an error occurred while preparing, just put all the components + on-line again. */ + if (error) + cancel_offline (offline_handler); + + e_free_string_list (component_ids); + + return ! error; +} + + +/* Finalizing the off-line procedure. */ + +static void +finalize_offline_hash_foreach (void *key, + void *value, + void *user_data) +{ + EShellOfflineHandler *offline_handler; + EShellOfflineHandlerPrivate *priv; + ComponentInfo *component_info; + CORBA_Environment ev; + + offline_handler = E_SHELL_OFFLINE_HANDLER (user_data); + priv = offline_handler->priv; + + component_info = (ComponentInfo *) value; + + CORBA_exception_init (&ev); + + g_print ("Putting component off-line -- %s", component_info->id); + + GNOME_Evolution_Offline_goOffline (component_info->offline_interface, + component_info->progress_listener_interface, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + /* FIXME: Should detect an error and put all the components + on-line again. */ + g_warning ("Error putting component off-line -- %s", component_info->id); + } + + CORBA_exception_free (&ev); +} + +static void +finalize_offline (EShellOfflineHandler *offline_handler) +{ + EShellOfflineHandlerPrivate *priv; + + priv = offline_handler->priv; + + g_hash_table_foreach (priv->id_to_component_info, finalize_offline_hash_foreach, offline_handler); + + if (priv->num_total_connections == 0) { + /* Nothing else to do, we are all set. */ + gtk_signal_emit (GTK_OBJECT (offline_handler), signals[OFFLINE_PROCEDURE_FINISHED], TRUE); + } +} + + +/* The confirmation dialog. */ + +static void +pop_up_confirmation_dialog (EShellOfflineHandler *offline_handler) +{ + EShellOfflineHandlerPrivate *priv; + + priv = offline_handler->priv; + + g_warning ("Should pop up dialog here"); + + finalize_offline (offline_handler); +} + + +/* GtkObject methods. */ + +static void +impl_destroy (GtkObject *object) +{ + EShellOfflineHandler *offline_handler; + EShellOfflineHandlerPrivate *priv; + + offline_handler = E_SHELL_OFFLINE_HANDLER (object); + priv = offline_handler->priv; + + if (priv->component_registry != NULL) + gtk_object_unref (GTK_OBJECT (priv->component_registry)); + + g_hash_table_foreach (priv->id_to_component_info, hash_foreach_free_component_info, NULL); + g_hash_table_destroy (priv->id_to_component_info); + + if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + +/* GTK type handling. */ + +static void +class_init (EShellOfflineHandlerClass *klass) +{ + GtkObjectClass *object_class; + + object_class = GTK_OBJECT_CLASS (klass); + object_class->destroy = impl_destroy; + + parent_class = gtk_type_class (gtk_object_get_type ()); + + signals[OFFLINE_PROCEDURE_STARTED] + = gtk_signal_new ("offline_procedure_started", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EShellOfflineHandlerClass, offline_procedure_started), + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, 0); + + signals[OFFLINE_PROCEDURE_FINISHED] + = gtk_signal_new ("offline_procedure_finished", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (EShellOfflineHandlerClass, offline_procedure_finished), + gtk_marshal_NONE__BOOL, + GTK_TYPE_NONE, 1, + GTK_TYPE_BOOL); + + gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL); +} + + +static void +init (EShellOfflineHandler *shell_offline_handler) +{ + EShellOfflineHandlerPrivate *priv; + + priv = g_new (EShellOfflineHandlerPrivate, 1); + + priv->component_registry = NULL; + priv->parent_shell_view = NULL; + priv->procedure_in_progress = TRUE; + + priv->num_total_connections = 0; + priv->id_to_component_info = g_hash_table_new (g_str_hash, g_str_equal); + + shell_offline_handler->priv = priv; +} + + +/** + * e_shell_offline_handler_construct: + * @offline_handler: A pointer to an EShellOfflineHandler to construct. + * @component_registry: The registry for the components that we want to put + * off-line. + * + * Construct the @offline_handler. + **/ +void +e_shell_offline_handler_construct (EShellOfflineHandler *offline_handler, + EComponentRegistry *component_registry) +{ + EShellOfflineHandlerPrivate *priv; + + g_return_if_fail (offline_handler != NULL); + g_return_if_fail (E_IS_SHELL_OFFLINE_HANDLER (offline_handler)); + g_return_if_fail (component_registry != NULL); + g_return_if_fail (E_IS_COMPONENT_REGISTRY (component_registry)); + + g_assert (priv->component_registry == NULL); + + gtk_object_ref (GTK_OBJECT (component_registry)); + priv->component_registry = component_registry; +} + +/** + * e_shell_offline_handler_new: + * @component_registry: The registry for the components that we want to put + * off-line. + * + * Create a new offline handler. + * + * Return value: A pointer to the newly created EShellOfflineHandler object. + **/ +EShellOfflineHandler * +e_shell_offline_handler_new (EComponentRegistry *component_registry) +{ + EShellOfflineHandler *offline_handler; + + g_return_val_if_fail (component_registry != NULL, NULL); + g_return_val_if_fail (E_IS_COMPONENT_REGISTRY (component_registry), NULL); + + offline_handler = (EShellOfflineHandler *) gtk_type_new (e_shell_offline_handler_get_type ()); + e_shell_offline_handler_construct (offline_handler, component_registry); + + return offline_handler; +} + + +/** + * e_shell_offline_handler_put_components_offline: + * @offline_handler: A pointer to an EShellOfflineHandler object. + * + * Put the components offline. + **/ +void +e_shell_offline_handler_put_components_offline (EShellOfflineHandler *offline_handler, + EShellView *parent_shell_view) +{ + EShellOfflineHandlerPrivate *priv; + + g_return_if_fail (offline_handler != NULL); + g_return_if_fail (E_IS_SHELL_OFFLINE_HANDLER (offline_handler)); + g_return_if_fail (parent_shell_view == NULL || E_IS_SHELL_VIEW (parent_shell_view)); + + priv = offline_handler->priv; + + priv->procedure_in_progress = TRUE; + priv->parent_shell_view = parent_shell_view; + + gtk_signal_emit (GTK_OBJECT (offline_handler), signals[OFFLINE_PROCEDURE_STARTED]); + + if (! prepare_for_offline (offline_handler)) { + /* FIXME: Maybe do something smarter here. */ + g_warning ("Couldn't put components off-line for some internal error"); + gtk_signal_emit (GTK_OBJECT (offline_handler), signals[OFFLINE_PROCEDURE_FINISHED], FALSE); + return; + } + + if (priv->num_total_connections == 0 && priv->parent_shell_view != NULL) + pop_up_confirmation_dialog (offline_handler); + else + finalize_offline (offline_handler); +} + + +E_MAKE_TYPE (e_shell_offline_handler, "EShellOfflineHandler", EShellOfflineHandler, class_init, init, PARENT_TYPE) |