* refactoring & add 'view-source' and 'view-message' (activate with context menu)

This commit is contained in:
Dirk-Jan C. Binnema 2011-01-21 18:46:33 -05:00
parent 21fcef13d9
commit 6daf2e9989
11 changed files with 804 additions and 135 deletions

View File

@ -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

View File

@ -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);

View File

@ -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:<action>" 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 <a href="cmd:refresh">Refresh</a> 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;
}

View File

@ -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);
};

View File

@ -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);
}
}

View File

@ -0,0 +1,219 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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);
}

View File

@ -0,0 +1,67 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <gtk/gtk.h>
#include <mu-msg.h>
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__ */

View File

@ -0,0 +1,174 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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);
}

View File

@ -0,0 +1,68 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <gtk/gtk.h>
#include <mu-msg.h>
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__ */

View File

@ -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 <mu-msg.h>
#include <mu-msg-part.h>
@ -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 */
}

View File

@ -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