/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* e-summary.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: Iain Holmes */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "e-summary.h" #include "e-summary-preferences.h" #include "my-evolution-html.h" #include "Mail.h" #include #include #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; gboolean frozen; gboolean redraw_pending; }; typedef struct _ProtocolListener { ESummaryProtocolListener listener; void *closure; } ProtocolListener; static GHashTable *images_cache = NULL; 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; } if (summary->priv->frozen == TRUE) { summary->priv->redraw_pending = TRUE; 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 */ ret = gnome_pixmap_file (filename); if (ret == NULL) { g_warning ("Could not find pixmap for %s", filename); } return ret; } struct _imgcache { char *buffer; int bufsize; }; static void close_callback (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer data) { DownloadInfo *info = data; struct _imgcache *img; if (images_cache == NULL) { images_cache = g_hash_table_new (g_str_hash, g_str_equal); } img = g_new (struct _imgcache, 1); img->buffer = info->buffer; img->bufsize = info->bufsize; g_hash_table_insert (images_cache, info->uri, img); 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->bufsize); 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; struct _imgcache *img = NULL; 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; } if (images_cache != NULL) { img = g_hash_table_lookup (images_cache, filename); } if (img == NULL) { 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); } else { gtk_html_stream_write (stream, img->buffer, img->bufsize); gtk_html_stream_close (stream, GTK_HTML_STREAM_OK); } } 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_with_zone (t, summary->tz); summary->priv->alarm = alarm_add (day_end, alarm_fn, summary, NULL); e_summary_reconfigure (summary); } #define DEFAULT_HTML "Summaryhello" 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->frozen = FALSE; priv->redraw_pending = FALSE; 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 (BonoboUIComponent *component, gpointer userdata, const char *cname) { ESummary *summary = userdata; 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 (BonoboUIComponent *component, gpointer userdata, const char *cname) { ESummary *summary = userdata; if (summary->rdf != NULL) { e_summary_rdf_update (summary); } if (summary->weather != NULL) { e_summary_weather_update (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); } void e_summary_freeze (ESummary *summary) { g_return_if_fail (IS_E_SUMMARY (summary)); if (summary->priv->frozen == TRUE) { return; } summary->priv->frozen = TRUE; } void e_summary_thaw (ESummary *summary) { g_return_if_fail (IS_E_SUMMARY (summary)); if (summary->priv->frozen == FALSE) { return; } summary->priv->frozen = FALSE; if (summary->priv->redraw_pending) { e_summary_draw (summary); } }