From b106b13525048195d99bcbde31e0e535972cbab4 Mon Sep 17 00:00:00 2001 From: Ettore Perazzoli Date: Sat, 23 Jun 2001 06:46:13 +0000 Subject: Implemented an Evolution::Activity interface for keeping track of background tasks. The Activity interface is added to the shell, and the status of the various tasks is now displayed in a task bar widget at the bottom of the EShellView. I also implemented a simple test component to test all this stuff. svn path=/trunk/; revision=10434 --- shell/ChangeLog | 58 ++++ shell/Evolution-Activity.idl | 108 +++++++ shell/Evolution-Icon.idl | 23 ++ shell/Evolution.idl | 2 + shell/GNOME_Evolution_TestComponent.oafinfo | 27 ++ shell/Makefile.am | 58 +++- shell/e-activity-handler.c | 459 ++++++++++++++++++++++++++++ shell/e-activity-handler.h | 73 +++++ shell/e-local-folder.c | 304 +++++++++++++++++- shell/e-local-folder.h | 31 +- shell/e-shell-view.c | 148 +++++---- shell/e-shell-view.h | 14 +- shell/e-shell.c | 44 ++- shell/e-shell.h | 1 + shell/e-task-bar.c | 154 ++++++++++ shell/e-task-bar.h | 72 +++++ shell/e-task-widget.c | 190 ++++++++++++ shell/e-task-widget.h | 75 +++++ shell/evolution-test-component.c | 375 +++++++++++++++++++++++ 19 files changed, 2131 insertions(+), 85 deletions(-) create mode 100644 shell/Evolution-Activity.idl create mode 100644 shell/Evolution-Icon.idl create mode 100644 shell/GNOME_Evolution_TestComponent.oafinfo create mode 100644 shell/e-activity-handler.c create mode 100644 shell/e-activity-handler.h create mode 100644 shell/e-task-bar.c create mode 100644 shell/e-task-bar.h create mode 100644 shell/e-task-widget.c create mode 100644 shell/e-task-widget.h create mode 100644 shell/evolution-test-component.c (limited to 'shell') diff --git a/shell/ChangeLog b/shell/ChangeLog index 201c814670..431b87ba5e 100644 --- a/shell/ChangeLog +++ b/shell/ChangeLog @@ -1,3 +1,61 @@ +2001-06-23 Ettore Perazzoli + + * Makefile.am: Add rules to build the test component and an + `install-test-component' target to install it. + + * GNOME_Evolution_TestComponent.oafinfo: New OAF file for the test + component. + + * evolution-test-component.c: New component, currently to test the + activity interfaces. We will be able use it for testing more + things in the future. + + * e-shell.c: New member `activity_handler' in `EShellViewPrivate'. + (init): Init here. + (class_init): Unref. + (setup_activity_interface): New helper function to create an + `EActivityHandler' object and aggregate it to the `EShell'. + (e_shell_construct): Call here. + (e_shell_new_view): Attach the task bar of the newly created view + to the activity handler by using + `e_activity_handler_attach_task_bar()'. + + * e-shell-view.c: Removed old progress bar code. New members + `status_bar' and `task_bar' in EShellViewPrivagte; removed member + `progress_bar'. + (init): Initialize the new fields; remove initialization for + `progress_bar'. + (impl_destroy): Handle the new fields here too. + (setup_task_bar): New helper function to set up an ETaskBar. + (create_status_bar): New helper function to create the status bar + with an ETaskBar in it. + (ui_engine_add_hint_callback): New callback for the "add_int" + BonoboUIEngine signal. + (setup_statusbar_hints): New helper function to set up the menu + hints for the status bar; connect `ui_engine_add_hint_callback()' + here. + (setup_widgets): Call `create_status_bar()' and + `setup_statusbar_hints()'; pack the status bar in a vbox instead + of using the Bonobo status bar stuff, which is kinda broken. + (e_shell_view_get_task_bar): New. + (get_type_for_folder): No use to get the EFolderTypeRegistry here. + + * Evolution.idl: #include and + . + + * Evolution-Activity.idl: New. + + * Evolution-Icon.idl: New. + + * e-activity-handler.c: New. + * e-activity-handler.h: New. + + * e-task-bar.c: New. + * e-task-bar.h: New. + + * e-task-widget.c: New. + * e-task-widget.h: New. + 2001-06-21 Jason Leach * evolution-storage.c (evolution_storage_deregister_on_shell): New diff --git a/shell/Evolution-Activity.idl b/shell/Evolution-Activity.idl new file mode 100644 index 0000000000..a2296d568b --- /dev/null +++ b/shell/Evolution-Activity.idl @@ -0,0 +1,108 @@ +/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Activity management for Evolution. + * + * Authors: + * Ettore Perazzoli + * + * Copyright (C) 2000, 2001 Ximian, Inc. + */ + +module GNOME { +module Evolution { + +interface Activity : Bonobo::Unknown { + typedef long ActivityID; + + enum DialogType { + DIALOG_TYPE_NONE, + DIALOG_TYPE_MESSAGE, + DIALOG_TYPE_WARNING, + DIALOG_TYPE_INPUT, + DIALOG_TYPE_ERROR + }; + + enum DialogAction { + DIALOG_ACTION_DISPLAY, + DIALOG_ACTION_POSTPONE + }; + + exception InvalidIcon {}; + exception IdNotFound {}; + + /* Events propagated through the listener: + + - "Clicked": The status widget has been clicked on. + + - "DisplayProgress": Display a nice progress dialog for this + operation. + + - "DisplayDialog": The dialog that the component has requested + through ::requestDialog() can now be safely displayed. + + - "Cancelled": The user wants the operation to be cancelled. + */ + + /** + * operationStarted: + * @component_id: ID of the component starting the operation. + * @information: Informative string about the operation being performed. + * @cancellable: Whether this operation should be cancellable by + * the user from the shell view. + * @event_listener: Listener which the events for the activity + * widget will be passed to. + * @activity_id: A unique ID for the activity, to be used to update the + * status of the operation. + * @suggest_display: Whether displaying the dialog might be a nice idea. + */ + void operationStarted (in string component_id, + in AnimatedIcon icon, + in string information, + in boolean cancellable, + in Bonobo::Listener event_listener, + out ActivityID activity_id, + out boolean suggest_display) + raises (InvalidIcon); + + /** + * operationProgressing: + * @activity: The unique ID for the activity whose status we want to update. + * @information: New informative string. If empty, the informative string + * isn't changed. + * @progress: A float from 0.0 to 1.0 indicating the status of completion. + * + * Update the status of the specified @activity. + */ + void operationProgressing (in ActivityID activity, + in string information, + in float progress) + raises (IdNotFound); + + /** + * operationFinished: + * @activity: The unique ID for the activity that has been completed. + * + * Report that the specified @activity has been completed. After this + * method is invoked, @activity is not considered to be a valid ID + * anymore. + */ + void operationFinished (in ActivityID activity); + + /** + * requestDialog: + * + * Inform the shell that the specified @activity requires user input + * from a dialog. The returned value specifies whether the shell wants + * the dialog to be shown now (%DIALOG_ACTION_DISPLAY) or postponed + * (%DIALOG_ACTION_POSTPONE). If the return value is + * %DIALOG_ACTION_POSTPONE, the component should wait for the + * "DisplayDialog" event before proceeding further. In that case, the + * shell will flash the label related to this activity, and emit + * "DisplayDialog" through the event source when the user clicks on it. + */ + DialogAction requestDialog (in ActivityID activity, + in DialogType dialog_type); +}; + +}; +}; diff --git a/shell/Evolution-Icon.idl b/shell/Evolution-Icon.idl new file mode 100644 index 0000000000..f8a3d66817 --- /dev/null +++ b/shell/Evolution-Icon.idl @@ -0,0 +1,23 @@ +/* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Icon definition. + * + * Authors: + * Ettore Perazzoli + * + * Copyright (C) 2000, 2001 Ximian, Inc. + */ + +module GNOME { +module Evolution { + +struct Icon { + short width, height; + boolean hasAlpha; + sequence rgba_data; // Row-by-row, left-to-right, top-to-bottom RGBA bytes +}; +typedef sequence AnimatedIcon; + +}; +}; + diff --git a/shell/Evolution.idl b/shell/Evolution.idl index b7d0ccc67f..1408699c6b 100644 --- a/shell/Evolution.idl +++ b/shell/Evolution.idl @@ -10,6 +10,8 @@ #include +#include +#include #include #include #include diff --git a/shell/GNOME_Evolution_TestComponent.oafinfo b/shell/GNOME_Evolution_TestComponent.oafinfo new file mode 100644 index 0000000000..5a203b51b4 --- /dev/null +++ b/shell/GNOME_Evolution_TestComponent.oafinfo @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/shell/Makefile.am b/shell/Makefile.am index 2dcbcb2496..08c24b5268 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = glade importer -INCLUDES = \ +INCLUDES = -O \ -I$(top_srcdir)/widgets \ -I$(top_srcdir)/widgets/misc \ -I$(top_srcdir) \ @@ -16,15 +16,17 @@ INCLUDES = \ # CORBA stuff IDLS = \ - Evolution-LocalStorage.idl \ - Evolution-Offline.idl \ - Evolution-Session.idl \ - Evolution-Shell.idl \ - Evolution-ShellComponent.idl \ - Evolution-ShellComponentDnd.idl \ - Evolution-ShellView.idl \ - Evolution-Storage.idl \ - Evolution-StorageSetView.idl \ + Evolution-Activity.idl \ + Evolution-Icon.idl \ + Evolution-LocalStorage.idl \ + Evolution-Offline.idl \ + Evolution-Session.idl \ + Evolution-Shell.idl \ + Evolution-ShellComponent.idl \ + Evolution-ShellComponentDnd.idl \ + Evolution-ShellView.idl \ + Evolution-Storage.idl \ + Evolution-StorageSetView.idl \ Evolution.idl IDL_GENERATED = \ @@ -88,6 +90,8 @@ libeshell_la_SOURCES = \ bin_PROGRAMS = evolution evolution_SOURCES = \ + e-activity-handler.c \ + e-activity-handler.h \ e-component-registry.c \ e-component-registry.h \ e-corba-storage-registry.c \ @@ -139,6 +143,10 @@ evolution_SOURCES = \ e-storage-set.h \ e-storage.c \ e-storage.h \ + e-task-bar.c \ + e-task-bar.h \ + e-task-widget.c \ + e-task-widget.h \ evolution-storage-set-view.c \ evolution-storage-set-view.h \ evolution-storage-set-view-factory.c \ @@ -155,6 +163,29 @@ evolution_LDADD = \ $(GTKHTML_LIBS) \ $(BONOBO_GNOME_LIBS) +# Test component + +noinst_PROGRAMS = \ + evolution-test-component + +evolution_test_component_SOURCES = \ + evolution-test-component.c + +evolution_test_component_LDADD = \ + libeshell.la \ + $(EXTRA_GNOME_LIBS) \ + $(GNOME_PRINT_LIBS) \ + $(GTKHTML_LIBS) \ + $(BONOBO_GNOME_LIBS) + +install-test-component: evolution-test-component + $(mkinstalldirs) $(DESTDIR)$(bindir) + $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) evolution-test-component $(DESTDIR)$(bindir)/evolution-test-component + $(mkinstalldirs) $(DESTDIR)$(oafdir) + $(INSTALL_DATA) $(srcdir)/GNOME_Evolution_TestComponent.oafinfo $(DESTDIR)$(oafdir)/GNOME_Evolution_TestComponent.oafinfo + +# Misc stuff + oafdir = $(datadir)/oaf oaf_in_files = GNOME_Evolution_Shell.oaf.in oaf_DATA = $(oaf_in_files:.oaf.in=.oaf) @@ -164,7 +195,12 @@ etspec_DATA = e-storage-set-view.etspec @XML_I18N_MERGE_OAF_RULE@ -EXTRA_DIST = $(IDLS) $(oaf_in_files) $(oaf_DATA) $(etspec_DATA) +EXTRA_DIST = \ + $(IDLS) \ + $(oaf_in_files) \ + $(oaf_DATA) \ + $(etspec_DATA) \ + GNOME_Evolution_TestComponent.oafinfo # Purify support diff --git a/shell/e-activity-handler.c b/shell/e-activity-handler.c new file mode 100644 index 0000000000..4ae3e0ccde --- /dev/null +++ b/shell/e-activity-handler.c @@ -0,0 +1,459 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-activity-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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "e-activity-handler.h" + +#include +#include + +#include + + +#define PARENT_TYPE bonobo_x_object_get_type () +static BonoboXObjectClass *parent_class = NULL; + + +#define ICON_SIZE 16 + + +struct _ActivityInfo { + GdkPixbuf *icon_pixbuf; + GNOME_Evolution_Activity_ActivityID id; + CORBA_char *information; + CORBA_boolean cancellable; + Bonobo_Listener event_listener; + CORBA_float progress; +}; +typedef struct _ActivityInfo ActivityInfo; + +struct _EActivityHandlerPrivate { + GNOME_Evolution_Activity_ActivityID next_activity_id; + GList *activity_infos; + GSList *task_bars; +}; + + +/* Utility functions. */ + +static GdkPixbuf * +create_gdk_pixbuf_from_corba_icon (const GNOME_Evolution_Icon *icon) +{ + GdkPixbuf *pixbuf; + GdkPixbuf *scaled_pixbuf; + unsigned char *p; + int src_offset; + int i, j; + int rowstride; + int total_width; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, icon->hasAlpha, 8, icon->width, icon->height); + + if (icon->hasAlpha) + total_width = 4 * icon->width; + else + total_width = 3 * icon->width; + + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + src_offset = 0; + p = gdk_pixbuf_get_pixels (pixbuf); + + for (i = 0; i < icon->height; i++) { + for (j = 0; j < total_width; j++) + p[j] = icon->rgba_data._buffer[src_offset ++]; + p += rowstride; + } + + if (icon->width == ICON_SIZE && icon->height == ICON_SIZE) + return pixbuf; + + scaled_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, icon->hasAlpha, 8, ICON_SIZE, ICON_SIZE); + gdk_pixbuf_scale (pixbuf, scaled_pixbuf, + 0, 0, ICON_SIZE, ICON_SIZE, + 0, 0, (double) ICON_SIZE / icon->width, (double) ICON_SIZE / icon->height, + GDK_INTERP_HYPER); + + gdk_pixbuf_unref (pixbuf); + + return scaled_pixbuf; +} + +static unsigned int +get_new_activity_id (EActivityHandler *activity_handler) +{ + EActivityHandlerPrivate *priv; + + priv = activity_handler->priv; + + return priv->next_activity_id ++; +} + +static GList * +lookup_activity (GList *list, + GNOME_Evolution_Activity_ActivityID activity_id, + int *order_number_return) +{ + GList *p; + int i; + + for (p = list, i = 0; p != NULL; p = p->next, i ++) { + ActivityInfo *activity_info; + + activity_info = (ActivityInfo *) p->data; + if (activity_info->id == activity_id) { + *order_number_return = i; + return p; + } + } + + *order_number_return = -1; + return NULL; +} + + +/* Creating and destroying ActivityInfos. */ + +static ActivityInfo * +activity_info_new (GNOME_Evolution_Activity_ActivityID id, + GdkPixbuf *icon, + const CORBA_char *information, + CORBA_boolean cancellable, + const Bonobo_Listener event_listener) +{ + ActivityInfo *info; + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + info = g_new (ActivityInfo, 1); + info->id = id; + info->icon_pixbuf = gdk_pixbuf_ref (icon); + info->information = CORBA_string_dup (information); + info->cancellable = cancellable; + info->event_listener = CORBA_Object_duplicate (event_listener, &ev); + info->progress = -1.0; /* (Unknown) */ + + CORBA_exception_free (&ev); + + return info; +} + +static void +activity_info_free (ActivityInfo *info) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + CORBA_free (info->information); + CORBA_Object_release (info->event_listener, &ev); + + g_free (info); + + CORBA_exception_free (&ev); +} + +static ETaskWidget * +task_widget_new_from_activity_info (ActivityInfo *activity_info) +{ + GtkWidget *widget; + + widget = e_task_widget_new (activity_info->icon_pixbuf, activity_info->information); + gtk_widget_show (widget); + + return E_TASK_WIDGET (widget); +} + + +/* Task Bar handling. */ + +static void +setup_task_bar (EActivityHandler *activity_handler, + ETaskBar *task_bar) +{ + EActivityHandlerPrivate *priv; + GList *p; + + priv = activity_handler->priv; + + for (p = g_list_last (priv->activity_infos); p != NULL; p = p->prev) { + e_task_bar_prepend_task (task_bar, + task_widget_new_from_activity_info ((ActivityInfo *) p->data)); + } +} + +static void +task_bar_destroy_callback (GtkObject *task_bar_object, + void *data) +{ + ETaskBar *task_bar; + EActivityHandler *activity_handler; + EActivityHandlerPrivate *priv; + + task_bar = E_TASK_BAR (task_bar_object); + + activity_handler = E_ACTIVITY_HANDLER (data); + priv = activity_handler->priv; + + priv->task_bars = g_slist_remove (priv->task_bars, task_bar); +} + + +/* GtkObject methods. */ + +static void +impl_destroy (GtkObject *object) +{ + EActivityHandler *handler; + EActivityHandlerPrivate *priv; + GList *p; + + handler = E_ACTIVITY_HANDLER (object); + priv = handler->priv; + + for (p = priv->activity_infos; p != NULL; p = p->next) { + ActivityInfo *info; + + info = (ActivityInfo *) p->data; + activity_info_free (info); + } + + g_free (priv); + + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + +/* CORBA methods. */ + +static void +impl_operationStarted (PortableServer_Servant servant, + const CORBA_char *component_id, + const GNOME_Evolution_AnimatedIcon *icon, + const CORBA_char *information, + const CORBA_boolean cancellable, + const Bonobo_Listener event_listener, + GNOME_Evolution_Activity_ActivityID *activity_id_return, + CORBA_boolean *suggest_display_return, + CORBA_Environment *ev) +{ + EActivityHandler *activity_handler; + EActivityHandlerPrivate *priv; + ActivityInfo *activity_info; + GdkPixbuf *icon_pixbuf; + unsigned int activity_id; + GSList *p; + + activity_handler = E_ACTIVITY_HANDLER (bonobo_object_from_servant (servant)); + priv = activity_handler->priv; + + if (icon->_length == 0) { + CORBA_exception_set (ev, CORBA_USER_EXCEPTION, + ex_GNOME_Evolution_Activity_InvalidIcon, NULL); + return; + } + + if (icon->_length > 1) + g_warning ("Animated icons are not supported for activities (yet)."); + + icon_pixbuf = create_gdk_pixbuf_from_corba_icon (icon->_buffer); + + activity_id = get_new_activity_id (activity_handler); + + activity_info = activity_info_new (activity_id, icon_pixbuf, information, cancellable, event_listener); + + for (p = priv->task_bars; p != NULL; p = p->next) + e_task_bar_prepend_task (E_TASK_BAR (p->data), + task_widget_new_from_activity_info (activity_info)); + + priv->activity_infos = g_list_prepend (priv->activity_infos, activity_info); + + *activity_id_return = activity_id; +} + +static void +impl_operationProgressing (PortableServer_Servant servant, + const GNOME_Evolution_Activity_ActivityID activity_id, + const CORBA_char *information, + const CORBA_float progress, + CORBA_Environment *ev) +{ + EActivityHandler *activity_handler; + EActivityHandlerPrivate *priv; + ActivityInfo *activity_info; + GList *p; + GSList *sp; + int order_number; + + /* FIXME? The complexity in this function sucks. */ + + activity_handler = E_ACTIVITY_HANDLER (bonobo_object_from_servant (servant)); + priv = activity_handler->priv; + + p = lookup_activity (priv->activity_infos, activity_id, &order_number); + if (p == NULL) { + CORBA_exception_set (ev, CORBA_USER_EXCEPTION, + ex_GNOME_Evolution_Activity_IdNotFound, NULL); + return; + } + + activity_info = (ActivityInfo *) p->data; + + CORBA_free (activity_info->information); + activity_info->information = CORBA_string_dup (information); + + activity_info->progress = progress; + + for (sp = priv->task_bars; sp != NULL; sp = sp->next) { + ETaskBar *task_bar; + ETaskWidget *task_widget; + + task_bar = E_TASK_BAR (sp->data); + task_widget = e_task_bar_get_task_widget (task_bar, order_number); + + e_task_widget_update (task_widget, information, progress); + } +} + +static void +impl_operationFinished (PortableServer_Servant servant, + const GNOME_Evolution_Activity_ActivityID activity_id, + CORBA_Environment *ev) +{ + EActivityHandler *activity_handler; + EActivityHandlerPrivate *priv; + GList *p; + GSList *sp; + int order_number; + + activity_handler = E_ACTIVITY_HANDLER (bonobo_object_from_servant (servant)); + priv = activity_handler->priv; + + p = lookup_activity (priv->activity_infos, activity_id, &order_number); + + activity_info_free ((ActivityInfo *) p->data); + priv->activity_infos = g_list_remove_link (priv->activity_infos, p); + + for (sp = priv->task_bars; sp != NULL; sp = sp->next) { + ETaskBar *task_bar; + + task_bar = E_TASK_BAR (sp->data); + e_task_bar_remove_task (task_bar, order_number); + } +} + +static GNOME_Evolution_Activity_DialogAction +impl_requestDialog (PortableServer_Servant servant, + const GNOME_Evolution_Activity_ActivityID activity_id, + const GNOME_Evolution_Activity_DialogType dialog_type, + CORBA_Environment *ev) +{ + /* FIXME implement. */ + g_warning ("Evolution::Activity::requestDialog not implemented"); + + return GNOME_Evolution_Activity_DIALOG_ACTION_DISPLAY; +} + + +/* GTK+ type stuff. */ + +static void +class_init (GtkObjectClass *object_class) +{ + EActivityHandlerClass *handler_class; + + parent_class = gtk_type_class (PARENT_TYPE); + + object_class->destroy = impl_destroy; + + handler_class = E_ACTIVITY_HANDLER_CLASS (object_class); + handler_class->epv.operationStarted = impl_operationStarted; + handler_class->epv.operationProgressing = impl_operationProgressing; + handler_class->epv.operationFinished = impl_operationFinished; + handler_class->epv.requestDialog = impl_requestDialog; +} + +static void +init (EActivityHandler *activity_handler) +{ + EActivityHandlerPrivate *priv; + + priv = g_new (EActivityHandlerPrivate, 1); + priv->next_activity_id = 0; + priv->activity_infos = NULL; + priv->task_bars = NULL; + + activity_handler->priv = priv; +} + + +void +e_activity_handler_construct (EActivityHandler *activity_handler) +{ + g_return_if_fail (activity_handler != NULL); + g_return_if_fail (E_IS_ACTIVITY_HANDLER (activity_handler)); + + /* Nothing to do here. */ +} + +EActivityHandler * +e_activity_handler_new (void) +{ + EActivityHandler *activity_handler; + + activity_handler = gtk_type_new (e_activity_handler_get_type ()); + e_activity_handler_construct (activity_handler); + + return activity_handler; +} + + +void +e_activity_handler_attach_task_bar (EActivityHandler *activity_handler, + ETaskBar *task_bar) +{ + EActivityHandlerPrivate *priv; + + g_return_if_fail (activity_handler != NULL); + g_return_if_fail (E_IS_ACTIVITY_HANDLER (activity_handler)); + g_return_if_fail (task_bar != NULL); + g_return_if_fail (E_IS_TASK_BAR (task_bar)); + + priv = activity_handler->priv; + + gtk_signal_connect_while_alive (GTK_OBJECT (task_bar), "destroy", + GTK_SIGNAL_FUNC (task_bar_destroy_callback), activity_handler, + GTK_OBJECT (activity_handler)); + + priv->task_bars = g_slist_prepend (priv->task_bars, task_bar); + + setup_task_bar (activity_handler, task_bar); +} + + +E_MAKE_X_TYPE (e_activity_handler, "EActivityHandler", EActivityHandler, class_init, init, PARENT_TYPE, + POA_GNOME_Evolution_Activity__init, + GTK_STRUCT_OFFSET (EActivityHandlerClass, epv)) diff --git a/shell/e-activity-handler.h b/shell/e-activity-handler.h new file mode 100644 index 0000000000..0a131bc5e9 --- /dev/null +++ b/shell/e-activity-handler.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-activity-handler.h + * + * 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 + */ + +#ifndef _E_ACTIVITY_HANDLER_H_ +#define _E_ACTIVITY_HANDLER_H_ + +#include "Evolution.h" + +#include "e-task-bar.h" + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define E_TYPE_ACTIVITY_HANDLER (e_activity_handler_get_type ()) +#define E_ACTIVITY_HANDLER(obj) (GTK_CHECK_CAST ((obj), E_TYPE_ACTIVITY_HANDLER, EActivityHandler)) +#define E_ACTIVITY_HANDLER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_ACTIVITY_HANDLER, EActivityHandlerClass)) +#define E_IS_ACTIVITY_HANDLER(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_ACTIVITY_HANDLER)) +#define E_IS_ACTIVITY_HANDLER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_ACTIVITY_HANDLER)) + + +typedef struct _EActivityHandler EActivityHandler; +typedef struct _EActivityHandlerPrivate EActivityHandlerPrivate; +typedef struct _EActivityHandlerClass EActivityHandlerClass; + +struct _EActivityHandler { + BonoboXObject parent; + + EActivityHandlerPrivate *priv; +}; + +struct _EActivityHandlerClass { + BonoboXObjectClass parent_class; + + POA_GNOME_Evolution_Activity__epv epv; +}; + + +GtkType e_activity_handler_get_type (void); +void e_activity_handler_construct (EActivityHandler *activity_hanlder); +EActivityHandler *e_activity_handler_new (void); + +void e_activity_handler_attach_task_bar (EActivityHandler *activity_hanlder, + ETaskBar *task_bar); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _E_ACTIVITY_HANDLER_H_ */ diff --git a/shell/e-local-folder.c b/shell/e-local-folder.c index 73ae6734da..21f6e6e1d8 100644 --- a/shell/e-local-folder.c +++ b/shell/e-local-folder.c @@ -60,11 +60,142 @@ static EFolderClass *parent_class = NULL; #define URI_PREFIX "file://" #define URI_PREFIX_LEN 7 +/* This provides the name and the description for a specific locale. */ +struct _I18nInfo { + char *language_id; + char *name; + char *description; +}; +typedef struct _I18nInfo I18nInfo; + struct _ELocalFolderPrivate { - int dummy; + GHashTable *language_id_to_i18n_info; }; +/* Locale information. */ + +static char *global_language_id = NULL; + + +/* I18nInfo handling. */ + +static I18nInfo * +i18n_info_new (const char *language_id, + const char *name, + const char *description) +{ + I18nInfo *info; + + info = g_new (I18nInfo, 1); + info->language_id = g_strdup (language_id); + info->name = g_strdup (name); + info->description = g_strdup (description); + + return info; +} + +static void +i18n_info_free (I18nInfo *info) +{ + g_free (info->language_id); + g_free (info->name); + g_free (info->description); + + g_free (info); +} + + +/* Language ID -> I18nInfo hash table handling. */ + +static void +add_i18n_info_to_hash (GHashTable *language_id_to_i18n_info_hash, + I18nInfo *i18n_info) +{ + I18nInfo *existing_i18n_info; + + existing_i18n_info = (I18nInfo *) g_hash_table_lookup (language_id_to_i18n_info_hash, + i18n_info->language_id); + if (existing_i18n_info != NULL) { + g_hash_table_remove (language_id_to_i18n_info_hash, + i18n_info->language_id); + i18n_info_free (existing_i18n_info); + } + + g_hash_table_insert (language_id_to_i18n_info_hash, i18n_info->language_id, i18n_info); +} + +static void +language_id_to_i18n_info_hash_foreach_free (void *key, + void *value, + void *data) +{ + i18n_info_free ((I18nInfo *) value); +} + +static I18nInfo * +get_i18n_info_for_language (ELocalFolder *local_folder, + const char *language_id) +{ + ELocalFolderPrivate *priv; + I18nInfo *i18n_info; + + priv = local_folder->priv; + + if (language_id == NULL) + language_id = global_language_id; + + i18n_info = g_hash_table_lookup (priv->language_id_to_i18n_info, language_id); + + /* For locale info like `en_UK@yadda', we try to use `en' as a backup. */ + /* Note: this is exactly the same thing that gnome-config does with the + I18N value handling. I hope it works. */ + if (i18n_info == NULL) { + size_t n; + + n = strcspn (language_id, "@_"); + if (language_id[n] != '\0') { + char *simplified_language_id; + + simplified_language_id = g_strndup (language_id, n); + i18n_info = g_hash_table_lookup (priv->language_id_to_i18n_info, + simplified_language_id); + } + } + + return i18n_info; +} + + +/* Locale handling. */ + +static void +setup_global_language_id (void) +{ + /* FIXME: Implement. */ + global_language_id = "C"; +} + +/* Update the EFolder attributes according to the current locale. */ +static void +update_for_global_locale (ELocalFolder *local_folder) +{ + I18nInfo *i18n_info; + + i18n_info = get_i18n_info_for_language (local_folder, NULL); + + if (i18n_info == NULL) + i18n_info = get_i18n_info_for_language (local_folder, "C"); + + g_assert (i18n_info != NULL); + + e_folder_set_name (E_FOLDER (local_folder), i18n_info->name); + e_folder_set_description (E_FOLDER (local_folder), i18n_info->description); +} + + +/* XML tree handling. */ + static char * get_string_value (xmlNode *node, const char *name) @@ -81,13 +212,51 @@ get_string_value (xmlNode *node, if (p == NULL) return NULL; - xml_string = xmlNodeListGetString (node->doc, p, 1); + xml_string = xmlNodeListGetString (node->doc, p, TRUE); retval = g_strdup ((char *) xml_string); xmlFree (xml_string); return retval; } +static void +retrieve_info_item (ELocalFolder *local_folder, + xmlNode *node) +{ + xmlChar *lang; + char *name; + char *description; + + lang = xmlGetProp (node, "lang"); + name = get_string_value (node, "name"); + description = get_string_value (node, "description"); + + if (lang == NULL) { + e_local_folder_add_i18n_info (local_folder, "C", name, description); + } else { + e_local_folder_add_i18n_info (local_folder, lang, name, description); + xmlFree (lang); + } + + g_free (name); + g_free (description); +} + +static void +retrieve_info (ELocalFolder *local_folder, + xmlNode *root_xml_node) +{ + ELocalFolderPrivate *priv; + xmlNode *p; + + priv = local_folder->priv; + + for (p = root_xml_node->childs; p != NULL; p = p->next) { + if (xmlStrcmp (p->name, "info") == 0) + retrieve_info_item (local_folder, p); + } +} + static gboolean construct_loading_metadata (ELocalFolder *local_folder, const char *path) @@ -96,7 +265,6 @@ construct_loading_metadata (ELocalFolder *local_folder, xmlDoc *doc; xmlNode *root; char *type; - char *description; char *metadata_path; char *physical_uri; @@ -118,12 +286,16 @@ construct_loading_metadata (ELocalFolder *local_folder, } type = get_string_value (root, "type"); - description = get_string_value (root, "description"); - - e_folder_construct (folder, g_basename (path), type, description); + if (type == NULL) { + g_free (metadata_path); + xmlFreeDoc (doc); + return FALSE; + } + e_local_folder_construct (local_folder, g_basename (path), type, NULL); g_free (type); - g_free (description); + + retrieve_info (local_folder, root); xmlFreeDoc (doc); @@ -180,7 +352,18 @@ save_metadata (ELocalFolder *local_folder) static void destroy (GtkObject *object) { - /* No ELocalFolder-specific data to free. */ + ELocalFolder *local_folder; + ELocalFolderPrivate *priv; + + local_folder = E_LOCAL_FOLDER (object); + priv = local_folder->priv; + + g_hash_table_foreach (priv->language_id_to_i18n_info, + language_id_to_i18n_info_hash_foreach_free, + NULL); + g_hash_table_destroy (priv->language_id_to_i18n_info); + + g_free (priv); (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } @@ -195,11 +378,19 @@ class_init (ELocalFolderClass *klass) object_class = GTK_OBJECT_CLASS (klass); object_class->destroy = destroy; + + setup_global_language_id (); } static void init (ELocalFolder *local_folder) { + ELocalFolderPrivate *priv; + + priv = g_new (ELocalFolderPrivate, 1); + priv->language_id_to_i18n_info = g_hash_table_new (g_str_hash, g_str_equal); + + local_folder->priv = priv; } @@ -209,12 +400,20 @@ e_local_folder_construct (ELocalFolder *local_folder, const char *type, const char *description) { + ELocalFolderPrivate *priv; + I18nInfo *i18n_info; + g_return_if_fail (local_folder != NULL); g_return_if_fail (E_IS_LOCAL_FOLDER (local_folder)); g_return_if_fail (name != NULL); g_return_if_fail (type != NULL); + priv = local_folder->priv; + e_folder_construct (E_FOLDER (local_folder), name, type, description); + + i18n_info = i18n_info_new ("C", name, description); + add_i18n_info_to_hash (priv->language_id_to_i18n_info, i18n_info); } EFolder * @@ -261,5 +460,94 @@ e_local_folder_save (ELocalFolder *local_folder) return save_metadata (local_folder); } + +/** + * e_local_folder_add_i18n_info: + * @local_folder: A pointer to an ELocalFolder object + * @language_id: An I1I8N locale ID + * @name: Name for @local_folder in the specified @language_id + * @description: Description for @local_folder in the specified @language_id + * + * Set the @name and @description for the specified @language_id locale. + **/ +void +e_local_folder_add_i18n_info (ELocalFolder *local_folder, + const char *language_id, + const char *name, + const char *description) +{ + ELocalFolderPrivate *priv; + I18nInfo *info; + + g_return_if_fail (local_folder != NULL); + g_return_if_fail (E_IS_LOCAL_FOLDER (local_folder)); + g_return_if_fail (language_id != NULL); + g_return_if_fail (name != NULL || description != NULL); + + priv = local_folder->priv; + + info = i18n_info_new (language_id, name, description); + add_i18n_info_to_hash (priv->language_id_to_i18n_info, info); + + update_for_global_locale (local_folder); +} + +/** + * e_local_folder_get_i18n_info: + * @local_folder: A pointer to an ELocalFolder object + * @language_id: The ID of the language whose locale we want to retrieve name + * and description for + * @language_id_return: The actual locale ID that the name and description are + * saved under (e.g. if you ask for an "en_UK@yadda", we might give you the + * info for just "en") + * @name_return: A pointer to a pointer that will point to the i18nized name on + * return. Can be NULL. + * @description_return: A pointer to a pointer that will point to the i18n + * description on return. Can be NULL. + * + * Retrieve the name and description for @local_folder in the specified locale. + * + * Return value: %TRUE if some info is found for that @language_id, %FALSE + * otherwise. + **/ +gboolean +e_local_folder_get_i18n_info (ELocalFolder *local_folder, + const char *language_id, + const char **language_id_return, + const char **name_return, + const char **description_return) +{ + ELocalFolderPrivate *priv; + I18nInfo *i18n_info; + + g_return_val_if_fail (local_folder != NULL, FALSE); + g_return_val_if_fail (E_IS_LOCAL_FOLDER (local_folder), FALSE); + g_return_val_if_fail (language_id != NULL, FALSE); + + priv = local_folder->priv; + + i18n_info = get_i18n_info_for_language (local_folder, language_id); + + if (i18n_info == NULL) { + if (language_id_return != NULL) + *language_id_return = NULL; + if (name_return != NULL) + *name_return = NULL; + if (description_return != NULL) + *description_return = NULL; + + return FALSE; + } + + if (language_id_return != NULL) + *language_id_return = i18n_info->language_id; + if (name_return != NULL) + *name_return = i18n_info->name; + if (description_return != NULL) + *description_return = i18n_info->description; + + return TRUE; +} + E_MAKE_TYPE (e_local_folder, "ELocalFolder", ELocalFolder, class_init, init, PARENT_TYPE) diff --git a/shell/e-local-folder.h b/shell/e-local-folder.h index b57abe8d6f..b41e0608ab 100644 --- a/shell/e-local-folder.h +++ b/shell/e-local-folder.h @@ -44,9 +44,12 @@ extern "C" { typedef struct _ELocalFolder ELocalFolder; typedef struct _ELocalFolderClass ELocalFolderClass; +typedef struct _ELocalFolderPrivate ELocalFolderPrivate; struct _ELocalFolder { EFolder parent; + + ELocalFolderPrivate *priv; }; struct _ELocalFolderClass { @@ -55,15 +58,25 @@ struct _ELocalFolderClass { GtkType e_local_folder_get_type (void); -void e_local_folder_construct (ELocalFolder *local_folder, - const char *name, - const char *type, - const char *description); -EFolder *e_local_folder_new (const char *name, - const char *type, - const char *description); -EFolder *e_local_folder_new_from_path (const char *physical_path); -gboolean e_local_folder_save (ELocalFolder *local_folder); +void e_local_folder_construct (ELocalFolder *local_folder, + const char *name, + const char *type, + const char *description); +EFolder *e_local_folder_new (const char *name, + const char *type, + const char *description); +EFolder *e_local_folder_new_from_path (const char *physical_path); +gboolean e_local_folder_save (ELocalFolder *local_folder); + +void e_local_folder_add_i18n_info (ELocalFolder *local_folder, + const char *language_id, + const char *name, + const char *description); +gboolean e_local_folder_get_i18n_info (ELocalFolder *local_folder, + const char *language_id, + const char **language_id_return, + const char **name_return, + const char **description_return); #ifdef __cplusplus } diff --git a/shell/e-shell-view.c b/shell/e-shell-view.c index 9ca860ff5f..d2d29c26dc 100644 --- a/shell/e-shell-view.c +++ b/shell/e-shell-view.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -100,9 +101,10 @@ struct _EShellViewPrivate { GtkWidget *storage_set_view_box; /* The status bar widgetry. */ + GtkWidget *status_bar; GtkWidget *offline_toggle; GtkWidget *offline_toggle_pixmap; - GtkWidget *progress_bar; + GtkWidget *task_bar; /* The view we have already open. */ GHashTable *uri_to_control; @@ -116,12 +118,6 @@ struct _EShellViewPrivate { EShellViewSubwindowMode shortcut_bar_mode; EShellViewSubwindowMode folder_bar_mode; - /* Timeout ID for the progress bar. */ - int progress_bar_timeout_id; - - /* Status of the progress bar. */ - int progress_bar_value; - /* List of sockets we created. */ GList *sockets; }; @@ -617,7 +613,6 @@ static void setup_offline_toggle (EShellView *shell_view) { EShellViewPrivate *priv; - BonoboControl *control; GtkWidget *toggle; GtkWidget *pixmap; @@ -637,46 +632,78 @@ setup_offline_toggle (EShellView *shell_view) gtk_widget_show (toggle); gtk_widget_show (pixmap); - control = bonobo_control_new (toggle); - g_return_if_fail (control != NULL); - - bonobo_ui_component_object_set (priv->ui_component, "/status/OfflineToggle", - BONOBO_OBJREF (control), - NULL); - bonobo_object_unref (BONOBO_OBJECT (control)); - priv->offline_toggle = toggle; priv->offline_toggle_pixmap = pixmap; update_offline_toggle_status (shell_view); + + g_assert (priv->status_bar != NULL); + + gtk_box_pack_start (GTK_BOX (priv->status_bar), priv->offline_toggle, FALSE, TRUE, 0); +} + +static void +setup_task_bar (EShellView *task_bar) +{ + EShellViewPrivate *priv; + + priv = task_bar->priv; + + priv->task_bar = e_task_bar_new (); + + g_assert (priv->status_bar != NULL); + + gtk_box_pack_start (GTK_BOX (priv->status_bar), priv->task_bar, TRUE, TRUE, 0); + gtk_widget_show (priv->task_bar); } static void -setup_progress_bar (EShellView *shell_view) +create_status_bar (EShellView *shell_view) { EShellViewPrivate *priv; - GtkProgressBar *progress_bar; - BonoboControl *control; priv = shell_view->priv; - progress_bar = (GTK_PROGRESS_BAR (gtk_progress_bar_new ())); + priv->status_bar = gtk_hbox_new (FALSE, 2); + gtk_widget_show (priv->status_bar); - gtk_progress_bar_set_orientation (progress_bar, GTK_PROGRESS_LEFT_TO_RIGHT); - gtk_progress_bar_set_bar_style (progress_bar, GTK_PROGRESS_CONTINUOUS); - - priv->progress_bar = GTK_WIDGET (progress_bar); - gtk_widget_show (priv->progress_bar); + setup_offline_toggle (shell_view); + setup_task_bar (shell_view); +} - control = bonobo_control_new (priv->progress_bar); - g_return_if_fail (control != NULL); + +/* Menu hints for the status bar. */ + +static void +ui_engine_add_hint_callback (BonoboUIEngine *engine, + const char *str, + void *data) +{ + EShellView *shell_view; + EShellViewPrivate *priv; + + shell_view = E_SHELL_VIEW (data); + priv = shell_view->priv; + + g_print ("Hint -- %s\n", str); + + /* FIXME: Implement me. */ +} - bonobo_ui_component_object_set (priv->ui_component, "/status/Progress", - BONOBO_OBJREF (control), - NULL); - bonobo_object_unref (BONOBO_OBJECT (control)); +static void +setup_statusbar_hints (EShellView *shell_view) +{ + EShellViewPrivate *priv; + + priv = shell_view->priv; + + g_assert (priv->status_bar != NULL); + + gtk_signal_connect (GTK_OBJECT (bonobo_window_get_ui_engine (BONOBO_WINDOW (shell_view))), "add_hint", + GTK_SIGNAL_FUNC (ui_engine_add_hint_callback), shell_view); } + void e_shell_view_set_current_shortcuts_group_num (EShellView *shell_view, int group_num) { @@ -711,17 +738,10 @@ static void setup_widgets (EShellView *shell_view) { EShellViewPrivate *priv; + GtkWidget *contents_vbox; priv = shell_view->priv; - /* The offline/online button toggle. */ - - setup_offline_toggle (shell_view); - - /* The progress bar. */ - - setup_progress_bar (shell_view); - /* The shortcut bar. */ priv->shortcut_bar = e_shortcuts_new_view (e_shell_get_shortcuts (priv->shell)); @@ -773,7 +793,19 @@ setup_widgets (EShellView *shell_view) e_paned_pack2 (E_PANED (priv->hpaned), priv->view_vbox, TRUE, FALSE); e_paned_set_position (E_PANED (priv->hpaned), DEFAULT_SHORTCUT_BAR_WIDTH); - bonobo_window_set_contents (BONOBO_WINDOW (shell_view), priv->hpaned); + /* The status bar. */ + + create_status_bar (shell_view); + setup_statusbar_hints (shell_view); + + /* The contents. */ + + contents_vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (contents_vbox), priv->hpaned, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (contents_vbox), priv->status_bar, FALSE, TRUE, 0); + gtk_widget_show (contents_vbox); + + bonobo_window_set_contents (BONOBO_WINDOW (shell_view), contents_vbox); /* Show stuff. */ @@ -786,6 +818,7 @@ setup_widgets (EShellView *shell_view) gtk_widget_show (priv->view_hpaned); gtk_widget_show (priv->view_vbox); gtk_widget_show (priv->view_title_bar); + gtk_widget_show (priv->status_bar); /* By default, both the folder bar and shortcut bar are visible. */ priv->shortcut_bar_mode = E_SHELL_VIEW_SUBWINDOW_STICKY; @@ -839,16 +872,10 @@ destroy (GtkObject *object) g_hash_table_foreach (priv->uri_to_control, hash_forall_destroy_control, NULL); g_hash_table_destroy (priv->uri_to_control); - gtk_widget_destroy (priv->offline_toggle); - gtk_widget_destroy (priv->progress_bar); - bonobo_object_unref (BONOBO_OBJECT (priv->ui_component)); g_free (priv->uri); - if (priv->progress_bar_timeout_id != 0) - gtk_timeout_remove (priv->progress_bar_timeout_id); - g_free (priv); (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); @@ -929,9 +956,10 @@ init (EShellView *shell_view) priv->storage_set_view_box = NULL; priv->shortcut_bar = NULL; - priv->progress_bar = NULL; + priv->status_bar = NULL; priv->offline_toggle = NULL; priv->offline_toggle_pixmap = NULL; + priv->task_bar = NULL; priv->shortcut_bar_mode = E_SHELL_VIEW_SUBWINDOW_HIDDEN; priv->folder_bar_mode = E_SHELL_VIEW_SUBWINDOW_HIDDEN; @@ -941,14 +969,13 @@ init (EShellView *shell_view) priv->uri_to_control = g_hash_table_new (g_str_hash, g_str_equal); - priv->progress_bar_timeout_id = 0; - priv->progress_bar_value = 0; priv->sockets = NULL; shell_view->priv = priv; } +#if 0 /* Progress bar handling. */ #define PROGRESS_BAR_TIMEOUT 80 @@ -1013,6 +1040,8 @@ stop_progress_bar (EShellView *shell_view) gtk_progress_set_value (GTK_PROGRESS (progress_bar), 0); } +#endif + /* EvolutionShellView interface callbacks. */ @@ -1045,10 +1074,12 @@ corba_interface_set_message_cb (EvolutionShellView *shell_view, g_free (status); +#if 0 if (busy) start_progress_bar (E_SHELL_VIEW (data)); else stop_progress_bar (E_SHELL_VIEW (data)); +#endif } static void @@ -1063,7 +1094,9 @@ corba_interface_unset_message_cb (EvolutionShellView *shell_view, bonobo_ui_component_set_status (view->priv->ui_component, "", NULL); +#if 0 stop_progress_bar (E_SHELL_VIEW (data)); +#endif } static void @@ -1222,6 +1255,11 @@ e_shell_view_construct (EShellView *shell_view, return view; } +/* WARNING: Don't use `e_shell_view_new()' to create new views for the shell + unless you know what you are doing; this is just the standard GTK+ + constructor thing and it won't allow the shell to do the required + bookkeeping for the created views. Instead, the right way to create a new + view is calling `e_shell_new_view()'. */ EShellView * e_shell_view_new (EShell *shell) { @@ -1644,7 +1682,6 @@ get_type_for_folder (EShellView *shell_view, { EShellViewPrivate *priv; EStorageSet *storage_set; - EFolderTypeRegistry *folder_type_registry; EFolder *folder; priv = shell_view->priv; @@ -1656,8 +1693,6 @@ get_type_for_folder (EShellView *shell_view, *physical_uri_return = e_folder_get_physical_uri (folder); - folder_type_registry = e_shell_get_folder_type_registry (e_shell_view_get_shell (shell_view)); - return e_folder_get_type_string (folder); } @@ -1989,6 +2024,15 @@ e_shell_view_get_folder_bar_mode (EShellView *shell_view) } +ETaskBar * +e_shell_view_get_task_bar (EShellView *shell_view) +{ + g_return_val_if_fail (shell_view != NULL, NULL); + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return E_TASK_BAR (shell_view->priv->task_bar); +} + EShell * e_shell_view_get_shell (EShellView *shell_view) { diff --git a/shell/e-shell-view.h b/shell/e-shell-view.h index c7ee7fa4af..d22e875b0d 100644 --- a/shell/e-shell-view.h +++ b/shell/e-shell-view.h @@ -24,9 +24,9 @@ #ifndef _E_SHELL_VIEW_H_ #define _E_SHELL_VIEW_H_ -#include +#include "e-task-bar.h" -#include "e-shell.h" +#include #ifdef __cplusplus extern "C" { @@ -44,6 +44,8 @@ typedef struct _EShellView EShellView; typedef struct _EShellViewPrivate EShellViewPrivate; typedef struct _EShellViewClass EShellViewClass; +#include "e-shell.h" + enum _EShellViewSubwindowMode { E_SHELL_VIEW_SUBWINDOW_HIDDEN, E_SHELL_VIEW_SUBWINDOW_TRANSIENT, @@ -66,6 +68,12 @@ struct _EShellViewClass { }; +/* WARNING: Don't use `e_shell_view_new()' to create new views for the shell + unless you know what you are doing; this is just the standard GTK+ + constructor thing and it won't allow the shell to do the required + bookkeeping for the created views. Instead, the right way to create a new + view is calling `e_shell_new_view()'. */ + GtkType e_shell_view_get_type (void); EShellView *e_shell_view_construct (EShellView *shell_view, EShell *shell); @@ -84,7 +92,7 @@ void e_shell_view_set_folder_bar_mode (EShellView EShellViewSubwindowMode mode); EShellViewSubwindowMode e_shell_view_get_folder_bar_mode (EShellView *shell_view); - +ETaskBar *e_shell_view_get_task_bar (EShellView *shell_view); EShell *e_shell_view_get_shell (EShellView *shell_view); BonoboUIComponent *e_shell_view_get_bonobo_ui_component (EShellView *shell_view); BonoboUIContainer *e_shell_view_get_bonobo_ui_container (EShellView *shell_view); diff --git a/shell/e-shell.c b/shell/e-shell.c index 405770aeb1..01392b0025 100644 --- a/shell/e-shell.c +++ b/shell/e-shell.c @@ -38,6 +38,7 @@ #include "Evolution.h" +#include "e-activity-handler.h" #include "e-component-registry.h" #include "e-corba-storage-registry.h" #include "e-folder-type-registry.h" @@ -75,6 +76,9 @@ struct _EShellPrivate { ECorbaStorageRegistry *corba_storage_registry; + /* ::Activity interface handler. */ + EActivityHandler *activity_handler; + /* 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; @@ -344,6 +348,26 @@ register_shell (EShell *shell, return oaf_active_server_register (iid, corba_object); } + +/* 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)); + + bonobo_object_ref (BONOBO_OBJECT (activity_handler)); + priv->activity_handler = activity_handler; +} + /* Initialization of the storages. */ @@ -618,6 +642,9 @@ destroy (GtkObject *object) if (priv->corba_storage_registry != NULL) bonobo_object_unref (BONOBO_OBJECT (priv->corba_storage_registry)); + if (priv->activity_handler != NULL) + bonobo_object_unref (BONOBO_OBJECT (priv->activity_handler)); + /* FIXME. Maybe we should do something special here. */ if (priv->offline_handler != NULL) gtk_object_unref (GTK_OBJECT (priv->offline_handler)); @@ -705,6 +732,7 @@ init (EShell *shell) priv->component_registry = NULL; priv->folder_type_registry = NULL; priv->corba_storage_registry = NULL; + priv->activity_handler = NULL; priv->offline_handler = NULL; priv->crash_type_names = NULL; priv->line_status = E_SHELL_LINE_STATUS_ONLINE; @@ -775,7 +803,12 @@ e_shell_construct (EShell *shell, /* The local storage depends on the component registry. */ setup_local_storage (shell); - /* Now that we have a local storage, we can tell the components we are here. */ + /* 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); + + /* Now that we have a local storage and an ::Activity interface, we can + tell the components we are here. */ set_owner_on_components (shell); /* Run the intelligent importers to find see if any data needs @@ -858,10 +891,14 @@ e_shell_new_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_widget_show (GTK_WIDGET (view)); @@ -875,6 +912,9 @@ e_shell_new_view (EShell *shell, 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; } @@ -1130,7 +1170,7 @@ e_shell_restore_from_settings (EShell *shell) EShellView *view; /* FIXME: restore the URI here. There should be an - e_shell_view_new_from_configuration() thingie. */ + e_shell_new_view_from_configuration() thingie. */ view = e_shell_new_view (shell, NULL); if (! e_shell_view_load_settings (view, i)) diff --git a/shell/e-shell.h b/shell/e-shell.h index 674ce24148..4786c49767 100644 --- a/shell/e-shell.h +++ b/shell/e-shell.h @@ -37,6 +37,7 @@ typedef struct _EShellPrivate EShellPrivate; typedef struct _EShellClass EShellClass; #include "Evolution.h" + #include "e-shortcuts.h" #include "e-shell-view.h" #include "e-local-storage.h" diff --git a/shell/e-task-bar.c b/shell/e-task-bar.c new file mode 100644 index 0000000000..efc552f661 --- /dev/null +++ b/shell/e-task-bar.c @@ -0,0 +1,154 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-task-bar.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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "e-task-bar.h" + +#include + + +#define PARENT_TYPE gtk_hbox_get_type () +static GtkHBoxClass *parent_class = NULL; + + +/* GtkObject methods. */ + +static void +impl_destroy (GtkObject *object) +{ + ETaskBar *task_bar; + + task_bar = E_TASK_BAR (object); + + /* Nothing to do here. */ + + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + +static void +class_init (GtkObjectClass *object_class) +{ + parent_class = gtk_type_class (PARENT_TYPE); + + object_class->destroy = impl_destroy; +} + +static void +init (ETaskBar *task_bar) +{ + /* Nothing to do here. */ +} + + +void +e_task_bar_construct (ETaskBar *task_bar) +{ + g_return_if_fail (task_bar != NULL); + g_return_if_fail (E_IS_TASK_BAR (task_bar)); + + /* Nothing to do here. */ +} + +GtkWidget * +e_task_bar_new (void) +{ + ETaskBar *task_bar; + + task_bar = gtk_type_new (e_task_bar_get_type ()); + e_task_bar_construct (task_bar); + + return GTK_WIDGET (task_bar); +} + +void +e_task_bar_prepend_task (ETaskBar *task_bar, + ETaskWidget *task_widget) +{ + GtkBoxChild *child_info; + GtkBox *box; + + g_return_if_fail (task_bar != NULL); + g_return_if_fail (E_IS_TASK_BAR (task_bar)); + g_return_if_fail (task_widget != NULL); + g_return_if_fail (E_IS_TASK_WIDGET (task_widget)); + + /* Hah hah. GTK+ sucks. This is adapted from `gtkhbox.c'. */ + + child_info = g_new (GtkBoxChild, 1); + child_info->widget = GTK_WIDGET (task_widget); + child_info->padding = 0; + child_info->expand = TRUE; + child_info->fill = TRUE; + child_info->pack = GTK_PACK_START; + + box = GTK_BOX (task_bar); + + box->children = g_list_append (box->children, child_info); + + gtk_widget_set_parent (GTK_WIDGET (task_widget), GTK_WIDGET (task_bar)); + + if (GTK_WIDGET_REALIZED (task_bar)) + gtk_widget_realize (GTK_WIDGET (task_widget)); + + if (GTK_WIDGET_VISIBLE (task_bar) && GTK_WIDGET_VISIBLE (task_widget)) { + if (GTK_WIDGET_MAPPED (task_bar)) + gtk_widget_map (GTK_WIDGET (task_widget)); + gtk_widget_queue_resize (GTK_WIDGET (task_widget)); + } +} + +void +e_task_bar_remove_task (ETaskBar *task_bar, + int n) +{ + ETaskWidget *task_widget; + + g_return_if_fail (task_bar != NULL); + g_return_if_fail (E_IS_TASK_BAR (task_bar)); + g_return_if_fail (n >= 0); + + task_widget = e_task_bar_get_task_widget (task_bar, n); + + gtk_container_remove (GTK_CONTAINER (task_bar), GTK_WIDGET (task_widget)); +} + +ETaskWidget * +e_task_bar_get_task_widget (ETaskBar *task_bar, + int n) +{ + GtkBoxChild *child_info; + + g_return_val_if_fail (task_bar != NULL, NULL); + g_return_val_if_fail (E_IS_TASK_BAR (task_bar), NULL); + + child_info = (GtkBoxChild *) g_list_nth (GTK_BOX (task_bar)->children, n)->data; + + return E_TASK_WIDGET (child_info->widget); +} + + +E_MAKE_TYPE (e_task_bar, "ETaskBar", ETaskBar, class_init, init, PARENT_TYPE) diff --git a/shell/e-task-bar.h b/shell/e-task-bar.h new file mode 100644 index 0000000000..14e4c961d7 --- /dev/null +++ b/shell/e-task-bar.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-task-bar.h + * + * 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 + */ + +#ifndef _E_TASK_BAR_H_ +#define _E_TASK_BAR_H_ + +#include "e-task-widget.h" + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define E_TYPE_TASK_BAR (e_task_bar_get_type ()) +#define E_TASK_BAR(obj) (GTK_CHECK_CAST ((obj), E_TYPE_TASK_BAR, ETaskBar)) +#define E_TASK_BAR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_TASK_BAR, ETaskBarClass)) +#define E_IS_TASK_BAR(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_TASK_BAR)) +#define E_IS_TASK_BAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_TASK_BAR)) + + +typedef struct _ETaskBar ETaskBar; +typedef struct _ETaskBarPrivate ETaskBarPrivate; +typedef struct _ETaskBarClass ETaskBarClass; + +struct _ETaskBar { + GtkHBox parent; +}; + +struct _ETaskBarClass { + GtkHBoxClass parent_class; +}; + + +GtkType e_task_bar_get_type (void); +void e_task_bar_construct (ETaskBar *task_bar); +GtkWidget *e_task_bar_new (void); + +void e_task_bar_prepend_task (ETaskBar *task_bar, + ETaskWidget *task_widget); +void e_task_bar_remove_task (ETaskBar *task_bar, + int n); + +ETaskWidget *e_task_bar_get_task_widget (ETaskBar *task_bar, + int n); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _E_TASK_BAR_H_ */ diff --git a/shell/e-task-widget.c b/shell/e-task-widget.c new file mode 100644 index 0000000000..e67baaf0e7 --- /dev/null +++ b/shell/e-task-widget.c @@ -0,0 +1,190 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-task-widget.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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "e-task-widget.h" + +#include +#include +#include + +/* HA HA. Just #including doesn't seem to work, so + I'll have to include the full thing. */ +#include + +#include + + +#define SPACING 2 + +#define PARENT_TYPE (gtk_frame_get_type ()) +static GtkFrameClass *parent_class = NULL; + +struct _ETaskWidgetPrivate { + GdkPixbuf *icon_pixbuf; + GtkWidget *label; + GtkWidget *pixmap; +}; + + +/* GtkObject methods. */ + +static void +impl_destroy (GtkObject *object) +{ + ETaskWidget *task_widget; + ETaskWidgetPrivate *priv; + + task_widget = E_TASK_WIDGET (object); + priv = task_widget->priv; + + gdk_pixbuf_unref (priv->icon_pixbuf); + + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + +static void +class_init (GtkObjectClass *object_class) +{ + parent_class = gtk_type_class (PARENT_TYPE); + + object_class->destroy = impl_destroy; +} + +static void +init (ETaskWidget *task_widget) +{ + ETaskWidgetPrivate *priv; + + priv = g_new (ETaskWidgetPrivate, 1); + + task_widget->priv = priv; +} + + +void +e_task_widget_construct (ETaskWidget *task_widget, + GdkPixbuf *icon_pixbuf, + const char *information) +{ + ETaskWidgetPrivate *priv; + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkWidget *box; + + g_return_if_fail (task_widget != NULL); + g_return_if_fail (E_IS_TASK_WIDGET (task_widget)); + g_return_if_fail (icon_pixbuf != NULL); + g_return_if_fail (information != NULL); + + priv = task_widget->priv; + + gtk_frame_set_shadow_type (GTK_FRAME (task_widget), GTK_SHADOW_IN); + + box = gtk_hbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (task_widget), box); + gtk_widget_show (box); + + priv->icon_pixbuf = gdk_pixbuf_ref (icon_pixbuf); + + gdk_pixbuf_render_pixmap_and_mask (icon_pixbuf, &pixmap, &mask, 128); + + priv->pixmap = gtk_pixmap_new (pixmap, mask); + gtk_widget_show (priv->pixmap); + gtk_box_pack_start (GTK_BOX (box), priv->pixmap, FALSE, TRUE, 0); + + priv->label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5); + gtk_widget_show (priv->label); + gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0); + + gdk_pixmap_unref (pixmap); + gdk_bitmap_unref (mask); + + e_task_widget_update (task_widget, information, -1.0); +} + +GtkWidget * +e_task_widget_new (GdkPixbuf *icon_pixbuf, + const char *information) +{ + ETaskWidget *task_widget; + + g_return_val_if_fail (icon_pixbuf != NULL, NULL); + g_return_val_if_fail (information != NULL, NULL); + + task_widget = gtk_type_new (e_task_widget_get_type ()); + e_task_widget_construct (task_widget, icon_pixbuf, information); + + return GTK_WIDGET (task_widget); +} + + +void +e_task_widget_update (ETaskWidget *task_widget, + const char *information, + double completion) +{ + ETaskWidgetPrivate *priv; + char *text; + + g_return_if_fail (task_widget != NULL); + g_return_if_fail (E_IS_TASK_WIDGET (task_widget)); + g_return_if_fail (information != NULL); + + priv = task_widget->priv; + + if (completion < 0.0) { + text = g_strdup_printf (_("%s (...)"), information); + } else { + int percent_complete; + + percent_complete = (int) (completion * 100.0 + .5); + text = g_strdup_printf (_("%s (%d%% complete)"), information, percent_complete); + } + + gtk_label_set_text (GTK_LABEL (priv->label), text); + + g_free (text); +} + +void +e_task_wiget_alert (ETaskWidget *task_widget) +{ + g_return_if_fail (task_widget != NULL); + g_return_if_fail (E_IS_TASK_WIDGET (task_widget)); +} + +void +e_task_wiget_unalert (ETaskWidget *task_widget) +{ + g_return_if_fail (task_widget != NULL); + g_return_if_fail (E_IS_TASK_WIDGET (task_widget)); +} + + +E_MAKE_TYPE (e_task_widget, "ETaskWidget", ETaskWidget, class_init, init, PARENT_TYPE) diff --git a/shell/e-task-widget.h b/shell/e-task-widget.h new file mode 100644 index 0000000000..cc177252ce --- /dev/null +++ b/shell/e-task-widget.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-task-widget.h + * + * 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 + */ + +#ifndef _E_TASK_WIDGET_H_ +#define _E_TASK_WIDGET_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define E_TYPE_TASK_WIDGET (e_task_widget_get_type ()) +#define E_TASK_WIDGET(obj) (GTK_CHECK_CAST ((obj), E_TYPE_TASK_WIDGET, ETaskWidget)) +#define E_TASK_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_TASK_WIDGET, ETaskWidgetClass)) +#define E_IS_TASK_WIDGET(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_TASK_WIDGET)) +#define E_IS_TASK_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_TASK_WIDGET)) + + +typedef struct _ETaskWidget ETaskWidget; +typedef struct _ETaskWidgetPrivate ETaskWidgetPrivate; +typedef struct _ETaskWidgetClass ETaskWidgetClass; + +struct _ETaskWidget { + GtkFrame parent; + + ETaskWidgetPrivate *priv; +}; + +struct _ETaskWidgetClass { + GtkFrameClass parent_class; +}; + + +GtkType e_task_widget_get_type (void); +void e_task_widget_construct (ETaskWidget *task_widget, + GdkPixbuf *icon_pixbuf, + const char *information); +GtkWidget *e_task_widget_new (GdkPixbuf *icon_pixbuf, + const char *information); + +void e_task_widget_update (ETaskWidget *task_widget, + const char *information, + double completion); + +void e_task_wiget_alert (ETaskWidget *task_widget); +void e_task_wiget_unalert (ETaskWidget *task_widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _E_TASK_WIDGET_H_ */ diff --git a/shell/evolution-test-component.c b/shell/evolution-test-component.c new file mode 100644 index 0000000000..1b5b428059 --- /dev/null +++ b/shell/evolution-test-component.c @@ -0,0 +1,375 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* evolution-test-component.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 + */ + +/* Simple test component for the Evolution shell. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "evolution-shell-component.h" + +#include +#include + +#include + + +#define COMPONENT_FACTORY_ID "OAFIID:GNOME_Evolution_TestComponent_ShellComponentFactory" +#define COMPONENT_ID "OAFIID:GNOME_Evolution_TestComponent_ShellComponent" + +static const EvolutionShellComponentFolderType folder_types[] = { + { "test", "/usr/share/pixmaps/gnome-money.png", NULL, NULL }, + { NULL } +}; + + +static EvolutionShellClient *parent_shell = NULL; +static GNOME_Evolution_Activity activity_interface = CORBA_OBJECT_NIL; + +static CORBA_long activity_id = 0; + +static BonoboListener *task_bar_event_listener; + +static int timeout_id = 0; +static int progress = -1; + + +static void +create_icon_from_pixbuf (GdkPixbuf *pixbuf, + GNOME_Evolution_Icon *frame_return) +{ + const char *sp; + CORBA_octet *dp; + int width, height, total_width, rowstride; + int i, j; + gboolean has_alpha; + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + + if (has_alpha) + total_width = 4 * width; + else + total_width = 3 * width; + + frame_return->width = width; + frame_return->height = height; + frame_return->hasAlpha = has_alpha; + + frame_return->rgba_data._length = frame_return->height * total_width; + frame_return->rgba_data._maximum = frame_return->rgba_data._length; + frame_return->rgba_data._buffer = CORBA_sequence_CORBA_octet_allocbuf (frame_return->rgba_data._maximum); + + sp = gdk_pixbuf_get_pixels (pixbuf); + dp = frame_return->rgba_data._buffer; + for (i = 0; i < height; i ++) { + for (j = 0; j < total_width; j++) { + *(dp ++) = sp[j]; + } + sp += rowstride; + } +} + +static GNOME_Evolution_AnimatedIcon * +create_animated_icon (void) +{ + GNOME_Evolution_AnimatedIcon *animated_icon; + GdkPixbuf *pixbuf; + + animated_icon = GNOME_Evolution_AnimatedIcon__alloc (); + + animated_icon->_length = 1; + animated_icon->_maximum = 1; + animated_icon->_buffer = CORBA_sequence_GNOME_Evolution_Icon_allocbuf (animated_icon->_maximum); + + pixbuf = gdk_pixbuf_new_from_file (gnome_pixmap_file ("gnome-money.png")); + create_icon_from_pixbuf (pixbuf, &animated_icon->_buffer[0]); + gdk_pixbuf_unref (pixbuf); + + CORBA_sequence_set_release (animated_icon, TRUE); + + return animated_icon; +} + + +static void +task_bar_event_listener_callback (BonoboListener *listener, + char *event_name, + CORBA_any *any, + CORBA_Environment *ev, + void *data) +{ + g_print ("Taskbar event -- %s\n", event_name); +} + +/* Timeout #3: We are done. */ +static int +timeout_callback_3 (void *data) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Activity_operationFinished (activity_interface, + activity_id, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot report operation as finished; exception returned -- %s\n", + ev._repo_id); + CORBA_exception_free (&ev); + return FALSE; + } + + CORBA_exception_free (&ev); + + return FALSE; +} + +/* Timeout #2: Update the progress until it reaches 100%. */ +static int +timeout_callback_2 (void *data) +{ + CORBA_Environment ev; + + if (progress < 0) + progress = 0; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Activity_operationProgressing (activity_interface, + activity_id, + "Operation Foo in progress", + (CORBA_float) progress / 100.0, + &ev); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot update operation; exception returned -- %s\n", + ev._repo_id); + CORBA_exception_free (&ev); + return FALSE; + } + + CORBA_exception_free (&ev); + + progress += 10; + if (progress > 100) { + gtk_timeout_add (1000, timeout_callback_3, NULL); + return FALSE; + } + + return TRUE; +} + +/* Timeout #1: Set busy. */ +static int +timeout_callback_1 (void *data) +{ + CORBA_boolean suggest_display; + CORBA_Environment ev; + GNOME_Evolution_AnimatedIcon *animated_icon; + + CORBA_exception_init (&ev); + + if (CORBA_Object_is_nil (activity_interface, &ev)) { + CORBA_exception_free (&ev); + return FALSE; + } + + g_print ("Component becoming busy -- %s\n", COMPONENT_ID); + + task_bar_event_listener = bonobo_listener_new (task_bar_event_listener_callback, NULL); + + animated_icon = create_animated_icon (); + + GNOME_Evolution_Activity_operationStarted (activity_interface, + COMPONENT_ID, + animated_icon, + "Operation Foo started!", + FALSE, + bonobo_object_corba_objref (BONOBO_OBJECT (task_bar_event_listener)), + &activity_id, + &suggest_display, + &ev); + + CORBA_free (animated_icon); + + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot start an operation; exception returned -- %s\n", + ev._repo_id); + CORBA_exception_free (&ev); + return FALSE; + } + + g_print (" --> Activity ID: %ld\n", (long) activity_id); + + if (suggest_display) + g_print (" --> Could display dialog box.\n"); + + CORBA_exception_free (&ev); + + gtk_timeout_add (3000, timeout_callback_2, NULL); + + return FALSE; +} + + +static EvolutionShellComponentResult +create_view_fn (EvolutionShellComponent *shell_component, + const char *physical_uri, + const char *folder_type, + BonoboControl **control_return, + void *closure) +{ + GtkWidget *vbox; + GtkWidget *label_1, *label_2; + GtkWidget *event_box_1, *event_box_2; + + label_1 = gtk_label_new ("This is just a test component, displaying the following URI:"); + label_2 = gtk_label_new (physical_uri); + + event_box_1 = gtk_event_box_new (); + event_box_2 = gtk_event_box_new (); + + vbox = gtk_vbox_new (FALSE, 5); + + gtk_box_pack_start (GTK_BOX (vbox), event_box_1, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), label_1, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), label_2, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), event_box_2, TRUE, TRUE, 0); + + gtk_widget_show (label_1); + gtk_widget_show (label_2); + gtk_widget_show (event_box_1); + gtk_widget_show (event_box_2); + + gtk_widget_show (vbox); + + *control_return = bonobo_control_new (vbox); + + g_assert (timeout_id == 0); + timeout_id = gtk_timeout_add (2000, timeout_callback_1, NULL); + + return EVOLUTION_SHELL_COMPONENT_OK; +} + + +/* Callbacks. */ + +static void +owner_set_callback (EvolutionShellComponent *shell_component, + EvolutionShellClient *shell_client, + const char *evolution_homedir) +{ + CORBA_Environment ev; + + g_assert (parent_shell == NULL); + + g_print ("We have an owner -- home directory is `%s'\n", evolution_homedir); + + parent_shell = shell_client; + + CORBA_exception_init (&ev); + + activity_interface = Bonobo_Unknown_queryInterface (bonobo_object_corba_objref (BONOBO_OBJECT (shell_client)), + "IDL:GNOME/Evolution/Activity:1.0", + &ev); + if (ev._major != CORBA_NO_EXCEPTION) + activity_interface = CORBA_OBJECT_NIL; + + if (CORBA_Object_is_nil (activity_interface, &ev)) + g_warning ("Shell doesn't have a ::Activity interface -- weird!"); + + CORBA_exception_free (&ev); +} + +static int +owner_unset_idle_callback (void *data) +{ + gtk_main_quit (); + return FALSE; +} + +static void +owner_unset_callback (EvolutionShellComponent *shell_component, + void *data) +{ + g_idle_add_full (G_PRIORITY_LOW, owner_unset_idle_callback, NULL, NULL); +} + + +static BonoboObject * +factory_fn (BonoboGenericFactory *factory, + void *closure) +{ + EvolutionShellComponent *shell_component; + + shell_component = evolution_shell_component_new (folder_types, + create_view_fn, + NULL, NULL, NULL, NULL, NULL, NULL); + + gtk_signal_connect (GTK_OBJECT (shell_component), "owner_set", + GTK_SIGNAL_FUNC (owner_set_callback), NULL); + gtk_signal_connect (GTK_OBJECT (shell_component), "owner_unset", + GTK_SIGNAL_FUNC (owner_unset_callback), NULL); + + return BONOBO_OBJECT (shell_component); +} + +static void +component_factory_init (void) +{ + BonoboGenericFactory *factory; + + factory = bonobo_generic_factory_new (COMPONENT_FACTORY_ID, factory_fn, NULL); + + if (factory == NULL) + g_error ("Cannot initialize test component."); +} + + +int +main (int argc, char **argv) +{ + CORBA_ORB orb; + + bindtextdomain (PACKAGE, EVOLUTION_LOCALEDIR); + textdomain (PACKAGE); + + gnome_init_with_popt_table ("evolution-test-component", VERSION, + argc, argv, oaf_popt_options, 0, NULL); + + orb = oaf_init (argc, argv); + + if (bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL) == FALSE) + g_error ("Cannot initialize the test component."); + + component_factory_init (); + + bonobo_main (); + + return 0; +} -- cgit