From 6daf2e9989ea013afd031c3c65cb62d1e0f94095 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Fri, 21 Jan 2011 18:46:33 -0500 Subject: [PATCH] * refactoring & add 'view-source' and 'view-message' (activate with context menu) --- widgets/Makefile.am | 4 + widgets/mu-msg-attach-view.c | 81 ++++++++++++- widgets/mu-msg-body-view.c | 104 ++++++++++++----- widgets/mu-msg-body-view.h | 3 + widgets/mu-msg-header-view.c | 2 +- widgets/mu-msg-normal-view.c | 219 +++++++++++++++++++++++++++++++++++ widgets/mu-msg-normal-view.h | 67 +++++++++++ widgets/mu-msg-source-view.c | 174 ++++++++++++++++++++++++++++ widgets/mu-msg-source-view.h | 68 +++++++++++ widgets/mu-msg-view.c | 215 ++++++++++++++++++---------------- widgets/mu-msg-view.h | 2 + 11 files changed, 804 insertions(+), 135 deletions(-) create mode 100644 widgets/mu-msg-normal-view.c create mode 100644 widgets/mu-msg-normal-view.h create mode 100644 widgets/mu-msg-source-view.c create mode 100644 widgets/mu-msg-source-view.h diff --git a/widgets/Makefile.am b/widgets/Makefile.am index b65449aa..5fbb4ace 100644 --- a/widgets/Makefile.am +++ b/widgets/Makefile.am @@ -39,6 +39,10 @@ libmuwidgets_la_SOURCES= \ mu-msg-body-view.h \ mu-msg-header-view.c \ mu-msg-header-view.h \ + mu-msg-normal-view.h \ + mu-msg-normal-view.c \ + mu-msg-source-view.h \ + mu-msg-source-view.c \ mu-msg-view.h \ mu-msg-view.c diff --git a/widgets/mu-msg-attach-view.c b/widgets/mu-msg-attach-view.c index c6ccc71d..e8502eec 100644 --- a/widgets/mu-msg-attach-view.c +++ b/widgets/mu-msg-attach-view.c @@ -98,6 +98,77 @@ item_activated (MuMsgAttachView *self, GtkTreePath *tpath) partnum, self->_priv->_msg); } +static void +accumulate_parts (MuMsgAttachView *self, GtkTreePath *path, GSList **lst) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + /* don't unref */ + model = gtk_icon_view_get_model (GTK_ICON_VIEW(self)); + + if (gtk_tree_model_get_iter (model, &iter, path)) { + gchar *filepath; + gint idx; + gtk_tree_model_get (model, &iter, PARTNUM_COL, &idx, -1); + filepath = mu_msg_part_filepath_cache (self->_priv->_msg, idx); + if (filepath) { + if (mu_msg_part_save (self->_priv->_msg, filepath, + idx, FALSE, TRUE)) { + GFile *file; + file = g_file_new_for_path (filepath); + *lst = g_slist_prepend (*lst, g_file_get_uri(file)); + g_object_unref (file); + g_free (filepath); + } + } + } +} + + + +static void +on_drag_data_get (MuMsgAttachView *self, GdkDragContext *drag_context, + GtkSelectionData *data, guint info, guint time, gpointer user_data) +{ + GSList *lst, *cur; + char **uris; + int i; + + lst = NULL; + gtk_icon_view_selected_foreach (GTK_ICON_VIEW(self), + (GtkIconViewForeachFunc)accumulate_parts, + &lst); + + uris = g_new(char*, g_slist_length(lst) + 1); + + /* now, create file uris for all of these ... */ + for (cur = lst, i = 0; cur; cur = g_slist_next(lst)) { + uris[i] = (gchar*)cur->data; + g_warning ("%s", (gchar*)cur->data); + } + uris[i] = NULL; + + gtk_selection_data_set_uris (data, uris); + + g_strfreev (uris); + g_slist_foreach (lst, (GFunc)g_free, NULL); + g_slist_free (lst); +} + +static void +init_drag_and_drop (MuMsgAttachView *self) +{ + GtkTargetEntry target; + target.target = "text/uri-list"; + target.flags = GTK_TARGET_OTHER_APP; + target.info = 0; + + gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW(self), + GDK_BUTTON1_MASK, + &target, 1, GDK_ACTION_COPY); + g_signal_connect (self, "drag-data-get", G_CALLBACK(on_drag_data_get), self); +} static void mu_msg_attach_view_init (MuMsgAttachView *obj) @@ -120,6 +191,9 @@ mu_msg_attach_view_init (MuMsgAttachView *obj) gtk_icon_view_set_item_padding (GTK_ICON_VIEW(obj), 0); gtk_icon_view_set_item_orientation (GTK_ICON_VIEW(obj), GTK_ORIENTATION_HORIZONTAL); + gtk_icon_view_set_selection_mode (GTK_ICON_VIEW(obj), + GTK_SELECTION_MULTIPLE); + init_drag_and_drop (obj); g_signal_connect (G_OBJECT(obj), "item-activated", G_CALLBACK(item_activated), NULL); @@ -163,9 +237,11 @@ each_part (MuMsg *msg, MuMsgPart *part, CBData *cbdata) return; if (!part->type || !part->subtype) - snprintf (ctype, sizeof(ctype), "%s", "application/octet-stream"); + snprintf (ctype, sizeof(ctype), "%s", + "application/octet-stream"); else - snprintf (ctype, sizeof(ctype), "%s/%s", part->type, part->subtype); + snprintf (ctype, sizeof(ctype), "%s/%s", + part->type, part->subtype); pixbuf = mu_widget_util_get_icon_pixbuf_for_content_type (ctype, 16); if (!pixbuf) { @@ -203,7 +279,6 @@ mu_msg_attach_view_set_message (MuMsgAttachView *self, MuMsg *msg) if (!msg) return 0; - self->_priv->_msg = mu_msg_ref (msg); diff --git a/widgets/mu-msg-body-view.c b/widgets/mu-msg-body-view.c index fbd48947..6199df3a 100644 --- a/widgets/mu-msg-body-view.c +++ b/widgets/mu-msg-body-view.c @@ -41,11 +41,8 @@ enum { struct _MuMsgBodyViewPrivate { WebKitWebSettings *_settings; - MuMsg *_message; - - /* 'mu_mode' means some internal mode where cmd: urls are - * acceptable */ - gboolean _mu_mode; + MuMsg *_msg; + gboolean _internal_msg; }; #define MU_MSG_BODY_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MU_TYPE_MSG_BODY_VIEW, \ @@ -126,15 +123,15 @@ on_navigation_policy_decision_requested (MuMsgBodyView *self, WebKitWebFrame *fr uri = webkit_network_request_get_uri (request); reason = webkit_web_navigation_action_get_reason (nav_action); - - /* if it wasn't a user click, do the navigation */ + + /* if it wasn't a user click, don't the navigation */ if (reason != WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) { webkit_web_policy_decision_ignore (policy_decision); return TRUE; } /* we handle links clicked ourselves, no need for navigation */ - webkit_web_policy_decision_ignore (policy_decision); + webkit_web_policy_decision_use (policy_decision); /* if there are 'cmd:" links in the body text of * mu-internal messages (ie., notification from mu, not real @@ -143,7 +140,7 @@ on_navigation_policy_decision_requested (MuMsgBodyView *self, WebKitWebFrame *fr * a Refresh link */ if (g_ascii_strncasecmp (uri, "cmd:", 4) == 0) { - if (self->_priv->_mu_mode) { + if (self->_priv->_internal_msg) { g_signal_emit (G_OBJECT(self), signals[ACTION_REQUESTED], 0, uri + 4); @@ -169,8 +166,10 @@ on_resource_request_starting (MuMsgBodyView *self, WebKitWebFrame *frame, const char* uri; MuMsg *msg; - msg = self->_priv->_message; + msg = self->_priv->_msg; uri = webkit_network_request_get_uri (request); + + /* g_warning ("%s: %s", __FUNCTION__, uri); */ if (g_ascii_strncasecmp (uri, "cid:", 4) == 0) { gchar *filepath; @@ -185,10 +184,52 @@ on_resource_request_starting (MuMsgBodyView *self, WebKitWebFrame *frame, } } + +static void +on_menu_item_activate (GtkMenuItem *item, MuMsgBodyView *self) +{ + g_signal_emit (G_OBJECT(self), + signals[ACTION_REQUESTED], 0, + g_object_get_data (G_OBJECT(item), "action")); +} + +static void +popup_menu (MuMsgBodyView *self, guint button, guint32 activate_time) +{ + GtkWidget *menu; + int i; + struct { + const char* title; + const char* action; + } actions[] = { + { "View source...", "view-source" } + }; + + menu = gtk_menu_new (); + + for (i = 0; i != G_N_ELEMENTS(actions); ++i) { + GtkWidget *item; + item = gtk_menu_item_new_with_label(actions[i].title); + g_object_set_data (G_OBJECT(item), "action", (gpointer)actions[i].action); + g_signal_connect (item, "activate", G_CALLBACK(on_menu_item_activate), + self); + gtk_menu_attach (GTK_MENU(menu), item, 0, 1, i, i+1); + gtk_widget_show (item); + } + gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, 0); +} + + static gboolean on_button_press_event (MuMsgBodyView *self, GdkEventButton *event, gpointer data) { - /* ignore all but the first (typically, left) mouse button */ + /* ignore all but the first (typically, left) mouse button */ + switch (event->button) { + case 1: return FALSE; /* propagate, let widget handle it */ + case 3: popup_menu (self, event->button, event->time); + default: return TRUE; /* ignore */ + } + return (event->button > 1) ? TRUE : FALSE; } @@ -198,8 +239,8 @@ mu_msg_body_view_init (MuMsgBodyView *obj) { obj->_priv = MU_MSG_BODY_VIEW_GET_PRIVATE(obj); - obj->_priv->_message = NULL; - obj->_priv->_mu_mode = FALSE; + obj->_priv->_msg = NULL; + obj->_priv->_internal_msg = FALSE; obj->_priv->_settings = webkit_web_settings_new (); g_object_set (G_OBJECT(obj->_priv->_settings), @@ -218,7 +259,7 @@ mu_msg_body_view_init (MuMsgBodyView *obj) /* handle navigation requests */ g_signal_connect (obj, "navigation-policy-decision-requested", G_CALLBACK (on_navigation_policy_decision_requested), NULL); - /* ignore right-button clicks */ + /* handle right-button clicks */ g_signal_connect (obj, "button-press-event", G_CALLBACK(on_button_press_event), NULL); } @@ -232,8 +273,8 @@ mu_msg_body_view_finalize (GObject *obj) if (priv && priv->_settings) g_object_unref (priv->_settings); - if (priv->_message) - mu_msg_unref (priv->_message); + if (priv->_msg) + mu_msg_unref (priv->_msg); G_OBJECT_CLASS(parent_class)->finalize (obj); } @@ -269,7 +310,6 @@ set_text (MuMsgBodyView *self, const char* txt) ""); } - void mu_msg_body_view_set_message (MuMsgBodyView *self, MuMsg *msg) { @@ -277,35 +317,37 @@ mu_msg_body_view_set_message (MuMsgBodyView *self, MuMsg *msg) g_return_if_fail (self); - if (self->_priv->_message) { - mu_msg_unref (self->_priv->_message); - self->_priv->_message = NULL; - } - - self->_priv->_message = msg ? mu_msg_ref (msg) : NULL; - + /* ref this one before unreffing priv->_msg, it may be the + * same...*/ + if (msg) + msg = mu_msg_ref (msg); + + if (self->_priv->_msg) { + mu_msg_unref (self->_priv->_msg); + self->_priv->_msg = NULL; + } + self->_priv->_msg = msg; + data = msg ? mu_msg_get_body_html (msg) : ""; if (data) set_html (self, data); else set_text (self, mu_msg_get_body_text (msg)); - self->_priv->_mu_mode = FALSE; + self->_priv->_internal_msg = FALSE; } - void mu_msg_body_view_set_note (MuMsgBodyView *self, const gchar *html) { g_return_if_fail (self); g_return_if_fail (html); - if (self->_priv->_message) { - mu_msg_unref (self->_priv->_message); - self->_priv->_message = NULL; + if (self->_priv->_msg) { + mu_msg_unref (self->_priv->_msg); + self->_priv->_msg = NULL; } set_html (self, html); - self->_priv->_mu_mode = TRUE; - + self->_priv->_internal_msg = TRUE; } diff --git a/widgets/mu-msg-body-view.h b/widgets/mu-msg-body-view.h index 80dcb739..8d430043 100644 --- a/widgets/mu-msg-body-view.h +++ b/widgets/mu-msg-body-view.h @@ -37,6 +37,7 @@ typedef struct _MuMsgBodyView MuMsgBodyView; typedef struct _MuMsgBodyViewClass MuMsgBodyViewClass; typedef struct _MuMsgBodyViewPrivate MuMsgBodyViewPrivate; + struct _MuMsgBodyView { WebKitWebView parent; /* insert public members, if any */ @@ -47,6 +48,8 @@ struct _MuMsgBodyView { struct _MuMsgBodyViewClass { WebKitWebViewClass parent_class; + + /* supported actions: "reindex", "view-source" */ void (* action_requested) (MuMsgBodyView* self, const char* action); }; diff --git a/widgets/mu-msg-header-view.c b/widgets/mu-msg-header-view.c index 9a054eeb..a8b15a44 100644 --- a/widgets/mu-msg-header-view.c +++ b/widgets/mu-msg-header-view.c @@ -177,7 +177,7 @@ mu_msg_header_view_set_message (MuMsgHeaderView *self, MuMsg *msg) if (msg) { self->_priv->_table = get_table (msg); gtk_box_pack_start (GTK_BOX(self), self->_priv->_table, - FALSE, FALSE, 2); + TRUE, TRUE, 0); gtk_widget_show_all (self->_priv->_table); } } diff --git a/widgets/mu-msg-normal-view.c b/widgets/mu-msg-normal-view.c new file mode 100644 index 00000000..6f840642 --- /dev/null +++ b/widgets/mu-msg-normal-view.c @@ -0,0 +1,219 @@ +/* +** 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-normal-view.h" + +#include "mu-msg-body-view.h" +#include "mu-msg-attach-view.h" +#include "mu-msg-header-view.h" +#include "mu-msg-part.h" + + +/* 'private'/'protected' functions */ +static void mu_msg_normal_view_class_init (MuMsgNormalViewClass *klass); +static void mu_msg_normal_view_init (MuMsgNormalView *obj); +static void mu_msg_normal_view_finalize (GObject *obj); + +/* list my signals */ +enum { + ACTION_REQUESTED, + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +struct _MuMsgNormalViewPrivate { + + GtkWidget *_headers; + GtkWidget *_body; + GtkWidget *_attach, *_attacharea; +}; +#define MU_MSG_NORMAL_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MU_TYPE_MSG_NORMAL_VIEW, \ + MuMsgNormalViewPrivate)) +/* globals */ +static GtkVBoxClass *parent_class = NULL; + + +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE (MuMsgNormalView, mu_msg_normal_view, GTK_TYPE_VBOX); + +static void +mu_msg_normal_view_class_init (MuMsgNormalViewClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mu_msg_normal_view_finalize; + + g_type_class_add_private (gobject_class, sizeof(MuMsgNormalViewPrivate)); + + signals[ACTION_REQUESTED] = + g_signal_new ("action-requested", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MuMsgNormalViewClass, + action_requested), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + + +static GtkWidget* +get_header_widget (MuMsgNormalView *self) +{ + GtkWidget *scrolledwin; + + self->_priv->_headers = mu_msg_header_view_new (); + + scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy ( + GTK_SCROLLED_WINDOW(scrolledwin), + GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport + (GTK_SCROLLED_WINDOW(scrolledwin), self->_priv->_headers); + + gtk_widget_show_all (scrolledwin); + + return scrolledwin; +} + +static void +on_body_action_requested (MuMsgBodyView *body, const char* action, + MuMsgNormalView *self) +{ + /* forward the signal */ + g_signal_emit (G_OBJECT(self), + signals[ACTION_REQUESTED], 0, action); +} + + +static GtkWidget* +get_body_widget (MuMsgNormalView *self) +{ + GtkWidget *scrolledwin; + + self->_priv->_body = mu_msg_body_view_new (); + + scrolledwin = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER(scrolledwin), + self->_priv->_body); + + g_signal_connect (self->_priv->_body, "action-requested", + G_CALLBACK(on_body_action_requested), self); + + gtk_widget_show_all (scrolledwin); + + return scrolledwin; +} + + +static void +on_attach_activated (GtkWidget *w, guint partnum, MuMsg *msg) +{ + gchar *filepath; + + filepath = mu_msg_part_filepath_cache (msg, partnum); + if (filepath) { + mu_msg_part_save (msg, filepath, partnum, FALSE, TRUE); + mu_util_play (filepath, TRUE, FALSE); + g_free (filepath); + } +} + + + + +static GtkWidget* +get_attach_widget (MuMsgNormalView *self) +{ + self->_priv->_attacharea = gtk_frame_new (""); + self->_priv->_attach = mu_msg_attach_view_new (); + gtk_container_add (GTK_CONTAINER(self->_priv->_attacharea), + self->_priv->_attach); + + g_signal_connect (self->_priv->_attach, "attach-activated", + G_CALLBACK(on_attach_activated), + self); + + gtk_widget_show_all (self->_priv->_attacharea); + + return self->_priv->_attacharea; +} + +static void +mu_msg_normal_view_init (MuMsgNormalView *self) +{ + self->_priv = MU_MSG_NORMAL_VIEW_GET_PRIVATE(self); + + gtk_box_pack_start (GTK_BOX(self),get_header_widget (self), + FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX(self), get_attach_widget (self), + FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX(self), get_body_widget (self), + TRUE, TRUE, 0); +} + +static void +mu_msg_normal_view_finalize (GObject *obj) +{ +/* free/unref instance resources here */ + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +mu_msg_normal_view_new (void) +{ + return GTK_WIDGET(g_object_new(MU_TYPE_MSG_NORMAL_VIEW, NULL)); +} + + + +static void +update_attachment_area (MuMsgNormalView *self, MuMsg *msg) +{ + gint attach_num; + + attach_num = 0; + if (msg) + attach_num = mu_msg_attach_view_set_message + (MU_MSG_ATTACH_VIEW(self->_priv->_attach), msg); + + if (attach_num > 0) + gtk_widget_show_all (self->_priv->_attacharea); + else + gtk_widget_hide_all (self->_priv->_attacharea); +} + + +void +mu_msg_normal_view_set_message (MuMsgNormalView *self, MuMsg *msg) +{ + g_return_if_fail (MU_IS_MSG_NORMAL_VIEW(self)); + + mu_msg_header_view_set_message + (MU_MSG_HEADER_VIEW(self->_priv->_headers), msg); + mu_msg_body_view_set_message + (MU_MSG_BODY_VIEW(self->_priv->_body), msg); + + update_attachment_area (self, msg); +} diff --git a/widgets/mu-msg-normal-view.h b/widgets/mu-msg-normal-view.h new file mode 100644 index 00000000..f3c914ab --- /dev/null +++ b/widgets/mu-msg-normal-view.h @@ -0,0 +1,67 @@ +/* +** 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_NORMAL_VIEW_H__ +#define __MU_MSG_NORMAL_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MU_TYPE_MSG_NORMAL_VIEW (mu_msg_normal_view_get_type()) +#define MU_MSG_NORMAL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MU_TYPE_MSG_NORMAL_VIEW,MuMsgNormalView)) +#define MU_MSG_NORMAL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MU_TYPE_MSG_NORMAL_VIEW,MuMsgNormalViewClass)) +#define MU_IS_MSG_NORMAL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MU_TYPE_MSG_NORMAL_VIEW)) +#define MU_IS_MSG_NORMAL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MU_TYPE_MSG_NORMAL_VIEW)) +#define MU_MSG_NORMAL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MU_TYPE_MSG_NORMAL_VIEW,MuMsgNormalViewClass)) + +typedef struct _MuMsgNormalView MuMsgNormalView; +typedef struct _MuMsgNormalViewClass MuMsgNormalViewClass; +typedef struct _MuMsgNormalViewPrivate MuMsgNormalViewPrivate; + +struct _MuMsgNormalView { + GtkVBox parent; + /* insert public members, if any */ + + /* private */ + MuMsgNormalViewPrivate *_priv; +}; + +struct _MuMsgNormalViewClass { + GtkVBoxClass parent_class; + + /* supported actions: "view-source" */ + void (* action_requested) (MuMsgNormalView* self, const char* action); +}; + +/* member functions */ +GType mu_msg_normal_view_get_type (void) G_GNUC_CONST; + + +GtkWidget* mu_msg_normal_view_new (void); +void mu_msg_normal_view_set_message (MuMsgNormalView *self, + MuMsg *msg); + +G_END_DECLS + +#endif /* __MU_MSG_NORMAL_VIEW_H__ */ + diff --git a/widgets/mu-msg-source-view.c b/widgets/mu-msg-source-view.c new file mode 100644 index 00000000..edd8329d --- /dev/null +++ b/widgets/mu-msg-source-view.c @@ -0,0 +1,174 @@ +/* +** 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-source-view.h" +/* include other impl specific header files */ + +/* 'private'/'protected' functions */ +static void mu_msg_source_view_class_init (MuMsgSourceViewClass *klass); +static void mu_msg_source_view_init (MuMsgSourceView *obj); +static void mu_msg_source_view_finalize (GObject *obj); + +/* list my signals */ +enum { + ACTION_REQUESTED, + /* MY_SIGNAL_2, */ + LAST_SIGNAL +}; + +struct _MuMsgSourceViewPrivate { + GtkWidget *_view; +}; +#define MU_MSG_SOURCE_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ + MU_TYPE_MSG_SOURCE_VIEW, \ + MuMsgSourceViewPrivate)) +/* globals */ +static GtkVBoxClass *parent_class = NULL; + + +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE (MuMsgSourceView, mu_msg_source_view, GTK_TYPE_VBOX); + +static void +mu_msg_source_view_class_init (MuMsgSourceViewClass *klass) +{ + GObjectClass *gobject_class; + gobject_class = (GObjectClass*) klass; + + parent_class = g_type_class_peek_parent (klass); + gobject_class->finalize = mu_msg_source_view_finalize; + + g_type_class_add_private (gobject_class, sizeof(MuMsgSourceViewPrivate)); + + signals[ACTION_REQUESTED] = + g_signal_new ("action-requested", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (MuMsgSourceViewClass, + action_requested), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); +} + + + +static void +on_menu_item_activate (GtkMenuItem *item, MuMsgSourceView *self) +{ + g_signal_emit (G_OBJECT(self), + signals[ACTION_REQUESTED], 0, + g_object_get_data (G_OBJECT(item), "action")); +} + +static void +popup_menu (MuMsgSourceView *self, guint button, guint32 activate_time) +{ + GtkWidget *menu; + int i; + struct { + const char* title; + const char* action; + } actions[] = { + { "View message...", "view-message" } + }; + + menu = gtk_menu_new (); + + for (i = 0; i != G_N_ELEMENTS(actions); ++i) { + GtkWidget *item; + item = gtk_menu_item_new_with_label(actions[i].title); + g_object_set_data (G_OBJECT(item), "action", (gpointer)actions[i].action); + g_signal_connect (item, "activate", G_CALLBACK(on_menu_item_activate), + self); + gtk_menu_attach (GTK_MENU(menu), item, 0, 1, i, i+1); + gtk_widget_show (item); + } + gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, 0); +} + + +static gboolean +on_button_press_event (GtkWidget *w, GdkEventButton *event, + MuMsgSourceView *self) +{ + /* ignore all but the first (typically, left) mouse button */ + switch (event->button) { + case 1: return FALSE; /* propagate, let widget handle it */ + case 3: popup_menu (self, event->button, event->time); + default: return TRUE; /* ignore */ + } + + return (event->button > 1) ? TRUE : FALSE; +} + + + +static void +mu_msg_source_view_init (MuMsgSourceView *self) +{ + self->_priv = MU_MSG_SOURCE_VIEW_GET_PRIVATE(self); + + self->_priv->_view = gtk_text_view_new (); + + /* handle right-button clicks */ + g_signal_connect (self->_priv->_view, "button-press-event", + G_CALLBACK(on_button_press_event), self); + + gtk_box_pack_start (GTK_BOX(self), self->_priv->_view, + TRUE, TRUE, 0); +} + +static void +mu_msg_source_view_finalize (GObject *obj) +{ +/* free/unref instance resources here */ + G_OBJECT_CLASS(parent_class)->finalize (obj); +} + +GtkWidget* +mu_msg_source_view_new (void) +{ + return GTK_WIDGET(g_object_new(MU_TYPE_MSG_SOURCE_VIEW, NULL)); +} + + +void +mu_msg_source_view_set_message (MuMsgSourceView *self, MuMsg *msg) +{ + GtkTextBuffer *buf; + const gchar *path; + gchar *data; + + g_return_if_fail (MU_IS_MSG_SOURCE_VIEW(self)); + g_return_if_fail (msg); + + buf = gtk_text_view_get_buffer + (GTK_TEXT_VIEW(self->_priv->_view)); + path = msg ? mu_msg_get_path (msg) : NULL; + + if (path && g_file_get_contents (path, &data, NULL, NULL)) { + gtk_text_buffer_set_text (buf, data, -1); + g_free (data); + } else + gtk_text_buffer_set_text (buf, "", 0); +} + diff --git a/widgets/mu-msg-source-view.h b/widgets/mu-msg-source-view.h new file mode 100644 index 00000000..0f94717c --- /dev/null +++ b/widgets/mu-msg-source-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_SOURCE_VIEW_H__ +#define __MU_MSG_SOURCE_VIEW_H__ + +#include +#include + +G_BEGIN_DECLS + +/* convenience macros */ +#define MU_TYPE_MSG_SOURCE_VIEW (mu_msg_source_view_get_type()) +#define MU_MSG_SOURCE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MU_TYPE_MSG_SOURCE_VIEW,MuMsgSourceView)) +#define MU_MSG_SOURCE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),MU_TYPE_MSG_SOURCE_VIEW,MuMsgSourceViewClass)) +#define MU_IS_MSG_SOURCE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),MU_TYPE_MSG_SOURCE_VIEW)) +#define MU_IS_MSG_SOURCE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),MU_TYPE_MSG_SOURCE_VIEW)) +#define MU_MSG_SOURCE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),MU_TYPE_MSG_SOURCE_VIEW,MuMsgSourceViewClass)) + +typedef struct _MuMsgSourceView MuMsgSourceView; +typedef struct _MuMsgSourceViewClass MuMsgSourceViewClass; +typedef struct _MuMsgSourceViewPrivate MuMsgSourceViewPrivate; + +struct _MuMsgSourceView { + GtkVBox parent; + /* insert public members, if any */ + + /* private */ + MuMsgSourceViewPrivate *_priv; +}; + +struct _MuMsgSourceViewClass { + GtkVBoxClass parent_class; + /* insert signal callback declarations, e.g. */ + /* supported actions: "view-source" */ + void (* action_requested) (MuMsgSourceView* self, const char* action); +}; + +/* member functions */ +GType mu_msg_source_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_source_view_new (void); +void mu_msg_source_view_set_message (MuMsgSourceView *self, + MuMsg *msg); + +G_END_DECLS + +#endif /* __MU_MSG_SOURCE_VIEW_H__ */ + diff --git a/widgets/mu-msg-view.c b/widgets/mu-msg-view.c index 56d4dc39..38a5f11c 100644 --- a/widgets/mu-msg-view.c +++ b/widgets/mu-msg-view.c @@ -18,9 +18,9 @@ */ #include "mu-msg-view.h" -#include "mu-msg-body-view.h" -#include "mu-msg-attach-view.h" -#include "mu-msg-header-view.h" + +#include "mu-msg-normal-view.h" +#include "mu-msg-source-view.h" #include #include @@ -38,9 +38,15 @@ enum { }; struct _MuMsgViewPrivate { - GtkWidget *_headers; - GtkWidget *_body; - GtkWidget *_attach, *_attacharea; + + /* 'normal' view */ + GtkWidget *_normal_view, *_source_view; + + /* TRUE if we're in view-source mode, FALSE otherwise */ + gboolean _view_source; + + /* the message */ + MuMsg *_msg; }; #define MU_MSG_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MU_TYPE_MSG_VIEW, \ @@ -73,100 +79,120 @@ mu_msg_view_class_init (MuMsgViewClass *klass) } static void -on_attach_activated (MuMsgView *self, guint partnum, MuMsg *msg) +each_child_remove (GtkWidget *child, MuMsgView *self) { - gchar* filepath; + gtk_container_remove (GTK_CONTAINER(self), child); +} - filepath = mu_msg_part_filepath_cache (msg, partnum); - if (filepath) { - mu_msg_part_save (msg, filepath, partnum, FALSE, TRUE); - mu_util_play (filepath, TRUE, FALSE); - g_free (filepath); + +static void +clear_widgets (MuMsgView *self) +{ + /* remove the old children */ + gtk_container_foreach (GTK_CONTAINER(self), + (GtkCallback)each_child_remove, + self); + + self->_priv->_normal_view = NULL; + self->_priv->_source_view = NULL; +} + +static void +on_body_action_requested (GtkWidget *w, const char* action, + MuMsgView *self) +{ + if (g_strcmp0 (action, "view-source") == 0) { + + self->_priv->_view_source = TRUE; + if (self->_priv->_msg) + mu_msg_ref (self->_priv->_msg); + mu_msg_view_set_message (self, self->_priv->_msg); + + } else if (g_strcmp0 (action, "view-message") == 0) { + + self->_priv->_view_source = FALSE; + if (self->_priv->_msg) + mu_msg_ref (self->_priv->_msg); + mu_msg_view_set_message (self, self->_priv->_msg); } } -static GtkWidget* -get_header_widget (MuMsgView *self) -{ - GtkWidget *scrolledwin; - self->_priv->_headers = mu_msg_header_view_new (); +static GtkWidget* +get_source_view (MuMsgView *self, MuMsg *msg) +{ + if (!self->_priv->_source_view) { + self->_priv->_source_view = mu_msg_source_view_new (); + g_signal_connect (self->_priv->_source_view, + "action-requested", + G_CALLBACK(on_body_action_requested), + self); + } + + mu_msg_source_view_set_message + (MU_MSG_SOURCE_VIEW (self->_priv->_source_view), msg); + + gtk_widget_show_all (self->_priv->_source_view); + + return self->_priv->_source_view; +} + +static GtkWidget* +get_normal_view (MuMsgView *self, MuMsg *msg) +{ + GtkWidget *scrolledwin; + + if (!self->_priv->_normal_view) { + self->_priv->_normal_view = mu_msg_normal_view_new (); + g_signal_connect (self->_priv->_normal_view, + "action-requested", + G_CALLBACK(on_body_action_requested), + self); + } + + mu_msg_normal_view_set_message + (MU_MSG_NORMAL_VIEW(self->_priv->_normal_view), msg); + + gtk_widget_show (self->_priv->_normal_view); scrolledwin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwin), GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_add_with_viewport - (GTK_SCROLLED_WINDOW(scrolledwin), self->_priv->_headers); + (GTK_SCROLLED_WINDOW(scrolledwin), + self->_priv->_normal_view); + + gtk_widget_show_all (scrolledwin); return scrolledwin; } -static void -on_body_action_requested (MuMsgBodyView *body, const char* action, - MuMsgView *self) -{ - g_printerr ("received request: %s\n", action); -} - - -static GtkWidget* -get_body_widget (MuMsgView *self) -{ - GtkWidget *scrolledwin; - - self->_priv->_body = mu_msg_body_view_new (); - scrolledwin = gtk_scrolled_window_new (NULL, NULL); - gtk_container_add (GTK_CONTAINER(scrolledwin), - self->_priv->_body); - - g_signal_connect (self->_priv->_body, "action-requested", - G_CALLBACK(on_body_action_requested), self); - - return scrolledwin; -} - -static GtkWidget* -get_attach_widget (MuMsgView *self) -{ - /* GtkWidget *scrolledwin; */ - - self->_priv->_attacharea = gtk_frame_new (""); - /* scrolledwin = gtk_scrolled_window_new (NULL, NULL); */ - /* gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolledwin), */ - /* GTK_POLICY_NEVER, */ - /* GTK_POLICY_AUTOMATIC); */ - - self->_priv->_attach = mu_msg_attach_view_new (); - gtk_container_add (GTK_CONTAINER(self->_priv->_attacharea), - self->_priv->_attach); - - g_signal_connect (self->_priv->_attach, "attach-activated", - G_CALLBACK(on_attach_activated), - self); - - return self->_priv->_attacharea; -} static void mu_msg_view_init (MuMsgView *self) { - self->_priv = MU_MSG_VIEW_GET_PRIVATE(self); - - gtk_box_pack_start (GTK_BOX(self), get_header_widget (self), - FALSE, FALSE, 2); + self->_priv = MU_MSG_VIEW_GET_PRIVATE(self); + - gtk_box_pack_start (GTK_BOX(self), get_attach_widget (self), - FALSE, FALSE, 2); + self->_priv->_normal_view = NULL; + self->_priv->_source_view = NULL; - gtk_box_pack_start (GTK_BOX(self), get_body_widget (self), - TRUE, TRUE, 2); + self->_priv->_view_source = FALSE; } static void mu_msg_view_finalize (GObject *obj) { + MuMsgViewPrivate *priv; + + priv = MU_MSG_VIEW_GET_PRIVATE(obj); + + if (priv->_msg) + mu_msg_unref (priv->_msg); + /* free/unref instance resources here */ G_OBJECT_CLASS(parent_class)->finalize (obj); } @@ -178,45 +204,34 @@ mu_msg_view_new (void) } -static void -update_attachment_area (MuMsgView *self, MuMsg *msg) -{ - gint attach_num; - - attach_num = 0; - if (msg) - attach_num = mu_msg_attach_view_set_message - (MU_MSG_ATTACH_VIEW(self->_priv->_attach), msg); - - if (attach_num > 0) - gtk_widget_show_all (self->_priv->_attacharea); - else - gtk_widget_hide_all (self->_priv->_attacharea); -} - void mu_msg_view_set_message (MuMsgView *self, MuMsg *msg) { g_return_if_fail (MU_IS_MSG_VIEW(self)); - - mu_msg_header_view_set_message (MU_MSG_HEADER_VIEW(self->_priv->_headers), - msg); - mu_msg_body_view_set_message (MU_MSG_BODY_VIEW(self->_priv->_body), - msg); - update_attachment_area (self, msg); -} + if (self->_priv->_msg) + mu_msg_unref (self->_priv->_msg); + self->_priv->_msg = msg ? mu_msg_ref (msg) : NULL; + + clear_widgets (self); + + if (!self->_priv->_view_source) + gtk_box_pack_start (GTK_BOX(self), + get_normal_view (self, msg), + TRUE, TRUE, 0); + else + gtk_box_pack_start (GTK_BOX(self), + get_source_view (self, msg), + TRUE, TRUE, 0); +} void mu_msg_view_set_note (MuMsgView *self, const char *html) { g_return_if_fail (MU_IS_MSG_VIEW(self)); - - mu_msg_header_view_set_message (MU_MSG_HEADER_VIEW(self->_priv->_headers), - NULL); - mu_msg_body_view_set_note (MU_MSG_BODY_VIEW(self->_priv->_body), - html); + /* FIXME */ } + diff --git a/widgets/mu-msg-view.h b/widgets/mu-msg-view.h index 6d6e6f27..8cf88cf1 100644 --- a/widgets/mu-msg-view.h +++ b/widgets/mu-msg-view.h @@ -60,6 +60,8 @@ GtkWidget* mu_msg_view_new (void); void mu_msg_view_set_message (MuMsgView *self, MuMsg *msg); void mu_msg_view_set_note (MuMsgView *self, const char *html); +typedef enum _MuMsgViewDisplayType MuMsgViewDisplayType; + G_END_DECLS