diff options
author | Harish Krishnaswamy <kharish@src.gnome.org> | 2005-01-08 18:47:59 +0800 |
---|---|---|
committer | Harish Krishnaswamy <kharish@src.gnome.org> | 2005-01-08 18:47:59 +0800 |
commit | eded2adbdb1ef63ade4c03920de113cd3abccf3a (patch) | |
tree | af480cc08ea206c15ec5b42caf2c9d349b7143af | |
parent | 0b6c6f6e5b2f4486a1955af2669604fa23f46183 (diff) | |
download | gsoc2013-evolution-eded2adbdb1ef63ade4c03920de113cd3abccf3a.tar.gz gsoc2013-evolution-eded2adbdb1ef63ade4c03920de113cd3abccf3a.tar.zst gsoc2013-evolution-eded2adbdb1ef63ade4c03920de113cd3abccf3a.zip |
New files that provide attachments support for calendar items.
svn path=/trunk/; revision=28286
-rw-r--r-- | calendar/gui/dialogs/cal-attachment-bar.c | 918 | ||||
-rw-r--r-- | calendar/gui/dialogs/cal-attachment-bar.h | 83 | ||||
-rw-r--r-- | calendar/gui/dialogs/cal-attachment-select-file.c | 224 | ||||
-rw-r--r-- | calendar/gui/dialogs/cal-attachment-select-file.h | 36 | ||||
-rw-r--r-- | calendar/gui/dialogs/cal-attachment.c | 486 | ||||
-rw-r--r-- | calendar/gui/dialogs/cal-attachment.glade | 237 | ||||
-rw-r--r-- | calendar/gui/dialogs/cal-attachment.h | 80 |
7 files changed, 2064 insertions, 0 deletions
diff --git a/calendar/gui/dialogs/cal-attachment-bar.c b/calendar/gui/dialogs/cal-attachment-bar.c new file mode 100644 index 0000000000..1d2c252476 --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment-bar.c @@ -0,0 +1,918 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Authors: Harish Krishnaswamy <kharish@novell.com> + * + * Copyright 2004 Novell, Inc. (www.novell.com) + * + * 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 Street #330, Boston, MA 02111-1307, USA. + * + */ + + +/* Much of this file has been shamelessly copied from the mail attachment + * handling code, including the fixmes. Modifications/Additions that are + * specific to the calendar component have been flagged by some comments + * fwiw */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <gconf/gconf.h> +#include <gconf/gconf-client.h> +#include <gdk/gdkkeysyms.h> +#include <libgnomevfs/gnome-vfs-mime-handlers.h> +#include <libgnome/gnome-i18n.h> +#include "comp-editor.h" +#include "cal-attachment-select-file.h" +#include "cal-attachment.h" +#include "cal-attachment-bar.h" + +#include <libedataserver/e-iconv.h> + +#include <camel/camel-data-wrapper.h> +#include <camel/camel-stream-fs.h> +#include <camel/camel-stream-null.h> +#include <camel/camel-stream-mem.h> +#include <camel/camel-stream-filter.h> +#include <camel/camel-mime-filter-bestenc.h> +#include <camel/camel-mime-part.h> +#include <camel/camel-file-utils.h> + +#include "e-util/e-gui-utils.h" +#include "e-util/e-icon-factory.h" +#include "widgets/misc/e-error.h" +#include "mail/em-popup.h" + +#define ICON_WIDTH 64 +#define ICON_SEPARATORS " /-_" +#define ICON_SPACING 2 +#define ICON_ROW_SPACING ICON_SPACING +#define ICON_COL_SPACING ICON_SPACING +#define ICON_BORDER 2 +#define ICON_TEXT_SPACING 2 + +static GnomeIconListClass *parent_class = NULL; + +struct _CalAttachmentBarPrivate { + GSList *attachments; + guint num_attachments; + /* The persistent copies of the attachments + * derive their location and name from the + * source_url and the comp_uid. + */ + char *source_url; + char *comp_uid; +}; + +enum { + CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void update (CalAttachmentBar *bar); + +static char * +size_to_string (gulong size) +{ + char *size_string; + + /* FIXME: The following should probably go into a separate module, as + we might have to do the same thing in other places as well. Also, + I am not sure this will be OK for all the languages. */ + + if (size < 1e3L) { + size_string = NULL; + } else { + gdouble displayed_size; + + if (size < 1e6L) { + displayed_size = (gdouble) size / 1.0e3; + size_string = g_strdup_printf (_("%.0fK"), displayed_size); + } else if (size < 1e9L) { + displayed_size = (gdouble) size / 1.0e6; + size_string = g_strdup_printf (_("%.0fM"), displayed_size); + } else { + displayed_size = (gdouble) size / 1.0e9; + size_string = g_strdup_printf (_("%.0fG"), displayed_size); + } + } + + return size_string; +} + +/* Attachment handling functions. */ + +static void +free_attachment_list (CalAttachmentBar *bar) +{ + CalAttachmentBarPrivate *priv; + GSList *p; + + priv = bar->priv; + + for (p = priv->attachments; p != NULL; p = p->next) + g_object_unref (p->data); + + priv->attachments = NULL; +} + +static void +attachment_changed_cb (CalAttachment *attachment, + gpointer data) +{ + update (CAL_ATTACHMENT_BAR (data)); +} + +static void +add_common (CalAttachmentBar *bar, + CalAttachment *attachment) +{ + g_return_if_fail (attachment != NULL); + + g_signal_connect (attachment, "changed", + G_CALLBACK (attachment_changed_cb), + bar); + + bar->priv->attachments = g_slist_append (bar->priv->attachments, + attachment); + bar->priv->num_attachments++; + + update (bar); + + g_signal_emit (bar, signals[CHANGED], 0); +} + +static void +add_from_mime_part (CalAttachmentBar *bar, + CamelMimePart *part) +{ + add_common (bar, cal_attachment_new_from_mime_part (part)); +} + +static void +add_from_file (CalAttachmentBar *bar, + const char *file_name, + const char *disposition) +{ + CalAttachment *attachment; + CamelException ex; + + camel_exception_init (&ex); + attachment = cal_attachment_new (file_name, disposition, &ex); + if (attachment) { + add_common (bar, attachment); + } else { + e_error_run((GtkWindow *)gtk_widget_get_toplevel((GtkWidget *)bar), "event-editor:no-attach", + file_name, camel_exception_get_description(&ex), NULL); + camel_exception_clear (&ex); + } +} + +static void +remove_attachment (CalAttachmentBar *bar, + CalAttachment *attachment) +{ + g_return_if_fail (E_IS_CAL_ATTACHMENT_BAR (bar)); + g_return_if_fail (g_slist_find (bar->priv->attachments, attachment) != NULL); + + bar->priv->attachments = g_slist_remove (bar->priv->attachments, + attachment); + bar->priv->num_attachments--; + if (attachment->editor_gui != NULL) { + GtkWidget *dialog = glade_xml_get_widget (attachment->editor_gui, "dialog"); + g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_CLOSE); + } + + g_object_unref(attachment); + + g_signal_emit (bar, signals[CHANGED], 0); +} + +/* Icon list contents handling. */ + +static void +update (CalAttachmentBar *bar) +{ + CalAttachmentBarPrivate *priv; + GnomeIconList *icon_list; + GSList *p; + + priv = bar->priv; + icon_list = GNOME_ICON_LIST (bar); + + gnome_icon_list_freeze (icon_list); + + gnome_icon_list_clear (icon_list); + + for (p = priv->attachments; p != NULL; p = p->next) { + CalAttachment *attachment; + CamelContentType *content_type; + char *size_string, *label; + GdkPixbuf *pixbuf; + const char *desc; + + attachment = p->data; + content_type = camel_mime_part_get_content_type (attachment->body); + /* Get the image out of the attachment + and create a thumbnail for it */ + pixbuf = attachment->pixbuf_cache; + if (pixbuf) { + g_object_ref(pixbuf); + } else if (camel_content_type_is(content_type, "image", "*")) { + CamelDataWrapper *wrapper; + CamelStreamMem *mstream; + GdkPixbufLoader *loader; + gboolean error = TRUE; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body)); + mstream = (CamelStreamMem *) camel_stream_mem_new (); + + camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); + + /* Stream image into pixbuf loader */ + loader = gdk_pixbuf_loader_new (); + error = !gdk_pixbuf_loader_write (loader, mstream->buffer->data, mstream->buffer->len, NULL); + gdk_pixbuf_loader_close (loader, NULL); + + if (!error) { + int ratio, width, height; + + /* Shrink pixbuf */ + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + if (width >= height) { + if (width > 48) { + ratio = width / 48; + width = 48; + height = height / ratio; + } + } else { + if (height > 48) { + ratio = height / 48; + height = 48; + width = width / ratio; + } + } + + attachment->pixbuf_cache = gdk_pixbuf_scale_simple + (pixbuf, + width, + height, + GDK_INTERP_BILINEAR); + pixbuf = attachment->pixbuf_cache; + g_object_ref(pixbuf); + } else { + pixbuf = NULL; + g_warning ("GdkPixbufLoader Error"); + } + + /* Destroy everything */ + g_object_unref (loader); + camel_object_unref (mstream); + } + + desc = camel_mime_part_get_description (attachment->body); + if (!desc || *desc == '\0') + desc = camel_mime_part_get_filename (attachment->body); + + if (!desc) + desc = _("attachment"); + + if (attachment->size + && (size_string = size_to_string (attachment->size))) { + label = g_strdup_printf ("%s (%s)", desc, size_string); + g_free (size_string); + } else + label = g_strdup (desc); + + if (pixbuf == NULL) { + char *mime_type; + + mime_type = camel_content_type_simple (content_type); + pixbuf = e_icon_for_mime_type (mime_type, 48); + if (pixbuf == NULL) { + g_warning("cannot find icon for mime type %s (installation problem?)", mime_type); + /* stock_attach would be better, but its fugly scaled up */ + pixbuf = e_icon_factory_get_icon("stock_unknown", E_ICON_SIZE_DIALOG); + } + g_free (mime_type); + } + + if (pixbuf) { + gnome_icon_list_append_pixbuf (icon_list, pixbuf, NULL, label); + g_object_unref(pixbuf); + } + + g_free (label); + } + gnome_icon_list_thaw (icon_list); +} + +static void +remove_selected (CalAttachmentBar *bar) +{ + GnomeIconList *icon_list; + CalAttachment *attachment; + GSList *attachment_list, *l; + GList *p; + int num = 0, left, dlen; + + icon_list = GNOME_ICON_LIST (bar); + + attachment_list = NULL; + p = gnome_icon_list_get_selection (icon_list); + dlen = g_list_length (p); + for ( ; p != NULL; p = p->next) { + num = GPOINTER_TO_INT (p->data); + attachment = CAL_ATTACHMENT (g_slist_nth_data (bar->priv->attachments, num)); + + if (g_slist_find (attachment_list, attachment) == NULL) { + attachment_list = g_slist_prepend (attachment_list, attachment); + } + } + + for (l = attachment_list; l != NULL; l = l->next) + remove_attachment (bar, CAL_ATTACHMENT (l->data)); + + g_slist_free (attachment_list); + + update (bar); + + left = gnome_icon_list_get_num_icons (icon_list); + num = num - dlen + 1; + if (left > 0) + gnome_icon_list_focus_icon (icon_list, left > num ? num : left - 1); +} + +static void +edit_selected (CalAttachmentBar *bar) +{ + GnomeIconList *icon_list; + GList *selection; + GSList *attach; + int num; + + icon_list = GNOME_ICON_LIST (bar); + + selection = gnome_icon_list_get_selection (icon_list); + if (selection) { + num = GPOINTER_TO_INT (selection->data); + attach = g_slist_nth (bar->priv->attachments, num); + if (attach) + cal_attachment_edit ((CalAttachment *)attach->data, GTK_WIDGET (bar)); + } +} + +/* "Attach" dialog. */ + +static void +add_from_user (CalAttachmentBar *bar) +{ + CompEditor *editor; + GPtrArray *file_list; + gboolean is_inline = FALSE; + int i; + + editor = COMP_EDITOR (gtk_widget_get_toplevel (GTK_WIDGET (bar))); + + + file_list = comp_editor_select_file_attachments (editor, &is_inline); + /*TODO add a good implementation here */ + if (!file_list) + return; + + for (i = 0; i < file_list->len; i++) { + add_from_file (bar, file_list->pdata[i], is_inline ? "inline" : "attachment"); + g_free (file_list->pdata[i]); + } + + g_ptr_array_free (file_list, TRUE); +} + +/* Callbacks. */ + +static void +cab_add(EPopup *ep, EPopupItem *item, void *data) +{ + CalAttachmentBar *bar = data; + + add_from_user(bar); +} + +static void +cab_properties(EPopup *ep, EPopupItem *item, void *data) +{ + CalAttachmentBar *bar = data; + + edit_selected(bar); +} + +static void +cab_remove(EPopup *ep, EPopupItem *item, void *data) +{ + CalAttachmentBar *bar = data; + + remove_selected(bar); +} + +/* Popup menu handling. */ +static EPopupItem cab_popups[] = { + { E_POPUP_ITEM, "10.attach", N_("_Remove"), cab_remove, NULL, GTK_STOCK_REMOVE, EM_POPUP_ATTACHMENTS_MANY }, + { E_POPUP_ITEM, "20.attach", N_("_Properties"), cab_properties, NULL, GTK_STOCK_PROPERTIES, EM_POPUP_ATTACHMENTS_ONE }, + { E_POPUP_BAR, "30.attach.00", NULL, NULL, NULL, NULL, EM_POPUP_ATTACHMENTS_MANY|EM_POPUP_ATTACHMENTS_ONE }, + { E_POPUP_ITEM, "30.attach.01", N_("_Add attachment..."), cab_add, NULL, GTK_STOCK_ADD, 0 }, +}; + +static void +cab_popup_position(GtkMenu *menu, int *x, int *y, gboolean *push_in, gpointer user_data) +{ + CalAttachmentBar *bar = user_data; + GnomeIconList *icon_list = user_data; + GList *selection; + GnomeCanvasPixbuf *image; + + gdk_window_get_origin (((GtkWidget*) bar)->window, x, y); + + selection = gnome_icon_list_get_selection (icon_list); + if (selection == NULL) + return; + + image = gnome_icon_list_get_icon_pixbuf_item (icon_list, (gint)selection->data); + if (image == NULL) + return; + + /* Put menu to the center of icon. */ + *x += (int)(image->item.x1 + image->item.x2) / 2; + *y += (int)(image->item.y1 + image->item.y2) / 2; +} + +static void +cab_popups_free(EPopup *ep, GSList *l, void *data) +{ + g_slist_free(l); +} + +/* if id != -1, then use it as an index for target of the popup */ +static void +cab_popup(CalAttachmentBar *bar, GdkEventButton *event, int id) +{ + GList *p; + GSList *attachments = NULL, *menus = NULL; + int i; + EMPopup *emp; + EMPopupTargetAttachments *t; + GtkMenu *menu; + CalAttachment *attachment; + + if (id == -1 + || (attachment = g_slist_nth_data(bar->priv->attachments, id)) == NULL) { + p = gnome_icon_list_get_selection((GnomeIconList *)bar); + for ( ; p != NULL; p = p->next) { + int num = GPOINTER_TO_INT(p->data); + CalAttachment *attachment = g_slist_nth_data(bar->priv->attachments, num); + + if (attachment && g_slist_find(attachments, attachment) == NULL) { + g_object_ref(attachment); + attachments = g_slist_prepend(attachments, attachment); + } + } + attachments = g_slist_reverse(attachments); + } else { + g_object_ref(attachment); + attachments = g_slist_prepend(attachments, attachment); + } + + for (i=0;i<sizeof(cab_popups)/sizeof(cab_popups[0]);i++) + menus = g_slist_prepend(menus, &cab_popups[i]); + + /** @HookPoint-EMPopup: Composer Attachment Bar Context Menu + * @Id: org.gnome.evolution.mail.composer.attachmentbar.popup + * @Class: org.gnome.evolution.mail.popup:1.0 + * @Target: EMPopupTargetAttachments + * + * This is the context menu on the composer attachment bar. + */ + emp = em_popup_new("org.gnome.evolution.mail.composer.attachmentbar.popup"); + e_popup_add_items((EPopup *)emp, menus, NULL, cab_popups_free, bar); + t = em_popup_target_new_attachments(emp, attachments); + t->target.widget = (GtkWidget *)bar; + menu = e_popup_create_menu_once((EPopup *)emp, (EPopupTarget *)t, 0); + + if (event == NULL) + gtk_menu_popup(menu, NULL, NULL, cab_popup_position, bar, 0, gtk_get_current_event_time()); + else + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time); +} + +/* GtkObject methods. */ + +static void +destroy (GtkObject *object) +{ + CalAttachmentBar *bar; + + bar = CAL_ATTACHMENT_BAR (object); + + if (bar->priv) { + free_attachment_list (bar); + g_free (bar->priv); + bar->priv = NULL; + } + + /* TODO leaking this here to prevent a crash */ + /* + if (bar->priv->source_url) + g_free (bar->priv->source_url); + if (bar->priv->comp_uid) + g_free (bar->priv->comp_uid); + */ + if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +/* GtkWidget methods. */ + +static gboolean +popup_menu_event (GtkWidget *widget) +{ + cab_popup((CalAttachmentBar *)widget, NULL, -1); + return TRUE; +} + + +static int +button_press_event (GtkWidget *widget, GdkEventButton *event) +{ + CalAttachmentBar *bar = (CalAttachmentBar *)widget; + GnomeIconList *icon_list = GNOME_ICON_LIST(widget); + int icon_number; + + if (event->button != 3) + return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event); + + icon_number = gnome_icon_list_get_icon_at (icon_list, event->x, event->y); + if (icon_number >= 0) { + gnome_icon_list_unselect_all(icon_list); + gnome_icon_list_select_icon (icon_list, icon_number); + } + + cab_popup(bar, event, icon_number); + + return TRUE; +} + +static gint +key_press_event(GtkWidget *widget, GdkEventKey *event) +{ + CalAttachmentBar *bar = CAL_ATTACHMENT_BAR (widget); + + if (event->keyval == GDK_Delete) { + remove_selected (bar); + return TRUE; + } + + return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event); +} + +/* Initialization. */ + +static void +class_init (CalAttachmentBarClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GnomeIconListClass *icon_list_class; + + object_class = GTK_OBJECT_CLASS (klass); + widget_class = GTK_WIDGET_CLASS (klass); + icon_list_class = GNOME_ICON_LIST_CLASS (klass); + + parent_class = g_type_class_ref (gnome_icon_list_get_type ()); + + object_class->destroy = destroy; + + widget_class->button_press_event = button_press_event; + widget_class->popup_menu = popup_menu_event; + widget_class->key_press_event = key_press_event; + + /* Setup signals. */ + + signals[CHANGED] = + g_signal_new ("changed", + E_TYPE_CAL_ATTACHMENT_BAR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (CalAttachmentBarClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +init (CalAttachmentBar *bar) +{ + CalAttachmentBarPrivate *priv; + + priv = g_new (CalAttachmentBarPrivate, 1); + + priv->attachments = NULL; + priv->num_attachments = 0; + priv->source_url = NULL; + priv->comp_uid = NULL; + + bar->priv = priv; +} + +GType +cal_attachment_bar_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof (CalAttachmentBarClass), + NULL, NULL, + (GClassInitFunc) class_init, + NULL, NULL, + sizeof (CalAttachmentBar), + 0, + (GInstanceInitFunc) init, + }; + + type = g_type_register_static (GNOME_TYPE_ICON_LIST, "CalAttachmentBar", &info, 0); + } + + return type; +} + +GtkWidget * +cal_attachment_bar_new (GtkAdjustment *adj) +{ + CalAttachmentBar *new; + GnomeIconList *icon_list; + int width, height, icon_width, window_height; + PangoFontMetrics *metrics; + PangoContext *context; + + new = g_object_new (cal_attachment_bar_get_type (), NULL); + + icon_list = GNOME_ICON_LIST (new); + + context = gtk_widget_get_pango_context ((GtkWidget *) new); + metrics = pango_context_get_metrics (context, ((GtkWidget *) new)->style->font_desc, pango_context_get_language (context)); + width = PANGO_PIXELS (pango_font_metrics_get_approximate_char_width (metrics)) * 15; + /* This should be *2, but the icon list creates too much space above ... */ + height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)) * 3; + pango_font_metrics_unref (metrics); + + icon_width = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING; + icon_width = MAX (icon_width, width); + + gnome_icon_list_construct (icon_list, icon_width, adj, 0); + + window_height = ICON_WIDTH + ICON_SPACING + ICON_BORDER + ICON_TEXT_SPACING + height; + gtk_widget_set_size_request (GTK_WIDGET (new), icon_width * 4, window_height); + + gnome_icon_list_set_separators (icon_list, ICON_SEPARATORS); + gnome_icon_list_set_row_spacing (icon_list, ICON_ROW_SPACING); + gnome_icon_list_set_col_spacing (icon_list, ICON_COL_SPACING); + gnome_icon_list_set_icon_border (icon_list, ICON_BORDER); + gnome_icon_list_set_text_spacing (icon_list, ICON_TEXT_SPACING); + gnome_icon_list_set_selection_mode (icon_list, GTK_SELECTION_MULTIPLE); + + return GTK_WIDGET (new); +} + +static char * +get_default_charset (void) +{ + GConfClient *gconf; + const char *locale; + char *charset; + + gconf = gconf_client_get_default (); + charset = gconf_client_get_string (gconf, "/apps/evolution/mail/composer/charset", NULL); + + if (!charset || charset[0] == '\0') { + g_free (charset); + charset = gconf_client_get_string (gconf, "/apps/evolution/mail/format/charset", NULL); + if (charset && charset[0] == '\0') { + g_free (charset); + charset = NULL; + } + } + + g_object_unref (gconf); + + if (!charset && (locale = e_iconv_locale_charset ())) + charset = g_strdup (locale); + + return charset ? charset : g_strdup ("us-ascii"); +} + +void +cal_attachment_bar_set_source_url (CalAttachmentBar *bar, char *source_url) +{ + if (bar->priv->source_url) + g_free (bar->priv->source_url); + bar->priv->source_url = source_url; +} + +void +cal_attachment_bar_set_comp_uid (CalAttachmentBar *bar, char *comp_uid) +{ + if (bar->priv->comp_uid) + g_free (bar->priv->comp_uid); + bar->priv->comp_uid = comp_uid; +} + +guint +cal_attachment_bar_get_num_attachments (CalAttachmentBar *bar) +{ + g_return_val_if_fail (bar != NULL, 0); + g_return_val_if_fail (E_IS_CAL_ATTACHMENT_BAR (bar), 0); + + return bar->priv->num_attachments; +} + +void +cal_attachment_bar_attach (CalAttachmentBar *bar, + const gchar *file_name) +{ + g_return_if_fail (E_IS_CAL_ATTACHMENT_BAR (bar)); + + if (file_name == NULL) + add_from_user (bar); + else + add_from_file (bar, file_name, "attachment"); +} + +/* + * TODO write a good documentation + * this method serializes the attached files on the filesystem + * and sets the urls to the component + */ +GSList * +cal_attachment_bar_get_attachment_list (CalAttachmentBar *bar) +{ + CalAttachmentBarPrivate *priv; + CalAttachment *attachment; + GSList *list = NULL, *p; + + priv = bar->priv; + for (p = priv->attachments; p != NULL; p = p->next) { + CamelDataWrapper *wrapper; + CamelStreamMem *mstream; + unsigned char *buffer = NULL; + int fd; + char *attach_file_url; + + attachment = p->data; + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attachment->body)); + mstream = (CamelStreamMem *) camel_stream_mem_new (); + + camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); + buffer = g_memdup (mstream->buffer->data, mstream->buffer->len); + + + /* Extract the content from the stream and write it down + * as a mime part file into the directory denoting the + * calendar source */ + /* TODO convert it into a generic path using the source + * uri and use content-id to generate filename */ + attach_file_url = g_strconcat (priv->source_url, + priv->comp_uid, "-", + camel_file_util_safe_filename + (camel_mime_part_get_filename (attachment->body)), NULL); + + fd = open (attach_file_url+7, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd == -1) { + /* TODO handle error conditions */ + g_message ("DEBUG: could not open the file descriptor\n"); + } + + /* write the camel mime part (attachment->body) into the file*/ + if (camel_write (fd, buffer, mstream->buffer->len) == -1) { + /* TODO handle error condition */ + g_message ("DEBUG: camel write failed.\n"); + } + list = g_slist_append (list, g_strdup (attach_file_url)); + /* do i need to close the fd */ + g_free (attach_file_url); + } + + return list; +} + +char * +cal_attachment_bar_get_nth_attachment_filename (CalAttachmentBar *bar, int n) +{ + CalAttachment *attach; + + g_return_val_if_fail (bar != NULL, 0); + g_return_val_if_fail (E_IS_CAL_ATTACHMENT_BAR (bar), 0); + + attach = g_slist_nth_data (bar->priv->attachments, n); + return g_strconcat (bar->priv->source_url, bar->priv->comp_uid, "-", camel_mime_part_get_filename + (attach->body), NULL); +} + +/* TODO (sigh) this loads the attachments + * from the file system just for displaying the iconlist + * need a better approach here. + */ +void +cal_attachment_bar_set_attachment_list (CalAttachmentBar *bar, GSList *attach_list) +{ + CalAttachmentBarPrivate *priv; + GSList *p; + + priv = bar->priv; + + if (priv->attachments) { + /* To prevent repopulating the + * bar due to redraw functions in fill_widget. + * Assumes it can be set only once. + */ + return; + } + + for (p = attach_list; p != NULL; p = p->next) { + char *attach_filename; + CalAttachment *attach; + + attach_filename = (char *) p->data; + /* should we assert if g_str_has_prefix (attach_filename, "file://")) + * here + */ + /* get url sans protocol and add it to the bar. + * how to set the filename properly */ + cal_attachment_bar_attach (bar, attach_filename +7); + /* set file name correctly on the display */ + attach = g_slist_nth_data (priv->attachments, + priv->num_attachments-1); + camel_mime_part_set_filename (attach->body, + attach_filename + strlen (priv->source_url)+ + strlen (priv->comp_uid) + 1); + update (bar); + } +} + +GSList * +cal_attachment_bar_get_mime_attach_list (CalAttachmentBar *bar) +{ + CalAttachmentBarPrivate *priv; + struct CalMimeAttach *cal_mime_attach; + GSList *attach_list = NULL, *l; + + /* TODO assert sanity of bar */ + priv = bar->priv; + for (l = priv->attachments; l ; l = l->next) { + + CalAttachment *attach = (CalAttachment *) l->data; + CamelDataWrapper *wrapper; + CamelStreamMem *mstream; + unsigned char *buffer = NULL; + char *desc; + + cal_mime_attach = g_malloc0 (sizeof (struct CalMimeAttach)); + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (attach->body)); + mstream = (CamelStreamMem *) camel_stream_mem_new (); + + camel_data_wrapper_decode_to_stream (wrapper, (CamelStream *) mstream); + buffer = g_memdup (mstream->buffer->data, mstream->buffer->len); + + cal_mime_attach->encoded_data = buffer; + cal_mime_attach->length = mstream->buffer->len; + cal_mime_attach->filename = g_strdup (camel_mime_part_get_filename + ((CamelMimePart *) attach->body)); + desc = camel_mime_part_get_description ((CamelMimePart *) attach->body); + if (!desc || *desc == '\0') + desc = _("attachment"); + cal_mime_attach->description = g_strdup (desc); + cal_mime_attach->content_type = g_strdup (camel_data_wrapper_get_mime_type (wrapper)); + + attach_list = g_slist_append (attach_list, cal_mime_attach); + + } + + return attach_list; +} diff --git a/calendar/gui/dialogs/cal-attachment-bar.h b/calendar/gui/dialogs/cal-attachment-bar.h new file mode 100644 index 0000000000..97c6ac0cf4 --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment-bar.h @@ -0,0 +1,83 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* msg-composer-attachment-bar.h + * + * Copyright (C) 2004 Novell, 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 + * published by the Free Software Foundation; either version 2 of the + * 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: Harish Krishnaswamy <kharish@novell.com> + */ + +#ifndef __CAL_ATTACHMENT_BAR_H__ +#define __CAL_ATTACHMENT_BAR_H__ + +#include <libgnomeui/gnome-icon-list.h> +#include <camel/camel-multipart.h> + + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define E_TYPE_CAL_ATTACHMENT_BAR \ + (cal_attachment_bar_get_type ()) +#define CAL_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_ATTACHMENT_BAR, CalAttachmentBar)) +#define CAL_ATTACHMENT_BAR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_ATTACHMENT_BAR, CalAttachmentBarClass)) +#define E_IS_CAL_ATTACHMENT_BAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_ATTACHMENT_BAR)) +#define E_IS_CAL_ATTACHMENT_BAR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_CAL_ATTACHMENT_BAR)) + +typedef struct _CalAttachmentBar CalAttachmentBar; +typedef struct _CalAttachmentBarClass CalAttachmentBarClass; +typedef struct _CalAttachmentBarPrivate CalAttachmentBarPrivate; + +struct _CalAttachmentBar { + GnomeIconList parent; + + CalAttachmentBarPrivate *priv; +}; + +struct _CalAttachmentBarClass { + GnomeIconListClass parent_class; + + void (* changed) (CalAttachmentBar *bar); +}; + + +GtkType cal_attachment_bar_get_type (void); + +GtkWidget *cal_attachment_bar_new (GtkAdjustment *adj); +void cal_attachment_bar_to_multipart (CalAttachmentBar *bar, CamelMultipart *multipart, + const char *default_charset); +guint cal_attachment_bar_get_num_attachments (CalAttachmentBar *bar); +void cal_attachment_bar_attach (CalAttachmentBar *bar, const char *file_name); +void cal_attachment_bar_attach_mime_part (CalAttachmentBar *bar, CamelMimePart *part); +GSList *cal_attachment_bar_get_attachment_list (CalAttachmentBar *bar); +char * cal_attachment_bar_get_nth_attachment_filename (CalAttachmentBar *bar, int n); +GSList *cal_attachment_bar_get_mime_attach_list (CalAttachmentBar *bar); +void cal_attachment_bar_set_attachment_list (CalAttachmentBar *bar, GSList *attach_list); +void cal_attachment_bar_set_source_url (CalAttachmentBar *bar, char *source_url); +void cal_attachment_bar_set_comp_uid (CalAttachmentBar *bar, char *comp_uid); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAL_ATTACHMENT_BAR_H__ */ diff --git a/calendar/gui/dialogs/cal-attachment-select-file.c b/calendar/gui/dialogs/cal-attachment-select-file.c new file mode 100644 index 0000000000..6e64ea09c7 --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment-select-file.c @@ -0,0 +1,224 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Authors: Harish Krishnaswamy <kharish@novell.com> + * + * Copyright 2004 Novell, Inc. (www.novell.com) + * + * 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 Street #330, Boston, MA 02111-1307, USA. + * + */ + +/* Much of this file has been shamelessly copied from the mail attachment + * handling code, including the fixmes. Modifications/Additions that are + * specific to the calendar component have been flagged by some comments + * fwiw */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtkbox.h> +#include <gtk/gtkcheckbutton.h> +#include <gtk/gtkmain.h> +#include <gtk/gtksignal.h> +#include <gtk/gtkversion.h> + +#ifdef USE_GTKFILECHOOSER +#include <gtk/gtkfilechooser.h> +#include <gtk/gtkfilechooserdialog.h> +#include <gtk/gtkstock.h> +#else +#include <gtk/gtkfilesel.h> +#endif + +#include <libgnomeui/gnome-uidefs.h> +#include <libgnome/gnome-i18n.h> + +#include "cal-attachment-select-file.h" +#include <e-util/e-icon-factory.h> + +enum { + SELECTOR_MODE_MULTI = (1 << 0), + SELECTOR_MODE_SAVE = (1 << 1) +}; + +static GtkWidget* +run_selector(CompEditor *editor, const char *title, guint32 flags, gboolean *showinline_p) +{ + GtkWidget *selection; + GtkWidget *showinline = NULL; + char *path; + GList *icon_list; + + path = g_object_get_data ((GObject *) editor, "attach_path"); + +#ifdef USE_GTKFILECHOOSER + if (flags & SELECTOR_MODE_SAVE) + selection = gtk_file_chooser_dialog_new (title, + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_OK, + NULL); + else + selection = gtk_file_chooser_dialog_new (title, + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (selection), GTK_RESPONSE_OK); + + if ((flags & SELECTOR_MODE_SAVE) == 0) + gtk_file_chooser_set_select_multiple ((GtkFileChooser *) selection, (flags & SELECTOR_MODE_MULTI)); + + /* restore last path used */ + if (!path) + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (selection), g_get_home_dir ()); + else + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (selection), path); + + if (showinline_p) { + showinline = gtk_check_button_new_with_label (_("Suggest automatic display of attachment")); + gtk_widget_show (showinline); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (selection), showinline); + } +#else + selection = gtk_file_selection_new (title); + + gtk_file_selection_set_select_multiple ((GtkFileSelection *) selection, (flags & SELECTOR_MODE_MULTI)); + + /* restore last path used */ + if (!path) { + path = g_strdup_printf ("%s/", g_get_home_dir ()); + gtk_file_selection_set_filename (GTK_FILE_SELECTION (selection), path); + g_free (path); + } else { + gtk_file_selection_set_filename (GTK_FILE_SELECTION (selection), path); + } + + if (showinline_p) { + showinline = gtk_check_button_new_with_label (_("Suggest automatic display of attachment")); + gtk_widget_show (showinline); + gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (selection)->main_vbox), showinline, FALSE, FALSE, 4); + } +#endif + + gtk_window_set_transient_for ((GtkWindow *) selection, (GtkWindow *) editor); + gtk_window_set_wmclass ((GtkWindow *) selection, "fileselection", "Evolution:editor"); + gtk_window_set_modal ((GtkWindow *) selection, TRUE); + + icon_list = e_icon_factory_get_icon_list ("stock_mail-compose"); + if (icon_list) { + gtk_window_set_icon_list (GTK_WINDOW (selection), icon_list); + g_list_foreach (icon_list, (GFunc) g_object_unref, NULL); + g_list_free (icon_list); + } + + if (gtk_dialog_run ((GtkDialog *) selection) == GTK_RESPONSE_OK) { + if (showinline_p) + *showinline_p = gtk_toggle_button_get_active ((GtkToggleButton *) showinline); + +#ifdef USE_GTKFILECHOOSER + path = g_path_get_dirname (gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (selection))); +#else + path = g_path_get_dirname (gtk_file_selection_get_filename (GTK_FILE_SELECTION (selection))); +#endif + + g_object_set_data_full ((GObject *) editor, "attach_path", g_strdup_printf ("%s/", path), g_free); + g_free (path); + } else { + gtk_widget_destroy (selection); + selection = NULL; + } + + return selection; +} + +/** + * comp_editor_select_file: + * @editor: a editor + * @title: the title for the file selection dialog box + * @save_mode: whether the file selection box should be shown in save mode or not + * + * This pops up a file selection dialog box with the given title + * and allows the user to select a file. + * + * Return value: the selected filename, or %NULL if the user + * cancelled. + **/ +char * +comp_editor_select_file (CompEditor *editor, const char *title, gboolean save_mode) +{ + guint32 flags = save_mode ? SELECTOR_MODE_SAVE : SELECTOR_MODE_MULTI; + GtkWidget *selection; + char *name = NULL; + + selection = run_selector (editor, title, flags, NULL); + if (selection) { +#ifdef USE_GTKFILECHOOSER + name = g_strdup (gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (selection))); +#else + name = g_strdup (gtk_file_selection_get_filename (GTK_FILE_SELECTION (selection))); +#endif + gtk_widget_destroy (selection); + } + + return name; +} + +GPtrArray * +comp_editor_select_file_attachments (CompEditor *editor, gboolean *showinline_p) +{ + GtkWidget *selection; + GPtrArray *list = NULL; + + selection = run_selector (editor, _("Attach file(s)"), SELECTOR_MODE_MULTI, showinline_p); + + if (selection) { +#ifdef USE_GTKFILECHOOSER + GSList *files, *l, *n; + + if ((l = files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (selection)))) { + list = g_ptr_array_new (); + + while (l) { + n = l->next; + g_ptr_array_add (list, l->data); + g_slist_free_1 (l); + l = n; + } + } +#else + char **files; + int i; + + if ((files = gtk_file_selection_get_selections (GTK_FILE_SELECTION (selection)))) { + list = g_ptr_array_new (); + for (i = 0; files[i]; i++) + g_ptr_array_add (list, files[i]); + + g_free (files); + } +#endif + + gtk_widget_destroy (selection); + } + + return list; +} + diff --git a/calendar/gui/dialogs/cal-attachment-select-file.h b/calendar/gui/dialogs/cal-attachment-select-file.h new file mode 100644 index 0000000000..99b113783a --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment-select-file.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* cal-attachment-select-file.c + * + * Copyright (C) 2004 Novell, 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 + * published by the Free Software Foundation; either version 2 of the + * 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: Harish K <kharish@novell.com> + */ + +#ifndef E_MSG_COMPOSER_SELECT_FILE_H +#define E_MSG_COMPOSER_SELECT_FILE_H + +#include "comp-editor.h" + +char *comp_editor_select_file (CompEditor *editor, + const char *title, + gboolean save_mode); + +GPtrArray *comp_editor_select_file_attachments (CompEditor *editor, + gboolean *inline_p); + +#endif /* CAL_ATTACHMENT_SELECT_FILE_H */ diff --git a/calendar/gui/dialogs/cal-attachment.c b/calendar/gui/dialogs/cal-attachment.c new file mode 100644 index 0000000000..2f36aaf887 --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment.c @@ -0,0 +1,486 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Authors: Harish Krishnaswamy <kharish@novell.com> + * Copyright 2004 Novell Inc. <www.novell.com> + * + * 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 Street #330, Boston, MA 02111-1307, USA. + * + */ + + +/* Much of this file has been shamelessly copied from the mail attachment + * handling code, including the fixmes. Modifications/Additions that are + * specific to the calendar component have been flagged by some comments + * fwiw */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/stat.h> +#include <string.h> +#include <errno.h> + +#include <gtk/gtknotebook.h> +#include <gtk/gtktogglebutton.h> +#include <gtk/gtkdialog.h> +#include <libgnomevfs/gnome-vfs.h> +#include <libgnome/gnome-i18n.h> + +#include "e-util/e-mktemp.h" +#include <camel/camel.h> + +#include "comp-editor.h" +#include "cal-attachment.h" + + +enum { + CHANGED, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +static GObjectClass *parent_class = NULL; + + +static void +changed (CalAttachment *attachment) +{ + g_signal_emit (attachment, signals[CHANGED], 0); +} + + +/* GtkObject methods. */ + +static void +finalise(GObject *object) +{ + CalAttachment *attachment; + + attachment = CAL_ATTACHMENT (object); + + camel_object_unref (attachment->body); + if (attachment->pixbuf_cache != NULL) + g_object_unref (attachment->pixbuf_cache); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +/* Signals. */ + +static void +real_changed (CalAttachment *cal_attachment) +{ + g_return_if_fail (E_IS_CAL_ATTACHMENT (cal_attachment)); +} + + +static void +class_init (CalAttachmentClass *klass) +{ + GObjectClass *object_class; + + object_class = (GObjectClass*) klass; + parent_class = g_type_class_ref (G_TYPE_OBJECT); + + object_class->finalize = finalise; + klass->changed = real_changed; + + signals[CHANGED] = g_signal_new ("changed", + E_TYPE_CAL_ATTACHMENT, + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (CalAttachmentClass, changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +init (CalAttachment *cal_attachment) +{ + cal_attachment->editor_gui = NULL; + cal_attachment->body = NULL; + cal_attachment->size = 0; + cal_attachment->pixbuf_cache = NULL; +} + +GType +cal_attachment_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof (CalAttachmentClass), + NULL, + NULL, + (GClassInitFunc) class_init, + NULL, + NULL, + sizeof (CalAttachment), + 0, + (GInstanceInitFunc) init, + }; + + type = g_type_register_static (G_TYPE_OBJECT, "CalAttachment", &info, 0); + } + + return type; +} + +/** + * cal_attachment_guess_mime_type: + * @file_name: filename + * Returns the guessed mime type of the file given by #file_name + **/ + +static char * +cal_attachment_guess_mime_type (const char *file_name) +{ + GnomeVFSFileInfo *info; + GnomeVFSResult result; + char *type = NULL; + + info = gnome_vfs_file_info_new (); + result = gnome_vfs_get_file_info (file_name, info, + GNOME_VFS_FILE_INFO_GET_MIME_TYPE | + GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE | + GNOME_VFS_FILE_INFO_FOLLOW_LINKS); + if (result == GNOME_VFS_OK) + type = g_strdup (gnome_vfs_file_info_get_mime_type (info)); + + gnome_vfs_file_info_unref (info); + + return type; +} + + +/** + * cal_attachment_new: + * @file_name: filename to attach + * @disposition: Content-Disposition of the attachment + * @ex: exception + * + * Return value: the new attachment, or %NULL on error + **/ +CalAttachment * +cal_attachment_new (const char *file_name, + const char *disposition, + CamelException *ex) +{ + CalAttachment *new; + CamelMimePart *part; + CamelDataWrapper *wrapper; + CamelStream *stream; + struct stat statbuf; + char *mime_type; + char *filename; + + g_return_val_if_fail (file_name != NULL, NULL); + + if (stat (file_name, &statbuf) < 0) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot attach file %s: %s"), + file_name, g_strerror (errno)); + return NULL; + } + + /* return if it's not a regular file */ + if (!S_ISREG (statbuf.st_mode)) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot attach file %s: not a regular file"), + file_name); + return NULL; + } + + stream = camel_stream_fs_new_with_name (file_name, O_RDONLY, 0); + if (!stream) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot attach file %s: %s"), + file_name, g_strerror (errno)); + return NULL; + } + + mime_type = cal_attachment_guess_mime_type (file_name); + if (mime_type) { + if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) { + wrapper = (CamelDataWrapper *) camel_mime_message_new (); + } else { + wrapper = camel_data_wrapper_new (); + } + + camel_data_wrapper_construct_from_stream (wrapper, stream); + camel_data_wrapper_set_mime_type (wrapper, mime_type); + g_free (mime_type); + } else { + wrapper = camel_data_wrapper_new (); + camel_data_wrapper_construct_from_stream (wrapper, stream); + camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream"); + } + + camel_object_unref (stream); + + part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper); + camel_object_unref (wrapper); + + camel_mime_part_set_disposition (part, disposition); + filename = g_path_get_basename (file_name); + camel_mime_part_set_filename (part, filename); + g_free (filename); + +#if 0 + /* Note: Outlook 2002 is broken with respect to Content-Ids on + non-multipart/related parts, so as an interoperability + workaround, don't set a Content-Id on these parts. Fixes + bug #10032 */ + /* set the Content-Id */ + content_id = camel_header_msgid_generate (); + camel_mime_part_set_content_id (part, content_id); + g_free (content_id); +#endif + + new = g_object_new (E_TYPE_CAL_ATTACHMENT, NULL); + new->editor_gui = NULL; + new->body = part; + new->size = statbuf.st_size; + new->guessed_type = TRUE; + + return new; +} + + +/** + * cal_attachment_new_from_mime_part: + * @part: a CamelMimePart + * + * Return value: a new CalAttachment based on the mime part + **/ +CalAttachment * +cal_attachment_new_from_mime_part (CamelMimePart *part) +{ + CalAttachment *new; + CamelMimePart *mime_part; + CamelStream *stream; + + g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL); + + stream = camel_stream_mem_new (); + if (camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), stream) == -1) { + camel_object_unref (stream); + return NULL; + } + + camel_stream_reset (stream); + mime_part = camel_mime_part_new (); + + if (camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (mime_part), stream) == -1) { + camel_object_unref (mime_part); + camel_object_unref (stream); + return NULL; + } + + camel_object_unref (stream); + + new = g_object_new (E_TYPE_CAL_ATTACHMENT, NULL); + new->editor_gui = NULL; + new->body = mime_part; + new->guessed_type = FALSE; + new->size = 0; + + return new; +} + + +/* The attachment property dialog. */ + +typedef struct { + GtkWidget *dialog; + GtkEntry *file_name_entry; + GtkEntry *description_entry; + GtkEntry *mime_type_entry; + GtkToggleButton *disposition_checkbox; + CalAttachment *attachment; +} DialogData; + +static void +destroy_dialog_data (DialogData *data) +{ + g_free (data); +} + +/* + * fixme: I am converting EVERYTHING to/from UTF-8, although mime types + * are in ASCII. This is not strictly necessary, but we want to be + * consistent and possibly check for errors somewhere. + */ + +static void +set_entry (GladeXML *xml, const char *widget_name, const char *value) +{ + GtkEntry *entry; + + entry = GTK_ENTRY (glade_xml_get_widget (xml, widget_name)); + if (entry == NULL) + g_warning ("Entry for `%s' not found.", widget_name); + else + gtk_entry_set_text (entry, value ? value : ""); +} + +static void +connect_widget (GladeXML *gui, const char *name, const char *signal_name, + GCallback func, gpointer data) +{ + GtkWidget *widget; + + widget = glade_xml_get_widget (gui, name); + g_signal_connect (widget, signal_name, func, data); +} + +static void +close_cb (GtkWidget *widget, gpointer data) +{ + CalAttachment *attachment; + DialogData *dialog_data; + + dialog_data = (DialogData *) data; + attachment = dialog_data->attachment; + + gtk_widget_destroy (dialog_data->dialog); + g_object_unref (attachment->editor_gui); + attachment->editor_gui = NULL; + + g_object_unref (attachment); + + destroy_dialog_data (dialog_data); +} + +static void +ok_cb (GtkWidget *widget, gpointer data) +{ + DialogData *dialog_data; + CalAttachment *attachment; + const char *str; + + dialog_data = (DialogData *) data; + attachment = dialog_data->attachment; + + str = gtk_entry_get_text (dialog_data->file_name_entry); + camel_mime_part_set_filename (attachment->body, str); + + str = gtk_entry_get_text (dialog_data->description_entry); + camel_mime_part_set_description (attachment->body, str); + + str = gtk_entry_get_text (dialog_data->mime_type_entry); + camel_mime_part_set_content_type (attachment->body, str); + + camel_data_wrapper_set_mime_type(camel_medium_get_content_object(CAMEL_MEDIUM (attachment->body)), str); + + switch (gtk_toggle_button_get_active (dialog_data->disposition_checkbox)) { + case 0: + camel_mime_part_set_disposition (attachment->body, "attachment"); + break; + case 1: + camel_mime_part_set_disposition (attachment->body, "inline"); + break; + default: + /* Hmmmm? */ + break; + } + + changed (attachment); + close_cb (widget, data); +} + +static void +response_cb (GtkWidget *widget, gint response, gpointer data) +{ + if (response == GTK_RESPONSE_OK) + ok_cb (widget, data); + else + close_cb (widget, data); +} + +void +cal_attachment_edit (CalAttachment *attachment, GtkWidget *parent) +{ + CamelContentType *content_type; + const char *disposition; + DialogData *dialog_data; + GladeXML *editor_gui; + char *type; + + g_return_if_fail (attachment != NULL); + g_return_if_fail (E_IS_CAL_ATTACHMENT (attachment)); + + if (attachment->editor_gui != NULL) { + GtkWidget *window; + + window = glade_xml_get_widget (attachment->editor_gui, + "dialog"); + gdk_window_show (window->window); + return; + } + + editor_gui = glade_xml_new (EVOLUTION_GLADEDIR "/cal-attachment.glade", + NULL, NULL); + if (editor_gui == NULL) { + g_warning ("Cannot load `cal-attachment.glade'"); + return; + } + + attachment->editor_gui = editor_gui; + + gtk_window_set_transient_for + (GTK_WINDOW (glade_xml_get_widget (editor_gui, "dialog")), + GTK_WINDOW (gtk_widget_get_toplevel (parent))); + + dialog_data = g_new (DialogData, 1); + g_object_ref (attachment); + dialog_data->attachment = attachment; + dialog_data->dialog = glade_xml_get_widget (editor_gui, "dialog"); + dialog_data->file_name_entry = GTK_ENTRY ( + glade_xml_get_widget (editor_gui, "file_name_entry")); + dialog_data->description_entry = GTK_ENTRY ( + glade_xml_get_widget (editor_gui, "description_entry")); + dialog_data->mime_type_entry = GTK_ENTRY ( + glade_xml_get_widget (editor_gui, "mime_type_entry")); + dialog_data->disposition_checkbox = GTK_TOGGLE_BUTTON ( + glade_xml_get_widget (editor_gui, "disposition_checkbox")); + + set_entry (editor_gui, "file_name_entry", + camel_mime_part_get_filename (attachment->body)); + set_entry (editor_gui, "description_entry", + camel_mime_part_get_description (attachment->body)); + content_type = camel_mime_part_get_content_type (attachment->body); + type = camel_content_type_simple (content_type); + set_entry (editor_gui, "mime_type_entry", type); + g_free (type); + + disposition = camel_mime_part_get_disposition (attachment->body); + gtk_toggle_button_set_active (dialog_data->disposition_checkbox, + disposition && !g_ascii_strcasecmp (disposition, "inline")); + + connect_widget (editor_gui, "dialog", "response", (GCallback)response_cb, dialog_data); +#warning "signal connect while alive" + /* make sure that when the composer gets hidden/closed that our windows also close */ + parent = gtk_widget_get_toplevel (parent); + gtk_signal_connect_while_alive (GTK_OBJECT (parent), "destroy", (GCallback)close_cb, dialog_data, + GTK_OBJECT (dialog_data->dialog)); + gtk_signal_connect_while_alive (GTK_OBJECT (parent), "hide", (GCallback)close_cb, dialog_data, + GTK_OBJECT (dialog_data->dialog)); +} diff --git a/calendar/gui/dialogs/cal-attachment.glade b/calendar/gui/dialogs/cal-attachment.glade new file mode 100644 index 0000000000..dc228eef70 --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment.glade @@ -0,0 +1,237 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkDialog" id="dialog"> + <property name="border_width">6</property> + <property name="visible">True</property> + <property name="title" translatable="yes">Attachment Properties</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="close_button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="ok_button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkEntry" id="description_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char" translatable="yes">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="mime_type_entry"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="editable">False</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char" translatable="yes">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="mime_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">MIME type:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">1</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="description_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">Description:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">1</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="filename_label"> + <property name="visible">True</property> + <property name="label" translatable="yes">File name:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">1</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="file_name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char" translatable="yes">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="disposition_checkbox"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Suggest automatic display of attachment</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/calendar/gui/dialogs/cal-attachment.h b/calendar/gui/dialogs/cal-attachment.h new file mode 100644 index 0000000000..842ab2ed4d --- /dev/null +++ b/calendar/gui/dialogs/cal-attachment.h @@ -0,0 +1,80 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* cal-attachment.h + * + * Copyright (C) 2004 Novell, 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 + * published by the Free Software Foundation; either version 2 of the + * 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: Harish Krishnaswamy + */ +#ifndef __CAL_ATTACHMENT_H__ +#define __CAL_ATTACHMENT_H__ + +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <glade/glade-xml.h> +#include <camel/camel-mime-part.h> +#include <camel/camel-exception.h> + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define E_TYPE_CAL_ATTACHMENT (cal_attachment_get_type ()) +#define CAL_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_ATTACHMENT, CalAttachment)) +#define CAL_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_ATTACHMENT, CalAttachmentClass)) +#define E_IS_CAL_ATTACHMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_ATTACHMENT)) +#define E_IS_CAL_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_CAL_ATTACHMENT)) + +typedef struct _CalAttachment CalAttachment; +typedef struct _CalAttachmentClass CalAttachmentClass; + +struct _CalAttachment { + GObject parent; + + GladeXML *editor_gui; + + CamelMimePart *body; + gboolean guessed_type; + gulong size; + + GdkPixbuf *pixbuf_cache; +}; + +struct _CalAttachmentClass { + GObjectClass parent_class; + + void (*changed) (CalAttachment *cal_attachment); +}; + +struct CalMimeAttach { + char *filename; + char *content_type; + char *description; + char *encoded_data; + guint length; +}; + +GType cal_attachment_get_type (void); +CalAttachment *cal_attachment_new (const char *file_name, const char *disposition, CamelException *ex); +CalAttachment *cal_attachment_new_from_mime_part (CamelMimePart *part); +void cal_attachment_edit (CalAttachment *attachment, GtkWidget *parent); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAL_ATTACHMENT_H__ */ |