/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* e-summary-mail.c
*
* Copyright (C) 2001, 2002, 2003 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.
*
* Author: Iain Holmes <iain@ximian.com>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gal/widgets/e-unicode.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h> /* gnome_util_prepend_user_home */
#include <gtk/gtksignal.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-listener.h>
#include <bonobo/bonobo-property-bag-client.h>
#include <Evolution.h>
#include <evolution-storage-listener.h>
#include <string.h>
#include "Mailer.h"
#include "e-summary.h"
#include "e-summary-mail.h"
#include "e-summary-table.h"
#include "e-summary-preferences.h"
#include "e-util/e-path.h"
#define MAIL_IID "OAFIID:GNOME_Evolution_FolderInfo"
typedef struct _FolderStore {
GNOME_Evolution_Shell shell;
GNOME_Evolution_FolderInfo folder_info;
GNOME_Evolution_StorageRegistry registry;
BonoboListener *registry_listener;
BonoboListener *listener;
EvolutionStorageListener *storage_listener;
GSList *storage_list;
GHashTable *path_to_folder;
GHashTable *physical_uri_to_folder;
GList *shown;
} FolderStore;
struct _ESummaryMail {
char *html;
};
typedef struct _StorageInfo {
char *name;
char *toplevel;
GNOME_Evolution_Storage storage;
EvolutionStorageListener *listener;
GList *folders;
} StorageInfo;
typedef struct _ESummaryMailFolder {
char *path;
char *uri;
char *physical_uri;
char *display_name;
int count;
int unread;
gboolean init; /* Has this folder been initialised? */
StorageInfo *si;
} ESummaryMailFolder;
static FolderStore *folder_store = NULL;
static char *
make_pretty_foldername (ESummary *summary,
const ESummaryMailFolder *folder)
{
GString *pretty_path;
const char *p, *previous_p;
char *retval;
if (! summary->preferences->show_full_path)
return g_strdup (folder->display_name);
g_assert (folder->path[0] == '/');
pretty_path = g_string_new ("");
previous_p = folder->path;
while (previous_p != NULL) {
const ESummaryMailFolder *ancestor_folder;
char *path;
g_string_append (pretty_path, "/");
p = strchr (previous_p + 1, '/');
if (p == NULL)
path = g_strdup (folder->path);
else
path = g_strndup (folder->path, p - folder->path);
ancestor_folder = g_hash_table_lookup (folder_store->path_to_folder, path);
if (ancestor_folder != NULL) {
g_string_append (pretty_path, ancestor_folder->display_name);
} else {
char *path_element;
if (p == NULL)
path_element = g_strdup (previous_p + 1);
else
path_element = g_strndup (previous_p + 1, p - (previous_p + 1));
g_string_append (pretty_path, path_element);
g_free (path_element);
}
g_free (path);
previous_p = p;
}
retval = pretty_path->str;
g_string_free (pretty_path, FALSE);
return retval;
}
static void
folder_gen_html (ESummary *summary,
ESummaryMailFolder *folder,
GString *string)
{
char *str, *pretty_name;
pretty_name = make_pretty_foldername (summary, folder);
str = g_strdup_printf ("<tr><td><a href=\"%s\">"
"<pre>%s</pre></a></td><td align=\"Left\">"
"<pre>%d/%d</pre>"
"</td></tr>",
folder->uri, pretty_name,
folder->unread, folder->count);
g_string_append (string, str);
g_free (pretty_name);
g_free (str);
}
static void
e_summary_mail_generate_html (ESummary *summary)
{
ESummaryMail *mail;
GString *string;
GList *p;
char *old;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
mail = summary->mail;
string = g_string_new ("<dl><dt><img src=\"myevo-mail-summary.png\" "
"align=\"middle\" alt=\"\" width=\"48\" "
"height=\"48\"> <b><a href=\"evolution:/local/Inbox\">");
g_string_append (string, _("Mail summary"));
g_string_append (string, "</a></b></dt><dd><table numcols=\"2\" width=\"100%\">");
for (p = folder_store->shown; p; p = p->next) {
ESummaryMailFolder *mail_folder = p->data;
folder_gen_html (summary, mail_folder, string);
}
g_string_append (string, "</table></dd></dl>");
old = mail->html;
mail->html = string->str;
g_free (old);
g_string_free (string, FALSE);
}
const char *
e_summary_mail_get_html (ESummary *summary)
{
/* Only regenerate HTML when it's needed */
e_summary_mail_generate_html (summary);
if (summary->mail == NULL) {
return NULL;
}
return summary->mail->html;
}
static void
e_summary_mail_get_info (const char *uri)
{
CORBA_Environment ev;
g_return_if_fail (folder_store->folder_info != CORBA_OBJECT_NIL);
CORBA_exception_init (&ev);
GNOME_Evolution_FolderInfo_getInfo (folder_store->folder_info, uri ? uri : "",
BONOBO_OBJREF (folder_store->listener), &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Error getting info for %s:\n%s", uri,
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return;
}
CORBA_exception_free (&ev);
return;
}
static gboolean
e_summary_mail_idle_get_info (gpointer user_data)
{
char *uri = user_data;
e_summary_mail_get_info (uri);
g_free (uri);
return FALSE;
}
static void
new_folder_cb (EvolutionStorageListener *listener,
const char *path,
const GNOME_Evolution_Folder *folder,
StorageInfo *si)
{
ESummaryPrefs *global_preferences;
ESummaryMailFolder *mail_folder;
GSList *p;
if (strcmp (folder->type, "mail") != 0)
return;
if (strncmp (folder->evolutionUri, "evolution:", 10) != 0)
return;
mail_folder = g_new (ESummaryMailFolder, 1);
mail_folder->si = si;
mail_folder->uri = g_strdup (folder->evolutionUri);
mail_folder->physical_uri = g_strdup (folder->physicalUri);
mail_folder->path = g_strdup (path);
mail_folder->display_name = g_strdup (folder->displayName);
mail_folder->count = -1;
mail_folder->unread = -1;
mail_folder->init = FALSE;
g_hash_table_insert (folder_store->path_to_folder, mail_folder->path, mail_folder);
g_hash_table_insert (folder_store->physical_uri_to_folder, mail_folder->physical_uri, mail_folder);
si->folders = g_list_prepend (si->folders, mail_folder);
global_preferences = e_summary_preferences_get_global ();
for (p = global_preferences->display_folders; p; p = p->next) {
ESummaryPrefsFolder *f = p->data;
if (strcmp (f->physical_uri, folder->physicalUri) == 0) {
folder_store->shown = g_list_append (folder_store->shown, mail_folder);
g_idle_add (e_summary_mail_idle_get_info,
g_strdup (mail_folder->physical_uri));
}
}
}
static void
update_folder_cb (EvolutionStorageListener *listener,
const char *path,
int unread_count,
StorageInfo *si)
{
ESummaryMailFolder *mail_folder;
GList *p;
mail_folder = g_hash_table_lookup (folder_store->path_to_folder, path);
if (mail_folder == NULL) {
return;
}
for (p = folder_store->shown; p; p = p->next) {
if (p->data == mail_folder) {
g_idle_add (e_summary_mail_idle_get_info,
g_strdup (mail_folder->physical_uri));
return;
}
}
}
static void
remove_folder_cb (EvolutionStorageListener *listener,
const char *path,
StorageInfo *si)
{
ESummaryMailFolder *mail_folder;
GList *p;
mail_folder = g_hash_table_lookup (folder_store->path_to_folder, path);
if (mail_folder == NULL) {
return;
}
/* Check if we're displaying it, because we can't display it if it
doesn't exist :) */
for (p = folder_store->shown; p; p = p->next) {
if (p->data == mail_folder) {
folder_store->shown = g_list_remove_link (folder_store->shown, p);
g_list_free (p);
}
}
g_hash_table_remove (folder_store->path_to_folder, path);
g_free (mail_folder->path);
g_free (mail_folder->uri);
g_free (mail_folder->physical_uri);
g_free (mail_folder->display_name);
g_free (mail_folder);
}
static void
mail_change_notify (BonoboListener *listener,
const char *name,
const BonoboArg *arg,
CORBA_Environment *ev,
gpointer data)
{
GNOME_Evolution_FolderInfo_MessageCount *count;
ESummaryMailFolder *folder;
ESummaryPrefs *global_preferences;
GSList *p;
count = arg->_value;
folder = g_hash_table_lookup (folder_store->physical_uri_to_folder, count->path);
if (folder == NULL) {
return;
}
folder->count = count->count;
folder->unread = count->unread;
folder->init = TRUE;
/* Are we displaying this folder? */
global_preferences = e_summary_preferences_get_global ();
for (p = global_preferences->display_folders; p; p = p->next) {
ESummaryPrefsFolder *f = p->data;
if (strcmp (f->physical_uri, folder->physical_uri) == 0) {
e_summary_redraw_all (); /* All summaries should be redrawn, not just this one */
return;
}
}
}
static void
e_summary_mail_protocol (ESummary *summary,
const char *uri,
void *closure)
{
}
static gboolean
e_summary_folder_register_storage (const char *name,
GNOME_Evolution_Storage corba_storage)
{
GNOME_Evolution_StorageListener corba_listener;
StorageInfo *si;
CORBA_Environment ev;
si = g_new (StorageInfo, 1);
si->name = g_strdup (name);
si->toplevel = NULL;
si->storage = corba_storage;
si->listener = evolution_storage_listener_new ();
si->folders = NULL;
folder_store->storage_list = g_slist_prepend (folder_store->storage_list, si);
g_signal_connect (si->listener, "new-folder", G_CALLBACK (new_folder_cb), si);
g_signal_connect (si->listener, "removed-folder", G_CALLBACK (remove_folder_cb), si);
g_signal_connect (si->listener, "update_folder", G_CALLBACK (update_folder_cb), si);
corba_listener = evolution_storage_listener_corba_objref (si->listener);
CORBA_exception_init (&ev);
GNOME_Evolution_Storage_addListener (corba_storage, corba_listener, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Exception adding listener: %s",
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return FALSE;
}
CORBA_exception_free (&ev);
return TRUE;
}
static gboolean
e_summary_folder_unregister_storage (StorageInfo *si, gboolean remove)
{
GNOME_Evolution_StorageListener corba_listener;
CORBA_Environment ev;
g_free (si->name);
corba_listener = evolution_storage_listener_corba_objref (si->listener);
CORBA_exception_init (&ev);
GNOME_Evolution_Storage_removeListener (si->storage, corba_listener, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Exception removing listener: %s",
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return FALSE;
}
CORBA_exception_free (&ev);
g_signal_handlers_disconnect_matched (si->listener, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, si);
g_object_unref (si->listener);
bonobo_object_release_unref (si->storage, NULL);
/* FIXME Folders */
if (remove)
folder_store->storage_list = g_slist_remove (folder_store->storage_list, si);
g_free (si);
return TRUE;
}
static void
e_summary_folder_register_local_storage (void)
{
GNOME_Evolution_Storage local_storage;
CORBA_Environment ev;
CORBA_exception_init (&ev);
local_storage = GNOME_Evolution_Shell_getLocalStorage (folder_store->shell, &ev);
if (BONOBO_EX (&ev) || local_storage == CORBA_OBJECT_NIL) {
g_warning ("Error getting local storage: %s", CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return;
}
CORBA_exception_free (&ev);
e_summary_folder_register_storage (_("Local Folders"), local_storage);
}
static void
storage_notify (BonoboListener *listener,
const char *name,
const BonoboArg *arg,
CORBA_Environment *ev,
gpointer data)
{
GNOME_Evolution_StorageRegistry_NotifyResult *nr;
GNOME_Evolution_Storage corba_storage;
CORBA_Environment ev2;
nr = arg->_value;
switch (nr->type) {
case GNOME_Evolution_StorageRegistry_STORAGE_CREATED:
/* These need to be special cased because they're special */
if (strcmp (nr->name, "summary") == 0) {
break;
}
if (strcmp (nr->name, "local") == 0) {
e_summary_folder_register_local_storage ();
break;
}
CORBA_exception_init (&ev2);
corba_storage = GNOME_Evolution_StorageRegistry_getStorageByName (folder_store->registry,
nr->name, &ev2);
if (BONOBO_EX (&ev2) || corba_storage == CORBA_OBJECT_NIL) {
g_warning ("Error getting storage %s\n%s", nr->name,
CORBA_exception_id (&ev2));
CORBA_exception_free (&ev2);
return;
}
CORBA_exception_free (&ev2);
e_summary_folder_register_storage (nr->name, corba_storage);
break;
case GNOME_Evolution_StorageRegistry_STORAGE_DESTROYED:
g_print ("%s removed\n", nr->name);
break;
default:
g_print ("Unknown response %d\n", nr->type);
break;
}
}
static gboolean
e_summary_folder_register_storages (GNOME_Evolution_Shell corba_shell)
{
Bonobo_Listener corba_listener;
CORBA_Environment ev;
CORBA_exception_init (&ev);
folder_store->registry = Bonobo_Unknown_queryInterface (corba_shell,
"IDL:GNOME/Evolution/StorageRegistry:1.0",
&ev);
if (BONOBO_EX (&ev) || folder_store->registry == NULL) {
g_warning ("No storage registry: %s", CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return FALSE;
}
folder_store->registry_listener = bonobo_listener_new (NULL, NULL);
g_signal_connect (folder_store->registry_listener, "event-notify", G_CALLBACK (storage_notify), NULL);
corba_listener = BONOBO_OBJREF (folder_store->registry_listener);
/* Storages will be added whenever the listener gets an event. */
GNOME_Evolution_StorageRegistry_addListener (folder_store->registry, corba_listener, &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Cannot add listener\n%s", CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return FALSE;
}
CORBA_exception_free (&ev);
return TRUE;
}
void
e_summary_mail_init (ESummary *summary)
{
ESummaryMail *mail;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
g_return_if_fail (folder_store != NULL);
mail = g_new0 (ESummaryMail, 1);
summary->mail = mail;
mail->html = NULL;
e_summary_add_protocol_listener (summary, "mail", e_summary_mail_protocol, mail);
return;
}
void
e_summary_mail_reconfigure (void)
{
ESummaryPrefs *preferences;
GList *old;
GSList *p;
old = folder_store->shown;
folder_store->shown = NULL;
preferences = e_summary_preferences_get_global ();
for (p = preferences->display_folders; p; p = p->next) {
ESummaryMailFolder *folder;
ESummaryPrefsFolder *f = p->data;
char *uri;
#if 0
if (strncmp (p->data, "file://", 7) == 0 ||
strncmp (p->data, "vfolder:", 8) == 0) {
uri = g_strdup (p->data);
} else {
uri = g_strconcat ("file://", p->data, NULL);
}
#endif
uri = g_strdup (f->physical_uri);
folder = g_hash_table_lookup (folder_store->physical_uri_to_folder, uri);
if (folder != NULL) {
if (folder->init == FALSE) {
e_summary_mail_get_info (folder->physical_uri);
}
folder_store->shown = g_list_append (folder_store->shown, folder);
}
g_free (uri);
}
/* Free the old list */
g_list_free (old);
/* e_summary_redraw_all (); */
}
void
e_summary_mail_free (ESummary *summary)
{
ESummaryMail *mail;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
mail = summary->mail;
#if 0
g_hash_table_foreach (mail->folders, free_folder, NULL);
g_hash_table_destroy (mail->folders);
#endif
g_free (mail->html);
#if 0
g_signal_handlers_disconnect_by_func (mail->storage_listener, G_CALLBACK (new_folder_cb), summary);
g_signal_handlers_disconnect_by_func (mail->storage_listener, G_CALLBACK (remove_folder_cb), summary);
g_signal_handlers_disconnect_by_func (mail->storage_listener, G_CALLBACK (update_folder_cb), summary);
#endif
g_free (mail);
summary->mail = NULL;
}
static void
folder_info_pb_changed (BonoboListener *listener,
const char *name,
const BonoboArg *arg,
CORBA_Environment *ev,
gpointer data)
{
e_summary_folder_register_storages (folder_store->shell);
bonobo_object_unref (listener);
}
static void
lazy_register_storages (void)
{
Bonobo_PropertyBag pb;
Bonobo_EventSource event;
BonoboListener *listener;
CORBA_Environment ev;
gboolean ready;
/* Get the PropertyBag */
CORBA_exception_init (&ev);
pb = Bonobo_Unknown_queryInterface (folder_store->folder_info,
"IDL:Bonobo/PropertyBag:1.0", &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Error getting propertybag interface: %s",
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
return;
}
/* Check the initial value */
ready = bonobo_property_bag_client_get_value_gboolean (pb,
"folder-info-ready",
NULL);
if (ready == TRUE) {
/* Register storages */
e_summary_folder_register_storages (folder_store->shell);
bonobo_object_release_unref (pb, NULL);
return;
}
/* Get thh event source for the bag */
event = Bonobo_Unknown_queryInterface (pb,
"IDL:Bonobo/EventSource:1.0", &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Error getting event source interface: %s",
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
bonobo_object_release_unref (pb, NULL);
return;
}
/* Connect a listener to it */
listener = bonobo_listener_new (NULL, NULL);
g_signal_connect (listener, "event-notify", G_CALLBACK (folder_info_pb_changed), NULL);
Bonobo_EventSource_addListener (event, BONOBO_OBJREF (listener), &ev);
if (BONOBO_EX (&ev)) {
g_warning ("Error adding listener: %s\n",
CORBA_exception_id (&ev));
CORBA_exception_free (&ev);
bonobo_object_unref (BONOBO_OBJECT (listener));
}
bonobo_object_release_unref (pb, NULL);
bonobo_object_release_unref (event, NULL);
}
gboolean
e_summary_folder_init_folder_store (GNOME_Evolution_Shell shell)
{
CORBA_Environment ev;
if (folder_store != NULL) {
return TRUE;
}
folder_store = g_new0 (FolderStore, 1);
folder_store->shell = shell;
CORBA_exception_init (&ev);
folder_store->folder_info = bonobo_activation_activate_from_id (MAIL_IID, 0, NULL, &ev);
if (BONOBO_EX (&ev) || folder_store->folder_info == NULL) {
g_warning ("Exception creating folderinfo: %s\n",
CORBA_exception_id (&ev) ? CORBA_exception_id (&ev) : "(null)");
CORBA_exception_free (&ev);
return FALSE;
}
CORBA_exception_free (&ev);
folder_store->listener = bonobo_listener_new (NULL, NULL);
g_signal_connect (folder_store->listener, "event-notify", G_CALLBACK (mail_change_notify), NULL);
/* Create a hash table for the folders */
folder_store->path_to_folder = g_hash_table_new (g_str_hash, g_str_equal);
folder_store->physical_uri_to_folder = g_hash_table_new (g_str_hash, g_str_equal);
/* Wait for the mailer to tell us we're ready to register */
lazy_register_storages ();
return TRUE;
}
gboolean
e_summary_folder_clear_folder_store (void)
{
GSList *l;
if (folder_store == NULL) {
return TRUE;
}
bonobo_object_release_unref (folder_store->folder_info, NULL);
bonobo_object_release_unref (folder_store->registry, NULL);
bonobo_object_unref (folder_store->registry_listener);
bonobo_object_unref (folder_store->listener);
for (l = folder_store->storage_list; l != NULL; l = l->next)
e_summary_folder_unregister_storage (l->data, FALSE);
g_slist_free (folder_store->storage_list);
g_free (folder_store);
folder_store = NULL;
return TRUE;
}