remove reference to gtkxtext.h
[claws.git] / src / messageview.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "defs.h"
21
22 #include <glib.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkvbox.h>
25 #include <gtk/gtkcontainer.h>
26 #include <gtk/gtkeditable.h>
27 #include <gtk/gtkwindow.h>
28 #include <gtk/gtktext.h>
29 #include <stdio.h>
30 #include <ctype.h>
31 #include <string.h>
32
33 #include "intl.h"
34 #include "main.h"
35 #include "messageview.h"
36 #include "headerview.h"
37 #include "summaryview.h"
38 #include "textview.h"
39 #include "imageview.h"
40 #include "mimeview.h"
41 #include "procmsg.h"
42 #include "procheader.h"
43 #include "procmime.h"
44 #include "prefs_common.h"
45 #include "gtkutils.h"
46 #include "utils.h"
47 #include "rfc2015.h"
48 #include "about.h"
49 #include "account.h"
50 #include "alertpanel.h"
51 #include "send.h"
52 #include "pgptext.h"
53 #include "menu.h"
54 #include "stock_pixmap.h"
55
56
57 static void messageview_change_view_type(MessageView    *messageview,
58                                          MessageType     type);
59 static void messageview_destroy_cb      (GtkWidget      *widget,
60                                          MessageView    *messageview);
61 static void messageview_size_allocate_cb(GtkWidget      *widget,
62                                          GtkAllocation  *allocation);
63 static void key_pressed                 (GtkWidget      *widget,
64                                          GdkEventKey    *event,
65                                          MessageView    *messageview);
66
67 static void return_receipt_show         (NoticeView     *noticeview, 
68                                          MsgInfo        *msginfo);      
69 static void return_receipt_send_clicked (NoticeView     *noticeview, 
70                                          MsgInfo        *msginfo);
71
72 static PrefsAccount *select_account_from_list
73                                         (GList          *ac_list);
74
75 static void messageview_menubar_cb      (MessageView    *msgview,
76                                          guint           action, 
77                                          GtkWidget      *widget);
78 static void messageview_delete_cb       (MessageView    *msgview, 
79                                          guint           action, 
80                                          GtkWidget      *widget);                                        
81 static void messageview_close_cb        (gpointer        data,
82                                          guint           action,
83                                          GtkWidget      *widget);
84 static void messageview_update          (MessageView *msgview);
85 static void messageview_update_all      (MessageView *msgview);
86
87 static GList *msgview_list = NULL;
88
89 MessageView *messageview_create(MainWindow *mainwin)
90 {
91         MessageView *messageview;
92         GtkWidget *vbox;
93         HeaderView *headerview;
94         TextView *textview;
95         ImageView *imageview;
96         MimeView *mimeview;
97         NoticeView *noticeview;
98
99         debug_print("Creating message view...\n");
100         messageview = g_new0(MessageView, 1);
101
102         messageview->type = MVIEW_TEXT;
103
104         headerview = headerview_create();
105
106         noticeview = noticeview_create(mainwin);
107
108         textview = textview_create();
109         textview->messageview = messageview;
110
111         imageview = imageview_create();
112         imageview->messageview = messageview;
113
114         mimeview = mimeview_create();
115         mimeview->textview = textview_create();
116         mimeview->textview->messageview = messageview;
117         mimeview->imageview = imageview;
118         mimeview->messageview = messageview;
119
120         vbox = gtk_vbox_new(FALSE, 0);
121         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(headerview),
122                            FALSE, FALSE, 0);
123         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(noticeview),
124                            FALSE, FALSE, 0);
125         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(textview),
126                            TRUE, TRUE, 0);
127
128         /* to remove without destroyed */
129         gtk_widget_ref(GTK_WIDGET_PTR(textview));
130         gtk_widget_ref(GTK_WIDGET_PTR(imageview));
131         gtk_widget_ref(GTK_WIDGET_PTR(mimeview));
132         gtk_widget_ref(GTK_WIDGET_PTR(mimeview->textview));
133
134         messageview->vbox       = vbox;
135         messageview->new_window = FALSE;
136         messageview->window     = NULL;
137         messageview->headerview = headerview;
138         messageview->textview   = textview;
139         messageview->imageview  = imageview;
140         messageview->mimeview   = mimeview;
141         messageview->noticeview = noticeview;
142         messageview->mainwin    = mainwin;
143
144         return messageview;
145 }
146
147 static GtkItemFactoryEntry messageview_entries[] =
148 {
149         {N_("/_File"),                          NULL, NULL, 0, "<Branch>"},
150         {N_("/_File/---"),                      NULL, NULL, 0, "<Separator>"},
151         {N_("/_File/_Close"),                   "<control>W", messageview_close_cb, 0, NULL},
152
153         {N_("/_Message"),                       NULL, NULL, 0, "<Branch>"},
154         {N_("/_Message/_Reply"),                "<control>R",   messageview_menubar_cb, COMPOSE_REPLY, NULL},
155         {N_("/_Message/Repl_y to"),             NULL, NULL, 0, "<Branch>"},
156         {N_("/_Message/Repl_y to/_all"),        "<shift><control>R", messageview_menubar_cb, COMPOSE_REPLY_TO_ALL, NULL},
157         {N_("/_Message/Repl_y to/_sender"),     NULL, messageview_menubar_cb, COMPOSE_REPLY_TO_SENDER, NULL},
158         {N_("/_Message/Repl_y to/mailing _list"),
159                                                 "<control>L", messageview_menubar_cb, COMPOSE_REPLY_TO_LIST, NULL},
160 /*      {N_("/_Message/Follow-up and reply to"),NULL, messageview_menubar_cb, COMPOSE_FOLLOWUP_AND_REPLY_TO, NULL}, */
161         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
162         {N_("/_Message/_Forward"),              "<control><alt>F", messageview_menubar_cb, COMPOSE_FORWARD, NULL},
163         {N_("/_Message/Redirect"),              NULL, messageview_menubar_cb, COMPOSE_REDIRECT, NULL},
164         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
165         {N_("/_Message/_Delete"),               "<control>D", messageview_delete_cb,  0, NULL},
166         
167         {N_("/_Help"),                          NULL, NULL, 0, "<Branch>"},
168         {N_("/_Help/_About"),                   NULL, about_show, 0, NULL}
169 };
170
171
172 GList *messageview_get_msgview_list(void)
173 {
174         return msgview_list;
175 }
176
177 void messageview_add_toolbar(MessageView *msgview, GtkWidget *window) 
178 {
179         GtkWidget *handlebox;
180         GtkWidget *vbox;
181         GtkWidget *menubar;
182         guint n_menu_entries;
183
184         vbox = gtk_vbox_new(FALSE, 0);
185         gtk_widget_show(vbox);
186         gtk_container_add(GTK_CONTAINER(window), vbox); 
187         
188         n_menu_entries = sizeof(messageview_entries) / sizeof(messageview_entries[0]);
189         menubar = menubar_create(window, messageview_entries,
190                                  n_menu_entries, "<MessageView>", msgview);
191         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
192
193         handlebox = gtk_handle_box_new();
194         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
195         msgview->toolbar = toolbar_create(TOOLBAR_MSGVIEW, handlebox,
196                                           (gpointer)msgview);
197         msgview->handlebox = handlebox;
198         msgview->menubar   = menubar;
199
200         gtk_container_add(GTK_CONTAINER(vbox),
201                           GTK_WIDGET_PTR(msgview));
202
203         msgview_list = g_list_append(msgview_list, msgview);
204 }
205
206 MessageView *messageview_create_with_new_window(MainWindow *mainwin)
207 {
208         GtkWidget *window;
209         MessageView *msgview;
210
211         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
212         gtk_window_set_title(GTK_WINDOW(window), _("Sylpheed - Message View"));
213         gtk_window_set_wmclass(GTK_WINDOW(window), "message_view", "Sylpheed");
214         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
215         gtk_widget_set_usize(window, prefs_common.msgwin_width,
216                              prefs_common.msgwin_height);
217
218         msgview = messageview_create(mainwin);
219
220         gtk_signal_connect(GTK_OBJECT(window), "size_allocate",
221                            GTK_SIGNAL_FUNC(messageview_size_allocate_cb),
222                            msgview);
223         gtk_signal_connect(GTK_OBJECT(window), "destroy",
224                            GTK_SIGNAL_FUNC(messageview_destroy_cb), msgview);
225         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
226                            GTK_SIGNAL_FUNC(key_pressed), msgview);
227
228         messageview_add_toolbar(msgview, window);
229
230         gtk_widget_grab_focus(msgview->textview->text);
231         gtk_widget_show_all(window);
232
233         msgview->new_window = TRUE;
234         msgview->window = window;
235         msgview->visible = TRUE;
236
237         toolbar_set_style(msgview->toolbar->toolbar, msgview->handlebox, 
238                           prefs_common.toolbar_style);
239         messageview_init(msgview);
240
241         return msgview;
242 }
243
244 void messageview_init(MessageView *messageview)
245 {
246         headerview_init(messageview->headerview);
247         textview_init(messageview->textview);
248         imageview_init(messageview->imageview);
249         mimeview_init(messageview->mimeview);
250         /*messageview_set_font(messageview);*/
251
252         noticeview_hide(messageview->noticeview);
253 }
254
255 static void notification_convert_header(gchar *dest, gint len, 
256                                         const gchar *src_,
257                                         gint header_len)
258 {
259         char *src;
260
261         g_return_if_fail(src_ != NULL);
262         g_return_if_fail(dest != NULL);
263
264         if (len < 1) return;
265
266         Xstrndup_a(src, src_, len, return);
267
268         remove_return(src);
269
270         if (is_ascii_str(src)) {
271                 strncpy2(dest, src, len);
272                 dest[len - 1] = '\0';
273                 return;
274         } else
275                 conv_encode_header(dest, len, src, header_len);
276 }
277
278 static gint disposition_notification_queue(PrefsAccount * account,
279                                            gchar * to, const gchar *file)
280 {
281         FolderItem *queue;
282         gchar *tmp;
283         FILE *fp, *src_fp;
284         gchar buf[BUFFSIZE];
285         gint num;
286
287         debug_print("queueing message...\n");
288         g_return_val_if_fail(account != NULL, -1);
289
290         tmp = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
291                               G_DIR_SEPARATOR, (gint)file);
292         if ((fp = fopen(tmp, "wb")) == NULL) {
293                 FILE_OP_ERROR(tmp, "fopen");
294                 g_free(tmp);
295                 return -1;
296         }
297         if ((src_fp = fopen(file, "rb")) == NULL) {
298                 FILE_OP_ERROR(file, "fopen");
299                 fclose(fp);
300                 unlink(tmp);
301                 g_free(tmp);
302                 return -1;
303         }
304         if (change_file_mode_rw(fp, tmp) < 0) {
305                 FILE_OP_ERROR(tmp, "chmod");
306                 g_warning("can't change file mode\n");
307         }
308
309         /* queueing variables */
310         fprintf(fp, "AF:\n");
311         fprintf(fp, "NF:0\n");
312         fprintf(fp, "PS:10\n");
313         fprintf(fp, "SRH:1\n");
314         fprintf(fp, "SFN:\n");
315         fprintf(fp, "DSR:\n");
316         fprintf(fp, "MID:\n");
317         fprintf(fp, "CFG:\n");
318         fprintf(fp, "PT:0\n");
319         fprintf(fp, "S:%s\n", account->address);
320         fprintf(fp, "RQ:\n");
321         if (account->smtp_server)
322                 fprintf(fp, "SSV:%s\n", account->smtp_server);
323         else
324                 fprintf(fp, "SSV:\n");
325         if (account->nntp_server)
326                 fprintf(fp, "NSV:%s\n", account->nntp_server);
327         else
328                 fprintf(fp, "NSV:\n");
329         fprintf(fp, "SSH:\n");
330         fprintf(fp, "R:<%s>", to);
331         fprintf(fp, "\n");
332         fprintf(fp, "\n");
333
334         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
335                 if (fputs(buf, fp) == EOF) {
336                         FILE_OP_ERROR(tmp, "fputs");
337                         fclose(fp);
338                         fclose(src_fp);
339                         unlink(tmp);
340                         g_free(tmp);
341                         return -1;
342                 }
343         }
344
345         fclose(src_fp);
346         if (fclose(fp) == EOF) {
347                 FILE_OP_ERROR(tmp, "fclose");
348                 unlink(tmp);
349                 g_free(tmp);
350                 return -1;
351         }
352
353         queue = folder_get_default_queue();
354         if ((num = folder_item_add_msg(queue, tmp, TRUE)) < 0) {
355                 g_warning("can't queue the message\n");
356                 unlink(tmp);
357                 g_free(tmp);
358                 return -1;
359         }
360         g_free(tmp);
361
362         return 0;
363 }
364
365 static gint disposition_notification_send(MsgInfo *msginfo)
366 {
367         gchar buf[BUFFSIZE];
368         gchar tmp[MAXPATHLEN + 1];
369         FILE *fp;
370         GSList *to_list;
371         GList *ac_list;
372         PrefsAccount *account;
373         gint ok;
374         gchar *to;
375
376         if ((!msginfo->returnreceiptto) && 
377             (!msginfo->dispositionnotificationto)) 
378                 return -1;
379
380         /* RFC2298: Test for Return-Path */
381         if (msginfo->dispositionnotificationto)
382                 to = msginfo->dispositionnotificationto;
383         else
384                 to = msginfo->returnreceiptto;
385
386         ok = get_header_from_msginfo(msginfo, buf, sizeof(buf),
387                                 "Return-Path:");
388         if (ok == 0) {
389                 gchar *to_addr = g_strdup(to);
390                 extract_address(to_addr);
391                 extract_address(buf);
392                 ok = strcmp(to_addr, buf);
393                 g_free(to_addr);
394         } else {
395                 strncpy(buf, _("<No Return-Path found>"), 
396                                 sizeof(buf));
397         }
398         
399         if (ok != 0) {
400                 AlertValue val;
401                 gchar *message;
402                 message = g_strdup_printf(
403                                  _("The notification address to which the "
404                                    "return receipt is to be sent\n"
405                                    "does not correspond to the return path:\n"
406                                    "Notification address: %s\n"
407                                    "Return path: %s\n"
408                                    "It is advised to not to send the return "
409                                    "receipt."), to, buf);
410                 val = alertpanel(_("Warning"), message, _("Send"),
411                                 _("+Don't Send"), NULL);
412                 if (val != G_ALERTDEFAULT)
413                         return -1;
414         }
415
416         ac_list = account_find_all_from_address(NULL, msginfo->to);
417         ac_list = account_find_all_from_address(ac_list, msginfo->cc);
418
419         if (ac_list == NULL) {
420                 alertpanel_error(_("This message is asking for a return "
421                                    "receipt notification\n"
422                                    "but according to its 'To:' and 'CC:' "
423                                    "headers it was not\nofficially addressed "
424                                    "to you.\n"
425                                    "Receipt notification cancelled."));
426                 return -1;
427         }
428
429         if (g_list_length(ac_list) > 1)
430                 account = select_account_from_list(ac_list);
431         else
432                 account = (PrefsAccount *) ac_list->data;
433         g_list_free(ac_list);
434
435         if (account == NULL)
436                 return -1;
437
438         /* write to temporary file */
439         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
440                    get_rc_dir(), G_DIR_SEPARATOR, (gint)msginfo);
441
442         if ((fp = fopen(tmp, "wb")) == NULL) {
443                 FILE_OP_ERROR(tmp, "fopen");
444                 return -1;
445         }
446
447         /* chmod for security */
448         if (change_file_mode_rw(fp, tmp) < 0) {
449                 FILE_OP_ERROR(tmp, "chmod");
450                 g_warning("can't change file mode\n");
451         }
452
453         /* Date */
454         get_rfc822_date(buf, sizeof(buf));
455         fprintf(fp, "Date: %s\n", buf);
456
457         /* From */
458         if (account->name && *account->name) {
459                 notification_convert_header
460                         (buf, sizeof(buf), account->name,
461                          strlen("From: "));
462                 fprintf(fp, "From: %s <%s>\n", buf, account->address);
463         } else
464                 fprintf(fp, "From: %s\n", account->address);
465
466         fprintf(fp, "To: %s\n", to);
467
468         /* Subject */
469         notification_convert_header(buf, sizeof(buf), msginfo->subject,
470                                     strlen("Subject: "));
471         fprintf(fp, "Subject: Disposition notification: %s\n", buf);
472
473         if (fclose(fp) == EOF) {
474                 FILE_OP_ERROR(tmp, "fclose");
475                 unlink(tmp);
476                 return -1;
477         }
478
479         to_list = address_list_append(NULL, to);
480         ok = send_message(tmp, account, to_list);
481         
482         if (ok < 0) {
483                 if (prefs_common.queue_msg) {
484                         AlertValue val;
485                         
486                         val = alertpanel
487                                 (_("Queueing"),
488                                  _("Error occurred while sending the notification.\n"
489                                    "Put this notification into queue folder?"),
490                                  _("OK"), _("Cancel"), NULL);
491                         if (G_ALERTDEFAULT == val) {
492                                 ok = disposition_notification_queue(account, to, tmp);
493                                 if (ok < 0)
494                                         alertpanel_error(_("Can't queue the notification."));
495                         }
496                 } else
497                         alertpanel_error_log(_("Error occurred while sending the notification."));
498         }
499
500         if (unlink(tmp) < 0) FILE_OP_ERROR(tmp, "unlink");
501
502         return ok;
503 }
504
505 void messageview_show(MessageView *messageview, MsgInfo *msginfo,
506                       gboolean all_headers)
507 {
508         FILE *fp;
509         gchar *file;
510         MimeInfo *mimeinfo;
511         MsgInfo *tmpmsginfo;
512
513         g_return_if_fail(msginfo != NULL);
514         messageview->msginfo = msginfo;
515
516 #if USE_GPGME
517         if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
518                 return;
519 #else /* !USE_GPGME */
520         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
521         mimeinfo = procmime_scan_mime_header(fp);
522 #endif /* USE_GPGME */
523         fclose(fp);
524         if (!mimeinfo) return;
525
526         file = procmsg_get_message_file_path(msginfo);
527         if (!file) {
528                 g_warning("can't get message file path.\n");
529                 procmime_mimeinfo_free_all(mimeinfo);
530                 return;
531         }
532
533         tmpmsginfo = procheader_parse_file(file, msginfo->flags, TRUE, TRUE);
534
535         headerview_show(messageview->headerview, tmpmsginfo);
536         procmsg_msginfo_free(tmpmsginfo);
537
538         messageview->all_headers = all_headers;
539         textview_set_all_headers(messageview->textview, all_headers);
540         textview_set_all_headers(messageview->mimeview->textview, all_headers);
541
542         if (mimeinfo->mime_type != MIME_TEXT &&
543             mimeinfo->mime_type != MIME_TEXT_HTML) {
544                 messageview_change_view_type(messageview, MVIEW_MIME);
545                 mimeview_show_message(messageview->mimeview, mimeinfo, file);
546         } else {
547                 messageview_change_view_type(messageview, MVIEW_TEXT);
548                 textview_show_message(messageview->textview, mimeinfo, file);
549                 procmime_mimeinfo_free_all(mimeinfo);
550         }
551
552         if (MSG_IS_RETRCPT_PENDING(msginfo->flags))
553                 return_receipt_show(messageview->noticeview, msginfo);
554         else 
555                 noticeview_hide(messageview->noticeview);
556
557         g_free(file);
558 }
559
560 static void messageview_change_view_type(MessageView *messageview,
561                                          MessageType type)
562 {
563         TextView *textview = messageview->textview;
564         MimeView *mimeview = messageview->mimeview;
565
566         if (messageview->type == type) return;
567
568         if (type == MVIEW_MIME) {
569                 gtkut_container_remove
570                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
571                          GTK_WIDGET_PTR(textview));
572                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
573                                    GTK_WIDGET_PTR(mimeview), TRUE, TRUE, 0);
574                 gtk_container_add(GTK_CONTAINER(mimeview->vbox),
575                                   GTK_WIDGET_PTR(textview));
576         } else if (type == MVIEW_TEXT) {
577                 gtkut_container_remove
578                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
579                          GTK_WIDGET_PTR(mimeview));
580
581                 if (mimeview->vbox == GTK_WIDGET_PTR(textview)->parent)
582                         gtkut_container_remove(GTK_CONTAINER(mimeview->vbox),
583                                                GTK_WIDGET_PTR(textview));
584
585                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
586                                    GTK_WIDGET_PTR(textview), TRUE, TRUE, 0);
587         } else
588                 return;
589
590         messageview->type = type;
591 }
592
593 void messageview_reflect_prefs_pixmap_theme(void)
594 {
595         GList *cur;
596         MessageView *msgview;
597
598         for (cur = msgview_list; cur != NULL; cur = cur->next) {
599                 msgview = (MessageView*)cur->data;
600                 toolbar_update(TOOLBAR_MSGVIEW, msgview);
601         }
602 }
603
604 void messageview_clear(MessageView *messageview)
605 {
606         messageview_change_view_type(messageview, MVIEW_TEXT);
607         headerview_clear(messageview->headerview);
608         textview_clear(messageview->textview);
609         imageview_clear(messageview->imageview);
610         noticeview_hide(messageview->noticeview);
611 }
612
613 void messageview_destroy(MessageView *messageview)
614 {
615         GtkWidget *textview  = GTK_WIDGET_PTR(messageview->textview);
616         GtkWidget *imageview = GTK_WIDGET_PTR(messageview->imageview);
617         GtkWidget *mimeview  = GTK_WIDGET_PTR(messageview->mimeview);
618
619         debug_print("destroy messageview\n");
620         headerview_destroy(messageview->headerview);
621         textview_destroy(messageview->textview);
622         imageview_destroy(messageview->imageview);
623         mimeview_destroy(messageview->mimeview);
624         noticeview_destroy(messageview->noticeview);
625
626         toolbar_clear_list(TOOLBAR_MSGVIEW);
627         if (messageview->toolbar) {
628                 toolbar_destroy(messageview->toolbar);
629                 g_free(messageview->toolbar);
630         }
631         
632         msgview_list = g_list_remove(msgview_list, messageview); 
633
634         g_free(messageview);
635
636         gtk_widget_unref(textview);
637         gtk_widget_unref(imageview);
638         gtk_widget_unref(mimeview);
639 }
640
641 void messageview_delete(MessageView *msgview)
642 {
643         MsgInfo *msginfo = (MsgInfo*)msgview->msginfo;
644         SummaryView *summaryview = (SummaryView*)msgview->mainwin->summaryview;
645         FolderItem *trash = folder_get_default_trash();
646         GSList *msg_list;
647
648         g_return_if_fail(msginfo != NULL);
649         g_return_if_fail(trash   != NULL);
650         
651         msg_list = folder_item_get_msg_list(msginfo->folder);
652         
653         if (msg_list == NULL) {
654                 alertpanel_error(_("Message already removed from folder."));
655                 return;
656         }
657         
658         for (; msg_list != NULL; msg_list = msg_list->next) {
659                 MsgInfo *msginfo_list = (MsgInfo*)msg_list->data;
660                 
661                 if (msginfo->msgnum == msginfo_list->msgnum) {
662
663                         if (prefs_common.immediate_exec)
664                                 folder_item_move_msg(trash, msginfo);
665                         else {
666                                 procmsg_msginfo_set_to_folder(msginfo, trash);
667                                 procmsg_msginfo_set_flags(msginfo, MSG_DELETED, 0);
668                                 /* NOTE: does not update to next message in summaryview
669                                  */
670                         }
671                                 
672                         messageview_update_all(msgview);
673                         break;
674                 }
675         }
676 }
677
678 /*      
679  * scan List of MessageViews checking whether there are any Views holding messages 
680  * which need to be updated (another view might have deleted the one this MessagView holds)
681  */
682 static void messageview_update_all(MessageView *msgview)
683 {
684         MsgInfo *msginfo = (MsgInfo*)msgview->msginfo;
685         GList *cur;
686         
687         g_return_if_fail(msginfo != NULL);
688
689         for (cur = msgview_list; cur != NULL; cur = cur->next) {
690                 MessageView *msgview = (MessageView*)cur->data;
691                 MsgInfo *msginfo_list = (MsgInfo*)msgview->msginfo;
692                 
693                 g_return_if_fail(msginfo != NULL);
694
695                 if (msginfo->msgnum == msginfo_list->msgnum)
696                         messageview_update(msgview);
697         }
698 }
699
700 /* 
701  * \brief update messageview with currently selected message in summaryview
702  *        leave unchanged if summaryview is empty
703  * \param pointer to MessageView
704  */     
705 static void messageview_update(MessageView *msgview)
706 {
707         SummaryView *summaryview = (SummaryView*)msgview->mainwin->summaryview;
708
709         g_return_if_fail(summaryview != NULL);
710         
711         if (summaryview->selected) {
712                 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
713                 MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, 
714                                                       summaryview->selected);
715                 g_return_if_fail(msginfo != NULL);
716
717                 messageview_show(msgview, msginfo, 
718                                  msgview->all_headers);
719         } 
720 }
721
722 void messageview_quote_color_set(void)
723 {
724 }
725
726 void messageview_set_font(MessageView *messageview)
727 {
728         textview_set_font(messageview->textview, NULL);
729 }
730
731 TextView *messageview_get_current_textview(MessageView *messageview)
732 {
733         TextView *text = NULL;
734
735         if (messageview->type == MVIEW_TEXT)
736                 text = messageview->textview;
737         else if (messageview->type == MVIEW_MIME) {
738                 if (gtk_notebook_get_current_page
739                         (GTK_NOTEBOOK(messageview->mimeview->notebook)) == 0)
740                         text = messageview->textview;
741                 else if (messageview->mimeview->type == MIMEVIEW_TEXT)
742                         text = messageview->mimeview->textview;
743         }
744
745         return text;
746 }
747
748 void messageview_copy_clipboard(MessageView *messageview)
749 {
750         TextView *text;
751
752         text = messageview_get_current_textview(messageview);
753         if (text)
754                 gtk_editable_copy_clipboard(GTK_EDITABLE(text->text));
755 }
756
757 void messageview_select_all(MessageView *messageview)
758 {
759         TextView *text;
760
761         text = messageview_get_current_textview(messageview);
762         if (text)
763                 gtk_editable_select_region(GTK_EDITABLE(text->text), 0, -1);
764 }
765
766 void messageview_set_position(MessageView *messageview, gint pos)
767 {
768         textview_set_position(messageview->textview, pos);
769 }
770
771 gboolean messageview_search_string(MessageView *messageview, const gchar *str,
772                                    gboolean case_sens)
773 {
774         return textview_search_string(messageview->textview, str, case_sens);
775         return FALSE;
776 }
777
778 gboolean messageview_search_string_backward(MessageView *messageview,
779                                             const gchar *str,
780                                             gboolean case_sens)
781 {
782         return textview_search_string_backward(messageview->textview,
783                                                str, case_sens);
784         return FALSE;
785 }
786
787 gboolean messageview_is_visible(MessageView *messageview)
788 {
789         return messageview->visible;
790 }
791
792 static void messageview_destroy_cb(GtkWidget *widget, MessageView *messageview)
793 {
794         messageview_destroy(messageview);
795 }
796
797 static void messageview_size_allocate_cb(GtkWidget *widget,
798                                          GtkAllocation *allocation)
799 {
800         g_return_if_fail(allocation != NULL);
801
802         prefs_common.msgwin_width  = allocation->width;
803         prefs_common.msgwin_height = allocation->height;
804 }
805
806 static void key_pressed(GtkWidget *widget, GdkEventKey *event,
807                         MessageView *messageview)
808 {
809         if (event && event->keyval == GDK_Escape && messageview->window)
810                 gtk_widget_destroy(messageview->window);
811 }
812
813 void messageview_toggle_view_real(MessageView *messageview)
814 {
815         MainWindow *mainwin = messageview->mainwin;
816         union CompositeWin *cwin = &mainwin->win;
817         GtkWidget *vpaned = NULL;
818         GtkWidget *container = NULL;
819         GtkItemFactory *ifactory = gtk_item_factory_from_widget(mainwin->menubar);
820         
821         switch (mainwin->type) {
822         case SEPARATE_NONE:
823                 vpaned = cwin->sep_none.vpaned;
824                 container = cwin->sep_none.hpaned;
825                 break;
826         case SEPARATE_FOLDER:
827                 vpaned = cwin->sep_folder.vpaned;
828                 container = mainwin->vbox_body;
829                 break;
830         case SEPARATE_MESSAGE:
831         case SEPARATE_BOTH:
832                 return;
833         }
834
835         if (vpaned->parent != NULL) {
836                 gtk_widget_ref(vpaned);
837                 gtkut_container_remove(GTK_CONTAINER(container), vpaned);
838                 gtk_widget_reparent(GTK_WIDGET_PTR(messageview), container);
839                 menu_set_sensitive(ifactory, "/View/Expand Summary View", FALSE);
840                 gtk_widget_grab_focus(GTK_WIDGET(messageview->textview->text));
841         } else {
842                 gtk_widget_reparent(GTK_WIDGET_PTR(messageview), vpaned);
843                 gtk_container_add(GTK_CONTAINER(container), vpaned);
844                 gtk_widget_unref(vpaned);
845                 menu_set_sensitive(ifactory, "/View/Expand Summary View", TRUE);
846                 gtk_widget_grab_focus(GTK_WIDGET(mainwin->summaryview->ctree));
847         }
848 }
849
850 static void return_receipt_show(NoticeView *noticeview, MsgInfo *msginfo)
851 {
852         noticeview_set_text(noticeview, _("This messages asks for a return receipt."));
853         noticeview_set_button_text(noticeview, _("Send receipt"));
854         noticeview_set_button_press_callback(noticeview,
855                                              GTK_SIGNAL_FUNC(return_receipt_send_clicked),
856                                              (gpointer) msginfo);
857         noticeview_show(noticeview);
858 }
859
860 static void return_receipt_send_clicked(NoticeView *noticeview, MsgInfo *msginfo)
861 {
862         MsgInfo *tmpmsginfo;
863         gchar *file;
864
865         file = procmsg_get_message_file_path(msginfo);
866         if (!file) {
867                 g_warning("can't get message file path.\n");
868                 return;
869         }
870
871         tmpmsginfo = procheader_parse_file(file, msginfo->flags, TRUE, TRUE);
872         tmpmsginfo->folder = msginfo->folder;
873         tmpmsginfo->msgnum = msginfo->msgnum;
874
875         if (disposition_notification_send(tmpmsginfo) >= 0) {
876                 procmsg_msginfo_unset_flags(msginfo, MSG_RETRCPT_PENDING, 0);
877                 noticeview_hide(noticeview);
878         }               
879
880         procmsg_msginfo_free(tmpmsginfo);
881         g_free(file);
882 }
883
884 static void select_account_cb(GtkWidget *w, gpointer data)
885 {
886         *(gint*)data = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(w)));
887 }
888         
889 static PrefsAccount *select_account_from_list(GList *ac_list)
890 {
891         GtkWidget *optmenu;
892         GtkWidget *menu;
893         gint account_id;
894
895         g_return_val_if_fail(ac_list != NULL, NULL);
896         g_return_val_if_fail(ac_list->data != NULL, NULL);
897         
898         optmenu = gtk_option_menu_new();
899         menu = gtkut_account_menu_new(ac_list, select_account_cb, &account_id);
900         if (!menu)
901                 return NULL;
902         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
903         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), 0);
904         account_id = ((PrefsAccount *) ac_list->data)->account_id;
905         if (alertpanel_with_widget(
906                                 _("Return Receipt Notification"),
907                                 _("The message was sent to several of your "
908                                   "accounts.\n"
909                                   "Please choose which account do you want to "
910                                   "use for sending the receipt notification:"),
911                                 _("Send Notification"), _("+Cancel"), NULL,
912                                 optmenu) != G_ALERTDEFAULT)
913                 return NULL;
914         return account_find_from_id(account_id);
915 }
916
917
918 /* 
919  * \brief return selected messageview text used by composing 
920  *        to reply to selected text only
921  *
922  * \param  pointer to Messageview 
923  *
924  * \return pointer to text (needs to be free'd by calling func)
925  */
926 gchar *messageview_get_selection(MessageView *msgview)
927 {
928         gchar *text = NULL;
929         
930         g_return_val_if_fail(msgview != NULL, NULL);
931
932         text = gtkut_editable_get_selection
933                 (GTK_EDITABLE(msgview->textview->text));
934         
935         if (!text && msgview->type == MVIEW_MIME
936             && msgview->mimeview->type == MIMEVIEW_TEXT
937             && msgview->mimeview->textview
938             && !msgview->mimeview->textview->default_text) {
939                 text = gtkut_editable_get_selection 
940                         (GTK_EDITABLE(msgview->mimeview->textview->text));   
941         }
942
943         return text;
944 }
945
946 static void messageview_delete_cb(MessageView *msgview, guint action, GtkWidget *widget)
947 {
948         messageview_delete(msgview);
949 }
950
951 static void messageview_menubar_cb(MessageView *msgview, guint action, GtkWidget *widget)
952 {
953         GSList *msginfo_list = NULL;
954         gchar *body;
955         MsgInfo *msginfo;
956
957         g_return_if_fail(msgview != NULL);
958
959         msginfo = (MsgInfo*)msgview->msginfo;
960         g_return_if_fail(msginfo != NULL);
961
962         msginfo_list = g_slist_append(msginfo_list, msginfo);
963         g_return_if_fail(msginfo_list);
964
965         body =  messageview_get_selection(msgview);
966         compose_reply_mode((ComposeMode)action, msginfo_list, body);
967         g_free(body);
968         g_slist_free(msginfo_list);
969 }
970
971 static void messageview_close_cb(gpointer data, guint action, GtkWidget *widget)
972 {
973         MessageView *messageview = (MessageView *)data;
974         
975         gtk_widget_destroy(messageview->window);
976 }