handle several messageviews correctly
[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 static void messageview_change_view_type(MessageView    *messageview,
57                                          MessageType     type);
58 static void messageview_destroy_cb      (GtkWidget      *widget,
59                                          MessageView    *messageview);
60 static void messageview_size_allocate_cb(GtkWidget      *widget,
61                                          GtkAllocation  *allocation);
62 static void key_pressed                 (GtkWidget      *widget,
63                                          GdkEventKey    *event,
64                                          MessageView    *messageview);
65 static void focus_in                    (GtkWidget      *widget, 
66                                          GdkEventFocus  *event,
67                                          gpointer        data);
68 static void messageview_toolbar_create  (MessageView    *messageview,
69                                          GtkWidget      *container);
70 static void toolbar_messageview_buttons_cb  (GtkWidget      *widget, 
71                                              ToolbarItem    *item);
72
73 static void return_receipt_show         (NoticeView     *noticeview, 
74                                          MsgInfo        *msginfo);      
75 static void return_receipt_send_clicked (NoticeView     *noticeview, 
76                                          MsgInfo        *msginfo);
77
78 static PrefsAccount *select_account_from_list
79                                         (GList          *ac_list);
80
81 static void messageview_reply_cb        (gpointer        data,
82                                          guint           action,
83                                          GtkWidget      *widget);
84                                          
85 static void messageview_delete_cb       (gpointer        data,
86                                          guint           action,
87                                          GtkWidget      *widget);
88                                          
89 static void messageview_close_cb        (gpointer        data,
90                                          guint           action,
91                                          GtkWidget      *widget);
92
93 static void set_toolbar_style(MessageView *messageview);
94
95
96 MessageView *messageview_create(MainWindow *mainwin)
97 {
98         MessageView *messageview;
99         GtkWidget *vbox;
100         HeaderView *headerview;
101         TextView *textview;
102         ImageView *imageview;
103         MimeView *mimeview;
104         NoticeView *noticeview;
105
106         debug_print("Creating message view...\n");
107         messageview = g_new0(MessageView, 1);
108
109         messageview->type = MVIEW_TEXT;
110
111         headerview = headerview_create();
112
113         noticeview = noticeview_create(mainwin);
114
115         textview = textview_create();
116         textview->messageview = messageview;
117
118         imageview = imageview_create();
119         imageview->messageview = messageview;
120
121         mimeview = mimeview_create();
122         mimeview->textview = textview_create();
123         mimeview->textview->messageview = messageview;
124         mimeview->imageview = imageview;
125         mimeview->messageview = messageview;
126
127         vbox = gtk_vbox_new(FALSE, 0);
128         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(headerview),
129                            FALSE, FALSE, 0);
130         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(noticeview),
131                            FALSE, FALSE, 0);
132         gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET_PTR(textview),
133                            TRUE, TRUE, 0);
134
135         /* to remove without destroyed */
136         gtk_widget_ref(GTK_WIDGET_PTR(textview));
137         gtk_widget_ref(GTK_WIDGET_PTR(imageview));
138         gtk_widget_ref(GTK_WIDGET_PTR(mimeview));
139         gtk_widget_ref(GTK_WIDGET_PTR(mimeview->textview));
140
141         messageview->vbox       = vbox;
142         messageview->new_window = FALSE;
143         messageview->window     = NULL;
144         messageview->headerview = headerview;
145         messageview->textview   = textview;
146         messageview->imageview  = imageview;
147         messageview->mimeview   = mimeview;
148         messageview->noticeview = noticeview;
149         messageview->mainwin    = mainwin;
150
151         return messageview;
152 }
153
154 static GtkItemFactoryEntry messageview_entries[] =
155 {
156         {N_("/_File"),                          NULL, NULL, 0, "<Branch>"},
157         {N_("/_File/---"),                      NULL, NULL, 0, "<Separator>"},
158         {N_("/_File/_Close"),                   "<control>W", messageview_close_cb, 0, NULL},
159
160         {N_("/_Message"),               NULL, NULL, 0, "<Branch>"},
161         {N_("/_Message/_Reply"),                "<control>R",   messageview_reply_cb, COMPOSE_REPLY, NULL},
162         {N_("/_Message/Repl_y to"),             NULL, NULL, 0, "<Branch>"},
163         {N_("/_Message/Repl_y to/_all"),        "<shift><control>R", messageview_reply_cb, COMPOSE_REPLY_TO_ALL, NULL},
164         {N_("/_Message/Repl_y to/_sender"),     NULL, messageview_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
165         {N_("/_Message/Repl_y to/mailing _list"),
166                                                 "<control>L", messageview_reply_cb, COMPOSE_REPLY_TO_LIST, NULL},
167         {N_("/_Message/Follow-up and reply to"),NULL, messageview_reply_cb, COMPOSE_FOLLOWUP_AND_REPLY_TO, NULL},
168         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
169         {N_("/_Message/_Delete"),               "<control>D", messageview_delete_cb,  0, NULL},
170         
171         {N_("/_Help"),                  NULL, NULL, 0, "<Branch>"},
172         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
173 };
174
175
176
177 MessageView *messageview_create_with_new_window(MainWindow *mainwin)
178 {
179         GtkWidget *window;
180         MessageView *msgview;
181         GtkWidget *vbox;
182         GtkWidget *menubar;
183         GtkWidget *handlebox;
184
185         guint n_menu_entries;
186
187         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
188         gtk_window_set_title(GTK_WINDOW(window), _("Sylpheed - Message View"));
189         gtk_window_set_wmclass(GTK_WINDOW(window), "message_view", "Sylpheed");
190         gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
191         gtk_widget_set_usize(window, prefs_common.msgwin_width,
192                              prefs_common.msgwin_height);
193
194         vbox = gtk_vbox_new(FALSE, 0);
195         gtk_widget_show(vbox);
196         gtk_container_add(GTK_CONTAINER(window), vbox);
197         
198         msgview = messageview_create(mainwin);
199
200         gtk_signal_connect(GTK_OBJECT(window), "size_allocate",
201                            GTK_SIGNAL_FUNC(messageview_size_allocate_cb),
202                            msgview);
203         gtk_signal_connect(GTK_OBJECT(window), "destroy",
204                            GTK_SIGNAL_FUNC(messageview_destroy_cb), msgview);
205         gtk_signal_connect(GTK_OBJECT(window), "key_press_event",
206                            GTK_SIGNAL_FUNC(key_pressed), msgview);
207         gtk_signal_connect(GTK_OBJECT(window), "focus_in_event",
208                            GTK_SIGNAL_FUNC(focus_in), msgview);
209
210         n_menu_entries = sizeof(messageview_entries) / sizeof(messageview_entries[0]);
211         menubar = menubar_create(window, messageview_entries,
212                                  n_menu_entries, "<MessageView>", msgview);
213         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
214
215         handlebox = gtk_handle_box_new();
216         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
217         messageview_toolbar_create(msgview,handlebox);
218
219         gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET_PTR(msgview));
220         gtk_widget_grab_focus(msgview->textview->text);
221         gtk_widget_show_all(window);
222
223         msgview->new_window = TRUE;
224         msgview->window = window;
225         msgview->visible = TRUE;
226         msgview->menubar = menubar; 
227         msgview->handlebox = handlebox;
228
229         
230         messageview_init(msgview);
231         common_toolbar_set_style(msgview, TOOLBAR_MSGVIEW);
232
233         return msgview;
234 }
235
236 void messageview_init(MessageView *messageview)
237 {
238         headerview_init(messageview->headerview);
239         textview_init(messageview->textview);
240         imageview_init(messageview->imageview);
241         mimeview_init(messageview->mimeview);
242         /*messageview_set_font(messageview);*/
243
244         noticeview_hide(messageview->noticeview);
245 }
246
247 static void notification_convert_header(gchar *dest, gint len, 
248                                         const gchar *src_,
249                                         gint header_len)
250 {
251         char *src;
252
253         g_return_if_fail(src_ != NULL);
254         g_return_if_fail(dest != NULL);
255
256         if (len < 1) return;
257
258         Xstrndup_a(src, src_, len, return);
259
260         remove_return(src);
261
262         if (is_ascii_str(src)) {
263                 strncpy2(dest, src, len);
264                 dest[len - 1] = '\0';
265                 return;
266         } else
267                 conv_encode_header(dest, len, src, header_len);
268 }
269
270 static gint disposition_notification_queue(PrefsAccount * account,
271                                            gchar * to, const gchar *file)
272 {
273         FolderItem *queue;
274         gchar *tmp;
275         FILE *fp, *src_fp;
276         gchar buf[BUFFSIZE];
277         gint num;
278
279         debug_print("queueing message...\n");
280         g_return_val_if_fail(account != NULL, -1);
281
282         tmp = g_strdup_printf("%s%cqueue.%d", g_get_tmp_dir(),
283                               G_DIR_SEPARATOR, (gint)file);
284         if ((fp = fopen(tmp, "wb")) == NULL) {
285                 FILE_OP_ERROR(tmp, "fopen");
286                 g_free(tmp);
287                 return -1;
288         }
289         if ((src_fp = fopen(file, "rb")) == NULL) {
290                 FILE_OP_ERROR(file, "fopen");
291                 fclose(fp);
292                 unlink(tmp);
293                 g_free(tmp);
294                 return -1;
295         }
296         if (change_file_mode_rw(fp, tmp) < 0) {
297                 FILE_OP_ERROR(tmp, "chmod");
298                 g_warning(_("can't change file mode\n"));
299         }
300
301         /* queueing variables */
302         fprintf(fp, "AF:\n");
303         fprintf(fp, "NF:0\n");
304         fprintf(fp, "PS:10\n");
305         fprintf(fp, "SRH:1\n");
306         fprintf(fp, "SFN:\n");
307         fprintf(fp, "DSR:\n");
308         fprintf(fp, "MID:\n");
309         fprintf(fp, "CFG:\n");
310         fprintf(fp, "PT:0\n");
311         fprintf(fp, "S:%s\n", account->address);
312         fprintf(fp, "RQ:\n");
313         if (account->smtp_server)
314                 fprintf(fp, "SSV:%s\n", account->smtp_server);
315         else
316                 fprintf(fp, "SSV:\n");
317         if (account->nntp_server)
318                 fprintf(fp, "NSV:%s\n", account->nntp_server);
319         else
320                 fprintf(fp, "NSV:\n");
321         fprintf(fp, "SSH:\n");
322         fprintf(fp, "R:<%s>", to);
323         fprintf(fp, "\n");
324         fprintf(fp, "\n");
325
326         while (fgets(buf, sizeof(buf), src_fp) != NULL) {
327                 if (fputs(buf, fp) == EOF) {
328                         FILE_OP_ERROR(tmp, "fputs");
329                         fclose(fp);
330                         fclose(src_fp);
331                         unlink(tmp);
332                         g_free(tmp);
333                         return -1;
334                 }
335         }
336
337         fclose(src_fp);
338         if (fclose(fp) == EOF) {
339                 FILE_OP_ERROR(tmp, "fclose");
340                 unlink(tmp);
341                 g_free(tmp);
342                 return -1;
343         }
344
345         queue = folder_get_default_queue();
346         if ((num = folder_item_add_msg(queue, tmp, TRUE)) < 0) {
347                 g_warning(_("can't queue the message\n"));
348                 unlink(tmp);
349                 g_free(tmp);
350                 return -1;
351         }
352         g_free(tmp);
353
354         folder_update_item(queue, TRUE);
355
356         return 0;
357 }
358
359 static gint disposition_notification_send(MsgInfo *msginfo)
360 {
361         gchar buf[BUFFSIZE];
362         gchar tmp[MAXPATHLEN + 1];
363         FILE *fp;
364         GSList *to_list;
365         GList *ac_list;
366         PrefsAccount *account;
367         gint ok;
368         gchar *to;
369
370         if ((!msginfo->returnreceiptto) && 
371             (!msginfo->dispositionnotificationto)) 
372                 return -1;
373
374         /* RFC2298: Test for Return-Path */
375         if (msginfo->dispositionnotificationto)
376                 to = msginfo->dispositionnotificationto;
377         else
378                 to = msginfo->returnreceiptto;
379
380         ok = get_header_from_msginfo(msginfo, buf, sizeof(buf),
381                                 "Return-Path:");
382         if (ok == 0) {
383                 gchar *to_addr = g_strdup(to);
384                 extract_address(to_addr);
385                 extract_address(buf);
386                 ok = strcmp(to_addr, buf);
387                 g_free(to_addr);
388         } else {
389                 strncpy(buf, _("<No Return-Path found>"), 
390                                 sizeof(buf));
391         }
392         
393         if (ok != 0) {
394                 AlertValue val;
395                 gchar *message;
396                 message = g_strdup_printf(
397                                  _("The notification address to which the "
398                                    "return receipt is to be sent\n"
399                                    "does not correspond to the return path:\n"
400                                    "Notification address: %s\n"
401                                    "Return path: %s\n"
402                                    "It is advised to not to send the return "
403                                    "receipt."), to, buf);
404                 val = alertpanel(_("Warning"), message, _("Send"),
405                                 _("+Don't Send"), NULL);
406                 if (val != G_ALERTDEFAULT)
407                         return -1;
408         }
409
410         ac_list = account_find_all_from_address(NULL, msginfo->to);
411         ac_list = account_find_all_from_address(ac_list, msginfo->cc);
412
413         if (ac_list == NULL) {
414                 alertpanel_error(_("This message is asking for a return "
415                                    "receipt notification\n"
416                                    "but according to its 'To:' and 'CC:' "
417                                    "headers it was not\nofficially addressed "
418                                    "to you.\n"
419                                    "Receipt notification cancelled."));
420                 return -1;
421         }
422
423         if (g_list_length(ac_list) > 1)
424                 account = select_account_from_list(ac_list);
425         else
426                 account = (PrefsAccount *) ac_list->data;
427         g_list_free(ac_list);
428
429         if (account == NULL)
430                 return -1;
431
432         /* write to temporary file */
433         g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg%d",
434                    get_rc_dir(), G_DIR_SEPARATOR, (gint)msginfo);
435
436         if ((fp = fopen(tmp, "wb")) == NULL) {
437                 FILE_OP_ERROR(tmp, "fopen");
438                 return -1;
439         }
440
441         /* chmod for security */
442         if (change_file_mode_rw(fp, tmp) < 0) {
443                 FILE_OP_ERROR(tmp, "chmod");
444                 g_warning(_("can't change file mode\n"));
445         }
446
447         /* Date */
448         get_rfc822_date(buf, sizeof(buf));
449         fprintf(fp, "Date: %s\n", buf);
450
451         /* From */
452         if (account->name && *account->name) {
453                 notification_convert_header
454                         (buf, sizeof(buf), account->name,
455                          strlen("From: "));
456                 fprintf(fp, "From: %s <%s>\n", buf, account->address);
457         } else
458                 fprintf(fp, "From: %s\n", account->address);
459
460         fprintf(fp, "To: %s\n", to);
461
462         /* Subject */
463         notification_convert_header(buf, sizeof(buf), msginfo->subject,
464                                     strlen("Subject: "));
465         fprintf(fp, "Subject: Disposition notification: %s\n", buf);
466
467         if (fclose(fp) == EOF) {
468                 FILE_OP_ERROR(tmp, "fclose");
469                 unlink(tmp);
470                 return -1;
471         }
472
473         to_list = address_list_append(NULL, to);
474         ok = send_message(tmp, account, to_list);
475         
476         if (ok < 0) {
477                 if (prefs_common.queue_msg) {
478                         AlertValue val;
479                         
480                         val = alertpanel
481                                 (_("Queueing"),
482                                  _("Error occurred while sending the notification.\n"
483                                    "Put this notification into queue folder?"),
484                                  _("OK"), _("Cancel"), NULL);
485                         if (G_ALERTDEFAULT == val) {
486                                 ok = disposition_notification_queue(account, to, tmp);
487                                 if (ok < 0)
488                                         alertpanel_error(_("Can't queue the notification."));
489                         }
490                 } else
491                         alertpanel_error(_("Error occurred while sending the notification."));
492         }
493
494         if (unlink(tmp) < 0) FILE_OP_ERROR(tmp, "unlink");
495
496         return ok;
497 }
498
499 void messageview_show(MessageView *messageview, MsgInfo *msginfo,
500                       gboolean all_headers)
501 {
502         FILE *fp;
503         gchar *file;
504         MimeInfo *mimeinfo;
505         MsgInfo *tmpmsginfo;
506
507         g_return_if_fail(msginfo != NULL);
508         messageview->msginfo = msginfo;
509
510 #if USE_GPGME
511         if ((fp = procmsg_open_message_decrypted(msginfo, &mimeinfo)) == NULL)
512                 return;
513 #else /* !USE_GPGME */
514         if ((fp = procmsg_open_message(msginfo)) == NULL) return;
515         mimeinfo = procmime_scan_mime_header(fp);
516 #endif /* USE_GPGME */
517         fclose(fp);
518         if (!mimeinfo) return;
519
520         file = procmsg_get_message_file_path(msginfo);
521         if (!file) {
522                 g_warning(_("can't get message file path.\n"));
523                 procmime_mimeinfo_free_all(mimeinfo);
524                 return;
525         }
526
527         tmpmsginfo = procheader_parse_file(file, msginfo->flags, TRUE, TRUE);
528
529         headerview_show(messageview->headerview, tmpmsginfo);
530         procmsg_msginfo_free(tmpmsginfo);
531
532         messageview->all_headers = all_headers;
533         textview_set_all_headers(messageview->textview, all_headers);
534         textview_set_all_headers(messageview->mimeview->textview, all_headers);
535
536         if (mimeinfo->mime_type != MIME_TEXT &&
537             mimeinfo->mime_type != MIME_TEXT_HTML) {
538                 messageview_change_view_type(messageview, MVIEW_MIME);
539                 mimeview_show_message(messageview->mimeview, mimeinfo, file);
540         } else {
541                 messageview_change_view_type(messageview, MVIEW_TEXT);
542                 textview_show_message(messageview->textview, mimeinfo, file);
543                 procmime_mimeinfo_free_all(mimeinfo);
544         }
545
546         if (MSG_IS_RETRCPT_PENDING(msginfo->flags))
547                 return_receipt_show(messageview->noticeview, msginfo);
548         else 
549                 noticeview_hide(messageview->noticeview);
550
551         g_free(file);
552 }
553
554 static void messageview_change_view_type(MessageView *messageview,
555                                          MessageType type)
556 {
557         TextView *textview = messageview->textview;
558         MimeView *mimeview = messageview->mimeview;
559
560         if (messageview->type == type) return;
561
562         if (type == MVIEW_MIME) {
563                 gtkut_container_remove
564                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
565                          GTK_WIDGET_PTR(textview));
566                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
567                                    GTK_WIDGET_PTR(mimeview), TRUE, TRUE, 0);
568                 gtk_container_add(GTK_CONTAINER(mimeview->vbox),
569                                   GTK_WIDGET_PTR(textview));
570         } else if (type == MVIEW_TEXT) {
571                 gtkut_container_remove
572                         (GTK_CONTAINER(GTK_WIDGET_PTR(messageview)),
573                          GTK_WIDGET_PTR(mimeview));
574
575                 if (mimeview->vbox == GTK_WIDGET_PTR(textview)->parent)
576                         gtkut_container_remove(GTK_CONTAINER(mimeview->vbox),
577                                                GTK_WIDGET_PTR(textview));
578
579                 gtk_box_pack_start(GTK_BOX(messageview->vbox),
580                                    GTK_WIDGET_PTR(textview), TRUE, TRUE, 0);
581         } else
582                 return;
583
584         messageview->type = type;
585 }
586
587 void messageview_clear(MessageView *messageview)
588 {
589         messageview_change_view_type(messageview, MVIEW_TEXT);
590         headerview_clear(messageview->headerview);
591         textview_clear(messageview->textview);
592         imageview_clear(messageview->imageview);
593         noticeview_hide(messageview->noticeview);
594 }
595
596 void messageview_destroy(MessageView *messageview)
597 {
598         GtkWidget *textview  = GTK_WIDGET_PTR(messageview->textview);
599         GtkWidget *imageview = GTK_WIDGET_PTR(messageview->imageview);
600         GtkWidget *mimeview  = GTK_WIDGET_PTR(messageview->mimeview);
601
602         headerview_destroy(messageview->headerview);
603         textview_destroy(messageview->textview);
604         imageview_destroy(messageview->imageview);
605         mimeview_destroy(messageview->mimeview);
606         noticeview_destroy(messageview->noticeview);
607
608         toolbar_clear_list(TOOLBAR_MSGVIEW);
609         TOOLBAR_DESTROY_ITEMS(messageview->toolbar->item_list); 
610         TOOLBAR_DESTROY_ACTIONS(messageview->toolbar->action_list);
611
612         g_free(messageview);
613
614         gtk_widget_unref(textview);
615         gtk_widget_unref(imageview);
616         gtk_widget_unref(mimeview);
617 }
618
619 void messageview_quote_color_set(void)
620 {
621 }
622
623 void messageview_set_font(MessageView *messageview)
624 {
625         textview_set_font(messageview->textview, NULL);
626 }
627
628 TextView *messageview_get_current_textview(MessageView *messageview)
629 {
630         TextView *text = NULL;
631
632         if (messageview->type == MVIEW_TEXT)
633                 text = messageview->textview;
634         else if (messageview->type == MVIEW_MIME) {
635                 if (gtk_notebook_get_current_page
636                         (GTK_NOTEBOOK(messageview->mimeview->notebook)) == 0)
637                         text = messageview->textview;
638                 else if (messageview->mimeview->type == MIMEVIEW_TEXT)
639                         text = messageview->mimeview->textview;
640         }
641
642         return text;
643 }
644
645 void messageview_copy_clipboard(MessageView *messageview)
646 {
647         TextView *text;
648
649         text = messageview_get_current_textview(messageview);
650         if (text)
651                 gtk_editable_copy_clipboard(GTK_EDITABLE(text->text));
652 }
653
654 void messageview_select_all(MessageView *messageview)
655 {
656         TextView *text;
657
658         text = messageview_get_current_textview(messageview);
659         if (text)
660                 gtk_editable_select_region(GTK_EDITABLE(text->text), 0, -1);
661 }
662
663 void messageview_set_position(MessageView *messageview, gint pos)
664 {
665         textview_set_position(messageview->textview, pos);
666 }
667
668 gboolean messageview_search_string(MessageView *messageview, const gchar *str,
669                                    gboolean case_sens)
670 {
671         return textview_search_string(messageview->textview, str, case_sens);
672         return FALSE;
673 }
674
675 gboolean messageview_search_string_backward(MessageView *messageview,
676                                             const gchar *str,
677                                             gboolean case_sens)
678 {
679         return textview_search_string_backward(messageview->textview,
680                                                str, case_sens);
681         return FALSE;
682 }
683
684 gboolean messageview_is_visible(MessageView *messageview)
685 {
686         return messageview->visible;
687 }
688
689 static void messageview_destroy_cb(GtkWidget *widget, MessageView *messageview)
690 {
691         messageview_destroy(messageview);
692 }
693
694 static void messageview_size_allocate_cb(GtkWidget *widget,
695                                          GtkAllocation *allocation)
696 {
697         g_return_if_fail(allocation != NULL);
698
699         prefs_common.msgwin_width  = allocation->width;
700         prefs_common.msgwin_height = allocation->height;
701 }
702
703 static void key_pressed(GtkWidget *widget, GdkEventKey *event,
704                         MessageView *messageview)
705 {
706         if (event && event->keyval == GDK_Escape && messageview->window)
707                 gtk_widget_destroy(messageview->window);
708 }
709
710 static void focus_in(GtkWidget *widget, GdkEventFocus *event,
711                      gpointer data)
712 {
713         MessageView *msgview = (MessageView*)data;
714
715         summary_select_by_msgnum(msgview->mainwin->summaryview, 
716                                  msgview->msginfo->msgnum);
717 }
718
719 void messageview_toggle_view_real(MessageView *messageview)
720 {
721         MainWindow *mainwin = messageview->mainwin;
722         union CompositeWin *cwin = &mainwin->win;
723         GtkWidget *vpaned = NULL;
724         GtkWidget *container = NULL;
725         GtkItemFactory *ifactory = gtk_item_factory_from_widget(mainwin->menubar);
726         
727         switch (mainwin->type) {
728         case SEPARATE_NONE:
729                 vpaned = cwin->sep_none.vpaned;
730                 container = cwin->sep_none.hpaned;
731                 break;
732         case SEPARATE_FOLDER:
733                 vpaned = cwin->sep_folder.vpaned;
734                 container = mainwin->vbox_body;
735                 break;
736         case SEPARATE_MESSAGE:
737         case SEPARATE_BOTH:
738                 return;
739         }
740
741         if (vpaned->parent != NULL) {
742                 gtk_widget_ref(vpaned);
743                 gtkut_container_remove(GTK_CONTAINER(container), vpaned);
744                 gtk_widget_reparent(GTK_WIDGET_PTR(messageview), container);
745                 menu_set_sensitive(ifactory, "/View/Expand Summary View", FALSE);
746                 gtk_widget_grab_focus(GTK_WIDGET(messageview->textview->text));
747         } else {
748                 gtk_widget_reparent(GTK_WIDGET_PTR(messageview), vpaned);
749                 gtk_container_add(GTK_CONTAINER(container), vpaned);
750                 gtk_widget_unref(vpaned);
751                 menu_set_sensitive(ifactory, "/View/Expand Summary View", TRUE);
752                 gtk_widget_grab_focus(GTK_WIDGET(mainwin->summaryview->ctree));
753         }
754 }
755
756 static void return_receipt_show(NoticeView *noticeview, MsgInfo *msginfo)
757 {
758         noticeview_set_text(noticeview, _("This messages asks for a return receipt."));
759         noticeview_set_button_text(noticeview, _("Send receipt"));
760         noticeview_set_button_press_callback(noticeview,
761                                              GTK_SIGNAL_FUNC(return_receipt_send_clicked),
762                                              (gpointer) msginfo);
763         noticeview_show(noticeview);
764 }
765
766 static void return_receipt_send_clicked(NoticeView *noticeview, MsgInfo *msginfo)
767 {
768         MsgInfo *tmpmsginfo;
769         gchar *file;
770
771         file = procmsg_get_message_file_path(msginfo);
772         if (!file) {
773                 g_warning(_("can't get message file path.\n"));
774                 return;
775         }
776
777         tmpmsginfo = procheader_parse_file(file, msginfo->flags, TRUE, TRUE);
778         tmpmsginfo->folder = msginfo->folder;
779         tmpmsginfo->msgnum = msginfo->msgnum;
780
781         if (disposition_notification_send(tmpmsginfo) >= 0) {
782                 procmsg_msginfo_unset_flags(msginfo, MSG_RETRCPT_PENDING, 0);
783                 noticeview_hide(noticeview);
784         }               
785
786         procmsg_msginfo_free(tmpmsginfo);
787         g_free(file);
788 }
789
790 static void select_account_cb(GtkWidget *w, gpointer data)
791 {
792         *(gint*)data = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(w)));
793 }
794         
795 static PrefsAccount *select_account_from_list(GList *ac_list)
796 {
797         GtkWidget *optmenu;
798         GtkWidget *menu;
799         gint account_id;
800
801         g_return_val_if_fail(ac_list != NULL, NULL);
802         g_return_val_if_fail(ac_list->data != NULL, NULL);
803         
804         optmenu = gtk_option_menu_new();
805         menu = gtkut_account_menu_new(ac_list, select_account_cb, &account_id);
806         if (!menu)
807                 return NULL;
808         gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
809         gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), 0);
810         account_id = ((PrefsAccount *) ac_list->data)->account_id;
811         if (alertpanel_with_widget(
812                                 _("Return Receipt Notification"),
813                                 _("The message was sent to several of your "
814                                   "accounts.\n"
815                                   "Please choose which account do you want to "
816                                   "use for sending the receipt notification:"),
817                                 _("Send Notification"), _("+Cancel"), NULL,
818                                 optmenu) != G_ALERTDEFAULT)
819                 return NULL;
820         return account_find_from_id(account_id);
821 }
822
823 static void messageview_toolbar_create(MessageView   *messageview, 
824                                        GtkWidget *container)
825 {
826         ToolbarItem *toolbar_item;
827
828         GtkWidget *toolbar;
829         GtkWidget *icon_wid = NULL;
830         GtkWidget *item;
831         GtkTooltips *toolbar_tips;
832         ToolbarSylpheedActions *action_item;
833         GSList *cur;
834         GSList *toolbar_list;
835         GList *elem;
836         toolbar_tips = gtk_tooltips_new();
837         
838         toolbar_read_config_file(TOOLBAR_MSGVIEW);
839         toolbar_list = toolbar_get_list(TOOLBAR_MSGVIEW);
840
841         messageview->toolbar = g_new0(Toolbar, 1); 
842
843         toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
844                                   GTK_TOOLBAR_BOTH);
845         gtk_container_add(GTK_CONTAINER(container), toolbar);
846         gtk_container_set_border_width(GTK_CONTAINER(container), 2);
847         gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
848         gtk_toolbar_set_space_style(GTK_TOOLBAR(toolbar),
849                                     GTK_TOOLBAR_SPACE_LINE);
850         
851         for (cur = toolbar_list; cur != NULL; cur = cur->next) {
852
853                 if (g_strcasecmp(((ToolbarItem*)cur->data)->file, SEPARATOR) == 0) {
854                         gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
855                         continue;
856                 }
857                 
858                 toolbar_item = g_new0(ToolbarItem, 1); 
859                 toolbar_item->file = g_strdup(((ToolbarItem*)cur->data)->file);
860                 toolbar_item->text = g_strdup(((ToolbarItem*)cur->data)->text);
861                 toolbar_item->index = ((ToolbarItem*)cur->data)->index;
862
863                 toolbar_item->parent = g_new0(ToolbarParent, 1);
864                 toolbar_item->parent->data = (gpointer)messageview;
865                 toolbar_item->parent->type = TOOLBAR_MSGVIEW;
866
867                 /* collect toolbar items in list to keep track */
868                 messageview->toolbar->item_list = 
869                         g_slist_append(messageview->toolbar->item_list, 
870                                        toolbar_item);
871
872                 icon_wid = stock_pixmap_widget(container, stock_pixmap_get_icon(toolbar_item->file));
873                 item  = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
874                                                 toolbar_item->text,
875                                                 (""),
876                                                 (""),
877                                                 icon_wid, toolbar_messageview_buttons_cb, 
878                                                 toolbar_item);
879                 
880                 switch (toolbar_item->index) {
881                 case A_COMPOSE_EMAIL:
882                         messageview->toolbar->compose_mail_btn = item;
883                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
884                                              messageview->toolbar->compose_mail_btn,
885                                            _("Compose Email"), NULL);
886                         break;
887                 case A_REPLY_MESSAGE:
888                         messageview->toolbar->reply_btn = item;
889                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
890                                              messageview->toolbar->reply_btn,
891                                            _("Reply to Message"), NULL);
892                         break;
893                 case A_REPLY_SENDER:
894                         messageview->toolbar->replysender_btn = item;
895                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
896                                              messageview->toolbar->replysender_btn,
897                                            _("Reply to Sender"), NULL);
898                         break;
899                 case A_REPLY_ALL:
900                         messageview->toolbar->replyall_btn = item;
901                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
902                                              messageview->toolbar->replyall_btn,
903                                            _("Reply to All"), NULL);
904                         break;
905                 case A_REPLY_ML:
906                         messageview->toolbar->replylist_btn = item;
907                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
908                                              messageview->toolbar->replylist_btn,
909                                            _("Reply to Mailing-list"), NULL);
910                         break;
911                 case A_FORWARD:
912                         messageview->toolbar->fwd_btn = item;
913                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
914                                              messageview->toolbar->fwd_btn,
915                                            _("Forward Message"), NULL);
916                         break;
917                 case A_DELETE:
918                         messageview->toolbar->delete_btn = item;
919                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
920                                              messageview->toolbar->delete_btn,
921                                            _("Delete Message"), NULL);
922                         break;
923                 case A_GOTO_NEXT:
924                         messageview->toolbar->next_btn = item;
925                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
926                                              messageview->toolbar->next_btn,
927                                            _("Goto Next Message"), NULL);
928                         break;
929                 case A_SYL_ACTIONS:
930                         action_item = g_new0(ToolbarSylpheedActions, 1);
931                         action_item->widget = item;
932                         action_item->name   = g_strdup(toolbar_item->text);
933
934                         messageview->toolbar->action_list = 
935                                 g_slist_append(messageview->toolbar->action_list,
936                                                action_item);
937
938                         gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tips), 
939                                              item,
940                                              action_item->name, NULL);
941
942                         gtk_widget_show(item);
943                         break;
944                 default:
945                         break;
946                 }
947
948         }
949         messageview->toolbar->toolbar = toolbar;
950
951         gtk_widget_show_all(toolbar);
952 }
953
954 static void toolbar_messageview_buttons_cb(GtkWidget   *widget, 
955                                            ToolbarItem *item)
956 {
957
958         struct {
959                 gint   index;
960                 void (*func)(GtkWidget *widget, gpointer data);
961         } messageview_action[] = {
962                 { A_COMPOSE_EMAIL,      common_toolbar_compose_cb               },
963                 { A_REPLY_MESSAGE,      common_toolbar_reply_cb                 },
964                 { A_REPLY_SENDER,       common_toolbar_reply_to_sender_cb       },
965                 { A_REPLY_ALL,          common_toolbar_reply_to_all_cb          },
966                 { A_REPLY_ML,           common_toolbar_reply_to_list_cb         },
967                 { A_FORWARD,            common_toolbar_forward_cb               },
968                 { A_DELETE,             common_toolbar_delete_cb                },
969                 { A_GOTO_NEXT,          common_toolbar_next_unread_cb           },
970                 { A_SYL_ACTIONS,        common_toolbar_actions_execute_cb       },
971                 };
972
973
974         gint num_items = sizeof(messageview_action)/sizeof(messageview_action[0]);
975         gint i;
976         for (i = 0; i < num_items; i++) {
977                 
978                 if (messageview_action[i].index == item->index) {
979                         messageview_action[i].func(widget, (gpointer)item->parent);
980                         break;
981                 }
982         }
983 }
984
985 void messageview_reply_cb(gpointer data, guint action, GtkWidget *widget)
986 {
987         MessageView *messageview = (MessageView *)data;
988         
989         switch (action) {
990         case COMPOSE_REPLY:
991                 common_toolbar_reply_cb(NULL,messageview);
992                 break;
993         case COMPOSE_REPLY_TO_ALL:
994                 common_toolbar_reply_to_all_cb(NULL,messageview);
995                 break;
996         case COMPOSE_REPLY_TO_SENDER:
997                 common_toolbar_reply_to_sender_cb(NULL,messageview);
998                 break;
999         case COMPOSE_REPLY_TO_LIST:
1000                 common_toolbar_reply_to_list_cb(NULL,messageview);
1001                 break;
1002         }
1003 }
1004
1005 static void messageview_delete_cb(gpointer data, guint action, GtkWidget *widget)
1006 {
1007         MessageView *messageview = (MessageView *)data;
1008
1009         common_toolbar_delete_cb(NULL,messageview);
1010 }
1011
1012 static void messageview_close_cb(gpointer data, guint action, GtkWidget *widget)
1013 {
1014         MessageView *messageview = (MessageView *)data;
1015
1016         gtk_widget_destroy(messageview->window);
1017 }