2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2015 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/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_ALT_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
87 #include "quoted-printable.h"
91 #include "gtkshruler.h"
93 #include "alertpanel.h"
94 #include "manage_window.h"
96 #include "folder_item_prefs.h"
97 #include "addr_compl.h"
98 #include "quote_fmt.h"
100 #include "foldersel.h"
103 #include "message_search.h"
104 #include "combobox.h"
108 #include "autofaces.h"
109 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
140 } ComposeCallAdvancedAction;
144 PRIORITY_HIGHEST = 1,
153 COMPOSE_INSERT_SUCCESS,
154 COMPOSE_INSERT_READ_ERROR,
155 COMPOSE_INSERT_INVALID_CHARACTER,
156 COMPOSE_INSERT_NO_FILE
157 } ComposeInsertResult;
161 COMPOSE_WRITE_FOR_SEND,
162 COMPOSE_WRITE_FOR_STORE
167 COMPOSE_QUOTE_FORCED,
174 SUBJECT_FIELD_PRESENT,
179 #define B64_LINE_SIZE 57
180 #define B64_BUFFSIZE 77
182 #define MAX_REFERENCES_LEN 999
184 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
185 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
187 static GList *compose_list = NULL;
188 static GSList *extra_headers = NULL;
190 static Compose *compose_generic_new (PrefsAccount *account,
194 GList *listAddress );
196 static Compose *compose_create (PrefsAccount *account,
201 static void compose_entry_mark_default_to (Compose *compose,
202 const gchar *address);
203 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
204 ComposeQuoteMode quote_mode,
208 static Compose *compose_forward_multiple (PrefsAccount *account,
209 GSList *msginfo_list);
210 static Compose *compose_reply (MsgInfo *msginfo,
211 ComposeQuoteMode quote_mode,
216 static Compose *compose_reply_mode (ComposeMode mode,
217 GSList *msginfo_list,
219 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
220 static void compose_update_privacy_systems_menu(Compose *compose);
222 static GtkWidget *compose_account_option_menu_create
224 static void compose_set_out_encoding (Compose *compose);
225 static void compose_set_template_menu (Compose *compose);
226 static void compose_destroy (Compose *compose);
228 static MailField compose_entries_set (Compose *compose,
230 ComposeEntryType to_type);
231 static gint compose_parse_header (Compose *compose,
233 static gint compose_parse_manual_headers (Compose *compose,
235 HeaderEntry *entries);
236 static gchar *compose_parse_references (const gchar *ref,
239 static gchar *compose_quote_fmt (Compose *compose,
245 gboolean need_unescape,
246 const gchar *err_msg);
248 static void compose_reply_set_entry (Compose *compose,
254 followup_and_reply_to);
255 static void compose_reedit_set_entry (Compose *compose,
258 static void compose_insert_sig (Compose *compose,
260 static ComposeInsertResult compose_insert_file (Compose *compose,
263 static gboolean compose_attach_append (Compose *compose,
266 const gchar *content_type,
267 const gchar *charset);
268 static void compose_attach_parts (Compose *compose,
271 static gboolean compose_beautify_paragraph (Compose *compose,
272 GtkTextIter *par_iter,
274 static void compose_wrap_all (Compose *compose);
275 static void compose_wrap_all_full (Compose *compose,
278 static void compose_set_title (Compose *compose);
279 static void compose_select_account (Compose *compose,
280 PrefsAccount *account,
283 static PrefsAccount *compose_current_mail_account(void);
284 /* static gint compose_send (Compose *compose); */
285 static gboolean compose_check_for_valid_recipient
287 static gboolean compose_check_entries (Compose *compose,
288 gboolean check_everything);
289 static gint compose_write_to_file (Compose *compose,
292 gboolean attach_parts);
293 static gint compose_write_body_to_file (Compose *compose,
295 static gint compose_remove_reedit_target (Compose *compose,
297 static void compose_remove_draft (Compose *compose);
298 static gint compose_queue_sub (Compose *compose,
302 gboolean check_subject,
303 gboolean remove_reedit_target);
304 static int compose_add_attachments (Compose *compose,
306 static gchar *compose_get_header (Compose *compose);
307 static gchar *compose_get_manual_headers_info (Compose *compose);
309 static void compose_convert_header (Compose *compose,
314 gboolean addr_field);
316 static void compose_attach_info_free (AttachInfo *ainfo);
317 static void compose_attach_remove_selected (GtkAction *action,
320 static void compose_template_apply (Compose *compose,
323 static void compose_attach_property (GtkAction *action,
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 GdkNativeWindow socket_wid);
341 static gboolean compose_ext_editor_kill (Compose *compose);
342 static gboolean compose_input_cb (GIOChannel *source,
343 GIOCondition condition,
345 static void compose_set_ext_editor_sensitive (Compose *compose,
347 static gboolean compose_get_ext_editor_cmd_valid();
348 static gboolean compose_get_ext_editor_uses_socket();
349 static gboolean compose_ext_editor_plug_removed_cb
352 #endif /* G_OS_UNIX */
354 static void compose_undo_state_changed (UndoMain *undostruct,
359 static void compose_create_header_entry (Compose *compose);
360 static void compose_add_header_entry (Compose *compose, const gchar *header,
361 gchar *text, ComposePrefType pref_type);
362 static void compose_remove_header_entries(Compose *compose);
364 static void compose_update_priority_menu_item(Compose * compose);
366 static void compose_spell_menu_changed (void *data);
367 static void compose_dict_changed (void *data);
369 static void compose_add_field_list ( Compose *compose,
370 GList *listAddress );
372 /* callback functions */
374 static void compose_notebook_size_alloc (GtkNotebook *notebook,
375 GtkAllocation *allocation,
377 static gboolean compose_edit_size_alloc (GtkEditable *widget,
378 GtkAllocation *allocation,
379 GtkSHRuler *shruler);
380 static void account_activated (GtkComboBox *optmenu,
382 static void attach_selected (GtkTreeView *tree_view,
383 GtkTreePath *tree_path,
384 GtkTreeViewColumn *column,
386 static gboolean attach_button_pressed (GtkWidget *widget,
387 GdkEventButton *event,
389 static gboolean attach_key_pressed (GtkWidget *widget,
392 static void compose_send_cb (GtkAction *action, gpointer data);
393 static void compose_send_later_cb (GtkAction *action, gpointer data);
395 static void compose_save_cb (GtkAction *action,
398 static void compose_attach_cb (GtkAction *action,
400 static void compose_insert_file_cb (GtkAction *action,
402 static void compose_insert_sig_cb (GtkAction *action,
404 static void compose_replace_sig_cb (GtkAction *action,
407 static void compose_close_cb (GtkAction *action,
409 static void compose_print_cb (GtkAction *action,
412 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
414 static void compose_address_cb (GtkAction *action,
416 static void about_show_cb (GtkAction *action,
418 static void compose_template_activate_cb(GtkWidget *widget,
421 static void compose_ext_editor_cb (GtkAction *action,
424 static gint compose_delete_cb (GtkWidget *widget,
428 static void compose_undo_cb (GtkAction *action,
430 static void compose_redo_cb (GtkAction *action,
432 static void compose_cut_cb (GtkAction *action,
434 static void compose_copy_cb (GtkAction *action,
436 static void compose_paste_cb (GtkAction *action,
438 static void compose_paste_as_quote_cb (GtkAction *action,
440 static void compose_paste_no_wrap_cb (GtkAction *action,
442 static void compose_paste_wrap_cb (GtkAction *action,
444 static void compose_allsel_cb (GtkAction *action,
447 static void compose_advanced_action_cb (GtkAction *action,
450 static void compose_grab_focus_cb (GtkWidget *widget,
453 static void compose_changed_cb (GtkTextBuffer *textbuf,
456 static void compose_wrap_cb (GtkAction *action,
458 static void compose_wrap_all_cb (GtkAction *action,
460 static void compose_find_cb (GtkAction *action,
462 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
464 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
467 static void compose_toggle_ruler_cb (GtkToggleAction *action,
469 static void compose_toggle_sign_cb (GtkToggleAction *action,
471 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
473 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
474 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
475 static void activate_privacy_system (Compose *compose,
476 PrefsAccount *account,
478 static void compose_use_signing(Compose *compose, gboolean use_signing);
479 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
480 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
482 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
484 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
485 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
486 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
488 static void compose_attach_drag_received_cb (GtkWidget *widget,
489 GdkDragContext *drag_context,
492 GtkSelectionData *data,
496 static void compose_insert_drag_received_cb (GtkWidget *widget,
497 GdkDragContext *drag_context,
500 GtkSelectionData *data,
504 static void compose_header_drag_received_cb (GtkWidget *widget,
505 GdkDragContext *drag_context,
508 GtkSelectionData *data,
513 static gboolean compose_drag_drop (GtkWidget *widget,
514 GdkDragContext *drag_context,
516 guint time, gpointer user_data);
517 static gboolean completion_set_focus_to_subject
522 static void text_inserted (GtkTextBuffer *buffer,
527 static Compose *compose_generic_reply(MsgInfo *msginfo,
528 ComposeQuoteMode quote_mode,
532 gboolean followup_and_reply_to,
535 static void compose_headerentry_changed_cb (GtkWidget *entry,
536 ComposeHeaderEntry *headerentry);
537 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
539 ComposeHeaderEntry *headerentry);
540 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
541 ComposeHeaderEntry *headerentry);
543 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
545 static void compose_allow_user_actions (Compose *compose, gboolean allow);
547 static void compose_nothing_cb (GtkAction *action, gpointer data)
553 static void compose_check_all (GtkAction *action, gpointer data);
554 static void compose_highlight_all (GtkAction *action, gpointer data);
555 static void compose_check_backwards (GtkAction *action, gpointer data);
556 static void compose_check_forwards_go (GtkAction *action, gpointer data);
559 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
561 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
564 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
565 FolderItem *folder_item);
567 static void compose_attach_update_label(Compose *compose);
568 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
569 gboolean respect_default_to);
570 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
571 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
573 static GtkActionEntry compose_popup_entries[] =
575 {"Compose", NULL, "Compose" },
576 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
577 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
578 {"Compose/---", NULL, "---", NULL, NULL, NULL },
579 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
582 static GtkActionEntry compose_entries[] =
584 {"Menu", NULL, "Menu" },
586 {"Message", NULL, N_("_Message") },
587 {"Edit", NULL, N_("_Edit") },
589 {"Spelling", NULL, N_("_Spelling") },
591 {"Options", NULL, N_("_Options") },
592 {"Tools", NULL, N_("_Tools") },
593 {"Help", NULL, N_("_Help") },
595 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
596 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
597 {"Message/---", NULL, "---" },
599 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
600 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
601 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
602 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
603 /* {"Message/---", NULL, "---" }, */
604 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
605 /* {"Message/---", NULL, "---" }, */
606 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
607 /* {"Message/---", NULL, "---" }, */
608 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
611 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
612 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
613 {"Edit/---", NULL, "---" },
615 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
616 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
617 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
619 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
620 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
621 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
622 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
624 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
626 {"Edit/Advanced", NULL, N_("A_dvanced") },
627 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
628 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
629 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
630 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
631 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
632 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
633 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
634 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
635 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
636 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
637 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
638 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
639 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
640 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
642 /* {"Edit/---", NULL, "---" }, */
643 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
645 /* {"Edit/---", NULL, "---" }, */
646 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
647 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
648 /* {"Edit/---", NULL, "---" }, */
649 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
652 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
653 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
654 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
655 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
657 {"Spelling/---", NULL, "---" },
658 {"Spelling/Options", NULL, N_("_Options") },
663 {"Options/ReplyMode", NULL, N_("Reply _mode") },
664 {"Options/---", NULL, "---" },
665 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
666 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
668 /* {"Options/---", NULL, "---" }, */
670 {"Options/Priority", NULL, N_("_Priority") },
672 {"Options/Encoding", NULL, N_("Character _encoding") },
673 {"Options/Encoding/---", NULL, "---" },
674 #define ENC_ACTION(cs_char,c_char,string) \
675 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
677 {"Options/Encoding/Western", NULL, N_("Western European") },
678 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
679 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
680 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
681 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
682 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
683 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
684 {"Options/Encoding/Korean", NULL, N_("Korean") },
685 {"Options/Encoding/Thai", NULL, N_("Thai") },
688 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
690 {"Tools/Template", NULL, N_("_Template") },
691 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
692 {"Tools/Actions", NULL, N_("Actio_ns") },
693 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
696 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
699 static GtkToggleActionEntry compose_toggle_entries[] =
701 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
702 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
703 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
704 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
705 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
706 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
707 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
710 static GtkRadioActionEntry compose_radio_rm_entries[] =
712 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
713 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
714 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
715 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
718 static GtkRadioActionEntry compose_radio_prio_entries[] =
720 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
721 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
722 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
723 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
724 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
727 static GtkRadioActionEntry compose_radio_enc_entries[] =
729 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
764 static GtkTargetEntry compose_mime_types[] =
766 {"text/uri-list", 0, 0},
767 {"UTF8_STRING", 0, 0},
771 static gboolean compose_put_existing_to_front(MsgInfo *info)
773 const GList *compose_list = compose_get_compose_list();
774 const GList *elem = NULL;
777 for (elem = compose_list; elem != NULL && elem->data != NULL;
779 Compose *c = (Compose*)elem->data;
781 if (!c->targetinfo || !c->targetinfo->msgid ||
785 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
786 gtkut_window_popup(c->window);
794 static GdkColor quote_color1 =
795 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
796 static GdkColor quote_color2 =
797 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
798 static GdkColor quote_color3 =
799 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
801 static GdkColor quote_bgcolor1 =
802 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
803 static GdkColor quote_bgcolor2 =
804 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
805 static GdkColor quote_bgcolor3 =
806 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
808 static GdkColor signature_color = {
815 static GdkColor uri_color = {
822 static void compose_create_tags(GtkTextView *text, Compose *compose)
824 GtkTextBuffer *buffer;
825 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
826 #if !GTK_CHECK_VERSION(2, 24, 0)
833 buffer = gtk_text_view_get_buffer(text);
835 if (prefs_common.enable_color) {
836 /* grab the quote colors, converting from an int to a GdkColor */
837 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
839 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
841 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
843 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
845 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
847 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
849 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
851 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
854 signature_color = quote_color1 = quote_color2 = quote_color3 =
855 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
858 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
859 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860 "foreground-gdk", "e_color1,
861 "paragraph-background-gdk", "e_bgcolor1,
863 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
864 "foreground-gdk", "e_color2,
865 "paragraph-background-gdk", "e_bgcolor2,
867 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
868 "foreground-gdk", "e_color3,
869 "paragraph-background-gdk", "e_bgcolor3,
872 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
873 "foreground-gdk", "e_color1,
875 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
876 "foreground-gdk", "e_color2,
878 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
879 "foreground-gdk", "e_color3,
883 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
884 "foreground-gdk", &signature_color,
887 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
888 "foreground-gdk", &uri_color,
890 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
891 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
893 #if !GTK_CHECK_VERSION(2, 24, 0)
894 color[0] = quote_color1;
895 color[1] = quote_color2;
896 color[2] = quote_color3;
897 color[3] = quote_bgcolor1;
898 color[4] = quote_bgcolor2;
899 color[5] = quote_bgcolor3;
900 color[6] = signature_color;
901 color[7] = uri_color;
903 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
904 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
906 for (i = 0; i < 8; i++) {
907 if (success[i] == FALSE) {
908 g_warning("Compose: color allocation failed.");
909 quote_color1 = quote_color2 = quote_color3 =
910 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
911 signature_color = uri_color = black;
917 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
920 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
923 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
925 return compose_generic_new(account, mailto, item, NULL, NULL);
928 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
930 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
933 #define SCROLL_TO_CURSOR(compose) { \
934 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
935 gtk_text_view_get_buffer( \
936 GTK_TEXT_VIEW(compose->text))); \
937 gtk_text_view_scroll_mark_onscreen( \
938 GTK_TEXT_VIEW(compose->text), \
942 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
945 if (folderidentifier) {
946 #if !GTK_CHECK_VERSION(2, 24, 0)
947 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
949 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
951 prefs_common.compose_save_to_history = add_history(
952 prefs_common.compose_save_to_history, folderidentifier);
953 #if !GTK_CHECK_VERSION(2, 24, 0)
954 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
955 prefs_common.compose_save_to_history);
957 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
958 prefs_common.compose_save_to_history);
962 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
963 if (folderidentifier)
964 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
966 gtk_entry_set_text(GTK_ENTRY(entry), "");
969 static gchar *compose_get_save_to(Compose *compose)
972 gchar *result = NULL;
973 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
974 result = gtk_editable_get_chars(entry, 0, -1);
977 #if !GTK_CHECK_VERSION(2, 24, 0)
978 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
980 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
982 prefs_common.compose_save_to_history = add_history(
983 prefs_common.compose_save_to_history, result);
984 #if !GTK_CHECK_VERSION(2, 24, 0)
985 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
986 prefs_common.compose_save_to_history);
988 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
989 prefs_common.compose_save_to_history);
995 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
996 GList *attach_files, GList *listAddress )
999 GtkTextView *textview;
1000 GtkTextBuffer *textbuf;
1002 const gchar *subject_format = NULL;
1003 const gchar *body_format = NULL;
1004 gchar *mailto_from = NULL;
1005 PrefsAccount *mailto_account = NULL;
1006 MsgInfo* dummyinfo = NULL;
1007 gint cursor_pos = -1;
1008 MailField mfield = NO_FIELD_PRESENT;
1012 /* check if mailto defines a from */
1013 if (mailto && *mailto != '\0') {
1014 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1015 /* mailto defines a from, check if we can get account prefs from it,
1016 if not, the account prefs will be guessed using other ways, but we'll keep
1019 mailto_account = account_find_from_address(mailto_from, TRUE);
1020 if (mailto_account == NULL) {
1022 Xstrdup_a(tmp_from, mailto_from, return NULL);
1023 extract_address(tmp_from);
1024 mailto_account = account_find_from_address(tmp_from, TRUE);
1028 account = mailto_account;
1031 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1032 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1033 account = account_find_from_id(item->prefs->default_account);
1035 /* if no account prefs set, fallback to the current one */
1036 if (!account) account = cur_account;
1037 cm_return_val_if_fail(account != NULL, NULL);
1039 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1041 /* override from name if mailto asked for it */
1043 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1044 g_free(mailto_from);
1046 /* override from name according to folder properties */
1047 if (item && item->prefs &&
1048 item->prefs->compose_with_format &&
1049 item->prefs->compose_override_from_format &&
1050 *item->prefs->compose_override_from_format != '\0') {
1055 dummyinfo = compose_msginfo_new_from_compose(compose);
1057 /* decode \-escape sequences in the internal representation of the quote format */
1058 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1059 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1062 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1063 compose->gtkaspell);
1065 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1067 quote_fmt_scan_string(tmp);
1070 buf = quote_fmt_get_buffer();
1072 alertpanel_error(_("New message From format error."));
1074 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1075 quote_fmt_reset_vartable();
1080 compose->replyinfo = NULL;
1081 compose->fwdinfo = NULL;
1083 textview = GTK_TEXT_VIEW(compose->text);
1084 textbuf = gtk_text_view_get_buffer(textview);
1085 compose_create_tags(textview, compose);
1087 undo_block(compose->undostruct);
1089 compose_set_dictionaries_from_folder_prefs(compose, item);
1092 if (account->auto_sig)
1093 compose_insert_sig(compose, FALSE);
1094 gtk_text_buffer_get_start_iter(textbuf, &iter);
1095 gtk_text_buffer_place_cursor(textbuf, &iter);
1097 if (account->protocol != A_NNTP) {
1098 if (mailto && *mailto != '\0') {
1099 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1102 compose_set_folder_prefs(compose, item, TRUE);
1104 if (item && item->ret_rcpt) {
1105 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1108 if (mailto && *mailto != '\0') {
1109 if (!strchr(mailto, '@'))
1110 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1112 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1113 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1114 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1115 mfield = TO_FIELD_PRESENT;
1118 * CLAWS: just don't allow return receipt request, even if the user
1119 * may want to send an email. simple but foolproof.
1121 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1123 compose_add_field_list( compose, listAddress );
1125 if (item && item->prefs && item->prefs->compose_with_format) {
1126 subject_format = item->prefs->compose_subject_format;
1127 body_format = item->prefs->compose_body_format;
1128 } else if (account->compose_with_format) {
1129 subject_format = account->compose_subject_format;
1130 body_format = account->compose_body_format;
1131 } else if (prefs_common.compose_with_format) {
1132 subject_format = prefs_common.compose_subject_format;
1133 body_format = prefs_common.compose_body_format;
1136 if (subject_format || body_format) {
1139 && *subject_format != '\0' )
1141 gchar *subject = NULL;
1146 dummyinfo = compose_msginfo_new_from_compose(compose);
1148 /* decode \-escape sequences in the internal representation of the quote format */
1149 tmp = g_malloc(strlen(subject_format)+1);
1150 pref_get_unescaped_pref(tmp, subject_format);
1152 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1154 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1155 compose->gtkaspell);
1157 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1159 quote_fmt_scan_string(tmp);
1162 buf = quote_fmt_get_buffer();
1164 alertpanel_error(_("New message subject format error."));
1166 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1167 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1168 quote_fmt_reset_vartable();
1172 mfield = SUBJECT_FIELD_PRESENT;
1176 && *body_format != '\0' )
1179 GtkTextBuffer *buffer;
1180 GtkTextIter start, end;
1184 dummyinfo = compose_msginfo_new_from_compose(compose);
1186 text = GTK_TEXT_VIEW(compose->text);
1187 buffer = gtk_text_view_get_buffer(text);
1188 gtk_text_buffer_get_start_iter(buffer, &start);
1189 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1190 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1192 compose_quote_fmt(compose, dummyinfo,
1194 NULL, tmp, FALSE, TRUE,
1195 _("The body of the \"New message\" template has an error at line %d."));
1196 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1197 quote_fmt_reset_vartable();
1201 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1202 gtkaspell_highlight_all(compose->gtkaspell);
1204 mfield = BODY_FIELD_PRESENT;
1208 procmsg_msginfo_free( &dummyinfo );
1214 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1215 ainfo = (AttachInfo *) curr->data;
1216 compose_attach_append(compose, ainfo->file, ainfo->file,
1217 ainfo->content_type, ainfo->charset);
1221 compose_show_first_last_header(compose, TRUE);
1223 /* Set save folder */
1224 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1225 gchar *folderidentifier;
1227 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1228 folderidentifier = folder_item_get_identifier(item);
1229 compose_set_save_to(compose, folderidentifier);
1230 g_free(folderidentifier);
1233 /* Place cursor according to provided input (mfield) */
1235 case NO_FIELD_PRESENT:
1236 if (compose->header_last)
1237 gtk_widget_grab_focus(compose->header_last->entry);
1239 case TO_FIELD_PRESENT:
1240 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1242 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1245 gtk_widget_grab_focus(compose->subject_entry);
1247 case SUBJECT_FIELD_PRESENT:
1248 textview = GTK_TEXT_VIEW(compose->text);
1251 textbuf = gtk_text_view_get_buffer(textview);
1254 mark = gtk_text_buffer_get_insert(textbuf);
1255 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1256 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1258 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1259 * only defers where it comes to the variable body
1260 * is not null. If no body is present compose->text
1261 * will be null in which case you cannot place the
1262 * cursor inside the component so. An empty component
1263 * is therefore created before placing the cursor
1265 case BODY_FIELD_PRESENT:
1266 cursor_pos = quote_fmt_get_cursor_pos();
1267 if (cursor_pos == -1)
1268 gtk_widget_grab_focus(compose->header_last->entry);
1270 gtk_widget_grab_focus(compose->text);
1274 undo_unblock(compose->undostruct);
1276 if (prefs_common.auto_exteditor)
1277 compose_exec_ext_editor(compose);
1279 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1281 SCROLL_TO_CURSOR(compose);
1283 compose->modified = FALSE;
1284 compose_set_title(compose);
1286 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1291 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1292 gboolean override_pref, const gchar *system)
1294 const gchar *privacy = NULL;
1296 cm_return_if_fail(compose != NULL);
1297 cm_return_if_fail(account != NULL);
1299 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1302 if (account->default_privacy_system && strlen(account->default_privacy_system))
1303 privacy = account->default_privacy_system;
1307 GSList *privacy_avail = privacy_get_system_ids();
1308 if (privacy_avail && g_slist_length(privacy_avail)) {
1309 privacy = (gchar *)(privacy_avail->data);
1312 if (privacy != NULL) {
1314 g_free(compose->privacy_system);
1315 compose->privacy_system = NULL;
1316 g_free(compose->encdata);
1317 compose->encdata = NULL;
1319 if (compose->privacy_system == NULL)
1320 compose->privacy_system = g_strdup(privacy);
1321 else if (*(compose->privacy_system) == '\0') {
1322 g_free(compose->privacy_system);
1323 g_free(compose->encdata);
1324 compose->encdata = NULL;
1325 compose->privacy_system = g_strdup(privacy);
1327 compose_update_privacy_system_menu_item(compose, FALSE);
1328 compose_use_encryption(compose, TRUE);
1332 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1334 const gchar *privacy = NULL;
1336 if (account->default_privacy_system && strlen(account->default_privacy_system))
1337 privacy = account->default_privacy_system;
1341 GSList *privacy_avail = privacy_get_system_ids();
1342 if (privacy_avail && g_slist_length(privacy_avail)) {
1343 privacy = (gchar *)(privacy_avail->data);
1347 if (privacy != NULL) {
1349 g_free(compose->privacy_system);
1350 compose->privacy_system = NULL;
1351 g_free(compose->encdata);
1352 compose->encdata = NULL;
1354 if (compose->privacy_system == NULL)
1355 compose->privacy_system = g_strdup(privacy);
1356 compose_update_privacy_system_menu_item(compose, FALSE);
1357 compose_use_signing(compose, TRUE);
1361 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1365 Compose *compose = NULL;
1367 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1369 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1370 cm_return_val_if_fail(msginfo != NULL, NULL);
1372 list_len = g_slist_length(msginfo_list);
1376 case COMPOSE_REPLY_TO_ADDRESS:
1377 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1378 FALSE, prefs_common.default_reply_list, FALSE, body);
1380 case COMPOSE_REPLY_WITH_QUOTE:
1381 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1382 FALSE, prefs_common.default_reply_list, FALSE, body);
1384 case COMPOSE_REPLY_WITHOUT_QUOTE:
1385 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1386 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1388 case COMPOSE_REPLY_TO_SENDER:
1389 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1390 FALSE, FALSE, TRUE, body);
1392 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1393 compose = compose_followup_and_reply_to(msginfo,
1394 COMPOSE_QUOTE_CHECK,
1395 FALSE, FALSE, body);
1397 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1398 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1399 FALSE, FALSE, TRUE, body);
1401 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1402 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1403 FALSE, FALSE, TRUE, NULL);
1405 case COMPOSE_REPLY_TO_ALL:
1406 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1407 TRUE, FALSE, FALSE, body);
1409 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1410 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1411 TRUE, FALSE, FALSE, body);
1413 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1414 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1415 TRUE, FALSE, FALSE, NULL);
1417 case COMPOSE_REPLY_TO_LIST:
1418 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1419 FALSE, TRUE, FALSE, body);
1421 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1422 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1423 FALSE, TRUE, FALSE, body);
1425 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1426 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1427 FALSE, TRUE, FALSE, NULL);
1429 case COMPOSE_FORWARD:
1430 if (prefs_common.forward_as_attachment) {
1431 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1434 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1438 case COMPOSE_FORWARD_INLINE:
1439 /* check if we reply to more than one Message */
1440 if (list_len == 1) {
1441 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1444 /* more messages FALL THROUGH */
1445 case COMPOSE_FORWARD_AS_ATTACH:
1446 compose = compose_forward_multiple(NULL, msginfo_list);
1448 case COMPOSE_REDIRECT:
1449 compose = compose_redirect(NULL, msginfo, FALSE);
1452 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1455 if (compose == NULL) {
1456 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1460 compose->rmode = mode;
1461 switch (compose->rmode) {
1463 case COMPOSE_REPLY_WITH_QUOTE:
1464 case COMPOSE_REPLY_WITHOUT_QUOTE:
1465 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1466 debug_print("reply mode Normal\n");
1467 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1468 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1470 case COMPOSE_REPLY_TO_SENDER:
1471 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1472 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1473 debug_print("reply mode Sender\n");
1474 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1476 case COMPOSE_REPLY_TO_ALL:
1477 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1478 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1479 debug_print("reply mode All\n");
1480 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1482 case COMPOSE_REPLY_TO_LIST:
1483 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1484 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1485 debug_print("reply mode List\n");
1486 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1488 case COMPOSE_REPLY_TO_ADDRESS:
1489 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1497 static Compose *compose_reply(MsgInfo *msginfo,
1498 ComposeQuoteMode quote_mode,
1504 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1505 to_sender, FALSE, body);
1508 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1509 ComposeQuoteMode quote_mode,
1514 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1515 to_sender, TRUE, body);
1518 static void compose_extract_original_charset(Compose *compose)
1520 MsgInfo *info = NULL;
1521 if (compose->replyinfo) {
1522 info = compose->replyinfo;
1523 } else if (compose->fwdinfo) {
1524 info = compose->fwdinfo;
1525 } else if (compose->targetinfo) {
1526 info = compose->targetinfo;
1529 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1530 MimeInfo *partinfo = mimeinfo;
1531 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1532 partinfo = procmime_mimeinfo_next(partinfo);
1534 compose->orig_charset =
1535 g_strdup(procmime_mimeinfo_get_parameter(
1536 partinfo, "charset"));
1538 procmime_mimeinfo_free_all(mimeinfo);
1542 #define SIGNAL_BLOCK(buffer) { \
1543 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1544 G_CALLBACK(compose_changed_cb), \
1546 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1547 G_CALLBACK(text_inserted), \
1551 #define SIGNAL_UNBLOCK(buffer) { \
1552 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1553 G_CALLBACK(compose_changed_cb), \
1555 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1556 G_CALLBACK(text_inserted), \
1560 static Compose *compose_generic_reply(MsgInfo *msginfo,
1561 ComposeQuoteMode quote_mode,
1562 gboolean to_all, gboolean to_ml,
1564 gboolean followup_and_reply_to,
1568 PrefsAccount *account = NULL;
1569 GtkTextView *textview;
1570 GtkTextBuffer *textbuf;
1571 gboolean quote = FALSE;
1572 const gchar *qmark = NULL;
1573 const gchar *body_fmt = NULL;
1574 gchar *s_system = NULL;
1576 cm_return_val_if_fail(msginfo != NULL, NULL);
1577 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1579 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1581 cm_return_val_if_fail(account != NULL, NULL);
1583 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1585 compose->updating = TRUE;
1587 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1590 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1591 if (!compose->replyinfo)
1592 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1594 compose_extract_original_charset(compose);
1596 if (msginfo->folder && msginfo->folder->ret_rcpt)
1597 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1599 /* Set save folder */
1600 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1601 gchar *folderidentifier;
1603 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1604 folderidentifier = folder_item_get_identifier(msginfo->folder);
1605 compose_set_save_to(compose, folderidentifier);
1606 g_free(folderidentifier);
1609 if (compose_parse_header(compose, msginfo) < 0) {
1610 compose->updating = FALSE;
1611 compose_destroy(compose);
1615 /* override from name according to folder properties */
1616 if (msginfo->folder && msginfo->folder->prefs &&
1617 msginfo->folder->prefs->reply_with_format &&
1618 msginfo->folder->prefs->reply_override_from_format &&
1619 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1624 /* decode \-escape sequences in the internal representation of the quote format */
1625 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1626 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1629 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1630 compose->gtkaspell);
1632 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1634 quote_fmt_scan_string(tmp);
1637 buf = quote_fmt_get_buffer();
1639 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1641 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1642 quote_fmt_reset_vartable();
1647 textview = (GTK_TEXT_VIEW(compose->text));
1648 textbuf = gtk_text_view_get_buffer(textview);
1649 compose_create_tags(textview, compose);
1651 undo_block(compose->undostruct);
1653 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1654 gtkaspell_block_check(compose->gtkaspell);
1657 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1658 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1659 /* use the reply format of folder (if enabled), or the account's one
1660 (if enabled) or fallback to the global reply format, which is always
1661 enabled (even if empty), and use the relevant quotemark */
1663 if (msginfo->folder && msginfo->folder->prefs &&
1664 msginfo->folder->prefs->reply_with_format) {
1665 qmark = msginfo->folder->prefs->reply_quotemark;
1666 body_fmt = msginfo->folder->prefs->reply_body_format;
1668 } else if (account->reply_with_format) {
1669 qmark = account->reply_quotemark;
1670 body_fmt = account->reply_body_format;
1673 qmark = prefs_common.quotemark;
1674 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1675 body_fmt = gettext(prefs_common.quotefmt);
1682 /* empty quotemark is not allowed */
1683 if (qmark == NULL || *qmark == '\0')
1685 compose_quote_fmt(compose, compose->replyinfo,
1686 body_fmt, qmark, body, FALSE, TRUE,
1687 _("The body of the \"Reply\" template has an error at line %d."));
1688 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1689 quote_fmt_reset_vartable();
1692 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1693 compose_force_encryption(compose, account, FALSE, s_system);
1696 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1697 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1698 compose_force_signing(compose, account, s_system);
1702 SIGNAL_BLOCK(textbuf);
1704 if (account->auto_sig)
1705 compose_insert_sig(compose, FALSE);
1707 compose_wrap_all(compose);
1710 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1711 gtkaspell_highlight_all(compose->gtkaspell);
1712 gtkaspell_unblock_check(compose->gtkaspell);
1714 SIGNAL_UNBLOCK(textbuf);
1716 gtk_widget_grab_focus(compose->text);
1718 undo_unblock(compose->undostruct);
1720 if (prefs_common.auto_exteditor)
1721 compose_exec_ext_editor(compose);
1723 compose->modified = FALSE;
1724 compose_set_title(compose);
1726 compose->updating = FALSE;
1727 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1728 SCROLL_TO_CURSOR(compose);
1730 if (compose->deferred_destroy) {
1731 compose_destroy(compose);
1739 #define INSERT_FW_HEADER(var, hdr) \
1740 if (msginfo->var && *msginfo->var) { \
1741 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1742 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1743 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1746 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1747 gboolean as_attach, const gchar *body,
1748 gboolean no_extedit,
1752 GtkTextView *textview;
1753 GtkTextBuffer *textbuf;
1754 gint cursor_pos = -1;
1757 cm_return_val_if_fail(msginfo != NULL, NULL);
1758 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1761 !(account = compose_guess_forward_account_from_msginfo
1763 account = cur_account;
1765 if (!prefs_common.forward_as_attachment)
1766 mode = COMPOSE_FORWARD_INLINE;
1768 mode = COMPOSE_FORWARD;
1769 compose = compose_create(account, msginfo->folder, mode, batch);
1771 compose->updating = TRUE;
1772 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1773 if (!compose->fwdinfo)
1774 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1776 compose_extract_original_charset(compose);
1778 if (msginfo->subject && *msginfo->subject) {
1779 gchar *buf, *buf2, *p;
1781 buf = p = g_strdup(msginfo->subject);
1782 p += subject_get_prefix_length(p);
1783 memmove(buf, p, strlen(p) + 1);
1785 buf2 = g_strdup_printf("Fw: %s", buf);
1786 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1792 /* override from name according to folder properties */
1793 if (msginfo->folder && msginfo->folder->prefs &&
1794 msginfo->folder->prefs->forward_with_format &&
1795 msginfo->folder->prefs->forward_override_from_format &&
1796 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1800 MsgInfo *full_msginfo = NULL;
1803 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1805 full_msginfo = procmsg_msginfo_copy(msginfo);
1807 /* decode \-escape sequences in the internal representation of the quote format */
1808 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1809 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1812 gtkaspell_block_check(compose->gtkaspell);
1813 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1814 compose->gtkaspell);
1816 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1818 quote_fmt_scan_string(tmp);
1821 buf = quote_fmt_get_buffer();
1823 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1825 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1826 quote_fmt_reset_vartable();
1829 procmsg_msginfo_free(&full_msginfo);
1832 textview = GTK_TEXT_VIEW(compose->text);
1833 textbuf = gtk_text_view_get_buffer(textview);
1834 compose_create_tags(textview, compose);
1836 undo_block(compose->undostruct);
1840 msgfile = procmsg_get_message_file(msginfo);
1841 if (!is_file_exist(msgfile))
1842 g_warning("%s: file does not exist", msgfile);
1844 compose_attach_append(compose, msgfile, msgfile,
1845 "message/rfc822", NULL);
1849 const gchar *qmark = NULL;
1850 const gchar *body_fmt = NULL;
1851 MsgInfo *full_msginfo;
1853 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1855 full_msginfo = procmsg_msginfo_copy(msginfo);
1857 /* use the forward format of folder (if enabled), or the account's one
1858 (if enabled) or fallback to the global forward format, which is always
1859 enabled (even if empty), and use the relevant quotemark */
1860 if (msginfo->folder && msginfo->folder->prefs &&
1861 msginfo->folder->prefs->forward_with_format) {
1862 qmark = msginfo->folder->prefs->forward_quotemark;
1863 body_fmt = msginfo->folder->prefs->forward_body_format;
1865 } else if (account->forward_with_format) {
1866 qmark = account->forward_quotemark;
1867 body_fmt = account->forward_body_format;
1870 qmark = prefs_common.fw_quotemark;
1871 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1872 body_fmt = gettext(prefs_common.fw_quotefmt);
1877 /* empty quotemark is not allowed */
1878 if (qmark == NULL || *qmark == '\0')
1881 compose_quote_fmt(compose, full_msginfo,
1882 body_fmt, qmark, body, FALSE, TRUE,
1883 _("The body of the \"Forward\" template has an error at line %d."));
1884 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1885 quote_fmt_reset_vartable();
1886 compose_attach_parts(compose, msginfo);
1888 procmsg_msginfo_free(&full_msginfo);
1891 SIGNAL_BLOCK(textbuf);
1893 if (account->auto_sig)
1894 compose_insert_sig(compose, FALSE);
1896 compose_wrap_all(compose);
1899 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1900 gtkaspell_highlight_all(compose->gtkaspell);
1901 gtkaspell_unblock_check(compose->gtkaspell);
1903 SIGNAL_UNBLOCK(textbuf);
1905 cursor_pos = quote_fmt_get_cursor_pos();
1906 if (cursor_pos == -1)
1907 gtk_widget_grab_focus(compose->header_last->entry);
1909 gtk_widget_grab_focus(compose->text);
1911 if (!no_extedit && prefs_common.auto_exteditor)
1912 compose_exec_ext_editor(compose);
1915 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1916 gchar *folderidentifier;
1918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1919 folderidentifier = folder_item_get_identifier(msginfo->folder);
1920 compose_set_save_to(compose, folderidentifier);
1921 g_free(folderidentifier);
1924 undo_unblock(compose->undostruct);
1926 compose->modified = FALSE;
1927 compose_set_title(compose);
1929 compose->updating = FALSE;
1930 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1931 SCROLL_TO_CURSOR(compose);
1933 if (compose->deferred_destroy) {
1934 compose_destroy(compose);
1938 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1943 #undef INSERT_FW_HEADER
1945 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1948 GtkTextView *textview;
1949 GtkTextBuffer *textbuf;
1953 gboolean single_mail = TRUE;
1955 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1957 if (g_slist_length(msginfo_list) > 1)
1958 single_mail = FALSE;
1960 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1961 if (((MsgInfo *)msginfo->data)->folder == NULL)
1964 /* guess account from first selected message */
1966 !(account = compose_guess_forward_account_from_msginfo
1967 (msginfo_list->data)))
1968 account = cur_account;
1970 cm_return_val_if_fail(account != NULL, NULL);
1972 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1973 if (msginfo->data) {
1974 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1975 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1979 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1980 g_warning("no msginfo_list");
1984 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1986 compose->updating = TRUE;
1988 /* override from name according to folder properties */
1989 if (msginfo_list->data) {
1990 MsgInfo *msginfo = msginfo_list->data;
1992 if (msginfo->folder && msginfo->folder->prefs &&
1993 msginfo->folder->prefs->forward_with_format &&
1994 msginfo->folder->prefs->forward_override_from_format &&
1995 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2000 /* decode \-escape sequences in the internal representation of the quote format */
2001 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2002 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2005 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2006 compose->gtkaspell);
2008 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2010 quote_fmt_scan_string(tmp);
2013 buf = quote_fmt_get_buffer();
2015 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2017 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2018 quote_fmt_reset_vartable();
2024 textview = GTK_TEXT_VIEW(compose->text);
2025 textbuf = gtk_text_view_get_buffer(textview);
2026 compose_create_tags(textview, compose);
2028 undo_block(compose->undostruct);
2029 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2030 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2032 if (!is_file_exist(msgfile))
2033 g_warning("%s: file does not exist", msgfile);
2035 compose_attach_append(compose, msgfile, msgfile,
2036 "message/rfc822", NULL);
2041 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2042 if (info->subject && *info->subject) {
2043 gchar *buf, *buf2, *p;
2045 buf = p = g_strdup(info->subject);
2046 p += subject_get_prefix_length(p);
2047 memmove(buf, p, strlen(p) + 1);
2049 buf2 = g_strdup_printf("Fw: %s", buf);
2050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2056 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2057 _("Fw: multiple emails"));
2060 SIGNAL_BLOCK(textbuf);
2062 if (account->auto_sig)
2063 compose_insert_sig(compose, FALSE);
2065 compose_wrap_all(compose);
2067 SIGNAL_UNBLOCK(textbuf);
2069 gtk_text_buffer_get_start_iter(textbuf, &iter);
2070 gtk_text_buffer_place_cursor(textbuf, &iter);
2072 gtk_widget_grab_focus(compose->header_last->entry);
2073 undo_unblock(compose->undostruct);
2074 compose->modified = FALSE;
2075 compose_set_title(compose);
2077 compose->updating = FALSE;
2078 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2079 SCROLL_TO_CURSOR(compose);
2081 if (compose->deferred_destroy) {
2082 compose_destroy(compose);
2086 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2091 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2093 GtkTextIter start = *iter;
2094 GtkTextIter end_iter;
2095 int start_pos = gtk_text_iter_get_offset(&start);
2097 if (!compose->account->sig_sep)
2100 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2101 start_pos+strlen(compose->account->sig_sep));
2103 /* check sig separator */
2104 str = gtk_text_iter_get_text(&start, &end_iter);
2105 if (!strcmp(str, compose->account->sig_sep)) {
2107 /* check end of line (\n) */
2108 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2109 start_pos+strlen(compose->account->sig_sep));
2110 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2111 start_pos+strlen(compose->account->sig_sep)+1);
2112 tmp = gtk_text_iter_get_text(&start, &end_iter);
2113 if (!strcmp(tmp,"\n")) {
2125 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2127 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2128 Compose *compose = (Compose *)data;
2129 FolderItem *old_item = NULL;
2130 FolderItem *new_item = NULL;
2131 gchar *old_id, *new_id;
2133 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2134 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2137 old_item = hookdata->item;
2138 new_item = hookdata->item2;
2140 old_id = folder_item_get_identifier(old_item);
2141 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2143 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2144 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2145 compose->targetinfo->folder = new_item;
2148 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2149 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2150 compose->replyinfo->folder = new_item;
2153 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2154 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2155 compose->fwdinfo->folder = new_item;
2163 static void compose_colorize_signature(Compose *compose)
2165 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2167 GtkTextIter end_iter;
2168 gtk_text_buffer_get_start_iter(buffer, &iter);
2169 while (gtk_text_iter_forward_line(&iter))
2170 if (compose_is_sig_separator(compose, buffer, &iter)) {
2171 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2172 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2176 #define BLOCK_WRAP() { \
2177 prev_autowrap = compose->autowrap; \
2178 buffer = gtk_text_view_get_buffer( \
2179 GTK_TEXT_VIEW(compose->text)); \
2180 compose->autowrap = FALSE; \
2182 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2183 G_CALLBACK(compose_changed_cb), \
2185 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2186 G_CALLBACK(text_inserted), \
2189 #define UNBLOCK_WRAP() { \
2190 compose->autowrap = prev_autowrap; \
2191 if (compose->autowrap) { \
2192 gint old = compose->draft_timeout_tag; \
2193 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2194 compose_wrap_all(compose); \
2195 compose->draft_timeout_tag = old; \
2198 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2199 G_CALLBACK(compose_changed_cb), \
2201 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2202 G_CALLBACK(text_inserted), \
2206 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2208 Compose *compose = NULL;
2209 PrefsAccount *account = NULL;
2210 GtkTextView *textview;
2211 GtkTextBuffer *textbuf;
2215 gchar buf[BUFFSIZE];
2216 gboolean use_signing = FALSE;
2217 gboolean use_encryption = FALSE;
2218 gchar *privacy_system = NULL;
2219 int priority = PRIORITY_NORMAL;
2220 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2221 gboolean autowrap = prefs_common.autowrap;
2222 gboolean autoindent = prefs_common.auto_indent;
2223 HeaderEntry *manual_headers = NULL;
2225 cm_return_val_if_fail(msginfo != NULL, NULL);
2226 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2228 if (compose_put_existing_to_front(msginfo)) {
2232 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2233 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2234 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2235 gchar queueheader_buf[BUFFSIZE];
2238 /* Select Account from queue headers */
2239 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2240 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2241 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2242 account = account_find_from_id(id);
2244 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2245 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2246 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2247 account = account_find_from_id(id);
2249 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2250 sizeof(queueheader_buf), "NAID:")) {
2251 id = atoi(&queueheader_buf[strlen("NAID:")]);
2252 account = account_find_from_id(id);
2254 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2255 sizeof(queueheader_buf), "MAID:")) {
2256 id = atoi(&queueheader_buf[strlen("MAID:")]);
2257 account = account_find_from_id(id);
2259 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2260 sizeof(queueheader_buf), "S:")) {
2261 account = account_find_from_address(queueheader_buf, FALSE);
2263 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2264 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2265 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2266 use_signing = param;
2269 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2270 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2271 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2272 use_signing = param;
2275 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2276 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2277 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2278 use_encryption = param;
2280 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2281 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2282 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2283 use_encryption = param;
2285 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2286 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2287 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2290 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2291 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2292 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2295 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2296 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2297 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2299 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2300 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2301 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2303 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2304 sizeof(queueheader_buf), "X-Priority: ")) {
2305 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2308 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2309 sizeof(queueheader_buf), "RMID:")) {
2310 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2311 if (tokens[0] && tokens[1] && tokens[2]) {
2312 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2313 if (orig_item != NULL) {
2314 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2319 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2320 sizeof(queueheader_buf), "FMID:")) {
2321 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2322 if (tokens[0] && tokens[1] && tokens[2]) {
2323 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2324 if (orig_item != NULL) {
2325 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2330 /* Get manual headers */
2331 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2332 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2333 if (*listmh != '\0') {
2334 debug_print("Got manual headers: %s\n", listmh);
2335 manual_headers = procheader_entries_from_str(listmh);
2340 account = msginfo->folder->folder->account;
2343 if (!account && prefs_common.reedit_account_autosel) {
2344 gchar from[BUFFSIZE];
2345 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2346 extract_address(from);
2347 account = account_find_from_address(from, FALSE);
2351 account = cur_account;
2353 cm_return_val_if_fail(account != NULL, NULL);
2355 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2357 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2358 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2359 compose->autowrap = autowrap;
2360 compose->replyinfo = replyinfo;
2361 compose->fwdinfo = fwdinfo;
2363 compose->updating = TRUE;
2364 compose->priority = priority;
2366 if (privacy_system != NULL) {
2367 compose->privacy_system = privacy_system;
2368 compose_use_signing(compose, use_signing);
2369 compose_use_encryption(compose, use_encryption);
2370 compose_update_privacy_system_menu_item(compose, FALSE);
2372 activate_privacy_system(compose, account, FALSE);
2375 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2377 compose_extract_original_charset(compose);
2379 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2380 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2381 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2382 gchar queueheader_buf[BUFFSIZE];
2384 /* Set message save folder */
2385 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2386 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2387 compose_set_save_to(compose, &queueheader_buf[4]);
2389 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2390 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2392 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2397 if (compose_parse_header(compose, msginfo) < 0) {
2398 compose->updating = FALSE;
2399 compose_destroy(compose);
2402 compose_reedit_set_entry(compose, msginfo);
2404 textview = GTK_TEXT_VIEW(compose->text);
2405 textbuf = gtk_text_view_get_buffer(textview);
2406 compose_create_tags(textview, compose);
2408 mark = gtk_text_buffer_get_insert(textbuf);
2409 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2411 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2412 G_CALLBACK(compose_changed_cb),
2415 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2416 fp = procmime_get_first_encrypted_text_content(msginfo);
2418 compose_force_encryption(compose, account, TRUE, NULL);
2421 fp = procmime_get_first_text_content(msginfo);
2424 g_warning("Can't get text part");
2428 gboolean prev_autowrap;
2429 GtkTextBuffer *buffer;
2431 while (fgets(buf, sizeof(buf), fp) != NULL) {
2433 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2439 compose_attach_parts(compose, msginfo);
2441 compose_colorize_signature(compose);
2443 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2444 G_CALLBACK(compose_changed_cb),
2447 if (manual_headers != NULL) {
2448 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2449 procheader_entries_free(manual_headers);
2450 compose->updating = FALSE;
2451 compose_destroy(compose);
2454 procheader_entries_free(manual_headers);
2457 gtk_widget_grab_focus(compose->text);
2459 if (prefs_common.auto_exteditor) {
2460 compose_exec_ext_editor(compose);
2462 compose->modified = FALSE;
2463 compose_set_title(compose);
2465 compose->updating = FALSE;
2466 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2467 SCROLL_TO_CURSOR(compose);
2469 if (compose->deferred_destroy) {
2470 compose_destroy(compose);
2474 compose->sig_str = account_get_signature_str(compose->account);
2476 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2481 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2488 cm_return_val_if_fail(msginfo != NULL, NULL);
2491 account = account_get_reply_account(msginfo,
2492 prefs_common.reply_account_autosel);
2493 cm_return_val_if_fail(account != NULL, NULL);
2495 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2497 compose->updating = TRUE;
2499 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2500 compose->replyinfo = NULL;
2501 compose->fwdinfo = NULL;
2503 compose_show_first_last_header(compose, TRUE);
2505 gtk_widget_grab_focus(compose->header_last->entry);
2507 filename = procmsg_get_message_file(msginfo);
2509 if (filename == NULL) {
2510 compose->updating = FALSE;
2511 compose_destroy(compose);
2516 compose->redirect_filename = filename;
2518 /* Set save folder */
2519 item = msginfo->folder;
2520 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2521 gchar *folderidentifier;
2523 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2524 folderidentifier = folder_item_get_identifier(item);
2525 compose_set_save_to(compose, folderidentifier);
2526 g_free(folderidentifier);
2529 compose_attach_parts(compose, msginfo);
2531 if (msginfo->subject)
2532 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2534 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2536 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2537 _("The body of the \"Redirect\" template has an error at line %d."));
2538 quote_fmt_reset_vartable();
2539 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2541 compose_colorize_signature(compose);
2544 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2545 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2546 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2548 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2549 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2550 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2551 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2552 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2553 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2554 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2555 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2556 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2558 if (compose->toolbar->draft_btn)
2559 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2560 if (compose->toolbar->insert_btn)
2561 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2562 if (compose->toolbar->attach_btn)
2563 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2564 if (compose->toolbar->sig_btn)
2565 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2566 if (compose->toolbar->exteditor_btn)
2567 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2568 if (compose->toolbar->linewrap_current_btn)
2569 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2570 if (compose->toolbar->linewrap_all_btn)
2571 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2573 compose->modified = FALSE;
2574 compose_set_title(compose);
2575 compose->updating = FALSE;
2576 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2577 SCROLL_TO_CURSOR(compose);
2579 if (compose->deferred_destroy) {
2580 compose_destroy(compose);
2584 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2589 const GList *compose_get_compose_list(void)
2591 return compose_list;
2594 void compose_entry_append(Compose *compose, const gchar *address,
2595 ComposeEntryType type, ComposePrefType pref_type)
2597 const gchar *header;
2599 gboolean in_quote = FALSE;
2600 if (!address || *address == '\0') return;
2607 header = N_("Bcc:");
2609 case COMPOSE_REPLYTO:
2610 header = N_("Reply-To:");
2612 case COMPOSE_NEWSGROUPS:
2613 header = N_("Newsgroups:");
2615 case COMPOSE_FOLLOWUPTO:
2616 header = N_( "Followup-To:");
2618 case COMPOSE_INREPLYTO:
2619 header = N_( "In-Reply-To:");
2626 header = prefs_common_translated_header_name(header);
2628 cur = begin = (gchar *)address;
2630 /* we separate the line by commas, but not if we're inside a quoted
2632 while (*cur != '\0') {
2634 in_quote = !in_quote;
2635 if (*cur == ',' && !in_quote) {
2636 gchar *tmp = g_strdup(begin);
2638 tmp[cur-begin]='\0';
2641 while (*tmp == ' ' || *tmp == '\t')
2643 compose_add_header_entry(compose, header, tmp, pref_type);
2650 gchar *tmp = g_strdup(begin);
2652 tmp[cur-begin]='\0';
2653 while (*tmp == ' ' || *tmp == '\t')
2655 compose_add_header_entry(compose, header, tmp, pref_type);
2660 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2662 #if !GTK_CHECK_VERSION(3, 0, 0)
2663 static GdkColor yellow;
2664 static GdkColor black;
2665 static gboolean yellow_initialised = FALSE;
2667 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2668 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2673 #if !GTK_CHECK_VERSION(3, 0, 0)
2674 if (!yellow_initialised) {
2675 gdk_color_parse("#f5f6be", &yellow);
2676 gdk_color_parse("#000000", &black);
2677 yellow_initialised = gdk_colormap_alloc_color(
2678 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2679 yellow_initialised &= gdk_colormap_alloc_color(
2680 gdk_colormap_get_system(), &black, FALSE, TRUE);
2684 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2685 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2686 if (gtk_entry_get_text(entry) &&
2687 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2688 #if !GTK_CHECK_VERSION(3, 0, 0)
2689 if (yellow_initialised) {
2691 gtk_widget_modify_base(
2692 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2693 GTK_STATE_NORMAL, &yellow);
2694 gtk_widget_modify_text(
2695 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2696 GTK_STATE_NORMAL, &black);
2697 #if !GTK_CHECK_VERSION(3, 0, 0)
2704 void compose_toolbar_cb(gint action, gpointer data)
2706 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2707 Compose *compose = (Compose*)toolbar_item->parent;
2709 cm_return_if_fail(compose != NULL);
2713 compose_send_cb(NULL, compose);
2716 compose_send_later_cb(NULL, compose);
2719 compose_draft(compose, COMPOSE_QUIT_EDITING);
2722 compose_insert_file_cb(NULL, compose);
2725 compose_attach_cb(NULL, compose);
2728 compose_insert_sig(compose, FALSE);
2731 compose_insert_sig(compose, TRUE);
2734 compose_ext_editor_cb(NULL, compose);
2736 case A_LINEWRAP_CURRENT:
2737 compose_beautify_paragraph(compose, NULL, TRUE);
2739 case A_LINEWRAP_ALL:
2740 compose_wrap_all_full(compose, TRUE);
2743 compose_address_cb(NULL, compose);
2746 case A_CHECK_SPELLING:
2747 compose_check_all(NULL, compose);
2755 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2760 gchar *subject = NULL;
2764 gchar **attach = NULL;
2765 gchar *inreplyto = NULL;
2766 MailField mfield = NO_FIELD_PRESENT;
2768 /* get mailto parts but skip from */
2769 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2772 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2773 mfield = TO_FIELD_PRESENT;
2776 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2778 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2780 if (!g_utf8_validate (subject, -1, NULL)) {
2781 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2782 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2785 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2787 mfield = SUBJECT_FIELD_PRESENT;
2790 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2791 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2794 gboolean prev_autowrap = compose->autowrap;
2796 compose->autowrap = FALSE;
2798 mark = gtk_text_buffer_get_insert(buffer);
2799 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2801 if (!g_utf8_validate (body, -1, NULL)) {
2802 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2803 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2806 gtk_text_buffer_insert(buffer, &iter, body, -1);
2808 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2810 compose->autowrap = prev_autowrap;
2811 if (compose->autowrap)
2812 compose_wrap_all(compose);
2813 mfield = BODY_FIELD_PRESENT;
2817 gint i = 0, att = 0;
2818 gchar *warn_files = NULL;
2819 while (attach[i] != NULL) {
2820 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2821 if (utf8_filename) {
2822 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2823 gchar *tmp = g_strdup_printf("%s%s\n",
2824 warn_files?warn_files:"",
2830 g_free(utf8_filename);
2832 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2837 alertpanel_notice(ngettext(
2838 "The following file has been attached: \n%s",
2839 "The following files have been attached: \n%s", att), warn_files);
2844 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2857 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2859 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2860 {"Cc:", NULL, TRUE},
2861 {"References:", NULL, FALSE},
2862 {"Bcc:", NULL, TRUE},
2863 {"Newsgroups:", NULL, TRUE},
2864 {"Followup-To:", NULL, TRUE},
2865 {"List-Post:", NULL, FALSE},
2866 {"X-Priority:", NULL, FALSE},
2867 {NULL, NULL, FALSE}};
2883 cm_return_val_if_fail(msginfo != NULL, -1);
2885 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2886 procheader_get_header_fields(fp, hentry);
2889 if (hentry[H_REPLY_TO].body != NULL) {
2890 if (hentry[H_REPLY_TO].body[0] != '\0') {
2892 conv_unmime_header(hentry[H_REPLY_TO].body,
2895 g_free(hentry[H_REPLY_TO].body);
2896 hentry[H_REPLY_TO].body = NULL;
2898 if (hentry[H_CC].body != NULL) {
2899 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2900 g_free(hentry[H_CC].body);
2901 hentry[H_CC].body = NULL;
2903 if (hentry[H_REFERENCES].body != NULL) {
2904 if (compose->mode == COMPOSE_REEDIT)
2905 compose->references = hentry[H_REFERENCES].body;
2907 compose->references = compose_parse_references
2908 (hentry[H_REFERENCES].body, msginfo->msgid);
2909 g_free(hentry[H_REFERENCES].body);
2911 hentry[H_REFERENCES].body = NULL;
2913 if (hentry[H_BCC].body != NULL) {
2914 if (compose->mode == COMPOSE_REEDIT)
2916 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2917 g_free(hentry[H_BCC].body);
2918 hentry[H_BCC].body = NULL;
2920 if (hentry[H_NEWSGROUPS].body != NULL) {
2921 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2922 hentry[H_NEWSGROUPS].body = NULL;
2924 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2925 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2926 compose->followup_to =
2927 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2930 g_free(hentry[H_FOLLOWUP_TO].body);
2931 hentry[H_FOLLOWUP_TO].body = NULL;
2933 if (hentry[H_LIST_POST].body != NULL) {
2934 gchar *to = NULL, *start = NULL;
2936 extract_address(hentry[H_LIST_POST].body);
2937 if (hentry[H_LIST_POST].body[0] != '\0') {
2938 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2940 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2941 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2944 g_free(compose->ml_post);
2945 compose->ml_post = to;
2948 g_free(hentry[H_LIST_POST].body);
2949 hentry[H_LIST_POST].body = NULL;
2952 /* CLAWS - X-Priority */
2953 if (compose->mode == COMPOSE_REEDIT)
2954 if (hentry[H_X_PRIORITY].body != NULL) {
2957 priority = atoi(hentry[H_X_PRIORITY].body);
2958 g_free(hentry[H_X_PRIORITY].body);
2960 hentry[H_X_PRIORITY].body = NULL;
2962 if (priority < PRIORITY_HIGHEST ||
2963 priority > PRIORITY_LOWEST)
2964 priority = PRIORITY_NORMAL;
2966 compose->priority = priority;
2969 if (compose->mode == COMPOSE_REEDIT) {
2970 if (msginfo->inreplyto && *msginfo->inreplyto)
2971 compose->inreplyto = g_strdup(msginfo->inreplyto);
2975 if (msginfo->msgid && *msginfo->msgid)
2976 compose->inreplyto = g_strdup(msginfo->msgid);
2978 if (!compose->references) {
2979 if (msginfo->msgid && *msginfo->msgid) {
2980 if (msginfo->inreplyto && *msginfo->inreplyto)
2981 compose->references =
2982 g_strdup_printf("<%s>\n\t<%s>",
2986 compose->references =
2987 g_strconcat("<", msginfo->msgid, ">",
2989 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2990 compose->references =
2991 g_strconcat("<", msginfo->inreplyto, ">",
2999 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3004 cm_return_val_if_fail(msginfo != NULL, -1);
3006 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3007 procheader_get_header_fields(fp, entries);
3011 while (he != NULL && he->name != NULL) {
3013 GtkListStore *model = NULL;
3015 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3016 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3017 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3018 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3019 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3026 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3028 GSList *ref_id_list, *cur;
3032 ref_id_list = references_list_append(NULL, ref);
3033 if (!ref_id_list) return NULL;
3034 if (msgid && *msgid)
3035 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3040 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3041 /* "<" + Message-ID + ">" + CR+LF+TAB */
3042 len += strlen((gchar *)cur->data) + 5;
3044 if (len > MAX_REFERENCES_LEN) {
3045 /* remove second message-ID */
3046 if (ref_id_list && ref_id_list->next &&
3047 ref_id_list->next->next) {
3048 g_free(ref_id_list->next->data);
3049 ref_id_list = g_slist_remove
3050 (ref_id_list, ref_id_list->next->data);
3052 slist_free_strings_full(ref_id_list);
3059 new_ref = g_string_new("");
3060 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3061 if (new_ref->len > 0)
3062 g_string_append(new_ref, "\n\t");
3063 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3066 slist_free_strings_full(ref_id_list);
3068 new_ref_str = new_ref->str;
3069 g_string_free(new_ref, FALSE);
3074 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3075 const gchar *fmt, const gchar *qmark,
3076 const gchar *body, gboolean rewrap,
3077 gboolean need_unescape,
3078 const gchar *err_msg)
3080 MsgInfo* dummyinfo = NULL;
3081 gchar *quote_str = NULL;
3083 gboolean prev_autowrap;
3084 const gchar *trimmed_body = body;
3085 gint cursor_pos = -1;
3086 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3087 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3092 SIGNAL_BLOCK(buffer);
3095 dummyinfo = compose_msginfo_new_from_compose(compose);
3096 msginfo = dummyinfo;
3099 if (qmark != NULL) {
3101 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3102 compose->gtkaspell);
3104 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3106 quote_fmt_scan_string(qmark);
3109 buf = quote_fmt_get_buffer();
3111 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3113 Xstrdup_a(quote_str, buf, goto error)
3116 if (fmt && *fmt != '\0') {
3119 while (*trimmed_body == '\n')
3123 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3124 compose->gtkaspell);
3126 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3128 if (need_unescape) {
3131 /* decode \-escape sequences in the internal representation of the quote format */
3132 tmp = g_malloc(strlen(fmt)+1);
3133 pref_get_unescaped_pref(tmp, fmt);
3134 quote_fmt_scan_string(tmp);
3138 quote_fmt_scan_string(fmt);
3142 buf = quote_fmt_get_buffer();
3144 gint line = quote_fmt_get_line();
3145 alertpanel_error(err_msg, line);
3151 prev_autowrap = compose->autowrap;
3152 compose->autowrap = FALSE;
3154 mark = gtk_text_buffer_get_insert(buffer);
3155 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3156 if (g_utf8_validate(buf, -1, NULL)) {
3157 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3159 gchar *tmpout = NULL;
3160 tmpout = conv_codeset_strdup
3161 (buf, conv_get_locale_charset_str_no_utf8(),
3163 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3165 tmpout = g_malloc(strlen(buf)*2+1);
3166 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3168 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3172 cursor_pos = quote_fmt_get_cursor_pos();
3173 if (cursor_pos == -1)
3174 cursor_pos = gtk_text_iter_get_offset(&iter);
3175 compose->set_cursor_pos = cursor_pos;
3177 gtk_text_buffer_get_start_iter(buffer, &iter);
3178 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3179 gtk_text_buffer_place_cursor(buffer, &iter);
3181 compose->autowrap = prev_autowrap;
3182 if (compose->autowrap && rewrap)
3183 compose_wrap_all(compose);
3190 SIGNAL_UNBLOCK(buffer);
3192 procmsg_msginfo_free( &dummyinfo );
3197 /* if ml_post is of type addr@host and from is of type
3198 * addr-anything@host, return TRUE
3200 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3202 gchar *left_ml = NULL;
3203 gchar *right_ml = NULL;
3204 gchar *left_from = NULL;
3205 gchar *right_from = NULL;
3206 gboolean result = FALSE;
3208 if (!ml_post || !from)
3211 left_ml = g_strdup(ml_post);
3212 if (strstr(left_ml, "@")) {
3213 right_ml = strstr(left_ml, "@")+1;
3214 *(strstr(left_ml, "@")) = '\0';
3217 left_from = g_strdup(from);
3218 if (strstr(left_from, "@")) {
3219 right_from = strstr(left_from, "@")+1;
3220 *(strstr(left_from, "@")) = '\0';
3223 if (right_ml && right_from
3224 && !strncmp(left_from, left_ml, strlen(left_ml))
3225 && !strcmp(right_from, right_ml)) {
3234 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3235 gboolean respect_default_to)
3239 if (!folder || !folder->prefs)
3242 if (respect_default_to && folder->prefs->enable_default_to) {
3243 compose_entry_append(compose, folder->prefs->default_to,
3244 COMPOSE_TO, PREF_FOLDER);
3245 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3247 if (folder->prefs->enable_default_cc)
3248 compose_entry_append(compose, folder->prefs->default_cc,
3249 COMPOSE_CC, PREF_FOLDER);
3250 if (folder->prefs->enable_default_bcc)
3251 compose_entry_append(compose, folder->prefs->default_bcc,
3252 COMPOSE_BCC, PREF_FOLDER);
3253 if (folder->prefs->enable_default_replyto)
3254 compose_entry_append(compose, folder->prefs->default_replyto,
3255 COMPOSE_REPLYTO, PREF_FOLDER);
3258 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3263 if (!compose || !msginfo)
3266 if (msginfo->subject && *msginfo->subject) {
3267 buf = p = g_strdup(msginfo->subject);
3268 p += subject_get_prefix_length(p);
3269 memmove(buf, p, strlen(p) + 1);
3271 buf2 = g_strdup_printf("Re: %s", buf);
3272 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3277 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3280 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3281 gboolean to_all, gboolean to_ml,
3283 gboolean followup_and_reply_to)
3285 GSList *cc_list = NULL;
3288 gchar *replyto = NULL;
3289 gchar *ac_email = NULL;
3291 gboolean reply_to_ml = FALSE;
3292 gboolean default_reply_to = FALSE;
3294 cm_return_if_fail(compose->account != NULL);
3295 cm_return_if_fail(msginfo != NULL);
3297 reply_to_ml = to_ml && compose->ml_post;
3299 default_reply_to = msginfo->folder &&
3300 msginfo->folder->prefs->enable_default_reply_to;
3302 if (compose->account->protocol != A_NNTP) {
3303 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3305 if (reply_to_ml && !default_reply_to) {
3307 gboolean is_subscr = is_subscription(compose->ml_post,
3310 /* normal answer to ml post with a reply-to */
3311 compose_entry_append(compose,
3313 COMPOSE_TO, PREF_ML);
3314 if (compose->replyto)
3315 compose_entry_append(compose,
3317 COMPOSE_CC, PREF_ML);
3319 /* answer to subscription confirmation */
3320 if (compose->replyto)
3321 compose_entry_append(compose,
3323 COMPOSE_TO, PREF_ML);
3324 else if (msginfo->from)
3325 compose_entry_append(compose,
3327 COMPOSE_TO, PREF_ML);
3330 else if (!(to_all || to_sender) && default_reply_to) {
3331 compose_entry_append(compose,
3332 msginfo->folder->prefs->default_reply_to,
3333 COMPOSE_TO, PREF_FOLDER);
3334 compose_entry_mark_default_to(compose,
3335 msginfo->folder->prefs->default_reply_to);
3341 compose_entry_append(compose, msginfo->from,
3342 COMPOSE_TO, PREF_NONE);
3344 Xstrdup_a(tmp1, msginfo->from, return);
3345 extract_address(tmp1);
3346 compose_entry_append(compose,
3347 (!account_find_from_address(tmp1, FALSE))
3350 COMPOSE_TO, PREF_NONE);
3352 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3353 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3354 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3355 if (compose->replyto) {
3356 compose_entry_append(compose,
3358 COMPOSE_TO, PREF_NONE);
3360 compose_entry_append(compose,
3361 msginfo->from ? msginfo->from : "",
3362 COMPOSE_TO, PREF_NONE);
3365 /* replying to own mail, use original recp */
3366 compose_entry_append(compose,
3367 msginfo->to ? msginfo->to : "",
3368 COMPOSE_TO, PREF_NONE);
3369 compose_entry_append(compose,
3370 msginfo->cc ? msginfo->cc : "",
3371 COMPOSE_CC, PREF_NONE);
3376 if (to_sender || (compose->followup_to &&
3377 !strncmp(compose->followup_to, "poster", 6)))
3378 compose_entry_append
3380 (compose->replyto ? compose->replyto :
3381 msginfo->from ? msginfo->from : ""),
3382 COMPOSE_TO, PREF_NONE);
3384 else if (followup_and_reply_to || to_all) {
3385 compose_entry_append
3387 (compose->replyto ? compose->replyto :
3388 msginfo->from ? msginfo->from : ""),
3389 COMPOSE_TO, PREF_NONE);
3391 compose_entry_append
3393 compose->followup_to ? compose->followup_to :
3394 compose->newsgroups ? compose->newsgroups : "",
3395 COMPOSE_NEWSGROUPS, PREF_NONE);
3398 compose_entry_append
3400 compose->followup_to ? compose->followup_to :
3401 compose->newsgroups ? compose->newsgroups : "",
3402 COMPOSE_NEWSGROUPS, PREF_NONE);
3404 compose_reply_set_subject(compose, msginfo);
3406 if (to_ml && compose->ml_post) return;
3407 if (!to_all || compose->account->protocol == A_NNTP) return;
3409 if (compose->replyto) {
3410 Xstrdup_a(replyto, compose->replyto, return);
3411 extract_address(replyto);
3413 if (msginfo->from) {
3414 Xstrdup_a(from, msginfo->from, return);
3415 extract_address(from);
3418 if (replyto && from)
3419 cc_list = address_list_append_with_comments(cc_list, from);
3420 if (to_all && msginfo->folder &&
3421 msginfo->folder->prefs->enable_default_reply_to)
3422 cc_list = address_list_append_with_comments(cc_list,
3423 msginfo->folder->prefs->default_reply_to);
3424 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3425 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3427 ac_email = g_utf8_strdown(compose->account->address, -1);
3430 for (cur = cc_list; cur != NULL; cur = cur->next) {
3431 gchar *addr = g_utf8_strdown(cur->data, -1);
3432 extract_address(addr);
3434 if (strcmp(ac_email, addr))
3435 compose_entry_append(compose, (gchar *)cur->data,
3436 COMPOSE_CC, PREF_NONE);
3438 debug_print("Cc address same as compose account's, ignoring\n");
3443 slist_free_strings_full(cc_list);
3449 #define SET_ENTRY(entry, str) \
3452 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3455 #define SET_ADDRESS(type, str) \
3458 compose_entry_append(compose, str, type, PREF_NONE); \
3461 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3463 cm_return_if_fail(msginfo != NULL);
3465 SET_ENTRY(subject_entry, msginfo->subject);
3466 SET_ENTRY(from_name, msginfo->from);
3467 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3468 SET_ADDRESS(COMPOSE_CC, compose->cc);
3469 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3470 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3471 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3472 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3474 compose_update_priority_menu_item(compose);
3475 compose_update_privacy_system_menu_item(compose, FALSE);
3476 compose_show_first_last_header(compose, TRUE);
3482 static void compose_insert_sig(Compose *compose, gboolean replace)
3484 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3485 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3487 GtkTextIter iter, iter_end;
3488 gint cur_pos, ins_pos;
3489 gboolean prev_autowrap;
3490 gboolean found = FALSE;
3491 gboolean exists = FALSE;
3493 cm_return_if_fail(compose->account != NULL);
3497 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3498 G_CALLBACK(compose_changed_cb),
3501 mark = gtk_text_buffer_get_insert(buffer);
3502 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3503 cur_pos = gtk_text_iter_get_offset (&iter);
3506 gtk_text_buffer_get_end_iter(buffer, &iter);
3508 exists = (compose->sig_str != NULL);
3511 GtkTextIter first_iter, start_iter, end_iter;
3513 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3515 if (!exists || compose->sig_str[0] == '\0')
3518 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3519 compose->signature_tag);
3522 /* include previous \n\n */
3523 gtk_text_iter_backward_chars(&first_iter, 1);
3524 start_iter = first_iter;
3525 end_iter = first_iter;
3527 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3528 compose->signature_tag);
3529 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3530 compose->signature_tag);
3532 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3538 g_free(compose->sig_str);
3539 compose->sig_str = account_get_signature_str(compose->account);
3541 cur_pos = gtk_text_iter_get_offset(&iter);
3543 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3544 g_free(compose->sig_str);
3545 compose->sig_str = NULL;
3547 if (compose->sig_inserted == FALSE)
3548 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3549 compose->sig_inserted = TRUE;
3551 cur_pos = gtk_text_iter_get_offset(&iter);
3552 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3554 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3555 gtk_text_iter_forward_chars(&iter, 1);
3556 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3557 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3559 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3560 cur_pos = gtk_text_buffer_get_char_count (buffer);
3563 /* put the cursor where it should be
3564 * either where the quote_fmt says, either where it was */
3565 if (compose->set_cursor_pos < 0)
3566 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3568 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3569 compose->set_cursor_pos);
3571 compose->set_cursor_pos = -1;
3572 gtk_text_buffer_place_cursor(buffer, &iter);
3573 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3574 G_CALLBACK(compose_changed_cb),
3580 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3583 GtkTextBuffer *buffer;
3586 const gchar *cur_encoding;
3587 gchar buf[BUFFSIZE];
3590 gboolean prev_autowrap;
3593 GString *file_contents = NULL;
3594 ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3596 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3598 /* get the size of the file we are about to insert */
3599 ret = g_stat(file, &file_stat);
3601 gchar *shortfile = g_path_get_basename(file);
3602 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3604 return COMPOSE_INSERT_NO_FILE;
3605 } else if (prefs_common.warn_large_insert == TRUE) {
3607 /* ask user for confirmation if the file is large */
3608 if (prefs_common.warn_large_insert_size < 0 ||
3609 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3613 msg = g_strdup_printf(_("You are about to insert a file of %s "
3614 "in the message body. Are you sure you want to do that?"),
3615 to_human_readable(file_stat.st_size));
3616 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3617 g_strconcat("+", _("_Insert"), NULL), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3620 /* do we ask for confirmation next time? */
3621 if (aval & G_ALERTDISABLE) {
3622 /* no confirmation next time, disable feature in preferences */
3623 aval &= ~G_ALERTDISABLE;
3624 prefs_common.warn_large_insert = FALSE;
3627 /* abort file insertion if user canceled action */
3628 if (aval != G_ALERTALTERNATE) {
3629 return COMPOSE_INSERT_NO_FILE;
3635 if ((fp = g_fopen(file, "rb")) == NULL) {
3636 FILE_OP_ERROR(file, "fopen");
3637 return COMPOSE_INSERT_READ_ERROR;
3640 prev_autowrap = compose->autowrap;
3641 compose->autowrap = FALSE;
3643 text = GTK_TEXT_VIEW(compose->text);
3644 buffer = gtk_text_view_get_buffer(text);
3645 mark = gtk_text_buffer_get_insert(buffer);
3646 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3648 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3649 G_CALLBACK(text_inserted),
3652 cur_encoding = conv_get_locale_charset_str_no_utf8();
3654 file_contents = g_string_new("");
3655 while (fgets(buf, sizeof(buf), fp) != NULL) {
3658 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3659 str = g_strdup(buf);
3661 codeconv_set_strict(TRUE);
3662 str = conv_codeset_strdup
3663 (buf, cur_encoding, CS_INTERNAL);
3664 codeconv_set_strict(FALSE);
3667 result = COMPOSE_INSERT_INVALID_CHARACTER;