Files
evolution/shell/e-shell-startup-wizard.c
Dan Winship 66b36e63de New routine to attempt to start each available startup wizard in priority
* e-shell-startup-wizard.c (start_wizard): New routine to attempt
        to start each available startup wizard in priority order, and
        return once it has successfully started one. (Allows Connector,
        etc, to override the mailer startup wizard.)
	(make_corba_dialog_pages): Use it.

svn path=/trunk/; revision=20072
2003-02-26 18:11:49 +00:00

779 lines
20 KiB
C

/*
* e-shell-startup-wizard.c
*
* Copyright (C) 2001, 2002 Ximian, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* 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.
*
* Authors: Iain Holmes <iain@ximian.com>
*/
/* WARNING: I LEAK. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-shell-startup-wizard.h"
#include "e-shell-corba-icon-utils.h"
#include "e-timezone-dialog/e-timezone-dialog.h"
#include "e-util/e-gtk-utils.h"
#include <gconf/gconf-client.h>
#include <evolution-wizard.h>
#include "Evolution.h"
#include "importer/GNOME_Evolution_Importer.h"
#include <string.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <gnome.h>
#include <glade/glade.h>
#include <bonobo/bonobo-object.h>
#include <bonobo/bonobo-widget.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-moniker-util.h>
#include <bonobo-activation/bonobo-activation.h>
#include <gal/widgets/e-gui-utils.h>
#include <widgets/e-timezone-dialog/e-timezone-dialog.h>
typedef struct _TimezoneDialogPage {
GtkWidget *page;
GtkWidget *vbox;
GObject *etd;
} TimezoneDialogPage;
typedef struct _ImportDialogPage {
GtkWidget *page;
GtkWidget *vbox;
GList *importers;
int running;
gboolean prepared;
} ImportDialogPage;
typedef struct _SWData {
GladeXML *wizard;
GtkWidget *dialog;
GtkWidget *druid;
GtkWidget *start, *finish;
GPtrArray *corba_pages;
TimezoneDialogPage *timezone_page;
ImportDialogPage *import_page;
gboolean cancel;
GNOME_Evolution_Wizard corba_wizard;
BonoboListener *listener;
GNOME_Evolution_Wizard_PageList *pagelist;
} SWData;
typedef struct _IntelligentImporterData {
CORBA_Object object;
Bonobo_Control control;
GtkWidget *widget;
char *name;
char *blurb;
char *iid;
} IntelligentImporterData;
typedef struct _SelectedImporterData{
CORBA_Object importer;
char *iid;
} SelectedImporterData;
static GHashTable *page_hash;
static GList *page_list = NULL;
static void
druid_event_notify_cb (BonoboListener *listener,
const char *name,
BonoboArg *arg,
CORBA_Environment *ev,
SWData *data)
{
int buttons, pagenum;
GnomeDruidPage *page;
if (strcmp (name, EVOLUTION_WIZARD_SET_BUTTONS_SENSITIVE) == 0) {
buttons = (int) *((CORBA_short *)arg->_value);
gnome_druid_set_buttons_sensitive (GNOME_DRUID (data->druid),
(buttons & 4) >> 2,
(buttons & 2) >> 1,
(buttons & 1),
FALSE);
} else if (strcmp (name, EVOLUTION_WIZARD_SET_SHOW_FINISH) == 0) {
gnome_druid_set_show_finish (GNOME_DRUID (data->druid),
(gboolean) *((CORBA_boolean *) arg->_value));
} else if (strcmp (name, EVOLUTION_WIZARD_SET_PAGE) == 0) {
pagenum = (int) *((CORBA_short *) arg->_value);
page = g_list_nth_data (page_list, pagenum);
gnome_druid_set_page (GNOME_DRUID (data->druid), page);
}
}
static GNOME_Evolution_Wizard
start_wizard (void)
{
char *const selection_order[] = { "0-evolution:startup_wizard:priority", NULL };
Bonobo_ServerInfoList *info;
CORBA_Environment ev;
GNOME_Evolution_Wizard wizard;
int i;
CORBA_exception_init (&ev);
info = bonobo_activation_query ("repo_ids.has ('IDL:GNOME/Evolution/StartupWizard:1.0')", selection_order, &ev);
if (BONOBO_EX (&ev) || info == CORBA_OBJECT_NIL) {
g_warning ("Cannot find startup wizard -- %s", BONOBO_EX_REPOID (&ev));
CORBA_exception_free (&ev);
return NULL;
}
CORBA_exception_free (&ev);
for (i = 0; i < info->_length; i++) {
CORBA_exception_init (&ev);
wizard = bonobo_activation_activate_from_id (info->_buffer[i].iid, 0, NULL, &ev);
if (!BONOBO_EX (&ev)) {
CORBA_free (info);
return wizard;
}
CORBA_exception_free (&ev);
}
if (info->_length)
g_warning ("Could not start any startup wizard!");
else
g_warning ("No startup wizard available!");
CORBA_free (info);
return NULL;
}
static void
make_corba_dialog_pages (SWData *data)
{
CORBA_Environment ev;
CORBA_Object object;
Bonobo_EventSource event_source;
data->corba_wizard = start_wizard ();
if (!data->corba_wizard)
return;
CORBA_exception_init (&ev);
data->pagelist = GNOME_Evolution_Wizard__get_pages (data->corba_wizard, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Could not get CORBA wizard pages (%s)", CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
data->pagelist = CORBA_OBJECT_NIL;
return;
}
CORBA_exception_init (&ev);
event_source = Bonobo_Unknown_queryInterface (data->corba_wizard, "IDL:Bonobo/EventSource:1.0", &ev);
CORBA_exception_free (&ev);
data->listener = bonobo_listener_new (NULL, NULL);
g_signal_connect (data->listener, "event-notify",
G_CALLBACK (druid_event_notify_cb), data);
object = bonobo_object_corba_objref (BONOBO_OBJECT (data->listener));
CORBA_exception_init (&ev);
Bonobo_EventSource_addListener (event_source, object, &ev);
CORBA_exception_free (&ev);
bonobo_object_release_unref (event_source, &ev);
CORBA_exception_free (&ev);
}
static int
page_to_num (GnomeDruidPage *page)
{
return GPOINTER_TO_INT (g_hash_table_lookup (page_hash, page));
}
static gboolean
next_func (GnomeDruidPage *page,
GnomeDruid *druid,
SWData *data)
{
CORBA_Environment ev;
int pagenum;
CORBA_exception_init (&ev);
pagenum = page_to_num (page);
GNOME_Evolution_Wizard_notifyAction (data->corba_wizard, pagenum, GNOME_Evolution_Wizard_NEXT, &ev);
CORBA_exception_free (&ev);
/* If on last page we own, let druid goto next page */
if (pagenum == g_list_length(page_list)-1)
return FALSE;
return TRUE;
}
static gboolean
prepare_func (GnomeDruidPage *page,
GnomeDruid *druid,
SWData *data)
{
CORBA_Environment ev;
int pagenum;
CORBA_exception_init (&ev);
pagenum = page_to_num (page);
GNOME_Evolution_Wizard_notifyAction (data->corba_wizard, pagenum, GNOME_Evolution_Wizard_PREPARE, &ev);
CORBA_exception_free (&ev);
return FALSE;
}
static gboolean
back_func (GnomeDruidPage *page,
GnomeDruid *druid,
SWData *data)
{
CORBA_Environment ev;
int pagenum;
CORBA_exception_init (&ev);
pagenum = page_to_num (page);
GNOME_Evolution_Wizard_notifyAction (data->corba_wizard, pagenum, GNOME_Evolution_Wizard_BACK, &ev);
CORBA_exception_free (&ev);
/* if we're on page 0, let the druid go back to the start page, if we have one */
if (pagenum == 0)
return FALSE;
return TRUE;
}
static void
free_importers (SWData *data)
{
GList *l;
for (l = data->import_page->importers; l; l = l->next) {
IntelligentImporterData *iid;
iid = l->data;
if (iid->object != CORBA_OBJECT_NIL) {
bonobo_object_release_unref (iid->object, NULL);
}
}
g_list_free (data->import_page->importers);
}
static void
start_importers (GList *p)
{
CORBA_Environment ev;
for (; p; p = p->next) {
SelectedImporterData *sid = p->data;
CORBA_exception_init (&ev);
GNOME_Evolution_IntelligentImporter_importData (sid->importer, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Error importing %s\n%s", sid->iid,
CORBA_exception_id (&ev));
}
CORBA_exception_free (&ev);
}
}
static void
do_import (SWData *data)
{
CORBA_Environment ev;
GList *l, *selected = NULL;
for (l = data->import_page->importers; l; l = l->next) {
IntelligentImporterData *importer_data;
SelectedImporterData *sid;
char *iid;
importer_data = l->data;
iid = g_strdup (importer_data->iid);
sid = g_new (SelectedImporterData, 1);
sid->iid = iid;
CORBA_exception_init (&ev);
sid->importer = bonobo_object_dup_ref (importer_data->object, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Error duplication %s\n(%s)", iid,
CORBA_exception_id (&ev));
g_free (iid);
CORBA_exception_free (&ev);
g_free (sid);
continue;
}
CORBA_exception_free (&ev);
selected = g_list_prepend (selected, sid);
}
free_importers (data);
if (selected != NULL) {
start_importers (selected);
for (l = selected; l; l = l->next) {
SelectedImporterData *sid = l->data;
CORBA_exception_init (&ev);
bonobo_object_release_unref (sid->importer, &ev);
CORBA_exception_free (&ev);
g_free (sid->iid);
g_free (sid);
}
g_list_free (selected);
}
}
static gboolean
finish_func (GnomeDruidPage *page,
GnomeDruid *druid,
SWData *data)
{
GConfClient *client;
CORBA_Environment ev;
const char *displayname;
char *tz;
icaltimezone *zone;
/* Notify mailer */
CORBA_exception_init (&ev);
GNOME_Evolution_Wizard_notifyAction (data->corba_wizard, 0, GNOME_Evolution_Wizard_FINISH, &ev);
CORBA_exception_free (&ev);
/* Set Timezone */
e_timezone_dialog_get_timezone (E_TIMEZONE_DIALOG (data->timezone_page->etd), &displayname);
/* We know it is a builtin timezone, as that is all the user can change
it to. */
zone = e_timezone_dialog_get_builtin_timezone (displayname);
if (zone == NULL)
tz = g_strdup ("UTC");
else
tz = g_strdup (icaltimezone_get_location (zone));
client = gconf_client_get_default ();
gconf_client_set_string (client, "/apps/evolution/calendar/display/timezone", tz, NULL);
g_object_unref (client);
g_free (tz);
do_import (data);
/* Free data */
data->cancel = FALSE;
/* Need to do this otherwise the timezone widget gets destroyed but the
timezone object isn't, and we can get a crash like #22047. */
g_object_unref (data->timezone_page->etd);
data->timezone_page->etd = NULL;
gtk_widget_destroy (data->dialog);
gtk_main_quit ();
return TRUE;
}
static void
connect_page (GtkWidget *page,
SWData *data)
{
g_signal_connect (page, "next",
G_CALLBACK (next_func), data);
g_signal_connect (page, "back",
G_CALLBACK (back_func), data);
g_signal_connect (page, "finish",
G_CALLBACK (finish_func), data);
/* At least in 2.0 (and probably 2.2 too),
* GnomeDruidPageStandard is broken and you need to
* connect_after to "prepare" or else its default method will
* run after your signal handler and undo its button
* sensitivity changes.
*/
g_signal_connect_after (page, "prepare",
G_CALLBACK (prepare_func), data);
}
static GtkWidget *
make_corba_page (SWData *data, int n, GtkWidget *prev)
{
GNOME_Evolution_Wizard_Page *corba_page;
GdkPixbuf *icon;
GtkWidget *page, *body;
corba_page = &data->pagelist->_buffer[n];
icon = e_new_gdk_pixbuf_from_corba_icon (&corba_page->icon, -1, -1);
page = gnome_druid_page_standard_new_with_vals (corba_page->title,
icon, NULL);
g_object_unref (icon);
body = bonobo_widget_new_control_from_objref (corba_page->control,
CORBA_OBJECT_NIL);
gtk_box_pack_start (GTK_BOX (GNOME_DRUID_PAGE_STANDARD (page)->vbox),
body, TRUE, TRUE, 0);
gtk_widget_show_all (page);
gnome_druid_insert_page (GNOME_DRUID (data->druid),
GNOME_DRUID_PAGE (prev),
GNOME_DRUID_PAGE (page));
connect_page (page, data);
g_hash_table_insert (page_hash, page, GINT_TO_POINTER (n));
page_list = g_list_append (page_list, page);
return page;
}
static TimezoneDialogPage *
make_timezone_page (SWData *data)
{
TimezoneDialogPage *page;
ETimezoneDialog *etd;
g_return_val_if_fail (data != NULL, NULL);
page = g_new0 (TimezoneDialogPage, 1);
page->page = glade_xml_get_widget (data->wizard, "timezone-page");
g_return_val_if_fail (page->page != NULL, NULL);
page->vbox = GTK_WIDGET (GNOME_DRUID_PAGE_STANDARD (page->page)->vbox);
etd = e_timezone_dialog_new ();
page->etd = G_OBJECT (etd);
e_timezone_dialog_reparent (E_TIMEZONE_DIALOG (page->etd), page->vbox);
return page;
}
static GList *
get_intelligent_importers (void)
{
Bonobo_ServerInfoList *info_list;
GList *iids_ret = NULL;
CORBA_Environment ev;
int i;
CORBA_exception_init (&ev);
info_list = bonobo_activation_query ("repo_ids.has ('IDL:GNOME/Evolution/IntelligentImporter:1.0')", NULL, &ev);
CORBA_exception_free (&ev);
for (i = 0; i < info_list->_length; i++) {
const Bonobo_ServerInfo *info;
info = info_list->_buffer + i;
iids_ret = g_list_prepend (iids_ret, g_strdup (info->iid));
}
return iids_ret;
}
static gboolean
prepare_importer_page (GnomeDruidPage *page,
GnomeDruid *druid,
SWData *data)
{
GtkWidget *dialog;
ImportDialogPage *import;
GList *l, *importers;
GtkWidget *table;
int running = 0;
if (data->import_page->prepared == TRUE) {
return TRUE;
}
data->import_page->prepared = TRUE;
dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_INFO,
GTK_BUTTONS_NONE,
_("Please wait...\nScanning for existing setups"));
e_make_widget_backing_stored (dialog);
gtk_window_set_title (GTK_WINDOW (dialog), _("Starting import"));
gtk_widget_show_all (dialog);
gtk_widget_show_now (dialog);
gtk_widget_queue_draw (dialog);
gdk_flush ();
while (gtk_events_pending ()) {
gtk_main_iteration ();
}
import = data->import_page;
importers = get_intelligent_importers ();
if (importers == NULL) {
/* No importers, go directly to finish, do not pass go
Do not collect $200 */
gnome_druid_set_page (druid, GNOME_DRUID_PAGE (data->finish));
gtk_widget_destroy (dialog);
return TRUE;
}
table = gtk_table_new (g_list_length (importers), 2, FALSE);
for (l = importers; l; l = l->next) {
GtkWidget *label;
CORBA_Environment ev;
gboolean can_run;
char *str;
IntelligentImporterData *id;
id = g_new0 (IntelligentImporterData, 1);
id->iid = g_strdup (l->data);
CORBA_exception_init (&ev);
id->object = bonobo_activation_activate_from_id ((char *) id->iid, 0, NULL, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Could not start %s:%s", id->iid,
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
/* Clean up the IID */
g_free (id->iid);
g_free (id);
continue;
}
if (id->object == CORBA_OBJECT_NIL) {
g_warning ("Could not activate component %s", id->iid);
CORBA_exception_free (&ev);
g_free (id->iid);
g_free (id);
continue;
}
can_run = GNOME_Evolution_IntelligentImporter_canImport (id->object, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Could not call canImport(%s): %s", id->iid,
CORBA_exception_id (&ev));
bonobo_object_release_unref (id->object, &ev);
CORBA_exception_free (&ev);
g_free (id->iid);
g_free (id);
continue;
}
if (can_run == FALSE) {
bonobo_object_release_unref (id->object, &ev);
CORBA_exception_free (&ev);
g_free (id->iid);
g_free (id);
continue;
}
running++;
id->name = GNOME_Evolution_IntelligentImporter__get_importername (id->object, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Could not get name(%s): %s", id->iid,
CORBA_exception_id (&ev));
bonobo_object_release_unref (id->object, &ev);
CORBA_exception_free (&ev);
g_free (id->iid);
g_free (id);
continue;
}
id->blurb = GNOME_Evolution_IntelligentImporter__get_message (id->object, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Could not get message(%s): %s",
id->iid, CORBA_exception_id (&ev));
bonobo_object_release_unref (id->object, &ev);
CORBA_exception_free (&ev);
g_free (id->iid);
CORBA_free (id->name);
g_free (id);
continue;
}
id->control = Bonobo_Unknown_queryInterface (id->object,
"IDL:Bonobo/Control:1.0", &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Could not QI for Bonobo/Control:1.0 %s:%s",
id->iid, CORBA_exception_id (&ev));
bonobo_object_release_unref (id->object, &ev);
CORBA_exception_free (&ev);
g_free (id->iid);
CORBA_free (id->name);
CORBA_free (id->blurb);
continue;
}
if (id->control != CORBA_OBJECT_NIL) {
id->widget = bonobo_widget_new_control_from_objref (id->control, CORBA_OBJECT_NIL);
gtk_widget_show (id->widget);
} else {
printf("no control\n");
id->widget = gtk_label_new ("");
gtk_widget_show (id->widget);
}
CORBA_exception_free (&ev);
import->importers = g_list_prepend (import->importers, id);
str = g_strdup_printf (_("From %s:"), id->name);
label = gtk_label_new (str);
g_free (str);
gtk_misc_set_alignment (GTK_MISC (label), 0, .5);
gtk_table_attach (GTK_TABLE (table), label, 0, 1, running - 1,
running, GTK_FILL, 0, 0, 0);
gtk_table_attach (GTK_TABLE (table), id->widget, 1, 2,
running - 1, running, GTK_FILL, 0, 3, 0);
gtk_widget_show_all (table);
gtk_box_pack_start (GTK_BOX (data->import_page->vbox), table,
FALSE, FALSE, 0);
}
if (running == 0) {
gnome_druid_set_page (druid, GNOME_DRUID_PAGE (data->finish));
gtk_widget_destroy (dialog);
return TRUE;
}
import->running = running;
gtk_widget_destroy (dialog);
return FALSE;
}
static ImportDialogPage *
make_importer_page (SWData *data)
{
ImportDialogPage *page;
GtkWidget *label, *sep;
g_return_val_if_fail (data != NULL, NULL);
page = g_new0 (ImportDialogPage, 1);
page->page = glade_xml_get_widget (data->wizard, "import-page");
g_return_val_if_fail (page->page != NULL, NULL);
g_signal_connect_after (page->page, "prepare",
G_CALLBACK (prepare_importer_page), data);
page->vbox = GNOME_DRUID_PAGE_STANDARD (page->page)->vbox;
gtk_container_set_border_width (GTK_CONTAINER (page->vbox), 4);
label = gtk_label_new (_("Please select the information that you would like to import:"));
gtk_box_pack_start (GTK_BOX (page->vbox), label, FALSE, FALSE, 3);
sep = gtk_hseparator_new ();
gtk_box_pack_start (GTK_BOX (page->vbox), sep, FALSE, FALSE, 3);
page->prepared = FALSE;
return page;
}
static void
startup_wizard_cancel (GnomeDruid *druid,
SWData *data)
{
/* Free data */
data->cancel = TRUE;
if (data->timezone_page->etd != NULL) {
/* Need to do this otherwise the timezone widget gets destroyed but the
timezone object isn't, and we can get a crash like #22047. */
g_object_unref (data->timezone_page->etd);
data->timezone_page->etd = NULL;
}
gtk_widget_destroy (data->dialog);
gtk_main_quit ();
}
gboolean
e_shell_startup_wizard_create (void)
{
GConfClient *client;
SWData *data;
GSList *accounts;
GtkWidget *page;
int i;
client = gconf_client_get_default ();
accounts = gconf_client_get_list (client, "/apps/evolution/mail/accounts", GCONF_VALUE_STRING, NULL);
g_object_unref (client);
if (accounts != NULL) {
g_slist_foreach (accounts, (GFunc) g_free, NULL);
g_slist_free (accounts);
return TRUE;
}
data = g_new0 (SWData, 1);
data->wizard = glade_xml_new (EVOLUTION_GLADEDIR "/evolution-startup-wizard.glade", NULL, NULL);
g_return_val_if_fail (data->wizard != NULL, FALSE);
data->dialog = glade_xml_get_widget (data->wizard, "startup-wizard");
g_return_val_if_fail (data->dialog != NULL, FALSE);
gtk_window_set_wmclass (GTK_WINDOW (data->dialog), "startup-wizard",
"Evolution:shell");
page_hash = g_hash_table_new (NULL, NULL);
data->druid = glade_xml_get_widget (data->wizard, "startup-druid");
g_return_val_if_fail (data->druid != NULL, FALSE);
gnome_druid_set_buttons_sensitive (GNOME_DRUID (data->druid),
FALSE, TRUE, FALSE, FALSE);
g_signal_connect (data->druid, "cancel",
G_CALLBACK (startup_wizard_cancel), data);
data->start = glade_xml_get_widget (data->wizard, "start-page");
data->finish = glade_xml_get_widget (data->wizard, "done-page");
g_return_val_if_fail (data->start != NULL, FALSE);
g_return_val_if_fail (data->finish != NULL, FALSE);
g_signal_connect (data->finish, "finish", G_CALLBACK (finish_func), data);
make_corba_dialog_pages (data);
g_return_val_if_fail (data->corba_wizard != CORBA_OBJECT_NIL, TRUE);
g_return_val_if_fail (data->pagelist != CORBA_OBJECT_NIL, TRUE);
page = data->start;
for (i = 0; i < data->pagelist->_length; i++)
page = make_corba_page (data, i, page);
data->timezone_page = make_timezone_page (data);
data->import_page = make_importer_page (data);
g_return_val_if_fail (data->timezone_page != NULL, TRUE);
g_return_val_if_fail (data->import_page != NULL, TRUE);
gnome_druid_set_buttons_sensitive (GNOME_DRUID (data->druid), FALSE, TRUE, TRUE, FALSE);
gtk_widget_show_all (data->dialog);
gtk_main ();
return !data->cancel;
}