Some balsa-message additions
- From: Mate Soos <zerok yifan net>
- To: balsa-list gnome org
- Subject: Some balsa-message additions
- Date: Sun, 2 Jul 2000 21:00:18 +0000
Hi!
I have been working on the balsa-message.c I have made the part_init_audio and
part_info_init_unknown complete. As you can see I had to create a link to the
file being opened (part_create_link), since anything that is going to open it,
will only recognise the file format, if it's got an extension that fits. So a link
is made that has the right extension. I dunno if the idea is the best, but I
could only manage it this way. In the sound section I choose XMMS as a viewer for
all files and esd if it's a wav file. (I assumed everybody has XMMS, since it's
so popular). The unknown foramts are handled with the mime_type from gnome, if
availabe.
I am just going to start SoftEng couse in a university, so my code probably
isn't the best.
Bye,
Mate Soos
/* -*-mode:c; c-style:k&r; c-basic-offset:2; -*- */
/* Balsa E-Mail Client
* Copyright (C) 1997-1999 Jay Painter and Stuart Parmenter
*
* 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, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "balsa-app.h"
#include "mailbackend.h"
#include "balsa-message.h"
#include "mime.h"
#include "misc.h"
#ifdef USE_PIXBUF
#include <gdk-pixbuf/gdk-pixbuf.h>
#endif
#define BGLINKCOLOR "LightSteelBlue1"
struct _BalsaPartInfo
{
BODY *body;
Message *message;
/* The widget to add to the container */
GtkWidget *widget;
/* The widget to give focus to */
GtkWidget *focus_widget;
/* The contect menu */
GtkWidget *popup_menu;
/* True if balsa knows how to display this part */
gboolean can_display;
};
/* widget */
static void balsa_message_class_init (BalsaMessageClass * klass);
static void balsa_message_init (BalsaMessage * bm);
static gint balsa_message_focus_in_part(GtkWidget *widget, GdkEventFocus *event, BalsaMessage *bm);
static gint balsa_message_focus_out_part(GtkWidget *widget, GdkEventFocus *event, BalsaMessage *bm);
static gint balsa_message_key_press_event(GtkWidget *widget, GdkEventKey *event, BalsaMessage *bm);
static void display_headers (BalsaMessage * bm);
static void display_content (BalsaMessage *bm);
static void display_part (BalsaMessage *bm, BODY * bdy);
static void display_multipart (BalsaMessage *bm, BODY * bdy);
static void save_part (BalsaPartInfo *info);
static gchar *save_mime_part (Message * message, BODY * body);
static void select_icon_cb(GnomeIconList *ilist, gint num,
GdkEventButton * event, BalsaMessage *bm);
static void select_part(BalsaMessage *bm, gint part);
static void free_icon_data(gpointer data);
static void part_context_menu_save(GtkWidget *menu_item, BalsaPartInfo *info);
static void add_header_gchar(BalsaMessage *bm, gchar *header, gchar * label,
gchar * value);
static void add_header_glist(BalsaMessage *bm, gchar *header, gchar* label,
GList * list);
static void scroll_set (GtkAdjustment* adj, gint value);
static void scroll_change (GtkAdjustment* adj, gint diff);
static void balsa_gtk_text_size_request (GtkWidget *widget, GtkRequisition *requisition, gpointer data);
static void balsa_icon_list_size_request (GtkWidget *widget, GtkRequisition *requisition, gpointer data);
static void part_info_init_image (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_other (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_mimetext (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_video (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_message (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_application (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_audio (BalsaMessage *bm, BalsaPartInfo *info);
static void part_info_init_unknown (BalsaMessage *bm, BalsaPartInfo *info);
static GtkViewportClass *parent_class = NULL;
guint
balsa_message_get_type ()
{
static guint balsa_message_type = 0;
if (!balsa_message_type)
{
GtkTypeInfo balsa_message_info =
{
"BalsaMessage",
sizeof (BalsaMessage),
sizeof (BalsaMessageClass),
(GtkClassInitFunc) balsa_message_class_init,
(GtkObjectInitFunc) balsa_message_init,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL,
(GtkClassInitFunc) NULL
};
balsa_message_type = gtk_type_unique (gtk_viewport_get_type (), &balsa_message_info);
}
return balsa_message_type;
}
static void
balsa_message_class_init (BalsaMessageClass * klass)
{
parent_class = gtk_type_class (gtk_viewport_get_type ());
}
static void
balsa_message_init (BalsaMessage * bm)
{
bm->table = gtk_table_new(3, 1, FALSE);
gtk_container_add(GTK_CONTAINER(bm), bm->table);
gtk_widget_show(bm->table);
bm->header_text = gtk_text_new(NULL, NULL);
gtk_signal_connect(GTK_OBJECT(bm->header_text), "key_press_event",
(GtkSignalFunc)balsa_message_key_press_event,
(gpointer)bm);
gtk_signal_connect(GTK_OBJECT(bm->header_text), "size_request",
(GtkSignalFunc)balsa_gtk_text_size_request,
(gpointer)bm);
gtk_table_attach(GTK_TABLE(bm->table), bm->header_text, 0, 1, 0, 1, GTK_EXPAND|GTK_FILL, 0, 0, 1);
bm->part_list = gnome_icon_list_new(100, NULL, FALSE);
gnome_icon_list_set_selection_mode(GNOME_ICON_LIST(bm->part_list),
GTK_SELECTION_SINGLE);
gtk_signal_connect (GTK_OBJECT (bm->part_list), "select_icon",
GTK_SIGNAL_FUNC (select_icon_cb),
bm);
gtk_signal_connect (GTK_OBJECT (bm->part_list), "size_request",
GTK_SIGNAL_FUNC (balsa_icon_list_size_request),
(gpointer)bm);
gtk_table_attach(GTK_TABLE(bm->table), bm->part_list, 0, 1, 2, 3, GTK_EXPAND|GTK_FILL, 0, 0, 1);
bm->current_part = NULL;
bm->message = NULL;
bm->wrap_text = balsa_app.browse_wrap;
bm->shown_headers = balsa_app.shown_headers;
}
static gint
balsa_message_focus_in_part(GtkWidget *widget, GdkEventFocus *event, BalsaMessage *bm)
{
g_return_val_if_fail(widget != NULL, FALSE);
g_return_val_if_fail(bm != NULL, FALSE);
g_return_val_if_fail(BALSA_IS_MESSAGE(bm), FALSE);
bm->content_has_focus = TRUE;
return FALSE;
}
static gint
balsa_message_focus_out_part(GtkWidget *widget, GdkEventFocus *event, BalsaMessage *bm)
{
g_return_val_if_fail(widget != NULL, FALSE);
g_return_val_if_fail(bm != NULL, FALSE);
g_return_val_if_fail(BALSA_IS_MESSAGE(bm), FALSE);
bm->content_has_focus = FALSE;
return FALSE;
}
static void
save_part (BalsaPartInfo *info)
{
gchar *filename;
gchar *msg_filename = NULL;
STATE s;
GtkWidget *save_dialog;
GtkWidget *file_entry;
GtkWidget *label;
gint button;
g_return_if_fail(info != 0);
save_dialog = gnome_dialog_new (_ ("Save MIME Part"),
_ ("Save"), _ ("Cancel"), NULL);
label = gtk_label_new( _("Please choose a filename to save this part of the message as:") );
file_entry = gnome_file_entry_new ("Balsa_MIME_Saver",
_ ("Save MIME Part"));
if (info->body->filename) {
gtk_entry_set_text (GTK_ENTRY (gnome_file_entry_gtk_entry (GNOME_FILE_ENTRY (file_entry))), info->body->filename);
}
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (save_dialog)->vbox), label,
FALSE, FALSE, 10);
gtk_widget_show (label);
gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (save_dialog)->vbox), file_entry,
FALSE, FALSE, 10);
gtk_widget_show (file_entry);
gnome_dialog_set_parent(GNOME_DIALOG(save_dialog), GTK_WINDOW(balsa_app.main_window));
gtk_window_set_modal (GTK_WINDOW (save_dialog), TRUE);
button = gnome_dialog_run(GNOME_DIALOG (save_dialog));
/* button 0 == OK */
if ( button == 0 ) {
switch (info->message->mailbox->type)
{
case MAILBOX_MH:
case MAILBOX_MAILDIR:
msg_filename = g_strdup_printf
("%s/%s", MAILBOX_LOCAL (info->message->mailbox)->path,
message_pathname (info->message));
break;
case MAILBOX_IMAP:
case MAILBOX_POP3:
msg_filename = g_strdup
(MAILBOX_IMAP (info->message->mailbox)->tmp_file_path);
break;
default:
msg_filename = g_strdup(MAILBOX_LOCAL (info->message->mailbox)->path);
break;
}
s.fpin = fopen (msg_filename, "r");
if (!s.fpin || ferror (s.fpin))
{
gchar *msg;
GtkWidget *msgbox;
msg = g_strdup_printf ( _ (" Open of %s failed:%s "),
msg_filename, strerror(errno));
gtk_object_destroy(GTK_OBJECT(save_dialog));
msgbox = gnome_message_box_new (msg, _ ("Error"), _ ("Ok"), NULL);
gnome_dialog_set_parent(GNOME_DIALOG(msgbox), GTK_WINDOW(balsa_app.main_window));
gtk_window_set_modal (GTK_WINDOW (msgbox), TRUE);
gnome_dialog_run (GNOME_DIALOG (msgbox));
g_free(msg);
g_free(msg_filename);
return;
}
filename = gtk_entry_get_text (GTK_ENTRY (gnome_file_entry_gtk_entry (GNOME_FILE_ENTRY (file_entry))));
s.prefix = 0;
s.fpout = fopen (filename, "w");
if (!s.fpout)
{
gchar *msg;
GtkWidget *msgbox;
msg = g_strdup_printf ( _ (" Open of %s failed:%s "), filename, strerror (errno));
gtk_object_destroy(GTK_OBJECT(save_dialog));
msgbox = gnome_message_box_new (msg, "Error", _ ("Ok"), NULL);
gnome_dialog_set_parent(GNOME_DIALOG(msgbox), GTK_WINDOW(balsa_app.main_window));
gtk_window_set_modal (GTK_WINDOW (msgbox), TRUE);
gnome_dialog_run (GNOME_DIALOG (msgbox));
g_free(msg);
return;
}
fseek (s.fpin, info->body->offset, 0);
mutt_decode_attachment (info->body, &s);
fclose (s.fpin);
fclose (s.fpout);
}
gtk_object_destroy(GTK_OBJECT(save_dialog));
}
GtkWidget *
balsa_message_create (void)
{
BalsaMessage *bm;
bm = gtk_type_new (balsa_message_get_type ());
return GTK_WIDGET (bm);
}
static void select_icon_cb (GnomeIconList * ilist, gint num, GdkEventButton * event, BalsaMessage *bm)
{
BalsaPartInfo *info;
if(event==NULL)
return;
if (event->button == 1) {
select_part(bm, num);
} else if ( event->button == 3 ) {
info = (BalsaPartInfo*)gnome_icon_list_get_icon_data(ilist, num);
if ( info && info->popup_menu ) {
gtk_menu_popup (GTK_MENU(info->popup_menu),
NULL, NULL, NULL, NULL,
event->button, event->time);
}
}
}
void
balsa_message_clear (BalsaMessage * bm)
{
g_return_if_fail (bm != NULL);
balsa_message_set(bm, NULL);
}
void
balsa_message_set (BalsaMessage * bm,
Message * message)
{
gboolean had_focus;
g_return_if_fail (bm != NULL);
/* Leave this out. When settings (eg wrap) are changed it is OK to
call message_set with the same messagr */
/* if (bm->message == message) */
/* return; */
had_focus = bm->content_has_focus;
select_part(bm, -1);
bm->message = NULL;
bm->part_count = 0;
gnome_icon_list_clear(GNOME_ICON_LIST(bm->part_list));
if (message == NULL) {
gtk_widget_hide(bm->header_text);
return;
}
/* mark message as read; no-op if it was read so don't worry.
and this is the right place to do the marking.
*/
message_read(message);
bm->message = message;
display_headers (bm);
message_body_ref (bm->message);
display_content (bm);
message_body_unref (bm->message);
gnome_icon_list_select_icon(GNOME_ICON_LIST(bm->part_list), 0);
select_part(bm, 0);
if ( had_focus && bm->current_part && bm->current_part->focus_widget )
gtk_widget_grab_focus(bm->current_part->focus_widget);
/* We show the part list if:
* there is > 1 part
* or we don't know how to display the one part.
*/
if ( bm->part_count > 1 ) {
gtk_widget_show(bm->part_list);
} else {
BalsaPartInfo *info = (BalsaPartInfo*)gnome_icon_list_get_icon_data
(GNOME_ICON_LIST(bm->part_list), 0);
if ( info->can_display )
gtk_widget_hide(bm->part_list);
else
gtk_widget_show(bm->part_list);
}
gtk_widget_show(bm->header_text);
}
void
balsa_message_save_current_part(BalsaMessage *bm)
{
g_return_if_fail(bm != NULL);
if ( bm->current_part )
save_part(bm->current_part);
}
void
balsa_message_set_displayed_headers(BalsaMessage *bmessage, ShownHeaders sh)
{
g_return_if_fail(bmessage != NULL);
g_return_if_fail(sh >= HEADERS_NONE && sh <= HEADERS_ALL);
bmessage->shown_headers = sh;
if ( bmessage->message )
display_headers(bmessage);
}
void
balsa_message_set_wrap(BalsaMessage *bm, gboolean wrap)
{
g_return_if_fail(bm != NULL);
bm->wrap_text = wrap;
/* This is easier than reformating all the widgets... */
if ( bm->message ) {
Message *msg = bm->message;
balsa_message_set(bm, msg);
}
}
/* This function should split \n into separate lines. */
static void
add_header_gchar(BalsaMessage *bm, gchar *header, gchar * label, gchar * value)
{
/* GtkWidget *w; */
GdkFont *fnt;
gchar pad[] = " ";
gchar cr[] = "\n";
gchar *line_start, *line_end;
gchar *wrapped_value;
if ( ! ( bm->shown_headers == HEADERS_ALL ||
find_word(header, balsa_app.selected_headers) ) )
return;
fnt = gdk_font_load(balsa_app.message_font);
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, label, -1);
if ( value && *value != '\0') {
if ( strlen(label) < 15 )
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, pad,
15-strlen(label) );
else
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, pad, 1);
wrapped_value = g_strdup(value);
wrap_string(wrapped_value, balsa_app.wraplength-15);
/* We must insert the first line. Each subsequent line must be indented
by 15 spaces. So we need to rewrap lines 2+
*/
line_end = wrapped_value;
while ( *line_end != '\0' ) {
line_start = line_end;
line_end++;
while ( *line_end != '\0' && *line_end != '\n')
line_end++;
if ( line_start != wrapped_value )
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, pad,
15);
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, line_start,
line_end - line_start );
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, cr, -1);
if ( *line_end != '\0' )
line_end++;
}
g_free(wrapped_value);
} else {
gtk_text_insert(GTK_TEXT(bm->header_text), fnt, NULL, NULL, cr, -1);
}
}
static void
add_header_glist(BalsaMessage *bm, gchar *header, gchar* label, GList * list)
{
gchar * value;
if ( list == NULL )
return;
if ( ! ( bm->shown_headers == HEADERS_ALL ||
find_word(header, balsa_app.selected_headers) ) )
return;
value = make_string_from_list (list);
add_header_gchar(bm, header, label, value);
g_free(value);
}
static void
display_headers (BalsaMessage * bm)
{
Message *message = bm->message;
GList *p, *lst;
gchar **pair, *hdr;
gtk_editable_delete_text(GTK_EDITABLE(bm->header_text), 0, -1);
if(bm->shown_headers == HEADERS_NONE) {
gtk_widget_hide(bm->header_text);
return;
} else {
gtk_widget_show(bm->header_text);
}
gtk_text_freeze(GTK_TEXT(bm->header_text));
add_header_gchar(bm, "date", _("Date:"), message->date);
if (message->from) {
gchar *from = address_to_gchar(message->from);
add_header_gchar(bm, "from", _("From:"), from);
g_free (from);
}
add_header_glist( bm, "to", _("To:"), message->to_list);
add_header_glist( bm, "cc", _("Cc:"), message->cc_list);
add_header_glist( bm, "bcc", _("Bcc:"), message->bcc_list);
if(message->fcc_mailbox)
add_header_gchar( bm, "fcc", _("Fcc:"), message->fcc_mailbox->name);
add_header_gchar( bm, "subject", _("Subject:"), message->subject);
/* remaining headers */
lst = message_user_hdrs(message);
for(p = g_list_first(lst); p; p = g_list_next(p)) {
pair = p->data;
hdr = g_strconcat(pair[0], ":", NULL);
add_header_gchar(bm, pair[0], hdr, pair[1]);
g_free(hdr);
g_strfreev(pair);
}
g_list_free(lst);
gtk_text_thaw(GTK_TEXT(bm->header_text));
gtk_widget_queue_resize(GTK_WIDGET(bm->header_text));
}
static void
part_info_init_other (BalsaMessage *bm, BalsaPartInfo *info)
{
g_print ("TODO: part_info_init_other\n");
part_info_init_unknown(bm, info);
}
static gchar*
part_create_link(gchar* filename,BalsaPartInfo* info)
{
gchar *link_exec,*tmp_filename=NULL;
gint i=0;
do
{
if (tmp_filename) g_free(tmp_filename);
tmp_filename=g_strdup_printf("/tmp/%d-%s",++i,info->body->filename);
} while(g_file_exists(tmp_filename));
link_exec=g_strdup_printf("ln %s %s",filename,tmp_filename);
gnome_execute_shell(NULL,link_exec);
return(tmp_filename);
}
static void
part_info_xmms_play (GtkWidget *widget,gpointer data)
{
gchar* xmms_exec;
gchar* filename,*linkname;
BalsaPartInfo *info;
info=(BalsaPartInfo*) data;
filename = save_mime_part (info->message, info->body);
linkname = part_create_link(filename,info);
xmms_exec=g_strdup_printf("xmms -p %s",linkname);
gnome_execute_shell(NULL,xmms_exec);
g_free(xmms_exec);
g_free(filename);
g_free(linkname);
}
static void
part_info_esd_play (GtkWidget *widget,gpointer data)
{
BalsaPartInfo *info;
gchar* filename;
info=(BalsaPartInfo*) data;
filename=save_mime_part (info->message, info->body);
gnome_sound_play(filename);
g_free(filename);
}
static void
part_info_init_audio (BalsaMessage *bm, BalsaPartInfo *info)
{
GtkWidget *text,*vbox,*button;
vbox=gtk_vbox_new(FALSE,1);
if (strcmp(info->body->subtype,"x-wav")==0)
{
text=gtk_label_new("Choose the sound playing application of your preference\nto play the sound file attached");
gtk_box_pack_start(GTK_BOX(vbox),text,FALSE, FALSE, 1 );
button=gtk_button_new_with_label("Play with XMMS");
gtk_box_pack_start(GTK_BOX(vbox),button,FALSE, FALSE, 1 );
gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(part_info_xmms_play),(gpointer) info);
button=gtk_button_new_with_label("Play with esd");
gtk_box_pack_start(GTK_BOX(vbox),button,FALSE, FALSE, 1 );
gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(part_info_esd_play),(gpointer) info);
}
else {
text=gtk_label_new("Unfortunately Balsa can not play this sound natively. Please use XMMS to play it");
gtk_box_pack_start(GTK_BOX(vbox),text,FALSE, FALSE, 1 );
button=gtk_button_new_with_label("Play with XMMS");
gtk_box_pack_start(GTK_BOX(vbox),button,FALSE, FALSE, 1 );
gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(part_info_xmms_play),(gpointer) info);
}
gtk_widget_show_all(vbox);
info->widget=vbox;
info->focus_widget=vbox;
info->can_display=TRUE;
}
static void
part_info_init_application (BalsaMessage *bm, BalsaPartInfo *info)
{
g_print("TODO: part_info_init_application\n");
part_info_init_unknown(bm, info);
}
static void
part_info_init_image (BalsaMessage *bm, BalsaPartInfo *info)
{
#ifndef USE_PIXBUF
GdkImlibImage *im;
#else
GdkPixbuf *pb;
#endif
gchar *filename;
GdkPixmap *pixmap;
GdkBitmap *mask;
GtkWidget *image;
filename = save_mime_part (info->message, info->body);
#ifndef USE_PIXBUF
im = gdk_imlib_load_image (filename);
if ( !gdk_imlib_render(im, im->rgb_width, im->rgb_height) ) {
g_print( _ ("Couldn't render image\n") );
}
pixmap = gdk_imlib_copy_image(im);
mask = gdk_imlib_copy_mask(im);
gdk_imlib_destroy_image(im);
#else
pb = gdk_pixbuf_new_from_file(filename);
gdk_pixbuf_render_pixmap_and_mask(pb, &pixmap, &mask, 0);
gdk_pixbuf_unref(pb);
#endif
if ( pixmap ) {
image = gtk_pixmap_new(pixmap, mask);
info->widget = image;
info->focus_widget = image;
info->can_display = TRUE;
}
if ( pixmap )
gdk_pixmap_unref(pixmap);
if ( mask )
gdk_bitmap_unref(mask);
unlink (filename);
g_free(filename);
}
static void
part_info_init_message (BalsaMessage *bm, BalsaPartInfo *info)
{
g_print("TODO: part_info_init_message\n");
part_info_init_unknown(bm, info);
}
static void
part_mime_handle(gchar *todo,BalsaPartInfo *info)
{
gchar *full_mime_type,*tmp,*open_command,*open_command_pre,*linkname,*filename;
full_mime_type=g_strdup_printf("%s/%s",TYPE(info->body),info->body->subtype);
open_command_pre=gnome_mime_get_value(full_mime_type,todo);
g_free(full_mime_type);
if (! open_command_pre) {
GtkWidget *dialog,*label,*ok_butt;
dialog=gtk_dialog_new();
label=gtk_label_new("MIME handler does not exist for this filetype");
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),label,TRUE,TRUE,10);
gtk_widget_show(label);
ok_butt=gnome_stock_button(GNOME_STOCK_BUTTON_OK);
gtk_signal_connect_object(GTK_OBJECT(ok_butt),"clicked",GTK_SIGNAL_FUNC(gtk_widget_destroy),GTK_OBJECT(dialog));
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),ok_butt,TRUE,TRUE,10);
gtk_widget_show(ok_butt);
gtk_widget_show(dialog);
}
else {
tmp=strstr(open_command_pre,"%f");
strncpy(tmp,"%s",2);
filename=save_mime_part (info->message, info->body);
linkname=part_create_link(filename,info);
g_free(filename);
open_command=g_strdup_printf(open_command_pre,linkname);
g_free(open_command_pre);
gnome_execute_shell(NULL,open_command);
g_free(open_command);
g_free(linkname);
}
}
static void
part_view_mime(GtkWidget *widget,gpointer data)
{
part_mime_handle("view",(BalsaPartInfo*) data);
}
static void
part_open_mime(GtkWidget *widget,gpointer data)
{
part_mime_handle("open",(BalsaPartInfo*) data);
}
static void
part_info_init_unknown (BalsaMessage *bm, BalsaPartInfo *info)
{
GtkWidget *vbox;
GtkWidget *label;
GtkWidget *button;
gchar *msg;
vbox = gtk_vbox_new ( FALSE, 1 );
gtk_container_set_border_width ( GTK_CONTAINER(vbox), 10 );
label = gtk_label_new ( _("Balsa does not know how to display this message part") );
gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, FALSE, 1 );
gtk_widget_show (label);
if ( info->body->subtype )
msg = g_strdup_printf( _("Content Type: %s/%s"), TYPE(info->body), info->body->subtype);
else
msg = g_strdup_printf( _("Content Type: %s"), TYPE(info->body));
label = gtk_label_new ( msg );
gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, FALSE, 1 );
gtk_widget_show (label);
g_free(msg);
if (info->body->filename) {
msg = g_strdup_printf( _("Filename: %s"), info->body->filename);
label = gtk_label_new ( msg );
gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, FALSE, 1 );
gtk_widget_show (label);
g_free(msg);
}
if (info->body->subtype) {
label = gtk_label_new ("The following options are avilable:");
gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, FALSE, 1 );
gtk_widget_show (label);
button = gtk_button_new_with_label (_("Open part"));
gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(part_open_mime),(gpointer) info);
gtk_box_pack_start ( GTK_BOX(vbox), button, FALSE, FALSE, 10 );
gtk_widget_show(button);
button = gtk_button_new_with_label (_("View part"));
gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(part_view_mime),(gpointer) info);
gtk_box_pack_start ( GTK_BOX(vbox), button, FALSE, FALSE, 10 );
gtk_widget_show(button);
}
button = gtk_button_new_with_label (_("Save part"));
gtk_box_pack_start ( GTK_BOX(vbox), button, FALSE, FALSE, 10 );
gtk_signal_connect(GTK_OBJECT(button), "clicked",
(GtkSignalFunc)part_context_menu_save, (gpointer)info);
gtk_widget_show (button);
gtk_widget_show(vbox);
info->focus_widget = vbox;
info->widget = vbox;
info->can_display = FALSE;
}
static void
display_multipart (BalsaMessage *bm, BODY * bdy)
{
BODY *p;
for (p = bdy->parts; p; p = p->next)
{
display_part (bm, p);
}
}
static void
part_info_init_video (BalsaMessage *bm, BalsaPartInfo *info)
{
g_print ("TODO: part_info_init_video\n");
part_info_init_unknown(bm, info);
}
/* the name should really be one_or_two_const_fields_to_end */
static gint
two_const_fields_to_end(const gchar* ptr) {
int cnt = 0;
while(*ptr && cnt<3) {
if(*ptr == '*') return 0;
if(*ptr++ == '-') cnt++;
}
return cnt<3;
}
/* get_font_name returns iso8859 font name based on given font
wildcard 'base' and given character set encoding.
Algorithm: copy max first 12 fields, cutting additionally
at most two last, if they are constant.
*/
gchar*
get_font_name(const gchar* base, int code) {
static gchar type[] ="iso8859";
gchar *res;
const gchar* ptr = base;
int dash_cnt = 0, len;
g_return_val_if_fail(base != NULL, NULL);
g_return_val_if_fail(code >= 0, NULL);
while(*ptr && dash_cnt<13) {
if(*ptr == '-') dash_cnt++;
if(two_const_fields_to_end(ptr)) break;
ptr++;
}
/* defense against a patologically short base font wildcard implemented
* in the chunk below
* extra space for dwo dashes and '\0' */
len = ptr-base;
/* if(dash_cnt>12) len--; */
if(len<1) len = 1;
res = (gchar*)g_malloc(len+sizeof(type)+3+(code>9?2:1));
if(balsa_app.debug)
fprintf(stderr,"base font name: %s and code: %d\n"
"mallocating %d bytes\n", base, code,
len+sizeof(type)+2+(code>9?2:1) );
if(len>1) strncpy(res, base, len);
else { strncpy(res, "*", 1); len = 1; }
sprintf(res+len,"-%s-%d", type, code);
return res;
}
gchar*
get_koi_font_name(const gchar* base, const gchar* code) {
static gchar type[] ="koi8";
gchar *res;
const gchar* ptr = base;
int dash_cnt = 0, len;
g_return_val_if_fail(base != NULL, NULL);
g_return_val_if_fail(code != NULL, NULL);
while(*ptr && dash_cnt<13) {
if(*ptr == '-') dash_cnt++;
if(two_const_fields_to_end(ptr)) break;
ptr++;
}
/* defense against a patologically short base font wildcard implemented
* in the chunk below
* extra space for dwo dashes and '\0' */
len = ptr-base;
/* if(dash_cnt>12) len--; */
if(len<1) len = 1;
res = (gchar*)g_malloc(len+sizeof(type)+3+strlen(code));
if(balsa_app.debug)
fprintf(stderr,"base font name: %s and code: %s\n"
"mallocating %d bytes\n", base, code,
len+sizeof(type)+3 );
if(len>1) strncpy(res, base, len);
else { strncpy(res, "*", 1); len = 1; }
sprintf(res+len,"-%s-%s", type, code);
return res;
}
/* HELPER FUNCTIONS ----------------------------------------------- */
static gchar*
find_body_font(BODY * bdy)
{
gchar * font_name = NULL, *charset;
if ((charset=mutt_get_parameter("charset", bdy->parameter)))
{
if(g_strncasecmp(charset,"iso-8859-",9) != 0 ) return NULL;
font_name = get_font_name(balsa_app.message_font, atoi(charset+9));
}
return font_name;
}
/* reflows a paragraph in given string. The paragraph to reflow is
determined by the cursor position. If mode is <0, whole string is
reflowed. Replace tabs with single spaces, squeeze neighboring spaces.
Single '\n' replaced with spaces, double - retained.
HQ piece of code, modify only after thorough testing.
*/
/* find_beg_and_end - finds beginning and end of a paragraph;
*l will store the pointer to the first character of the paragraph,
*u - to the '\0' or first '\n' character delimiting the paragraph.
*/
static
void find_beg_and_end(gchar *str, gint pos, gchar **l, gchar **u)
{
gint ln;
*l = str + pos;
while(*l>str && !(**l == '\n' && *(*l-1) == '\n') )
(*l)--;
if(*l+1<=str+pos && **l == '\n') (*l)++;
*u = str + pos;
ln = 0;
while(**u && !(ln && **u == '\n') )
ln = *(*u)++ == '\n';
if(ln) (*u)--;
}
/* lspace - last was space, iidx - insertion index. */
void
reflow_string(gchar* str, gint mode, gint *cur_pos, int width)
{
gchar *l, *u, *sppos, *lnbeg, *iidx;
gint lnl = 0, lspace = 0; // 1 -> skip leading spaces
if(mode<0) {
l = str; u = str + strlen(str);
}
else find_beg_and_end(str, *cur_pos, &l, &u);
lnbeg = sppos = iidx = l;
while(l<u) {
if(lnl && *l == '\n') {
*(iidx-1) = '\n';
*iidx++ = '\n';
lspace = 1;
lnbeg = sppos = iidx;
} else if(isspace((unsigned char)*l)) {
lnl = *l == '\n';
if(!lspace) {
sppos = iidx;
*iidx++= ' ';
} else if(iidx-str<*cur_pos) (*cur_pos)--;
lspace = 1;
} else {
lspace = 0; lnl = 0;
if(iidx-lnbeg>=width && lnbeg < sppos){
*sppos='\n';
lnbeg=sppos+1;
}
*iidx++ = *l;
}
l++;
}
/* job is done, shrink remainings */
while( (*iidx++ =*u++) )
;
}
/* END OF HELPER FUNCTIONS ----------------------------------------------- */
static void
part_info_init_mimetext (BalsaMessage *bm, BalsaPartInfo *info)
{
FILE *fp;
gchar *ptr = 0;
size_t alloced;
GtkWidget *item = NULL;
GdkFont *fnt = NULL;
gchar *filename = save_mime_part(info->message, info->body);
fp = fopen( filename, "r" );
alloced = readfile( fp, &ptr );
if( ptr ) {
gboolean showtext;
if( g_strcasecmp( info->body->subtype, "html" ) == 0 )
showtext = FALSE;
else
showtext = TRUE;
/* This causes a memory leak */
/* if( info->body->filename == NULL ) */
/* info->body->filename = g_strdup( "textfile" ); */
if( showtext ) {
gchar *font_name;
font_name = find_body_font(info->body);
if(bm->wrap_text)
wrap_string(ptr, balsa_app.wraplength);
if(font_name) {
fnt = gdk_font_load(font_name);
if ( !fnt )
fprintf(stderr,"message/text:: font not found: %s\n", font_name);
g_free(font_name);
}
if (!fnt)
fnt = gdk_font_load(balsa_app.message_font);
item = gtk_text_new(NULL, NULL);
gtk_signal_connect(GTK_OBJECT(item), "key_press_event",
(GtkSignalFunc)balsa_message_key_press_event, (gpointer)bm);
gtk_signal_connect(GTK_OBJECT(item), "focus_in_event",
(GtkSignalFunc)balsa_message_focus_in_part, (gpointer)bm);
gtk_signal_connect(GTK_OBJECT(item), "focus_out_event",
(GtkSignalFunc)balsa_message_focus_out_part, (gpointer)bm);
gtk_signal_connect(GTK_OBJECT(item), "size_request",
(GtkSignalFunc)balsa_gtk_text_size_request, (gpointer)bm);
gtk_text_insert(GTK_TEXT(item), fnt, NULL, NULL, ptr, -1);
gtk_text_set_editable(GTK_TEXT(item), FALSE);
gtk_widget_show(item);
info->focus_widget = item;
info->widget = item;
info->can_display = TRUE;
} else {
part_info_init_unknown(bm, info);
}
g_free( ptr );
}
fclose( fp );
unlink( filename );
g_free( filename );
}
static gchar *
find_part_icon(BalsaPartInfo *info, gchar *content_type)
{
gchar *pix;
gchar *filename = g_strdup_printf("gnome-%s.png", content_type);
gchar *p = filename;
while(*p) {
*p = tolower(*p);
if ( *p == '/' )
*p = '-';
p++;
}
pix = gnome_pixmap_file(filename);
g_free(filename);
if ( ! pix )
pix = gnome_pixmap_file("balsa/attachment.png");
return pix;
}
static void
display_part (BalsaMessage *bm, BODY * body)
{
BalsaPartInfo *info = NULL;
gchar *pix = NULL;
gchar *content_type = NULL;
gchar *icon_title = NULL;
gint pos;
GtkWidget *menu_item;
if ( is_multipart(body) ) {
if (balsa_app.debug)
fprintf (stderr, "part: multipart\n");
display_multipart (bm, body);
if (balsa_app.debug)
fprintf (stderr, "part end: multipart\n");
return; /* we don't want a multipart icon */
}
bm->part_count++;
info = (BalsaPartInfo*)g_new0(BalsaPartInfo, 1);
info->body = body;
info->message = bm->message;
info->popup_menu = gtk_menu_new();
info->can_display = FALSE;
switch (body->type)
{
case TYPEOTHER:
if (balsa_app.debug)
fprintf (stderr, "part: other\n");
part_info_init_other (bm, info);
break;
case TYPEAUDIO:
if (balsa_app.debug)
fprintf (stderr, "part: audio\n");
part_info_init_audio (bm, info);
break;
case TYPEAPPLICATION:
if (balsa_app.debug){
fprintf (stderr, "part: application\n");
fprintf (stderr, "subtype: %s\n", body->subtype);
}
part_info_init_application(bm, info);
break;
case TYPEIMAGE:
if (balsa_app.debug)
fprintf (stderr, "part: image\n");
part_info_init_image (bm, info);
break;
case TYPEMESSAGE:
if (balsa_app.debug)
fprintf (stderr, "part: message\n");
part_info_init_message (bm, info);
fprintf (stderr, "part end: multipart\n");
break;
case TYPEMULTIPART:
g_print ("Got a TYPEMULTIPART in part2canvas!\n");
break;
case TYPETEXT:
if (balsa_app.debug)
fprintf (stderr, "part: text\n");
part_info_init_mimetext (bm, info);
break;
case TYPEVIDEO:
if (balsa_app.debug)
fprintf (stderr, "part: video\n");
part_info_init_video (bm, info);
break;
}
if ( body->subtype )
content_type = g_strdup_printf("%s/%s", TYPE(body), body->subtype);
else
content_type = g_strdup(TYPE(body));
if ( body->filename )
icon_title = g_strdup_printf("%s (%s)", body->filename, content_type);
else
icon_title = g_strdup_printf("(%s)", content_type);
if ( ! pix )
pix = find_part_icon (info, content_type);
if ( balsa_app.debug ) {
PARAMETER *param = body->parameter;
g_print ("content_type = %s xtype = %s subtype = %s\n",
content_type, body->xtype, body->subtype);
g_print ("description = %s\n", body->description);
g_print ("form_name = %s\n", body->form_name);
g_print ("filename = %s\n", body->filename);
g_print ("d_filename = %s\n", body->d_filename);
g_print ("content-paramters: \n");
while (param) {
g_print ("%s = %s\n", param->attribute, param->value);
param = param->next;
}
g_print ("\n");
}
menu_item = gtk_menu_item_new_with_label( _("Save") );
gtk_menu_append(GTK_MENU(info->popup_menu), menu_item);
gtk_widget_show(menu_item);
gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
(GtkSignalFunc)part_context_menu_save, (gpointer)info);
pos = gnome_icon_list_append ( GNOME_ICON_LIST(bm->part_list),
pix, icon_title);
/* The widget is unref'd in free_icon_data */
if ( info->widget )
gtk_object_ref(GTK_OBJECT(info->widget));
gnome_icon_list_set_icon_data_full (GNOME_ICON_LIST(bm->part_list), pos,
info,
free_icon_data);
g_free(content_type);
g_free(icon_title);
g_free(pix);
}
static void
display_content (BalsaMessage *bm)
{
GList *body_list;
Body *body;
body_list = bm->message->body_list;
while (body_list)
{
body = (Body *) body_list->data;
display_part (bm, body->mutt_body);
body_list = g_list_next (body_list);
}
}
static gchar *
save_mime_part (Message * message, BODY * body)
{
gchar msg_filename[PATH_MAX + 1];
STATE s;
msg_filename[0] = '\0';
switch (message->mailbox->type)
{
case MAILBOX_MH:
case MAILBOX_MAILDIR:
snprintf(msg_filename, PATH_MAX, "%s/%s",
MAILBOX_LOCAL (message->mailbox)->path,
message_pathname (message));
s.fpin = fopen(msg_filename, "r");
break;
case MAILBOX_IMAP:
case MAILBOX_POP3:
s.fpin = fopen (MAILBOX_IMAP (message->mailbox)->tmp_file_path, "r");
break;
default:
s.fpin = fopen (MAILBOX_LOCAL (message->mailbox)->path, "r");
break;
}
mutt_mktemp (msg_filename);
s.prefix = 0;
if( (s.fpout = fopen (msg_filename, "w")) == NULL) {
return NULL;
}
fseek (s.fpin, body->offset, 0);
if (!s.fpout)
{
/* error */
}
mutt_decode_attachment (body, &s);
fclose (s.fpin);
fclose (s.fpout);
return g_strdup(msg_filename);
}
static void free_icon_data(gpointer data)
{
BalsaPartInfo *info = (BalsaPartInfo*)data;
if ( info == NULL )
return;
if ( info->widget )
gtk_object_unref(GTK_OBJECT(info->widget));
if ( info->popup_menu )
gtk_object_unref(GTK_OBJECT(info->popup_menu));
/* if ( info->hadj ) */
/* gtk_object_unref(GTK_OBJECT(info->hadj)); */
/* if ( info->vadj ) */
/* gtk_object_unref(GTK_OBJECT(info->vadj)); */
g_free(data);
}
static void
part_context_menu_save(GtkWidget *menu_item, BalsaPartInfo *info)
{
save_part(info);
}
void
balsa_message_next_part (BalsaMessage *bmessage)
{
GnomeIconList *gil;
gint index = 0;
g_return_if_fail(bmessage != NULL);
g_return_if_fail(bmessage->part_list != NULL);
gil = GNOME_ICON_LIST(bmessage->part_list);
if ( gil->icons == 0 || gil->icons == 1 )
return;
if ( gil->selection ) {
index = (gint)(gil->selection->data);
if ( ++index >= gil->icons )
index = 0;
}
gnome_icon_list_select_icon(gil, index);
select_part(bmessage, index);
}
void
balsa_message_previous_part (BalsaMessage *bmessage)
{
GnomeIconList *gil;
gint index = 0;
g_return_if_fail(bmessage != NULL);
g_return_if_fail(bmessage->part_list != NULL);
gil = GNOME_ICON_LIST(bmessage->part_list);
if ( gil->icons == 0 || gil->icons == 1)
return;
if ( gil->selection ) {
index = (gint)(gil->selection->data);
if ( --index < 0 )
index = gil->icons - 1;
}
gnome_icon_list_select_icon(gil, index);
select_part(bmessage, index);
}
/*
* If part == -1 then change to no part
*/
static void
select_part(BalsaMessage *bm, gint part)
{
BalsaPartInfo *info;
if ( bm->current_part && bm->current_part->widget ) {
gtk_widget_hide(bm->current_part->widget);
gtk_container_remove(GTK_CONTAINER(bm->table), bm->current_part->widget);
}
if ( part != -1 ) {
info = (BalsaPartInfo*)gnome_icon_list_get_icon_data
(GNOME_ICON_LIST(bm->part_list), part);
if ( info && info->widget ) {
gtk_widget_show(info->widget);
gtk_table_attach(GTK_TABLE(bm->table), info->widget, 0, 1, 1, 2, GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 1);
} else {
/* HACK! This is a spacer, so that the attachment icons will stay at
the bottom of the window.
*/
GtkWidget *box;
box = gtk_hbox_new(FALSE, FALSE);
gtk_widget_show(box);
gtk_table_attach(GTK_TABLE(bm->table), box, 0, 1, 1, 2,
GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
}
bm->current_part = info;
} else {
bm->current_part = NULL;
}
scroll_set(GTK_VIEWPORT(bm)->hadjustment, 0);
scroll_set(GTK_VIEWPORT(bm)->vadjustment, 0);
gtk_widget_queue_resize(GTK_WIDGET(bm));
}
static void
scroll_set (GtkAdjustment* adj, gint value)
{
gfloat upper;
if ( !adj )
return;
adj->value = value;
upper = adj->upper - adj->page_size;
adj->value = MIN (adj->value, upper);
adj->value = MAX (adj->value, 0.0);
gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
}
static void
scroll_change (GtkAdjustment* adj, gint diff)
{
gfloat upper;
adj->value += diff;
upper = adj->upper - adj->page_size;
adj->value = MIN (adj->value, upper);
adj->value = MAX (adj->value, 0.0);
gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
}
static gint
balsa_message_key_press_event(GtkWidget *widget, GdkEventKey *event, BalsaMessage *bm)
{
GtkViewport *viewport;
viewport = GTK_VIEWPORT(bm);
switch (event->keyval) {
case GDK_Up: scroll_change(viewport->vadjustment,
-viewport->vadjustment->step_increment);
break;
case GDK_Down: scroll_change(viewport->vadjustment,
viewport->vadjustment->step_increment);
break;
case GDK_Page_Up: scroll_change(viewport->vadjustment,
-viewport->vadjustment->page_increment);
break;
case GDK_Page_Down: scroll_change(viewport->vadjustment,
viewport->vadjustment->page_increment);
break;
case GDK_Home:
if (event->state & GDK_CONTROL_MASK)
scroll_change(viewport->vadjustment, -viewport->vadjustment->value);
else
return FALSE;
break;
case GDK_End:
if (event->state & GDK_CONTROL_MASK)
scroll_change(viewport->vadjustment, viewport->vadjustment->upper);
else
return FALSE;
break;
default: return FALSE;
}
return TRUE;
}
static void
balsa_gtk_text_size_request (GtkWidget *widget, GtkRequisition *requisition, gpointer data)
{
GtkText *text;
g_return_if_fail(widget != NULL);
g_return_if_fail(GTK_IS_TEXT (widget));
g_return_if_fail(requisition != NULL);
text = GTK_TEXT(widget);
requisition->width = ( widget->style->klass->xthickness + 1 ) * 2;
requisition->height = ( widget->style->klass->ythickness + 1 ) * 2;
requisition->width += text->hadj->upper;
requisition->height += text->vadj->upper;
}
static void
balsa_icon_list_size_request (GtkWidget *widget, GtkRequisition *requisition, gpointer data)
{
GnomeIconList *gil;
g_return_if_fail(widget != NULL);
g_return_if_fail(GNOME_IS_ICON_LIST (widget));
g_return_if_fail(requisition != NULL);
gil = GNOME_ICON_LIST(widget);
requisition->width = ( GTK_CONTAINER (widget)->border_width + widget->style->klass->xthickness + 1 ) * 2;
requisition->height = ( GTK_CONTAINER (widget)->border_width + widget->style->klass->ythickness + 1 ) * 2;
/* requisition->width = gil->hadj->upper; */
requisition->height += gil->adj->upper;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]