2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
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 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtk/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkvpaned.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkeditable.h>
43 #include <gtk/gtkwindow.h>
44 #include <gtk/gtksignal.h>
45 #include <gtk/gtkvbox.h>
46 #include <gtk/gtkcontainer.h>
47 #include <gtk/gtkhandlebox.h>
48 #include <gtk/gtktoolbar.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtklabel.h>
52 #include <gtk/gtkscrolledwindow.h>
53 #include <gtk/gtktreeview.h>
54 #include <gtk/gtkliststore.h>
55 #include <gtk/gtktreeselection.h>
56 #include <gtk/gtktreemodel.h>
58 #include <gtk/gtkdnd.h>
59 #include <gtk/gtkclipboard.h>
60 #include <pango/pango-break.h>
65 #include <sys/types.h>
71 # include <sys/wait.h>
75 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
79 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
86 #include "mainwindow.h"
88 #include "addressbook.h"
89 #include "folderview.h"
92 #include "stock_pixmap.h"
93 #include "send_message.h"
96 #include "customheader.h"
97 #include "prefs_common.h"
98 #include "prefs_account.h"
102 #include "procheader.h"
103 #include "procmime.h"
104 #include "statusbar.h"
107 #include "quoted-printable.h"
108 #include "codeconv.h"
110 #include "gtkutils.h"
112 #include "alertpanel.h"
113 #include "manage_window.h"
114 #include "gtkshruler.h"
116 #include "addr_compl.h"
117 #include "quote_fmt.h"
119 #include "foldersel.h"
122 #include "message_search.h"
123 #include "combobox.h"
138 #define N_ATTACH_COLS (N_COL_COLUMNS)
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
144 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
145 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
146 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
147 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
148 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
149 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
150 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
151 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
152 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
153 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
154 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
155 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
156 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
157 } ComposeCallAdvancedAction;
161 PRIORITY_HIGHEST = 1,
170 COMPOSE_INSERT_SUCCESS,
171 COMPOSE_INSERT_READ_ERROR,
172 COMPOSE_INSERT_INVALID_CHARACTER,
173 COMPOSE_INSERT_NO_FILE
174 } ComposeInsertResult;
178 COMPOSE_WRITE_FOR_SEND,
179 COMPOSE_WRITE_FOR_STORE
184 COMPOSE_QUOTE_FORCED,
189 #define B64_LINE_SIZE 57
190 #define B64_BUFFSIZE 77
192 #define MAX_REFERENCES_LEN 999
194 static GList *compose_list = NULL;
196 static Compose *compose_generic_new (PrefsAccount *account,
199 GPtrArray *attach_files,
200 GList *listAddress );
202 static Compose *compose_create (PrefsAccount *account,
207 static void compose_entry_mark_default_to (Compose *compose,
208 const gchar *address);
209 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
210 ComposeQuoteMode quote_mode,
214 static Compose *compose_forward_multiple (PrefsAccount *account,
215 GSList *msginfo_list);
216 static Compose *compose_reply (MsgInfo *msginfo,
217 ComposeQuoteMode quote_mode,
222 static Compose *compose_reply_mode (ComposeMode mode,
223 GSList *msginfo_list,
225 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
226 static void compose_update_privacy_systems_menu(Compose *compose);
228 static GtkWidget *compose_account_option_menu_create
230 static void compose_set_out_encoding (Compose *compose);
231 static void compose_set_template_menu (Compose *compose);
232 static void compose_template_apply (Compose *compose,
235 static void compose_destroy (Compose *compose);
237 static void compose_entries_set (Compose *compose,
239 ComposeEntryType to_type);
240 static gint compose_parse_header (Compose *compose,
242 static gchar *compose_parse_references (const gchar *ref,
245 static gchar *compose_quote_fmt (Compose *compose,
251 gboolean need_unescape,
252 const gchar *err_msg);
254 static void compose_reply_set_entry (Compose *compose,
260 followup_and_reply_to);
261 static void compose_reedit_set_entry (Compose *compose,
264 static void compose_insert_sig (Compose *compose,
266 static gchar *compose_get_signature_str (Compose *compose);
267 static ComposeInsertResult compose_insert_file (Compose *compose,
270 static gboolean compose_attach_append (Compose *compose,
273 const gchar *content_type);
274 static void compose_attach_parts (Compose *compose,
277 static gboolean compose_beautify_paragraph (Compose *compose,
278 GtkTextIter *par_iter,
280 static void compose_wrap_all (Compose *compose);
281 static void compose_wrap_all_full (Compose *compose,
284 static void compose_set_title (Compose *compose);
285 static void compose_select_account (Compose *compose,
286 PrefsAccount *account,
289 static PrefsAccount *compose_current_mail_account(void);
290 /* static gint compose_send (Compose *compose); */
291 static gboolean compose_check_for_valid_recipient
293 static gboolean compose_check_entries (Compose *compose,
294 gboolean check_everything);
295 static gint compose_write_to_file (Compose *compose,
298 gboolean attach_parts);
299 static gint compose_write_body_to_file (Compose *compose,
301 static gint compose_remove_reedit_target (Compose *compose,
303 static void compose_remove_draft (Compose *compose);
304 static gint compose_queue_sub (Compose *compose,
308 gboolean check_subject,
309 gboolean remove_reedit_target);
310 static void compose_add_attachments (Compose *compose,
312 static gchar *compose_get_header (Compose *compose);
314 static void compose_convert_header (Compose *compose,
319 gboolean addr_field);
321 static void compose_attach_info_free (AttachInfo *ainfo);
322 static void compose_attach_remove_selected (Compose *compose);
324 static void compose_attach_property (Compose *compose);
325 static void compose_attach_property_create (gboolean *cancelled);
326 static void attach_property_ok (GtkWidget *widget,
327 gboolean *cancelled);
328 static void attach_property_cancel (GtkWidget *widget,
329 gboolean *cancelled);
330 static gint attach_property_delete_event (GtkWidget *widget,
332 gboolean *cancelled);
333 static gboolean attach_property_key_pressed (GtkWidget *widget,
335 gboolean *cancelled);
337 static void compose_exec_ext_editor (Compose *compose);
339 static gint compose_exec_ext_editor_real (const gchar *file);
340 static gboolean compose_ext_editor_kill (Compose *compose);
341 static gboolean compose_input_cb (GIOChannel *source,
342 GIOCondition condition,
344 static void compose_set_ext_editor_sensitive (Compose *compose,
346 #endif /* G_OS_UNIX */
348 static void compose_undo_state_changed (UndoMain *undostruct,
353 static void compose_create_header_entry (Compose *compose);
354 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
355 static void compose_remove_header_entries(Compose *compose);
357 static void compose_update_priority_menu_item(Compose * compose);
359 static void compose_spell_menu_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static gboolean compose_edit_size_alloc (GtkEditable *widget,
367 GtkAllocation *allocation,
368 GtkSHRuler *shruler);
369 static void account_activated (GtkComboBox *optmenu,
371 static void attach_selected (GtkTreeView *tree_view,
372 GtkTreePath *tree_path,
373 GtkTreeViewColumn *column,
375 static gboolean attach_button_pressed (GtkWidget *widget,
376 GdkEventButton *event,
378 static gboolean attach_key_pressed (GtkWidget *widget,
381 static void compose_send_cb (gpointer data,
384 static void compose_send_later_cb (gpointer data,
388 static void compose_draft_cb (gpointer data,
392 static void compose_attach_cb (gpointer data,
395 static void compose_insert_file_cb (gpointer data,
398 static void compose_insert_sig_cb (gpointer data,
402 static void compose_close_cb (gpointer data,
406 static void compose_set_encoding_cb (gpointer data,
410 static void compose_address_cb (gpointer data,
413 static void compose_template_activate_cb(GtkWidget *widget,
416 static void compose_ext_editor_cb (gpointer data,
420 static gint compose_delete_cb (GtkWidget *widget,
424 static void compose_undo_cb (Compose *compose);
425 static void compose_redo_cb (Compose *compose);
426 static void compose_cut_cb (Compose *compose);
427 static void compose_copy_cb (Compose *compose);
428 static void compose_paste_cb (Compose *compose);
429 static void compose_paste_as_quote_cb (Compose *compose);
430 static void compose_paste_no_wrap_cb (Compose *compose);
431 static void compose_paste_wrap_cb (Compose *compose);
432 static void compose_allsel_cb (Compose *compose);
434 static void compose_advanced_action_cb (Compose *compose,
435 ComposeCallAdvancedAction action);
437 static void compose_grab_focus_cb (GtkWidget *widget,
440 static void compose_changed_cb (GtkTextBuffer *textbuf,
443 static void compose_wrap_cb (gpointer data,
446 static void compose_find_cb (gpointer data,
449 static void compose_toggle_autowrap_cb (gpointer data,
453 static void compose_toggle_ruler_cb (gpointer data,
456 static void compose_toggle_sign_cb (gpointer data,
459 static void compose_toggle_encrypt_cb (gpointer data,
462 static void compose_set_privacy_system_cb(GtkWidget *widget,
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system (Compose *compose,
466 PrefsAccount *account,
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
472 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
474 static void compose_set_priority_cb (gpointer data,
477 static void compose_reply_change_mode (gpointer data,
481 static void compose_attach_drag_received_cb (GtkWidget *widget,
482 GdkDragContext *drag_context,
485 GtkSelectionData *data,
489 static void compose_insert_drag_received_cb (GtkWidget *widget,
490 GdkDragContext *drag_context,
493 GtkSelectionData *data,
497 static void compose_header_drag_received_cb (GtkWidget *widget,
498 GdkDragContext *drag_context,
501 GtkSelectionData *data,
506 static gboolean compose_drag_drop (GtkWidget *widget,
507 GdkDragContext *drag_context,
509 guint time, gpointer user_data);
511 static void text_inserted (GtkTextBuffer *buffer,
516 static Compose *compose_generic_reply(MsgInfo *msginfo,
517 ComposeQuoteMode quote_mode,
521 gboolean followup_and_reply_to,
524 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
525 ComposeHeaderEntry *headerentry);
526 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
528 ComposeHeaderEntry *headerentry);
530 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
532 static void compose_allow_user_actions (Compose *compose, gboolean allow);
535 static void compose_check_all (Compose *compose);
536 static void compose_highlight_all (Compose *compose);
537 static void compose_check_backwards (Compose *compose);
538 static void compose_check_forwards_go (Compose *compose);
541 static gint compose_defer_auto_save_draft (Compose *compose);
542 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
544 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
547 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
548 FolderItem *folder_item);
550 static void compose_attach_update_label(Compose *compose);
552 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
554 static GtkItemFactoryEntry compose_popup_entries[] =
556 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
557 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
558 {"/---", NULL, NULL, 0, "<Separator>"},
559 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
562 static GtkItemFactoryEntry compose_entries[] =
564 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
565 {N_("/_Message/S_end"), "<control>Return",
566 compose_send_cb, 0, NULL},
567 {N_("/_Message/Send _later"), "<shift><control>S",
568 compose_send_later_cb, 0, NULL},
569 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
570 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
571 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
572 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
573 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
574 {N_("/_Message/_Save"),
575 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
576 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
577 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
579 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
580 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
581 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
582 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
583 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
584 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
585 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
586 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
587 {N_("/_Edit/Special paste/as _quotation"),
588 NULL, compose_paste_as_quote_cb, 0, NULL},
589 {N_("/_Edit/Special paste/_wrapped"),
590 NULL, compose_paste_wrap_cb, 0, NULL},
591 {N_("/_Edit/Special paste/_unwrapped"),
592 NULL, compose_paste_no_wrap_cb, 0, NULL},
593 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
594 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
595 {N_("/_Edit/A_dvanced/Move a character backward"),
597 compose_advanced_action_cb,
598 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
600 {N_("/_Edit/A_dvanced/Move a character forward"),
602 compose_advanced_action_cb,
603 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
605 {N_("/_Edit/A_dvanced/Move a word backward"),
607 compose_advanced_action_cb,
608 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
610 {N_("/_Edit/A_dvanced/Move a word forward"),
612 compose_advanced_action_cb,
613 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
615 {N_("/_Edit/A_dvanced/Move to beginning of line"),
616 NULL, /* "<control>A" */
617 compose_advanced_action_cb,
618 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
620 {N_("/_Edit/A_dvanced/Move to end of line"),
622 compose_advanced_action_cb,
623 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
625 {N_("/_Edit/A_dvanced/Move to previous line"),
627 compose_advanced_action_cb,
628 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
630 {N_("/_Edit/A_dvanced/Move to next line"),
632 compose_advanced_action_cb,
633 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
635 {N_("/_Edit/A_dvanced/Delete a character backward"),
637 compose_advanced_action_cb,
638 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
640 {N_("/_Edit/A_dvanced/Delete a character forward"),
642 compose_advanced_action_cb,
643 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
645 {N_("/_Edit/A_dvanced/Delete a word backward"),
646 NULL, /* "<control>W" */
647 compose_advanced_action_cb,
648 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
650 {N_("/_Edit/A_dvanced/Delete a word forward"),
651 NULL, /* "<alt>D", */
652 compose_advanced_action_cb,
653 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
655 {N_("/_Edit/A_dvanced/Delete line"),
657 compose_advanced_action_cb,
658 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
660 {N_("/_Edit/A_dvanced/Delete entire line"),
662 compose_advanced_action_cb,
663 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
665 {N_("/_Edit/A_dvanced/Delete to end of line"),
667 compose_advanced_action_cb,
668 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
670 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
672 "<control>F", compose_find_cb, 0, NULL},
673 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
674 {N_("/_Edit/_Wrap current paragraph"),
675 "<control>L", compose_wrap_cb, 0, NULL},
676 {N_("/_Edit/Wrap all long _lines"),
677 "<control><alt>L", compose_wrap_cb, 1, NULL},
678 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
679 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
680 {N_("/_Edit/Edit with e_xternal editor"),
681 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
683 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
684 {N_("/_Spelling/_Check all or check selection"),
685 NULL, compose_check_all, 0, NULL},
686 {N_("/_Spelling/_Highlight all misspelled words"),
687 NULL, compose_highlight_all, 0, NULL},
688 {N_("/_Spelling/Check _backwards misspelled word"),
689 NULL, compose_check_backwards , 0, NULL},
690 {N_("/_Spelling/_Forward to next misspelled word"),
691 NULL, compose_check_forwards_go, 0, NULL},
692 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
693 {N_("/_Spelling/Options"),
694 NULL, NULL, 0, "<Branch>"},
696 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
697 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
698 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
699 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
700 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
701 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
702 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
703 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
704 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
705 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
706 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
707 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
708 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
709 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
710 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
711 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
712 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
713 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
714 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
715 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
716 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
717 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
718 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
720 #define ENC_ACTION(action) \
721 NULL, compose_set_encoding_cb, action, \
722 "/Options/Character encoding/Automatic"
724 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
725 {N_("/_Options/Character _encoding/_Automatic"),
726 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
727 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
729 {N_("/_Options/Character _encoding/7bit ASCII (US-ASC_II)"),
730 ENC_ACTION(C_US_ASCII)},
731 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
732 ENC_ACTION(C_UTF_8)},
733 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
735 {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
736 {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
737 ENC_ACTION(C_ISO_8859_1)},
738 {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
739 ENC_ACTION(C_ISO_8859_15)},
740 {N_("/_Options/Character _encoding/Western European/Windows-1252"),
741 ENC_ACTION(C_WINDOWS_1252)},
743 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
744 ENC_ACTION(C_ISO_8859_2)},
746 {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
747 {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
748 ENC_ACTION(C_ISO_8859_13)},
749 {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
750 ENC_ACTION(C_ISO_8859_4)},
752 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
753 ENC_ACTION(C_ISO_8859_7)},
755 {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
756 {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
757 ENC_ACTION(C_ISO_8859_8)},
758 {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
759 ENC_ACTION(C_WINDOWS_1255)},
761 {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
762 {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
763 ENC_ACTION(C_ISO_8859_6)},
764 {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
765 ENC_ACTION(C_CP1256)},
767 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
768 ENC_ACTION(C_ISO_8859_9)},
770 {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
771 {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
772 ENC_ACTION(C_ISO_8859_5)},
773 {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
774 ENC_ACTION(C_KOI8_R)},
775 {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
776 ENC_ACTION(C_KOI8_U)},
777 {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
778 ENC_ACTION(C_WINDOWS_1251)},
780 {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
781 {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
782 ENC_ACTION(C_ISO_2022_JP)},
783 {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
784 ENC_ACTION(C_ISO_2022_JP_2)},
785 {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
786 ENC_ACTION(C_EUC_JP)},
787 {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
788 ENC_ACTION(C_SHIFT_JIS)},
790 {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
791 {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
792 ENC_ACTION(C_GB2312)},
793 {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
795 {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
797 {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
798 ENC_ACTION(C_EUC_TW)},
800 {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
801 {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
802 ENC_ACTION(C_EUC_KR)},
803 {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
804 ENC_ACTION(C_ISO_2022_KR)},
806 {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
807 {N_("/_Options/Character _encoding/Thai/TIS-620"),
808 ENC_ACTION(C_TIS_620)},
809 {N_("/_Options/Character _encoding/Thai/Windows-874"),
810 ENC_ACTION(C_WINDOWS_874)},
812 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
813 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
814 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
815 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
816 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
817 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
818 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
821 static GtkTargetEntry compose_mime_types[] =
823 {"text/uri-list", 0, 0},
824 {"UTF8_STRING", 0, 0},
828 static gboolean compose_put_existing_to_front(MsgInfo *info)
830 GList *compose_list = compose_get_compose_list();
834 for (elem = compose_list; elem != NULL && elem->data != NULL;
836 Compose *c = (Compose*)elem->data;
838 if (!c->targetinfo || !c->targetinfo->msgid ||
842 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
843 gtkut_window_popup(c->window);
851 static GdkColor quote_color1 =
852 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
853 static GdkColor quote_color2 =
854 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
855 static GdkColor quote_color3 =
856 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
858 static GdkColor quote_bgcolor1 =
859 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
860 static GdkColor quote_bgcolor2 =
861 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
862 static GdkColor quote_bgcolor3 =
863 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
865 static GdkColor signature_color = {
872 static GdkColor uri_color = {
879 static void compose_create_tags(GtkTextView *text, Compose *compose)
881 GtkTextBuffer *buffer;
882 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
888 buffer = gtk_text_view_get_buffer(text);
890 if (prefs_common.enable_color) {
891 /* grab the quote colors, converting from an int to a GdkColor */
892 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
894 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
896 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
898 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
900 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
902 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
904 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
906 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
909 signature_color = quote_color1 = quote_color2 = quote_color3 =
910 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
913 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
914 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
915 "foreground-gdk", "e_color1,
916 "paragraph-background-gdk", "e_bgcolor1,
918 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
919 "foreground-gdk", "e_color2,
920 "paragraph-background-gdk", "e_bgcolor2,
922 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
923 "foreground-gdk", "e_color3,
924 "paragraph-background-gdk", "e_bgcolor3,
927 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
928 "foreground-gdk", "e_color1,
930 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
931 "foreground-gdk", "e_color2,
933 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
934 "foreground-gdk", "e_color3,
938 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
939 "foreground-gdk", &signature_color,
942 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
943 "foreground-gdk", &uri_color,
945 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
946 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
948 color[0] = quote_color1;
949 color[1] = quote_color2;
950 color[2] = quote_color3;
951 color[3] = quote_bgcolor1;
952 color[4] = quote_bgcolor2;
953 color[5] = quote_bgcolor3;
954 color[6] = signature_color;
955 color[7] = uri_color;
956 cmap = gdk_drawable_get_colormap(compose->window->window);
957 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
959 for (i = 0; i < 8; i++) {
960 if (success[i] == FALSE) {
963 g_warning("Compose: color allocation failed.\n");
964 style = gtk_widget_get_style(GTK_WIDGET(text));
965 quote_color1 = quote_color2 = quote_color3 =
966 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
967 signature_color = uri_color = black;
972 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
973 GPtrArray *attach_files)
975 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
978 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
980 return compose_generic_new(account, mailto, item, NULL, NULL);
983 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
985 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
988 #define SCROLL_TO_CURSOR(compose) { \
989 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
990 gtk_text_view_get_buffer( \
991 GTK_TEXT_VIEW(compose->text))); \
992 gtk_text_view_scroll_mark_onscreen( \
993 GTK_TEXT_VIEW(compose->text), \
997 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
998 GPtrArray *attach_files, GList *listAddress )
1001 GtkTextView *textview;
1002 GtkTextBuffer *textbuf;
1004 GtkItemFactory *ifactory;
1005 const gchar *subject_format = NULL;
1006 const gchar *body_format = NULL;
1007 gchar *mailto_from = NULL;
1008 PrefsAccount *mailto_account = NULL;
1009 MsgInfo* dummyinfo = NULL;
1011 /* check if mailto defines a from */
1012 if (mailto && *mailto != '\0') {
1013 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
1014 /* mailto defines a from, check if we can get account prefs from it,
1015 if not, the account prefs will be guessed using other ways, but we'll keep
1018 mailto_account = account_find_from_address(mailto_from, TRUE);
1020 account = mailto_account;
1023 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1024 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1025 account = account_find_from_id(item->prefs->default_account);
1027 /* if no account prefs set, fallback to the current one */
1028 if (!account) account = cur_account;
1029 g_return_val_if_fail(account != NULL, NULL);
1031 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1033 /* override from name if mailto asked for it */
1035 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1036 g_free(mailto_from);
1038 /* override from name according to folder properties */
1039 if (item && item->prefs &&
1040 item->prefs->compose_with_format &&
1041 item->prefs->compose_override_from_format &&
1042 *item->prefs->compose_override_from_format != '\0') {
1047 dummyinfo = compose_msginfo_new_from_compose(compose);
1049 /* decode \-escape sequences in the internal representation of the quote format */
1050 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
1051 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1054 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1055 compose->gtkaspell);
1057 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1059 quote_fmt_scan_string(tmp);
1062 buf = quote_fmt_get_buffer();
1064 alertpanel_error(_("New message From format error."));
1066 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1067 quote_fmt_reset_vartable();
1072 ifactory = gtk_item_factory_from_widget(compose->menubar);
1074 compose->replyinfo = NULL;
1075 compose->fwdinfo = NULL;
1077 textview = GTK_TEXT_VIEW(compose->text);
1078 textbuf = gtk_text_view_get_buffer(textview);
1079 compose_create_tags(textview, compose);
1081 undo_block(compose->undostruct);
1083 compose_set_dictionaries_from_folder_prefs(compose, item);
1086 if (account->auto_sig)
1087 compose_insert_sig(compose, FALSE);
1088 gtk_text_buffer_get_start_iter(textbuf, &iter);
1089 gtk_text_buffer_place_cursor(textbuf, &iter);
1091 if (account->protocol != A_NNTP) {
1092 if (mailto && *mailto != '\0') {
1093 compose_entries_set(compose, mailto, COMPOSE_TO);
1095 } else if (item && item->prefs->enable_default_to) {
1096 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1097 compose_entry_mark_default_to(compose, item->prefs->default_to);
1099 if (item && item->ret_rcpt) {
1100 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1103 if (mailto && *mailto != '\0') {
1104 if (!strchr(mailto, '@'))
1105 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1107 compose_entries_set(compose, mailto, COMPOSE_TO);
1108 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1109 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1112 * CLAWS: just don't allow return receipt request, even if the user
1113 * may want to send an email. simple but foolproof.
1115 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1117 compose_add_field_list( compose, listAddress );
1119 if (item && item->prefs && item->prefs->compose_with_format) {
1120 subject_format = item->prefs->compose_subject_format;
1121 body_format = item->prefs->compose_body_format;
1122 } else if (account->compose_with_format) {
1123 subject_format = account->compose_subject_format;
1124 body_format = account->compose_body_format;
1125 } else if (prefs_common.compose_with_format) {
1126 subject_format = prefs_common.compose_subject_format;
1127 body_format = prefs_common.compose_body_format;
1130 if (subject_format || body_format) {
1133 && *subject_format != '\0' )
1135 gchar *subject = NULL;
1140 dummyinfo = compose_msginfo_new_from_compose(compose);
1142 /* decode \-escape sequences in the internal representation of the quote format */
1143 tmp = malloc(strlen(subject_format)+1);
1144 pref_get_unescaped_pref(tmp, subject_format);
1146 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1148 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1149 compose->gtkaspell);
1151 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1153 quote_fmt_scan_string(tmp);
1156 buf = quote_fmt_get_buffer();
1158 alertpanel_error(_("New message subject format error."));
1160 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1161 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1162 quote_fmt_reset_vartable();
1169 && *body_format != '\0' )
1172 GtkTextBuffer *buffer;
1173 GtkTextIter start, end;
1177 dummyinfo = compose_msginfo_new_from_compose(compose);
1179 text = GTK_TEXT_VIEW(compose->text);
1180 buffer = gtk_text_view_get_buffer(text);
1181 gtk_text_buffer_get_start_iter(buffer, &start);
1182 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1183 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1185 compose_quote_fmt(compose, dummyinfo,
1187 NULL, tmp, FALSE, TRUE,
1188 _("New message body format error at line %d."));
1189 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1190 quote_fmt_reset_vartable();
1196 procmsg_msginfo_free( dummyinfo );
1202 for (i = 0; i < attach_files->len; i++) {
1203 file = g_ptr_array_index(attach_files, i);
1204 compose_attach_append(compose, file, file, NULL);
1208 compose_show_first_last_header(compose, TRUE);
1210 /* Set save folder */
1211 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1212 gchar *folderidentifier;
1214 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1215 folderidentifier = folder_item_get_identifier(item);
1216 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1217 g_free(folderidentifier);
1220 gtk_widget_grab_focus(compose->header_last->entry);
1222 undo_unblock(compose->undostruct);
1224 if (prefs_common.auto_exteditor)
1225 compose_exec_ext_editor(compose);
1227 compose->draft_timeout_tag = -1;
1228 SCROLL_TO_CURSOR(compose);
1230 compose->modified = FALSE;
1231 compose_set_title(compose);
1235 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1236 gboolean override_pref)
1238 gchar *privacy = NULL;
1240 g_return_if_fail(compose != NULL);
1241 g_return_if_fail(account != NULL);
1243 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1246 if (account->default_privacy_system
1247 && strlen(account->default_privacy_system)) {
1248 privacy = account->default_privacy_system;
1250 GSList *privacy_avail = privacy_get_system_ids();
1251 if (privacy_avail && g_slist_length(privacy_avail)) {
1252 privacy = (gchar *)(privacy_avail->data);
1255 if (privacy != NULL) {
1256 if (compose->privacy_system == NULL)
1257 compose->privacy_system = g_strdup(privacy);
1258 else if (*(compose->privacy_system) == '\0') {
1259 g_free(compose->privacy_system);
1260 compose->privacy_system = g_strdup(privacy);
1262 compose_update_privacy_system_menu_item(compose, FALSE);
1263 compose_use_encryption(compose, TRUE);
1267 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1269 gchar *privacy = NULL;
1271 if (account->default_privacy_system
1272 && strlen(account->default_privacy_system)) {
1273 privacy = account->default_privacy_system;
1275 GSList *privacy_avail = privacy_get_system_ids();
1276 if (privacy_avail && g_slist_length(privacy_avail)) {
1277 privacy = (gchar *)(privacy_avail->data);
1280 if (privacy != NULL) {
1281 if (compose->privacy_system == NULL)
1282 compose->privacy_system = g_strdup(privacy);
1283 compose_update_privacy_system_menu_item(compose, FALSE);
1284 compose_use_signing(compose, TRUE);
1288 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1292 Compose *compose = NULL;
1293 GtkItemFactory *ifactory = NULL;
1295 g_return_val_if_fail(msginfo_list != NULL, NULL);
1297 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1298 g_return_val_if_fail(msginfo != NULL, NULL);
1300 list_len = g_slist_length(msginfo_list);
1304 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1305 FALSE, prefs_common.default_reply_list, FALSE, body);
1307 case COMPOSE_REPLY_WITH_QUOTE:
1308 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1309 FALSE, prefs_common.default_reply_list, FALSE, body);
1311 case COMPOSE_REPLY_WITHOUT_QUOTE:
1312 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1313 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1315 case COMPOSE_REPLY_TO_SENDER:
1316 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1317 FALSE, FALSE, TRUE, body);
1319 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1320 compose = compose_followup_and_reply_to(msginfo,
1321 COMPOSE_QUOTE_CHECK,
1322 FALSE, FALSE, body);
1324 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1325 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1326 FALSE, FALSE, TRUE, body);
1328 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1329 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1330 FALSE, FALSE, TRUE, NULL);
1332 case COMPOSE_REPLY_TO_ALL:
1333 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1334 TRUE, FALSE, FALSE, body);
1336 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1337 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1338 TRUE, FALSE, FALSE, body);
1340 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1341 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1342 TRUE, FALSE, FALSE, NULL);
1344 case COMPOSE_REPLY_TO_LIST:
1345 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1346 FALSE, TRUE, FALSE, body);
1348 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1349 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1350 FALSE, TRUE, FALSE, body);
1352 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1353 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1354 FALSE, TRUE, FALSE, NULL);
1356 case COMPOSE_FORWARD:
1357 if (prefs_common.forward_as_attachment) {
1358 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1361 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1365 case COMPOSE_FORWARD_INLINE:
1366 /* check if we reply to more than one Message */
1367 if (list_len == 1) {
1368 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1371 /* more messages FALL THROUGH */
1372 case COMPOSE_FORWARD_AS_ATTACH:
1373 compose = compose_forward_multiple(NULL, msginfo_list);
1375 case COMPOSE_REDIRECT:
1376 compose = compose_redirect(NULL, msginfo, FALSE);
1379 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1382 if (compose == NULL) {
1383 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1386 ifactory = gtk_item_factory_from_widget(compose->menubar);
1388 compose->rmode = mode;
1389 switch (compose->rmode) {
1391 case COMPOSE_REPLY_WITH_QUOTE:
1392 case COMPOSE_REPLY_WITHOUT_QUOTE:
1393 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1394 debug_print("reply mode Normal\n");
1395 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1396 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1398 case COMPOSE_REPLY_TO_SENDER:
1399 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1400 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1401 debug_print("reply mode Sender\n");
1402 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1404 case COMPOSE_REPLY_TO_ALL:
1405 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1406 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1407 debug_print("reply mode All\n");
1408 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1410 case COMPOSE_REPLY_TO_LIST:
1411 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1412 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1413 debug_print("reply mode List\n");
1414 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1422 static Compose *compose_reply(MsgInfo *msginfo,
1423 ComposeQuoteMode quote_mode,
1429 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1430 to_sender, FALSE, body);
1433 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1434 ComposeQuoteMode quote_mode,
1439 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1440 to_sender, TRUE, body);
1443 static void compose_extract_original_charset(Compose *compose)
1445 MsgInfo *info = NULL;
1446 if (compose->replyinfo) {
1447 info = compose->replyinfo;
1448 } else if (compose->fwdinfo) {
1449 info = compose->fwdinfo;
1450 } else if (compose->targetinfo) {
1451 info = compose->targetinfo;
1454 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1455 MimeInfo *partinfo = mimeinfo;
1456 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1457 partinfo = procmime_mimeinfo_next(partinfo);
1459 compose->orig_charset =
1460 g_strdup(procmime_mimeinfo_get_parameter(
1461 partinfo, "charset"));
1463 procmime_mimeinfo_free_all(mimeinfo);
1467 #define SIGNAL_BLOCK(buffer) { \
1468 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1469 G_CALLBACK(compose_changed_cb), \
1471 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1472 G_CALLBACK(text_inserted), \
1476 #define SIGNAL_UNBLOCK(buffer) { \
1477 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1478 G_CALLBACK(compose_changed_cb), \
1480 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(text_inserted), \
1485 static Compose *compose_generic_reply(MsgInfo *msginfo,
1486 ComposeQuoteMode quote_mode,
1487 gboolean to_all, gboolean to_ml,
1489 gboolean followup_and_reply_to,
1492 GtkItemFactory *ifactory;
1494 PrefsAccount *account = NULL;
1495 GtkTextView *textview;
1496 GtkTextBuffer *textbuf;
1497 gboolean quote = FALSE;
1498 const gchar *qmark = NULL;
1499 const gchar *body_fmt = NULL;
1501 g_return_val_if_fail(msginfo != NULL, NULL);
1502 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1504 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1506 g_return_val_if_fail(account != NULL, NULL);
1508 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1510 compose->updating = TRUE;
1512 ifactory = gtk_item_factory_from_widget(compose->menubar);
1514 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1515 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1517 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1518 if (!compose->replyinfo)
1519 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1521 compose_extract_original_charset(compose);
1523 if (msginfo->folder && msginfo->folder->ret_rcpt)
1524 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1526 /* Set save folder */
1527 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1528 gchar *folderidentifier;
1530 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1531 folderidentifier = folder_item_get_identifier(msginfo->folder);
1532 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1533 g_free(folderidentifier);
1536 if (compose_parse_header(compose, msginfo) < 0) {
1537 compose->updating = FALSE;
1538 compose_destroy(compose);
1542 /* override from name according to folder properties */
1543 if (msginfo->folder && msginfo->folder->prefs &&
1544 msginfo->folder->prefs->reply_with_format &&
1545 msginfo->folder->prefs->reply_override_from_format &&
1546 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1551 /* decode \-escape sequences in the internal representation of the quote format */
1552 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1553 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1556 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1557 compose->gtkaspell);
1559 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1561 quote_fmt_scan_string(tmp);
1564 buf = quote_fmt_get_buffer();
1566 alertpanel_error(_("Message reply From format error."));
1568 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1569 quote_fmt_reset_vartable();
1574 textview = (GTK_TEXT_VIEW(compose->text));
1575 textbuf = gtk_text_view_get_buffer(textview);
1576 compose_create_tags(textview, compose);
1578 undo_block(compose->undostruct);
1580 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1583 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1584 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1585 /* use the reply format of folder (if enabled), or the account's one
1586 (if enabled) or fallback to the global reply format, which is always
1587 enabled (even if empty), and use the relevant quotemark */
1589 if (msginfo->folder && msginfo->folder->prefs &&
1590 msginfo->folder->prefs->reply_with_format) {
1591 qmark = msginfo->folder->prefs->reply_quotemark;
1592 body_fmt = msginfo->folder->prefs->reply_body_format;
1594 } else if (account->reply_with_format) {
1595 qmark = account->reply_quotemark;
1596 body_fmt = account->reply_body_format;
1599 qmark = prefs_common.quotemark;
1600 body_fmt = prefs_common.quotefmt;
1605 /* empty quotemark is not allowed */
1606 if (qmark == NULL || *qmark == '\0')
1608 compose_quote_fmt(compose, compose->replyinfo,
1609 body_fmt, qmark, body, FALSE, TRUE,
1610 _("Message reply format error at line %d."));
1611 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1612 quote_fmt_reset_vartable();
1615 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1616 compose_force_encryption(compose, account, FALSE);
1619 SIGNAL_BLOCK(textbuf);
1621 if (account->auto_sig)
1622 compose_insert_sig(compose, FALSE);
1624 compose_wrap_all(compose);
1626 SIGNAL_UNBLOCK(textbuf);
1628 gtk_widget_grab_focus(compose->text);
1630 undo_unblock(compose->undostruct);
1632 if (prefs_common.auto_exteditor)
1633 compose_exec_ext_editor(compose);
1635 compose->modified = FALSE;
1636 compose_set_title(compose);
1638 compose->updating = FALSE;
1639 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1640 SCROLL_TO_CURSOR(compose);
1642 if (compose->deferred_destroy) {
1643 compose_destroy(compose);
1650 #define INSERT_FW_HEADER(var, hdr) \
1651 if (msginfo->var && *msginfo->var) { \
1652 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1653 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1654 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1657 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1658 gboolean as_attach, const gchar *body,
1659 gboolean no_extedit,
1663 GtkTextView *textview;
1664 GtkTextBuffer *textbuf;
1667 g_return_val_if_fail(msginfo != NULL, NULL);
1668 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1671 !(account = compose_guess_forward_account_from_msginfo
1673 account = cur_account;
1675 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1677 compose->updating = TRUE;
1678 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1679 if (!compose->fwdinfo)
1680 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1682 compose_extract_original_charset(compose);
1684 if (msginfo->subject && *msginfo->subject) {
1685 gchar *buf, *buf2, *p;
1687 buf = p = g_strdup(msginfo->subject);
1688 p += subject_get_prefix_length(p);
1689 memmove(buf, p, strlen(p) + 1);
1691 buf2 = g_strdup_printf("Fw: %s", buf);
1692 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1698 /* override from name according to folder properties */
1699 if (msginfo->folder && msginfo->folder->prefs &&
1700 msginfo->folder->prefs->forward_with_format &&
1701 msginfo->folder->prefs->forward_override_from_format &&
1702 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1706 MsgInfo *full_msginfo = NULL;
1709 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1711 full_msginfo = procmsg_msginfo_copy(msginfo);
1713 /* decode \-escape sequences in the internal representation of the quote format */
1714 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1715 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1718 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1719 compose->gtkaspell);
1721 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1723 quote_fmt_scan_string(tmp);
1726 buf = quote_fmt_get_buffer();
1728 alertpanel_error(_("Message forward From format error."));
1730 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1731 quote_fmt_reset_vartable();
1734 procmsg_msginfo_free(full_msginfo);
1737 textview = GTK_TEXT_VIEW(compose->text);
1738 textbuf = gtk_text_view_get_buffer(textview);
1739 compose_create_tags(textview, compose);
1741 undo_block(compose->undostruct);
1745 msgfile = procmsg_get_message_file(msginfo);
1746 if (!is_file_exist(msgfile))
1747 g_warning("%s: file not exist\n", msgfile);
1749 compose_attach_append(compose, msgfile, msgfile,
1754 const gchar *qmark = NULL;
1755 const gchar *body_fmt = prefs_common.fw_quotefmt;
1756 MsgInfo *full_msginfo;
1758 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1760 full_msginfo = procmsg_msginfo_copy(msginfo);
1762 /* use the forward format of folder (if enabled), or the account's one
1763 (if enabled) or fallback to the global forward format, which is always
1764 enabled (even if empty), and use the relevant quotemark */
1765 if (msginfo->folder && msginfo->folder->prefs &&
1766 msginfo->folder->prefs->forward_with_format) {
1767 qmark = msginfo->folder->prefs->forward_quotemark;
1768 body_fmt = msginfo->folder->prefs->forward_body_format;
1770 } else if (account->forward_with_format) {
1771 qmark = account->forward_quotemark;
1772 body_fmt = account->forward_body_format;
1775 qmark = prefs_common.fw_quotemark;
1776 body_fmt = prefs_common.fw_quotefmt;
1779 /* empty quotemark is not allowed */
1780 if (qmark == NULL || *qmark == '\0')
1783 compose_quote_fmt(compose, full_msginfo,
1784 body_fmt, qmark, body, FALSE, TRUE,
1785 _("Message forward format error at line %d."));
1786 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1787 quote_fmt_reset_vartable();
1788 compose_attach_parts(compose, msginfo);
1790 procmsg_msginfo_free(full_msginfo);
1793 SIGNAL_BLOCK(textbuf);
1795 if (account->auto_sig)
1796 compose_insert_sig(compose, FALSE);
1798 compose_wrap_all(compose);
1800 SIGNAL_UNBLOCK(textbuf);
1802 gtk_text_buffer_get_start_iter(textbuf, &iter);
1803 gtk_text_buffer_place_cursor(textbuf, &iter);
1805 gtk_widget_grab_focus(compose->header_last->entry);
1807 if (!no_extedit && prefs_common.auto_exteditor)
1808 compose_exec_ext_editor(compose);
1811 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1812 gchar *folderidentifier;
1814 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1815 folderidentifier = folder_item_get_identifier(msginfo->folder);
1816 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1817 g_free(folderidentifier);
1820 undo_unblock(compose->undostruct);
1822 compose->modified = FALSE;
1823 compose_set_title(compose);
1825 compose->updating = FALSE;
1826 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1827 SCROLL_TO_CURSOR(compose);
1829 if (compose->deferred_destroy) {
1830 compose_destroy(compose);
1837 #undef INSERT_FW_HEADER
1839 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1842 GtkTextView *textview;
1843 GtkTextBuffer *textbuf;
1847 gboolean single_mail = TRUE;
1849 g_return_val_if_fail(msginfo_list != NULL, NULL);
1851 if (g_slist_length(msginfo_list) > 1)
1852 single_mail = FALSE;
1854 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1855 if (((MsgInfo *)msginfo->data)->folder == NULL)
1858 /* guess account from first selected message */
1860 !(account = compose_guess_forward_account_from_msginfo
1861 (msginfo_list->data)))
1862 account = cur_account;
1864 g_return_val_if_fail(account != NULL, NULL);
1866 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1867 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1868 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1871 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1873 compose->updating = TRUE;
1875 /* override from name according to folder properties */
1876 if (msginfo_list->data) {
1877 MsgInfo *msginfo = msginfo_list->data;
1879 if (msginfo->folder && msginfo->folder->prefs &&
1880 msginfo->folder->prefs->forward_with_format &&
1881 msginfo->folder->prefs->forward_override_from_format &&
1882 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1887 /* decode \-escape sequences in the internal representation of the quote format */
1888 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1889 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1892 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1893 compose->gtkaspell);
1895 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1897 quote_fmt_scan_string(tmp);
1900 buf = quote_fmt_get_buffer();
1902 alertpanel_error(_("Message forward From format error."));
1904 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1905 quote_fmt_reset_vartable();
1911 textview = GTK_TEXT_VIEW(compose->text);
1912 textbuf = gtk_text_view_get_buffer(textview);
1913 compose_create_tags(textview, compose);
1915 undo_block(compose->undostruct);
1916 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1917 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1919 if (!is_file_exist(msgfile))
1920 g_warning("%s: file not exist\n", msgfile);
1922 compose_attach_append(compose, msgfile, msgfile,
1928 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1929 if (info->subject && *info->subject) {
1930 gchar *buf, *buf2, *p;
1932 buf = p = g_strdup(info->subject);
1933 p += subject_get_prefix_length(p);
1934 memmove(buf, p, strlen(p) + 1);
1936 buf2 = g_strdup_printf("Fw: %s", buf);
1937 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1943 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1944 _("Fw: multiple emails"));
1947 SIGNAL_BLOCK(textbuf);
1949 if (account->auto_sig)
1950 compose_insert_sig(compose, FALSE);
1952 compose_wrap_all(compose);
1954 SIGNAL_UNBLOCK(textbuf);
1956 gtk_text_buffer_get_start_iter(textbuf, &iter);
1957 gtk_text_buffer_place_cursor(textbuf, &iter);
1959 gtk_widget_grab_focus(compose->header_last->entry);
1960 undo_unblock(compose->undostruct);
1961 compose->modified = FALSE;
1962 compose_set_title(compose);
1964 compose->updating = FALSE;
1965 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1966 SCROLL_TO_CURSOR(compose);
1968 if (compose->deferred_destroy) {
1969 compose_destroy(compose);
1976 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1978 GtkTextIter start = *iter;
1979 GtkTextIter end_iter;
1980 int start_pos = gtk_text_iter_get_offset(&start);
1982 if (!compose->account->sig_sep)
1985 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1986 start_pos+strlen(compose->account->sig_sep));
1988 /* check sig separator */
1989 str = gtk_text_iter_get_text(&start, &end_iter);
1990 if (!strcmp(str, compose->account->sig_sep)) {
1992 /* check end of line (\n) */
1993 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1994 start_pos+strlen(compose->account->sig_sep));
1995 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1996 start_pos+strlen(compose->account->sig_sep)+1);
1997 tmp = gtk_text_iter_get_text(&start, &end_iter);
1998 if (!strcmp(tmp,"\n")) {
2010 static void compose_colorize_signature(Compose *compose)
2012 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2014 GtkTextIter end_iter;
2015 gtk_text_buffer_get_start_iter(buffer, &iter);
2016 while (gtk_text_iter_forward_line(&iter))
2017 if (compose_is_sig_separator(compose, buffer, &iter)) {
2018 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2019 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2023 #define BLOCK_WRAP() { \
2024 prev_autowrap = compose->autowrap; \
2025 buffer = gtk_text_view_get_buffer( \
2026 GTK_TEXT_VIEW(compose->text)); \
2027 compose->autowrap = FALSE; \
2029 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2030 G_CALLBACK(compose_changed_cb), \
2032 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2033 G_CALLBACK(text_inserted), \
2036 #define UNBLOCK_WRAP() { \
2037 compose->autowrap = prev_autowrap; \
2038 if (compose->autowrap) { \
2039 gint old = compose->draft_timeout_tag; \
2040 compose->draft_timeout_tag = -2; \
2041 compose_wrap_all(compose); \
2042 compose->draft_timeout_tag = old; \
2045 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2046 G_CALLBACK(compose_changed_cb), \
2048 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2049 G_CALLBACK(text_inserted), \
2053 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2055 Compose *compose = NULL;
2056 PrefsAccount *account = NULL;
2057 GtkTextView *textview;
2058 GtkTextBuffer *textbuf;
2062 gchar buf[BUFFSIZE];
2063 gboolean use_signing = FALSE;
2064 gboolean use_encryption = FALSE;
2065 gchar *privacy_system = NULL;
2066 int priority = PRIORITY_NORMAL;
2067 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2069 g_return_val_if_fail(msginfo != NULL, NULL);
2070 g_return_val_if_fail(msginfo->folder != NULL, NULL);
2072 if (compose_put_existing_to_front(msginfo)) {
2076 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2077 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2078 gchar queueheader_buf[BUFFSIZE];
2081 /* Select Account from queue headers */
2082 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2083 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2084 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2085 account = account_find_from_id(id);
2087 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2088 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2089 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2090 account = account_find_from_id(id);
2092 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2093 sizeof(queueheader_buf), "NAID:")) {
2094 id = atoi(&queueheader_buf[strlen("NAID:")]);
2095 account = account_find_from_id(id);
2097 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2098 sizeof(queueheader_buf), "MAID:")) {
2099 id = atoi(&queueheader_buf[strlen("MAID:")]);
2100 account = account_find_from_id(id);
2102 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2103 sizeof(queueheader_buf), "S:")) {
2104 account = account_find_from_address(queueheader_buf, FALSE);
2106 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2107 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2108 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2109 use_signing = param;
2112 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2113 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2114 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2115 use_signing = param;
2118 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2119 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2120 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2121 use_encryption = param;
2123 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2124 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2125 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2126 use_encryption = param;
2128 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2129 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2130 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2132 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2133 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2134 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2136 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "X-Priority: ")) {
2138 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2141 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2142 sizeof(queueheader_buf), "RMID:")) {
2143 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2144 if (tokens[0] && tokens[1] && tokens[2]) {
2145 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2146 if (orig_item != NULL) {
2147 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2152 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2153 sizeof(queueheader_buf), "FMID:")) {
2154 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2155 if (tokens[0] && tokens[1] && tokens[2]) {
2156 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2157 if (orig_item != NULL) {
2158 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2164 account = msginfo->folder->folder->account;
2167 if (!account && prefs_common.reedit_account_autosel) {
2168 gchar from[BUFFSIZE];
2169 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2170 extract_address(from);
2171 account = account_find_from_address(from, FALSE);
2175 account = cur_account;
2177 g_return_val_if_fail(account != NULL, NULL);
2179 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2181 compose->replyinfo = replyinfo;
2182 compose->fwdinfo = fwdinfo;
2184 compose->updating = TRUE;
2185 compose->priority = priority;
2187 if (privacy_system != NULL) {
2188 compose->privacy_system = privacy_system;
2189 compose_use_signing(compose, use_signing);
2190 compose_use_encryption(compose, use_encryption);
2191 compose_update_privacy_system_menu_item(compose, FALSE);
2193 activate_privacy_system(compose, account, FALSE);
2196 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2198 compose_extract_original_charset(compose);
2200 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2201 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2202 gchar queueheader_buf[BUFFSIZE];
2204 /* Set message save folder */
2205 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2208 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2209 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2210 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2213 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2215 GtkItemFactory *ifactory;
2216 ifactory = gtk_item_factory_from_widget(compose->menubar);
2217 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2222 if (compose_parse_header(compose, msginfo) < 0) {
2223 compose->updating = FALSE;
2224 compose_destroy(compose);
2227 compose_reedit_set_entry(compose, msginfo);
2229 textview = GTK_TEXT_VIEW(compose->text);
2230 textbuf = gtk_text_view_get_buffer(textview);
2231 compose_create_tags(textview, compose);
2233 mark = gtk_text_buffer_get_insert(textbuf);
2234 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2236 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2237 G_CALLBACK(compose_changed_cb),
2240 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2241 fp = procmime_get_first_encrypted_text_content(msginfo);
2243 compose_force_encryption(compose, account, TRUE);
2246 fp = procmime_get_first_text_content(msginfo);
2249 g_warning("Can't get text part\n");
2253 gboolean prev_autowrap = compose->autowrap;
2254 GtkTextBuffer *buffer = textbuf;
2256 while (fgets(buf, sizeof(buf), fp) != NULL) {
2258 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2264 compose_attach_parts(compose, msginfo);
2266 compose_colorize_signature(compose);
2268 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2269 G_CALLBACK(compose_changed_cb),
2272 gtk_widget_grab_focus(compose->text);
2274 if (prefs_common.auto_exteditor) {
2275 compose_exec_ext_editor(compose);
2277 compose->modified = FALSE;
2278 compose_set_title(compose);
2280 compose->updating = FALSE;
2281 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2282 SCROLL_TO_CURSOR(compose);
2284 if (compose->deferred_destroy) {
2285 compose_destroy(compose);
2289 compose->sig_str = compose_get_signature_str(compose);
2294 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2299 GtkItemFactory *ifactory;
2302 g_return_val_if_fail(msginfo != NULL, NULL);
2305 account = account_get_reply_account(msginfo,
2306 prefs_common.reply_account_autosel);
2307 g_return_val_if_fail(account != NULL, NULL);
2309 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2311 compose->updating = TRUE;
2313 ifactory = gtk_item_factory_from_widget(compose->menubar);
2314 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2315 compose->replyinfo = NULL;
2316 compose->fwdinfo = NULL;
2318 compose_show_first_last_header(compose, TRUE);
2320 gtk_widget_grab_focus(compose->header_last->entry);
2322 filename = procmsg_get_message_file(msginfo);
2324 if (filename == NULL) {
2325 compose->updating = FALSE;
2326 compose_destroy(compose);
2331 compose->redirect_filename = filename;
2333 /* Set save folder */
2334 item = msginfo->folder;
2335 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2336 gchar *folderidentifier;
2338 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2339 folderidentifier = folder_item_get_identifier(item);
2340 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2341 g_free(folderidentifier);
2344 compose_attach_parts(compose, msginfo);
2346 if (msginfo->subject)
2347 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2349 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2351 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2352 _("Message redirect format error at line %d."));
2353 quote_fmt_reset_vartable();
2354 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2356 compose_colorize_signature(compose);
2358 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2359 menu_set_sensitive(ifactory, "/Add...", FALSE);
2360 menu_set_sensitive(ifactory, "/Remove", FALSE);
2361 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2363 ifactory = gtk_item_factory_from_widget(compose->menubar);
2364 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2365 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2366 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2367 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2368 menu_set_sensitive(ifactory, "/Edit", FALSE);
2369 menu_set_sensitive(ifactory, "/Options", FALSE);
2370 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2371 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2373 if (compose->toolbar->draft_btn)
2374 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2375 if (compose->toolbar->insert_btn)
2376 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2377 if (compose->toolbar->attach_btn)
2378 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2379 if (compose->toolbar->sig_btn)
2380 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2381 if (compose->toolbar->exteditor_btn)
2382 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2383 if (compose->toolbar->linewrap_current_btn)
2384 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2385 if (compose->toolbar->linewrap_all_btn)
2386 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2388 compose->modified = FALSE;
2389 compose_set_title(compose);
2390 compose->updating = FALSE;
2391 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2392 SCROLL_TO_CURSOR(compose);
2394 if (compose->deferred_destroy) {
2395 compose_destroy(compose);
2402 GList *compose_get_compose_list(void)
2404 return compose_list;
2407 void compose_entry_append(Compose *compose, const gchar *address,
2408 ComposeEntryType type)
2410 const gchar *header;
2412 gboolean in_quote = FALSE;
2413 if (!address || *address == '\0') return;
2420 header = N_("Bcc:");
2422 case COMPOSE_REPLYTO:
2423 header = N_("Reply-To:");
2425 case COMPOSE_NEWSGROUPS:
2426 header = N_("Newsgroups:");
2428 case COMPOSE_FOLLOWUPTO:
2429 header = N_( "Followup-To:");
2436 header = prefs_common_translated_header_name(header);
2438 cur = begin = (gchar *)address;
2440 /* we separate the line by commas, but not if we're inside a quoted
2442 while (*cur != '\0') {
2444 in_quote = !in_quote;
2445 if (*cur == ',' && !in_quote) {
2446 gchar *tmp = g_strdup(begin);
2448 tmp[cur-begin]='\0';
2451 while (*tmp == ' ' || *tmp == '\t')
2453 compose_add_header_entry(compose, header, tmp);
2460 gchar *tmp = g_strdup(begin);
2462 tmp[cur-begin]='\0';
2465 while (*tmp == ' ' || *tmp == '\t')
2467 compose_add_header_entry(compose, header, tmp);
2472 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2474 static GdkColor yellow;
2475 static GdkColor black;
2476 static gboolean yellow_initialised = FALSE;
2480 if (!yellow_initialised) {
2481 gdk_color_parse("#f5f6be", &yellow);
2482 gdk_color_parse("#000000", &black);
2483 yellow_initialised = gdk_colormap_alloc_color(
2484 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2485 yellow_initialised &= gdk_colormap_alloc_color(
2486 gdk_colormap_get_system(), &black, FALSE, TRUE);
2489 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2490 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2491 if (gtk_entry_get_text(entry) &&
2492 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2493 if (yellow_initialised) {
2494 gtk_widget_modify_base(
2495 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2496 GTK_STATE_NORMAL, &yellow);
2497 gtk_widget_modify_text(
2498 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2499 GTK_STATE_NORMAL, &black);
2505 void compose_toolbar_cb(gint action, gpointer data)
2507 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2508 Compose *compose = (Compose*)toolbar_item->parent;
2510 g_return_if_fail(compose != NULL);
2514 compose_send_cb(compose, 0, NULL);
2517 compose_send_later_cb(compose, 0, NULL);
2520 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2523 compose_insert_file_cb(compose, 0, NULL);
2526 compose_attach_cb(compose, 0, NULL);
2529 compose_insert_sig(compose, FALSE);
2532 compose_ext_editor_cb(compose, 0, NULL);
2534 case A_LINEWRAP_CURRENT:
2535 compose_beautify_paragraph(compose, NULL, TRUE);
2537 case A_LINEWRAP_ALL:
2538 compose_wrap_all_full(compose, TRUE);
2541 compose_address_cb(compose, 0, NULL);
2544 case A_CHECK_SPELLING:
2545 compose_check_all(compose);
2553 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2558 gchar *subject = NULL;
2562 gchar **attach = NULL;
2564 /* get mailto parts but skip from */
2565 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2568 compose_entry_append(compose, to, to_type);
2570 compose_entry_append(compose, cc, COMPOSE_CC);
2572 compose_entry_append(compose, bcc, COMPOSE_BCC);
2574 if (!g_utf8_validate (subject, -1, NULL)) {
2575 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2576 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2579 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2583 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2584 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2587 gboolean prev_autowrap = compose->autowrap;
2589 compose->autowrap = FALSE;
2591 mark = gtk_text_buffer_get_insert(buffer);
2592 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2594 if (!g_utf8_validate (body, -1, NULL)) {
2595 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2596 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2599 gtk_text_buffer_insert(buffer, &iter, body, -1);
2601 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2603 compose->autowrap = prev_autowrap;
2604 if (compose->autowrap)
2605 compose_wrap_all(compose);
2609 gint i = 0, att = 0;
2610 gchar *warn_files = NULL;
2611 while (attach[i] != NULL) {
2612 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2613 if (utf8_filename) {
2614 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2615 gchar *tmp = g_strdup_printf("%s%s\n",
2616 warn_files?warn_files:"",
2622 g_free(utf8_filename);
2624 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2629 alertpanel_notice(ngettext(
2630 "The following file has been attached: \n%s",
2631 "The following files have been attached: \n%s", att), warn_files);
2643 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2645 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2646 {"Cc:", NULL, TRUE},
2647 {"References:", NULL, FALSE},
2648 {"Bcc:", NULL, TRUE},
2649 {"Newsgroups:", NULL, TRUE},
2650 {"Followup-To:", NULL, TRUE},
2651 {"List-Post:", NULL, FALSE},
2652 {"X-Priority:", NULL, FALSE},
2653 {NULL, NULL, FALSE}};
2669 g_return_val_if_fail(msginfo != NULL, -1);
2671 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2672 procheader_get_header_fields(fp, hentry);
2675 if (hentry[H_REPLY_TO].body != NULL) {
2676 if (hentry[H_REPLY_TO].body[0] != '\0') {
2678 conv_unmime_header(hentry[H_REPLY_TO].body,
2681 g_free(hentry[H_REPLY_TO].body);
2682 hentry[H_REPLY_TO].body = NULL;
2684 if (hentry[H_CC].body != NULL) {
2685 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2686 g_free(hentry[H_CC].body);
2687 hentry[H_CC].body = NULL;
2689 if (hentry[H_REFERENCES].body != NULL) {
2690 if (compose->mode == COMPOSE_REEDIT)
2691 compose->references = hentry[H_REFERENCES].body;
2693 compose->references = compose_parse_references
2694 (hentry[H_REFERENCES].body, msginfo->msgid);
2695 g_free(hentry[H_REFERENCES].body);
2697 hentry[H_REFERENCES].body = NULL;
2699 if (hentry[H_BCC].body != NULL) {
2700 if (compose->mode == COMPOSE_REEDIT)
2702 conv_unmime_header(hentry[H_BCC].body, NULL);
2703 g_free(hentry[H_BCC].body);
2704 hentry[H_BCC].body = NULL;
2706 if (hentry[H_NEWSGROUPS].body != NULL) {
2707 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2708 hentry[H_NEWSGROUPS].body = NULL;
2710 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2711 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2712 compose->followup_to =
2713 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2716 g_free(hentry[H_FOLLOWUP_TO].body);
2717 hentry[H_FOLLOWUP_TO].body = NULL;
2719 if (hentry[H_LIST_POST].body != NULL) {
2722 extract_address(hentry[H_LIST_POST].body);
2723 if (hentry[H_LIST_POST].body[0] != '\0') {
2724 scan_mailto_url(hentry[H_LIST_POST].body,
2725 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2727 g_free(compose->ml_post);
2728 compose->ml_post = to;
2731 g_free(hentry[H_LIST_POST].body);
2732 hentry[H_LIST_POST].body = NULL;
2735 /* CLAWS - X-Priority */
2736 if (compose->mode == COMPOSE_REEDIT)
2737 if (hentry[H_X_PRIORITY].body != NULL) {
2740 priority = atoi(hentry[H_X_PRIORITY].body);
2741 g_free(hentry[H_X_PRIORITY].body);
2743 hentry[H_X_PRIORITY].body = NULL;
2745 if (priority < PRIORITY_HIGHEST ||
2746 priority > PRIORITY_LOWEST)
2747 priority = PRIORITY_NORMAL;
2749 compose->priority = priority;
2752 if (compose->mode == COMPOSE_REEDIT) {
2753 if (msginfo->inreplyto && *msginfo->inreplyto)
2754 compose->inreplyto = g_strdup(msginfo->inreplyto);
2758 if (msginfo->msgid && *msginfo->msgid)
2759 compose->inreplyto = g_strdup(msginfo->msgid);
2761 if (!compose->references) {
2762 if (msginfo->msgid && *msginfo->msgid) {
2763 if (msginfo->inreplyto && *msginfo->inreplyto)
2764 compose->references =
2765 g_strdup_printf("<%s>\n\t<%s>",
2769 compose->references =
2770 g_strconcat("<", msginfo->msgid, ">",
2772 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2773 compose->references =
2774 g_strconcat("<", msginfo->inreplyto, ">",
2782 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2784 GSList *ref_id_list, *cur;
2788 ref_id_list = references_list_append(NULL, ref);
2789 if (!ref_id_list) return NULL;
2790 if (msgid && *msgid)
2791 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2796 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2797 /* "<" + Message-ID + ">" + CR+LF+TAB */
2798 len += strlen((gchar *)cur->data) + 5;
2800 if (len > MAX_REFERENCES_LEN) {
2801 /* remove second message-ID */
2802 if (ref_id_list && ref_id_list->next &&
2803 ref_id_list->next->next) {
2804 g_free(ref_id_list->next->data);
2805 ref_id_list = g_slist_remove
2806 (ref_id_list, ref_id_list->next->data);
2808 slist_free_strings(ref_id_list);
2809 g_slist_free(ref_id_list);
2816 new_ref = g_string_new("");
2817 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2818 if (new_ref->len > 0)
2819 g_string_append(new_ref, "\n\t");
2820 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2823 slist_free_strings(ref_id_list);
2824 g_slist_free(ref_id_list);
2826 new_ref_str = new_ref->str;
2827 g_string_free(new_ref, FALSE);
2832 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2833 const gchar *fmt, const gchar *qmark,
2834 const gchar *body, gboolean rewrap,
2835 gboolean need_unescape,
2836 const gchar *err_msg)
2838 MsgInfo* dummyinfo = NULL;
2839 gchar *quote_str = NULL;
2841 gboolean prev_autowrap;
2842 const gchar *trimmed_body = body;
2843 gint cursor_pos = -1;
2844 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2845 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2850 SIGNAL_BLOCK(buffer);
2853 dummyinfo = compose_msginfo_new_from_compose(compose);
2854 msginfo = dummyinfo;
2857 if (qmark != NULL) {
2859 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2860 compose->gtkaspell);
2862 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2864 quote_fmt_scan_string(qmark);
2867 buf = quote_fmt_get_buffer();
2869 alertpanel_error(_("Quote mark format error."));
2871 Xstrdup_a(quote_str, buf, goto error)
2874 if (fmt && *fmt != '\0') {
2877 while (*trimmed_body == '\n')
2881 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2882 compose->gtkaspell);
2884 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2886 if (need_unescape) {
2889 /* decode \-escape sequences in the internal representation of the quote format */
2890 tmp = malloc(strlen(fmt)+1);
2891 pref_get_unescaped_pref(tmp, fmt);
2892 quote_fmt_scan_string(tmp);
2896 quote_fmt_scan_string(fmt);
2900 buf = quote_fmt_get_buffer();
2902 gint line = quote_fmt_get_line();
2903 alertpanel_error(err_msg, line);
2909 prev_autowrap = compose->autowrap;
2910 compose->autowrap = FALSE;
2912 mark = gtk_text_buffer_get_insert(buffer);
2913 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2914 if (g_utf8_validate(buf, -1, NULL)) {
2915 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2917 gchar *tmpout = NULL;
2918 tmpout = conv_codeset_strdup
2919 (buf, conv_get_locale_charset_str_no_utf8(),
2921 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2923 tmpout = g_malloc(strlen(buf)*2+1);
2924 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2926 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2930 cursor_pos = quote_fmt_get_cursor_pos();
2931 compose->set_cursor_pos = cursor_pos;
2932 if (cursor_pos == -1) {
2935 gtk_text_buffer_get_start_iter(buffer, &iter);
2936 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2937 gtk_text_buffer_place_cursor(buffer, &iter);
2939 compose->autowrap = prev_autowrap;
2940 if (compose->autowrap && rewrap)
2941 compose_wrap_all(compose);
2948 SIGNAL_UNBLOCK(buffer);
2950 procmsg_msginfo_free( dummyinfo );
2955 /* if ml_post is of type addr@host and from is of type
2956 * addr-anything@host, return TRUE
2958 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2960 gchar *left_ml = NULL;
2961 gchar *right_ml = NULL;
2962 gchar *left_from = NULL;
2963 gchar *right_from = NULL;
2964 gboolean result = FALSE;
2966 if (!ml_post || !from)
2969 left_ml = g_strdup(ml_post);
2970 if (strstr(left_ml, "@")) {
2971 right_ml = strstr(left_ml, "@")+1;
2972 *(strstr(left_ml, "@")) = '\0';
2975 left_from = g_strdup(from);
2976 if (strstr(left_from, "@")) {
2977 right_from = strstr(left_from, "@")+1;
2978 *(strstr(left_from, "@")) = '\0';
2981 if (left_ml && left_from && right_ml && right_from
2982 && !strncmp(left_from, left_ml, strlen(left_ml))
2983 && !strcmp(right_from, right_ml)) {
2992 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2994 gchar *my_addr1, *my_addr2;
2996 if (!addr1 || !addr2)
2999 Xstrdup_a(my_addr1, addr1, return FALSE);
3000 Xstrdup_a(my_addr2, addr2, return FALSE);
3002 extract_address(my_addr1);
3003 extract_address(my_addr2);
3005 return !strcasecmp(my_addr1, my_addr2);
3008 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3009 gboolean to_all, gboolean to_ml,
3011 gboolean followup_and_reply_to)
3013 GSList *cc_list = NULL;
3016 gchar *replyto = NULL;
3017 GHashTable *to_table;
3019 gboolean reply_to_ml = FALSE;
3020 gboolean default_reply_to = FALSE;
3022 g_return_if_fail(compose->account != NULL);
3023 g_return_if_fail(msginfo != NULL);
3025 reply_to_ml = to_ml && compose->ml_post;
3027 default_reply_to = msginfo->folder &&
3028 msginfo->folder->prefs->enable_default_reply_to;
3030 if (compose->account->protocol != A_NNTP) {
3031 if (reply_to_ml && !default_reply_to) {
3033 gboolean is_subscr = is_subscription(compose->ml_post,
3036 /* normal answer to ml post with a reply-to */
3037 compose_entry_append(compose,
3040 if (compose->replyto
3041 && !same_address(compose->ml_post, compose->replyto))
3042 compose_entry_append(compose,
3046 /* answer to subscription confirmation */
3047 if (compose->replyto)
3048 compose_entry_append(compose,
3051 else if (msginfo->from)
3052 compose_entry_append(compose,
3057 else if (!(to_all || to_sender) && default_reply_to) {
3058 compose_entry_append(compose,
3059 msginfo->folder->prefs->default_reply_to,
3061 compose_entry_mark_default_to(compose,
3062 msginfo->folder->prefs->default_reply_to);
3067 Xstrdup_a(tmp1, msginfo->from, return);
3068 extract_address(tmp1);
3069 if (to_all || to_sender ||
3070 !account_find_from_address(tmp1, FALSE))
3071 compose_entry_append(compose,
3072 (compose->replyto && !to_sender)
3073 ? compose->replyto :
3074 msginfo->from ? msginfo->from : "",
3076 else if (!to_all && !to_sender) {
3077 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3078 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3079 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3080 if (compose->replyto) {
3081 compose_entry_append(compose,
3085 compose_entry_append(compose,
3086 msginfo->from ? msginfo->from : "",
3090 /* replying to own mail, use original recp */
3091 compose_entry_append(compose,
3092 msginfo->to ? msginfo->to : "",
3094 compose_entry_append(compose,
3095 msginfo->cc ? msginfo->cc : "",
3101 if (to_sender || (compose->followup_to &&
3102 !strncmp(compose->followup_to, "poster", 6)))
3103 compose_entry_append
3105 (compose->replyto ? compose->replyto :
3106 msginfo->from ? msginfo->from : ""),
3109 else if (followup_and_reply_to || to_all) {
3110 compose_entry_append
3112 (compose->replyto ? compose->replyto :
3113 msginfo->from ? msginfo->from : ""),
3116 compose_entry_append
3118 compose->followup_to ? compose->followup_to :
3119 compose->newsgroups ? compose->newsgroups : "",
3120 COMPOSE_NEWSGROUPS);
3123 compose_entry_append
3125 compose->followup_to ? compose->followup_to :
3126 compose->newsgroups ? compose->newsgroups : "",
3127 COMPOSE_NEWSGROUPS);
3130 if (msginfo->subject && *msginfo->subject) {
3134 buf = p = g_strdup(msginfo->subject);
3135 p += subject_get_prefix_length(p);
3136 memmove(buf, p, strlen(p) + 1);
3138 buf2 = g_strdup_printf("Re: %s", buf);
3139 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3144 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3146 if (to_ml && compose->ml_post) return;
3147 if (!to_all || compose->account->protocol == A_NNTP) return;
3149 if (compose->replyto) {