Files
evolution/my-evolution/e-summary.c
Dan Winship 982c014bff Hack around gdkpixbuf lossage by not trying to display the images
* e-summary.c (read_callback, etc): Hack around gdkpixbuf lossage
	by not trying to display the images incrementall. Instead, just
	wait until we've read the whole file, then display it all at once.
	Prevents garbage when rendering the icons.

svn path=/trunk/; revision=12982
2001-09-19 18:15:57 +00:00

790 lines
18 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* e-summary.c: ESummary object.
*
* Copyright (C) 2001 Ximian, Inc.
*
* Authors: Iain Holmes <iain@ximian.com>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
#include <gtkhtml/gtkhtml.h>
#include <gtkhtml/gtkhtml-stream.h>
#include <gtkhtml/htmlengine.h>
#include <gtkhtml/htmlselection.h>
#include <libgnomevfs/gnome-vfs.h>
#include <gal/util/e-util.h>
#include <gal/widgets/e-gui-utils.h>
#include <gal/widgets/e-unicode.h>
#include <bonobo/bonobo-listener.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-moniker-util.h>
#include <bonobo-conf/bonobo-config-database.h>
#include <libgnome/gnome-paper.h>
#include <libgnome/gnome-url.h>
#include <libgnomeprint/gnome-print-master.h>
#include <libgnomeprint/gnome-print-master-preview.h>
#include <gui/alarm-notify/alarm.h>
#include <cal-util/timeutil.h>
#include "e-summary.h"
#include "e-summary-preferences.h"
#include "my-evolution-html.h"
#include "Mail.h"
#include <Evolution.h>
#include <time.h>
#define PARENT_TYPE (gtk_vbox_get_type ())
extern char *evolution_dir;
static GtkObjectClass *e_summary_parent_class;
struct _ESummaryMailFolderInfo {
char *name;
int count;
int unread;
};
typedef struct _DownloadInfo {
GtkHTMLStream *stream;
char *uri;
char *buffer, *ptr;
guint32 bufsize;
} DownloadInfo;
struct _ESummaryPrivate {
GNOME_Evolution_Shell shell;
GNOME_Evolution_ShellView shell_view_interface;
GtkWidget *html_scroller;
GtkWidget *html;
GHashTable *protocol_hash;
GList *connections;
gpointer alarm;
};
typedef struct _ProtocolListener {
ESummaryProtocolListener listener;
void *closure;
} ProtocolListener;
static void
free_protocol (gpointer key, gpointer value, gpointer user_data)
{
g_free (key);
g_free (value);
}
static void
destroy (GtkObject *object)
{
ESummary *summary;
ESummaryPrivate *priv;
summary = E_SUMMARY (object);
priv = summary->priv;
if (priv == NULL) {
return;
}
if (summary->mail) {
e_summary_mail_free (summary);
}
if (summary->calendar) {
e_summary_calendar_free (summary);
}
if (summary->rdf) {
e_summary_rdf_free (summary);
}
if (summary->weather) {
e_summary_weather_free (summary);
}
if (summary->tasks) {
e_summary_tasks_free (summary);
}
alarm_remove (priv->alarm);
alarm_done ();
if (priv->protocol_hash) {
g_hash_table_foreach (priv->protocol_hash, free_protocol, NULL);
g_hash_table_destroy (priv->protocol_hash);
}
g_free (priv);
summary->priv = NULL;
e_summary_parent_class->destroy (object);
}
void
e_summary_draw (ESummary *summary)
{
GString *string;
GtkHTMLStream *stream;
char *html;
char date[256], *date_utf;
time_t t;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
if (summary->mail == NULL || summary->calendar == NULL
|| summary->rdf == NULL || summary->weather == NULL
|| summary->tasks == NULL) {
return;
}
string = g_string_new (HTML_1);
t = time (NULL);
strftime (date, 255, _("%A, %B %e %Y"), localtime (&t));
date_utf = e_utf8_from_locale_string (date);
html = g_strdup_printf (HTML_2, date_utf);
g_free (date_utf);
g_string_append (string, html);
g_free (html);
g_string_append (string, HTML_3);
/* Weather and RDF stuff here */
html = e_summary_weather_get_html (summary);
if (html != NULL) {
g_string_append (string, html);
g_free (html);
}
html = e_summary_rdf_get_html (summary);
if (html != NULL) {
g_string_append (string, html);
g_free (html);
}
g_string_append (string, HTML_4);
html = (char *) e_summary_mail_get_html (summary);
if (html != NULL) {
g_string_append (string, html);
}
html = (char *) e_summary_calendar_get_html (summary);
if (html != NULL) {
g_string_append (string, html);
}
html = (char *) e_summary_tasks_get_html (summary);
if (html != NULL) {
g_string_append (string, html);
}
g_string_append (string, HTML_5);
stream = gtk_html_begin (GTK_HTML (summary->priv->html));
GTK_HTML (summary->priv->html)->engine->newPage = FALSE;
gtk_html_write (GTK_HTML (summary->priv->html), stream, string->str, strlen (string->str));
gtk_html_end (GTK_HTML (summary->priv->html), stream, GTK_HTML_STREAM_OK);
g_string_free (string, TRUE);
}
static char *
e_pixmap_file (const char *filename)
{
char *ret;
char *edir;
if (g_file_exists (filename)) {
ret = g_strdup (filename);
return ret;
}
/* Try the evolution images dir */
edir = g_concat_dir_and_file (EVOLUTION_DATADIR "/images/evolution",
filename);
if (g_file_exists (edir)) {
ret = g_strdup (edir);
g_free (edir);
return ret;
}
g_free (edir);
/* Try the evolution button images dir */
edir = g_concat_dir_and_file (EVOLUTION_DATADIR "/images/evolution/buttons",
filename);
if (g_file_exists (edir)) {
ret = g_strdup (edir);
g_free (edir);
return ret;
}
g_free (edir);
/* Fall back to the gnome_pixmap_file */
return gnome_pixmap_file (filename);
}
static void
close_callback (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
gpointer data)
{
DownloadInfo *info = data;
g_free (info->uri);
g_free (info->buffer);
g_free (info);
}
/* The way this behaves is a workaround for ximian bug 10235: loading
* the image into gtkhtml progressively will result in garbage being
* drawn, so we wait until we've read the whole thing and then write
* it all at once.
*/
static void
read_callback (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
gpointer buffer,
GnomeVFSFileSize bytes_requested,
GnomeVFSFileSize bytes_read,
gpointer data)
{
DownloadInfo *info = data;
if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
gtk_html_stream_close (info->stream, GTK_HTML_STREAM_ERROR);
gnome_vfs_async_close (handle, close_callback, info);
} else if (bytes_read == 0) {
gtk_html_stream_write (info->stream, info->buffer, info->ptr - info->buffer);
gtk_html_stream_close (info->stream, GTK_HTML_STREAM_OK);
gnome_vfs_async_close (handle, close_callback, info);
} else {
bytes_read += info->ptr - info->buffer;
info->bufsize += 4096;
info->buffer = g_realloc (info->buffer, info->bufsize);
info->ptr = info->buffer + bytes_read;
gnome_vfs_async_read (handle, info->ptr, 4095, read_callback, info);
}
}
static void
open_callback (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
DownloadInfo *info)
{
if (result != GNOME_VFS_OK) {
gtk_html_stream_close (info->stream, GTK_HTML_STREAM_ERROR);
g_free (info->uri);
g_free (info);
return;
}
info->bufsize = 4096;
info->buffer = info->ptr = g_new (char, info->bufsize);
gnome_vfs_async_read (handle, info->buffer, 4095, read_callback, info);
}
static void
e_summary_url_clicked (GtkHTML *html,
const char *url,
ESummary *summary)
{
char *protocol, *protocol_end;
ProtocolListener *protocol_listener;
protocol_end = strchr (url, ':');
if (protocol_end == NULL) {
/* No url, let gnome work it out */
gnome_url_show (url);
return;
}
protocol = g_strndup (url, protocol_end - url);
protocol_listener = g_hash_table_lookup (summary->priv->protocol_hash,
protocol);
g_free (protocol);
if (protocol_listener == NULL) {
/* Again, let gnome work it out */
gnome_url_show (url);
return;
}
protocol_listener->listener (summary, url, protocol_listener->closure);
}
static void
e_summary_url_requested (GtkHTML *html,
const char *url,
GtkHTMLStream *stream,
ESummary *summary)
{
char *filename;
GnomeVFSAsyncHandle *handle;
DownloadInfo *info;
if (strncasecmp (url, "file:", 5) == 0) {
url += 5;
filename = e_pixmap_file (url);
} else if (strchr (url, ':') >= strchr (url, '/')) {
filename = e_pixmap_file (url);
} else {
filename = g_strdup (url);
}
if (filename == NULL) {
gtk_html_stream_close (stream, GTK_HTML_STREAM_ERROR);
return;
}
info = g_new (DownloadInfo, 1);
info->stream = stream;
info->uri = filename;
gnome_vfs_async_open (&handle, filename, GNOME_VFS_OPEN_READ,
(GnomeVFSAsyncOpenCallback) open_callback, info);
}
static void
e_summary_evolution_protocol_listener (ESummary *summary,
const char *uri,
void *closure)
{
e_summary_change_current_view (summary, uri);
}
static void
e_summary_class_init (GtkObjectClass *object_class)
{
object_class->destroy = destroy;
e_summary_parent_class = gtk_type_class (PARENT_TYPE);
}
static void
alarm_fn (gpointer alarm_id,
time_t trigger,
gpointer data)
{
ESummary *summary;
time_t t, day_end;
summary = data;
t = time (NULL);
day_end = time_day_end (t);
summary->priv->alarm = alarm_add (day_end, alarm_fn, summary, NULL);
e_summary_reconfigure (summary);
}
#define DEFAULT_HTML "<html><head><title>Summary</title></head><body bgcolor=\"#ffffff\">hello</body></html>"
static void
e_summary_init (ESummary *summary)
{
Bonobo_ConfigDatabase db;
CORBA_Environment ev;
ESummaryPrivate *priv;
GdkColor bgcolor = {0, 0xffff, 0xffff, 0xffff};
time_t t, day_end;
summary->priv = g_new (ESummaryPrivate, 1);
priv = summary->priv;
priv->html_scroller = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->html_scroller),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
priv->html = gtk_html_new ();
gtk_html_set_editable (GTK_HTML (priv->html), FALSE);
gtk_html_set_default_content_type (GTK_HTML (priv->html),
"text/html; charset=utf-8");
gtk_html_set_default_background_color (GTK_HTML (priv->html), &bgcolor);
gtk_html_load_from_string (GTK_HTML (priv->html), DEFAULT_HTML,
strlen (DEFAULT_HTML));
gtk_signal_connect (GTK_OBJECT (priv->html), "url-requested",
GTK_SIGNAL_FUNC (e_summary_url_requested), summary);
gtk_signal_connect (GTK_OBJECT (priv->html), "link-clicked",
GTK_SIGNAL_FUNC (e_summary_url_clicked), summary);
#if 0
gtk_signal_connect (GTK_OBJECT (priv->html), "on-url",
GTK_SIGNAL_FUNC (e_summary_on_url), summary);
#endif
gtk_container_add (GTK_CONTAINER (priv->html_scroller), priv->html);
gtk_widget_show_all (priv->html_scroller);
gtk_box_pack_start (GTK_BOX (summary), priv->html_scroller,
TRUE, TRUE, 0);
priv->protocol_hash = NULL;
priv->connections = NULL;
summary->prefs_window = NULL;
e_summary_preferences_init (summary);
CORBA_exception_init (&ev);
db = bonobo_get_object ("wombat:", "Bonobo/ConfigDatabase", &ev);
if (BONOBO_EX (&ev) || db == CORBA_OBJECT_NIL) {
CORBA_exception_free (&ev);
g_warning ("Error getting Wombat. Using defaults");
return;
}
summary->timezone = bonobo_config_get_string (db, "/Calendar/Display/Timezone", NULL);
summary->tz = icaltimezone_get_builtin_timezone (summary->timezone);
bonobo_object_release_unref (db, NULL);
CORBA_exception_free (&ev);
t = time (NULL);
if (summary->tz == NULL) {
day_end = time_day_end (t);
} else {
day_end = time_day_end_with_zone (t, summary->tz);
}
priv->alarm = alarm_add (day_end, alarm_fn, summary, NULL);
}
E_MAKE_TYPE (e_summary, "ESummary", ESummary, e_summary_class_init,
e_summary_init, PARENT_TYPE);
GtkWidget *
e_summary_new (const GNOME_Evolution_Shell shell)
{
ESummary *summary;
summary = gtk_type_new (e_summary_get_type ());
summary->priv->shell = shell;
e_summary_add_protocol_listener (summary, "evolution", e_summary_evolution_protocol_listener, summary);
e_summary_mail_init (summary, shell);
e_summary_calendar_init (summary);
e_summary_tasks_init (summary);
e_summary_rdf_init (summary);
e_summary_weather_init (summary);
e_summary_draw (summary);
return GTK_WIDGET (summary);
}
static void
do_summary_print (ESummary *summary,
gboolean preview)
{
GnomePrintContext *print_context;
GnomePrintMaster *print_master;
GnomePrintDialog *gpd;
GnomePrinter *printer = NULL;
int copies = 1;
int collate = FALSE;
if (!preview) {
gpd = GNOME_PRINT_DIALOG (gnome_print_dialog_new (_("Print Summary"), GNOME_PRINT_DIALOG_COPIES));
gnome_dialog_set_default (GNOME_DIALOG (gpd), GNOME_PRINT_PRINT);
switch (gnome_dialog_run (GNOME_DIALOG (gpd))) {
case GNOME_PRINT_PRINT:
break;
case GNOME_PRINT_PREVIEW:
preview = TRUE;
break;
case -1:
return;
default:
gnome_dialog_close (GNOME_DIALOG (gpd));
return;
}
gnome_print_dialog_get_copies (gpd, &copies, &collate);
printer = gnome_print_dialog_get_printer (gpd);
gnome_dialog_close (GNOME_DIALOG (gpd));
}
print_master = gnome_print_master_new ();
if (printer) {
gnome_print_master_set_printer (print_master, printer);
}
gnome_print_master_set_copies (print_master, copies, collate);
print_context = gnome_print_master_get_context (print_master);
gtk_html_print (GTK_HTML (summary->priv->html), print_context);
gnome_print_master_close (print_master);
if (preview) {
gboolean landscape = FALSE;
GnomePrintMasterPreview *preview;
preview = gnome_print_master_preview_new_with_orientation (
print_master, _("Print Preview"), landscape);
gtk_widget_show (GTK_WIDGET (preview));
} else {
int result = gnome_print_master_print (print_master);
if (result == -1) {
e_notice (NULL, GNOME_MESSAGE_BOX_ERROR,
_("Printing of Summary failed"));
}
}
gtk_object_unref (GTK_OBJECT (print_master));
}
void
e_summary_print (GtkWidget *widget,
ESummary *summary)
{
do_summary_print (summary, FALSE);
}
void
e_summary_add_protocol_listener (ESummary *summary,
const char *protocol,
ESummaryProtocolListener listener,
void *closure)
{
ProtocolListener *old;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
g_return_if_fail (protocol != NULL);
g_return_if_fail (listener != NULL);
if (summary->priv->protocol_hash == NULL) {
summary->priv->protocol_hash = g_hash_table_new (g_str_hash,
g_str_equal);
old = NULL;
} else {
old = g_hash_table_lookup (summary->priv->protocol_hash, protocol);
}
if (old != NULL) {
return;
}
old = g_new (ProtocolListener, 1);
old->listener = listener;
old->closure = closure;
g_hash_table_insert (summary->priv->protocol_hash, g_strdup (protocol), old);
}
void
e_summary_change_current_view (ESummary *summary,
const char *uri)
{
GNOME_Evolution_ShellView svi;
CORBA_Environment ev;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
svi = summary->shell_view_interface;
if (svi == NULL) {
return;
}
CORBA_exception_init (&ev);
GNOME_Evolution_ShellView_changeCurrentView (svi, uri, &ev);
CORBA_exception_free (&ev);
}
void
e_summary_set_message (ESummary *summary,
const char *message,
gboolean busy)
{
GNOME_Evolution_ShellView svi;
CORBA_Environment ev;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
svi = summary->shell_view_interface;
if (svi == NULL) {
return;
}
CORBA_exception_init (&ev);
GNOME_Evolution_ShellView_setMessage (svi, message ? message : "", busy, &ev);
CORBA_exception_free (&ev);
}
void
e_summary_unset_message (ESummary *summary)
{
GNOME_Evolution_ShellView svi;
CORBA_Environment ev;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
svi = summary->shell_view_interface;
if (svi == NULL) {
return;
}
CORBA_exception_init (&ev);
GNOME_Evolution_ShellView_unsetMessage (svi, &ev);
CORBA_exception_free (&ev);
}
void
e_summary_reconfigure (ESummary *summary)
{
if (summary->mail != NULL) {
e_summary_mail_reconfigure (summary);
}
if (summary->rdf != NULL) {
e_summary_rdf_reconfigure (summary);
}
if (summary->weather != NULL) {
e_summary_weather_reconfigure (summary);
}
if (summary->calendar != NULL) {
e_summary_calendar_reconfigure (summary);
}
if (summary->tasks != NULL) {
e_summary_tasks_reconfigure (summary);
}
}
void
e_summary_reload (GtkWidget *widget,
ESummary *summary)
{
e_summary_reconfigure (summary);
}
int
e_summary_count_connections (ESummary *summary)
{
GList *p;
int count = 0;
if (summary == NULL) {
return 0;
}
for (p = summary->priv->connections; p; p = p->next) {
ESummaryConnection *c;
c = p->data;
count += c->count (summary, c->closure);
}
return count;
}
GList *
e_summary_add_connections (ESummary *summary)
{
GList *p;
GList *connections = NULL;
if (summary == NULL) {
return NULL;
}
for (p = summary->priv->connections; p; p = p->next) {
ESummaryConnection *c;
GList *r;
c = p->data;
r = c->add (summary, c->closure);
connections = g_list_concat (connections, r);
}
return connections;
}
void
e_summary_set_online (ESummary *summary,
GNOME_Evolution_OfflineProgressListener progress,
gboolean online,
ESummaryOnlineCallback callback,
void *closure)
{
GList *p;
if (summary == NULL) {
return;
}
for (p = summary->priv->connections; p; p = p->next) {
ESummaryConnection *c;
c = p->data;
c->callback = callback;
c->callback_closure = closure;
c->set_online (summary, progress, online, c->closure);
}
}
void
e_summary_add_online_connection (ESummary *summary,
ESummaryConnection *connection)
{
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
g_return_if_fail (connection != NULL);
summary->priv->connections = g_list_prepend (summary->priv->connections,
connection);
}
void
e_summary_remove_online_connection (ESummary *summary,
ESummaryConnection *connection)
{
GList *p;
g_return_if_fail (summary != NULL);
g_return_if_fail (IS_E_SUMMARY (summary));
g_return_if_fail (connection != NULL);
p = g_list_find (summary->priv->connections, connection);
g_return_if_fail (p != NULL);
summary->priv->connections = g_list_remove_link (summary->priv->connections, p);
g_list_free (p);
}