diff --git a/widgets/Makefile.am b/widgets/Makefile.am index 5bc6a98a..3f202981 100644 --- a/widgets/Makefile.am +++ b/widgets/Makefile.am @@ -18,7 +18,7 @@ include $(top_srcdir)/gtest.mk # enforce compiling this dir first before decending into tests/ SUBDIRS= . -INCLUDES=-I${top_srcdir}/src $(GTK_CFLAGS) $(WEBKIT_CFLAGS) -DICONDIR='"$(icondir)"' \ +INCLUDES=-I${top_srcdir}/src $(GTK_CFLAGS) $(WEBKIT_CFLAGS) $(GIO_CFLAGS) -DICONDIR='"$(icondir)"' \ -DGTK_DISABLE_DEPRECATED -DGSEAL_ENABLE # don't use -Werror, as it might break on other compilers @@ -31,6 +31,10 @@ noinst_LTLIBRARIES= \ libmuwidgets.la libmuwidgets_la_SOURCES= \ + mu-widget-util.h \ + mu-widget-util.c \ + mu-msg-attach-view.c \ + mu-msg-attach-view.h \ mu-msg-body-view.c \ mu-msg-body-view.h \ mu-msg-view.h \ @@ -39,5 +43,6 @@ libmuwidgets_la_SOURCES= \ libmuwidgets_la_LIBADD= \ ${top_builddir}/src/libmu.la \ ${GTK_LIBS} \ - ${WEBKIT_LIBS} + ${WEBKIT_LIBS} \ + ${GIO_LIBS} diff --git a/widgets/mu-msg-attach-view.c b/widgets/mu-msg-attach-view.c new file mode 100644 index 00000000..b48b833f --- /dev/null +++ b/widgets/mu-msg-attach-view.c @@ -0,0 +1,213 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#include "mu-msg-attach-view.h" +#include "mu-widget-util.h" +#include + +enum { + ICON_COL, + NAME_COL, + PARTNUM_COL, + + NUM_COL +}; + + +/* 'private'/'protected' functions */ +static void mu_msg_attach_view_class_init (MuMsgAttachViewClass *klass); +static void mu_msg_attach_view_init (MuMsgAttachView *obj); +static void mu_msg_attach_view_finalize (GObject *obj); + +/* list my signals */ +enum { + ATTACH_ACTIVATED, + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +struct _MuMsgAttachViewPrivate { + MuMsg *_msg; +}; +#define MU_MSG_ATTACH_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MU_TYPE_MSG_ATTACH_VIEW, \ + MuMsgAttachViewPrivate)) +/* globals */ +static GtkIconViewClass *parent_class = NULL; + +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE (MuMsgAttachView, mu_msg_attach_view, GTK_TYPE_ICON_VIEW); + +static void +mu_msg_attach_view_class_init (MuMsgAttachViewClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mu_msg_attach_view_finalize; + + g_type_class_add_private (gobject_class, sizeof(MuMsgAttachViewPrivate)); + + signals[ATTACH_ACTIVATED] = + g_signal_new ("attach-activated", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MuMsgAttachViewClass, + attach_activated), + NULL, NULL, + g_cclosure_marshal_VOID__UINT_POINTER, + G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER); +} + +static void +item_activated (MuMsgAttachView *self, GtkTreePath *tpath) +{ + GtkTreeModel *model; + GtkTreeIter iter; + guint partnum; + + model = gtk_icon_view_get_model (GTK_ICON_VIEW(self)); + if (!gtk_tree_model_get_iter (model, &iter, tpath)) { + g_warning ("could not find path"); + } + + gtk_tree_model_get (model, &iter, + PARTNUM_COL, &partnum, + -1); + + g_signal_emit (G_OBJECT (self), + signals[ATTACH_ACTIVATED], 0, + partnum, self->_priv->_msg); +} + + +static void +mu_msg_attach_view_init (MuMsgAttachView *obj) +{ + GtkListStore *store; + + obj->_priv = MU_MSG_ATTACH_VIEW_GET_PRIVATE(obj); + obj->_priv->_msg = NULL; + + store = gtk_list_store_new (NUM_COL,GDK_TYPE_PIXBUF, + G_TYPE_STRING, G_TYPE_UINT); + gtk_icon_view_set_model (GTK_ICON_VIEW(obj), GTK_TREE_MODEL(store)); + g_object_unref (store); + + gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW(obj), ICON_COL); + gtk_icon_view_set_text_column (GTK_ICON_VIEW(obj), NAME_COL); + + gtk_icon_view_set_margin (GTK_ICON_VIEW(obj), 0); + gtk_icon_view_set_spacing (GTK_ICON_VIEW(obj), 0); + gtk_icon_view_set_item_padding (GTK_ICON_VIEW(obj), 0); + gtk_icon_view_set_item_orientation (GTK_ICON_VIEW(obj), + GTK_ORIENTATION_HORIZONTAL); + + g_signal_connect (G_OBJECT(obj), "item-activated", + G_CALLBACK(item_activated), NULL); +} + + +static void +mu_msg_attach_view_finalize (GObject *obj) +{ + mu_msg_unref (MU_MSG_ATTACH_VIEW(obj)->_priv->_msg); + + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +mu_msg_attach_view_new (void) +{ + return GTK_WIDGET(g_object_new(MU_TYPE_MSG_ATTACH_VIEW, NULL)); +} + +struct _CBData { + GtkListStore *store; + guint count; +}; +typedef struct _CBData CBData; + + + +static void +each_part (MuMsgPart *part, CBData *cbdata) +{ + GtkTreeIter treeiter; + GdkPixbuf *pixbuf; + char ctype[128]; + + /* not a real attachment */ + if (!mu_msg_part_file_name(part)) + return; + + if (!part->type || !part->subtype) + snprintf (ctype, sizeof(ctype), "%s", "application/octet-stream"); + else + snprintf (ctype, sizeof(ctype), "%s/%s", part->type, part->subtype); + + pixbuf = mu_widget_util_get_icon_pixbuf_for_content_type (ctype, 16); + if (!pixbuf) { + g_warning ("%s: could not get icon pixbuf for '%s'", + __FUNCTION__, ctype); + pixbuf = mu_widget_util_get_icon_pixbuf_for_content_type + ("application/octet-stream", 16); + } + + gtk_list_store_append (cbdata->store, &treeiter); + gtk_list_store_set (cbdata->store, &treeiter, + NAME_COL, mu_msg_part_file_name (part), + ICON_COL, pixbuf, + PARTNUM_COL, part->index, + -1); + if (pixbuf) + g_object_unref (pixbuf); + + ++cbdata->count; +} + +gint +mu_msg_attach_view_set_message (MuMsgAttachView *self, MuMsg *msg) +{ + GtkListStore *store; + CBData cbdata; + + g_return_val_if_fail (MU_IS_MSG_ATTACH_VIEW(self), -1); + + store = GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW(self))); + gtk_list_store_clear (store); + + if (self->_priv->_msg) + mu_msg_unref (self->_priv->_msg); + + if (!msg) + return 0; + + + self->_priv->_msg = mu_msg_ref (msg); + + cbdata.store = store; + cbdata.count = 0; + mu_msg_msg_part_foreach (msg, (MuMsgPartForeachFunc)each_part, &cbdata); + + return cbdata.count; +} + diff --git a/widgets/mu-msg-attach-view.h b/widgets/mu-msg-attach-view.h new file mode 100644 index 00000000..c379990f --- /dev/null +++ b/widgets/mu-msg-attach-view.h @@ -0,0 +1,68 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + + +#ifndef __MU_MSG_ATTACH_VIEW_H__ +#define __MU_MSG_ATTACH_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MU_TYPE_MSG_ATTACH_VIEW (mu_msg_attach_view_get_type()) +#define MU_MSG_ATTACH_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MU_TYPE_MSG_ATTACH_VIEW,MuMsgAttachView)) +#define MU_MSG_ATTACH_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MU_TYPE_MSG_ATTACH_VIEW,MuMsgAttachViewClass)) +#define MU_IS_MSG_ATTACH_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MU_TYPE_MSG_ATTACH_VIEW)) +#define MU_IS_MSG_ATTACH_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MU_TYPE_MSG_ATTACH_VIEW)) +#define MU_MSG_ATTACH_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MU_TYPE_MSG_ATTACH_VIEW,MuMsgAttachViewClass)) + +typedef struct _MuMsgAttachView MuMsgAttachView; +typedef struct _MuMsgAttachViewClass MuMsgAttachViewClass; +typedef struct _MuMsgAttachViewPrivate MuMsgAttachViewPrivate; + +struct _MuMsgAttachView { + GtkIconView parent; + /* insert public members, if any */ + + /* private */ + MuMsgAttachViewPrivate *_priv; +}; + +struct _MuMsgAttachViewClass { + GtkIconViewClass parent_class; + void (* attach_activated) (MuMsgAttachView* obj, guint partnum, + MuMsg *msg); +}; + +/* member functions */ +GType mu_msg_attach_view_get_type (void) G_GNUC_CONST; + +/* parameter-less _new function (constructor) */ +/* if this is a kind of GtkWidget, it should probably return at GtkWidget* */ +GtkWidget* mu_msg_attach_view_new (void); + + +int mu_msg_attach_view_set_message (MuMsgAttachView *self, MuMsg *msg); + +G_END_DECLS + +#endif /* __MU_MSG_ATTACH_VIEW_H__ */ + diff --git a/widgets/mu-msg-view.c b/widgets/mu-msg-view.c index fbfe0b88..9834c14f 100644 --- a/widgets/mu-msg-view.c +++ b/widgets/mu-msg-view.c @@ -19,6 +19,7 @@ #include "mu-msg-view.h" #include "mu-msg-body-view.h" +#include "mu-msg-attach-view.h" #include "mu-msg.h" /* 'private'/'protected' functions */ @@ -35,6 +36,7 @@ enum { struct _MuMsgViewPrivate { GtkWidget *_body; + GtkWidget *_attach, *_attacharea; }; #define MU_MSG_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MU_TYPE_MSG_VIEW, \ @@ -66,23 +68,44 @@ mu_msg_view_class_init (MuMsgViewClass *klass) /* etc. */ } +static void +on_attach_activated (MuMsgView *self, guint partnum, MuMsg *msg) +{ + char *tmpdir; + + tmpdir = mu_util_create_tmpdir (); + if (!tmpdir) + return; + + mu_msg_mime_part_save (msg, partnum, tmpdir, FALSE, TRUE); + g_free (tmpdir); +} + + static void mu_msg_view_init (MuMsgView *obj) { GtkWidget *scrolledwin; obj->_priv = MU_MSG_VIEW_GET_PRIVATE(obj); - + obj->_priv->_body = mu_msg_body_view_new (); scrolledwin = gtk_scrolled_window_new (NULL, NULL); gtk_container_add (GTK_CONTAINER(scrolledwin), obj->_priv->_body); - gtk_box_pack_start (GTK_BOX(obj), scrolledwin, TRUE, TRUE, 2); - - /* to init any of the private data, do e.g: */ -/* obj->_priv->_frobnicate_mode = FALSE; */ + + obj->_priv->_attacharea = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(obj->_priv->_attacharea), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + obj->_priv->_attach = mu_msg_attach_view_new (); + gtk_container_add (GTK_CONTAINER(obj->_priv->_attacharea), obj->_priv->_attach); + + g_signal_connect (obj->_priv->_attach, "attach-activated", + G_CALLBACK(on_attach_activated), + obj); } static void @@ -98,6 +121,42 @@ mu_msg_view_new (void) return GTK_WIDGET(g_object_new(MU_TYPE_MSG_VIEW, NULL)); } + +static void +add_attachment_area_maybe (MuMsgView *self, MuMsg *msg) +{ + gint attach_num; + GList *children, *cur; + gboolean has_area; + + has_area = FALSE; + cur = children = gtk_container_get_children (GTK_CONTAINER(self)); + while (cur) { + if (cur->data == self->_priv->_attacharea) { + has_area = TRUE; + break; + } + cur = g_list_next (cur); + } + g_list_free (children); + + attach_num = 0; + if (msg) + attach_num = mu_msg_attach_view_set_message + (MU_MSG_ATTACH_VIEW(self->_priv->_attach), msg); + + if (attach_num < 1 && has_area) { + g_object_ref (self->_priv->_attacharea); + gtk_container_remove (GTK_CONTAINER(self), + self->_priv->_attacharea); + } else if (attach_num >= 1 && !has_area) { + gtk_box_pack_start (GTK_BOX(self), self->_priv->_attacharea, + FALSE, FALSE, 0); + gtk_widget_show_all (self->_priv->_attacharea); + } +} + + void mu_msg_view_set_message (MuMsgView *self, MuMsg *msg) { @@ -105,18 +164,14 @@ mu_msg_view_set_message (MuMsgView *self, MuMsg *msg) g_return_if_fail (MU_IS_MSG_VIEW(self)); - if (!msg) { /* clear */ - mu_msg_body_view_set_text - (MU_MSG_BODY_VIEW(self->_priv->_body),""); - return; - } - - data = mu_msg_get_body_html (msg); + data = msg ? mu_msg_get_body_html (msg) : ""; if (data) mu_msg_body_view_set_html (MU_MSG_BODY_VIEW(self->_priv->_body), data); else mu_msg_body_view_set_text (MU_MSG_BODY_VIEW(self->_priv->_body), mu_msg_get_body_text (msg)); + + add_attachment_area_maybe (self, msg); } diff --git a/widgets/mu-widget-util.c b/widgets/mu-widget-util.c new file mode 100644 index 00000000..f9824510 --- /dev/null +++ b/widgets/mu-widget-util.c @@ -0,0 +1,59 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + + +#include +#include +#include "mu-widget-util.h" + +GdkPixbuf* +mu_widget_util_get_icon_pixbuf_for_content_type (const char *ctype, + size_t size) +{ + GIcon *icon; + GdkPixbuf *pixbuf; + + g_return_val_if_fail (ctype, NULL); + g_return_val_if_fail (size > 0, NULL); + + icon = g_content_type_get_icon (ctype); + pixbuf = NULL; + + /* if (g_strcmp0 (ctype, "image/pjpeg") == 0) */ + /* ctype = "image/jpeg"; */ + + /* based on a snippet from http://www.gtkforums.com/about4721.html */ + if (G_IS_THEMED_ICON(icon)) { + gchar const * const *names; + names = g_themed_icon_get_names (G_THEMED_ICON(icon)); + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(), + *names, size, 0, NULL); + } else if (G_IS_FILE_ICON(icon)) { + GFile *icon_file; + gchar *path; + icon_file = g_file_icon_get_file (G_FILE_ICON(icon)); + path = g_file_get_path (icon_file); + pixbuf = gdk_pixbuf_new_from_file_at_size (path, size, size, NULL); + g_free (path); + g_object_unref(icon_file); + } + g_object_unref(icon); + + return pixbuf; +} diff --git a/widgets/mu-widget-util.h b/widgets/mu-widget-util.h new file mode 100644 index 00000000..5d47fe38 --- /dev/null +++ b/widgets/mu-widget-util.h @@ -0,0 +1,37 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef __MU_WIDGET_UTIL_H__ +#define __MU_WIDGET_UTIL_H__ + +#include + +/** + * get a pixbuf (icon) for a certain content-type (ie., 'image/jpeg') + * + * @param ctype the content-type (MIME-type) + * @param size the size of the icon + * + * @return a new GdkPixbuf, or NULL in case of error. Use + * g_object_unref when the pixbuf is no longer needed. + */ +GdkPixbuf* mu_widget_util_get_icon_pixbuf_for_content_type (const char *ctype, + size_t size); + +#endif /*__MU_WIDGET_UTIL_H__*/