2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 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/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
121 #define N_ATTACH_COLS (N_COL_COLUMNS)
125 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
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 GdkColor default_header_bgcolor = {
194 static GdkColor default_header_color = {
201 static GList *compose_list = NULL;
202 static GSList *extra_headers = NULL;
204 static Compose *compose_generic_new (PrefsAccount *account,
208 GList *listAddress );
210 static Compose *compose_create (PrefsAccount *account,
215 static void compose_entry_indicate (Compose *compose,
216 const gchar *address);
217 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
218 ComposeQuoteMode quote_mode,
222 static Compose *compose_forward_multiple (PrefsAccount *account,
223 GSList *msginfo_list);
224 static Compose *compose_reply (MsgInfo *msginfo,
225 ComposeQuoteMode quote_mode,
230 static Compose *compose_reply_mode (ComposeMode mode,
231 GSList *msginfo_list,
233 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
234 static void compose_update_privacy_systems_menu(Compose *compose);
236 static GtkWidget *compose_account_option_menu_create
238 static void compose_set_out_encoding (Compose *compose);
239 static void compose_set_template_menu (Compose *compose);
240 static void compose_destroy (Compose *compose);
242 static MailField compose_entries_set (Compose *compose,
244 ComposeEntryType to_type);
245 static gint compose_parse_header (Compose *compose,
247 static gint compose_parse_manual_headers (Compose *compose,
249 HeaderEntry *entries);
250 static gchar *compose_parse_references (const gchar *ref,
253 static gchar *compose_quote_fmt (Compose *compose,
259 gboolean need_unescape,
260 const gchar *err_msg);
262 static void compose_reply_set_entry (Compose *compose,
268 followup_and_reply_to);
269 static void compose_reedit_set_entry (Compose *compose,
272 static void compose_insert_sig (Compose *compose,
274 static ComposeInsertResult compose_insert_file (Compose *compose,
277 static gboolean compose_attach_append (Compose *compose,
280 const gchar *content_type,
281 const gchar *charset);
282 static void compose_attach_parts (Compose *compose,
285 static gboolean compose_beautify_paragraph (Compose *compose,
286 GtkTextIter *par_iter,
288 static void compose_wrap_all (Compose *compose);
289 static void compose_wrap_all_full (Compose *compose,
292 static void compose_set_title (Compose *compose);
293 static void compose_select_account (Compose *compose,
294 PrefsAccount *account,
297 static PrefsAccount *compose_current_mail_account(void);
298 /* static gint compose_send (Compose *compose); */
299 static gboolean compose_check_for_valid_recipient
301 static gboolean compose_check_entries (Compose *compose,
302 gboolean check_everything);
303 static gint compose_write_to_file (Compose *compose,
306 gboolean attach_parts);
307 static gint compose_write_body_to_file (Compose *compose,
309 static gint compose_remove_reedit_target (Compose *compose,
311 static void compose_remove_draft (Compose *compose);
312 static gint compose_queue_sub (Compose *compose,
316 gboolean check_subject,
317 gboolean remove_reedit_target);
318 static int compose_add_attachments (Compose *compose,
320 static gchar *compose_get_header (Compose *compose);
321 static gchar *compose_get_manual_headers_info (Compose *compose);
323 static void compose_convert_header (Compose *compose,
328 gboolean addr_field);
330 static void compose_attach_info_free (AttachInfo *ainfo);
331 static void compose_attach_remove_selected (GtkAction *action,
334 static void compose_template_apply (Compose *compose,
337 static void compose_attach_property (GtkAction *action,
339 static void compose_attach_property_create (gboolean *cancelled);
340 static void attach_property_ok (GtkWidget *widget,
341 gboolean *cancelled);
342 static void attach_property_cancel (GtkWidget *widget,
343 gboolean *cancelled);
344 static gint attach_property_delete_event (GtkWidget *widget,
346 gboolean *cancelled);
347 static gboolean attach_property_key_pressed (GtkWidget *widget,
349 gboolean *cancelled);
351 static void compose_exec_ext_editor (Compose *compose);
353 static gint compose_exec_ext_editor_real (const gchar *file,
354 GdkNativeWindow socket_wid);
355 static gboolean compose_ext_editor_kill (Compose *compose);
356 static gboolean compose_input_cb (GIOChannel *source,
357 GIOCondition condition,
359 static void compose_set_ext_editor_sensitive (Compose *compose,
361 static gboolean compose_get_ext_editor_cmd_valid();
362 static gboolean compose_get_ext_editor_uses_socket();
363 static gboolean compose_ext_editor_plug_removed_cb
366 #endif /* G_OS_UNIX */
368 static void compose_undo_state_changed (UndoMain *undostruct,
373 static void compose_create_header_entry (Compose *compose);
374 static void compose_add_header_entry (Compose *compose, const gchar *header,
375 gchar *text, ComposePrefType pref_type);
376 static void compose_remove_header_entries(Compose *compose);
378 static void compose_update_priority_menu_item(Compose * compose);
380 static void compose_spell_menu_changed (void *data);
381 static void compose_dict_changed (void *data);
383 static void compose_add_field_list ( Compose *compose,
384 GList *listAddress );
386 /* callback functions */
388 static void compose_notebook_size_alloc (GtkNotebook *notebook,
389 GtkAllocation *allocation,
391 static gboolean compose_edit_size_alloc (GtkEditable *widget,
392 GtkAllocation *allocation,
393 GtkSHRuler *shruler);
394 static void account_activated (GtkComboBox *optmenu,
396 static void attach_selected (GtkTreeView *tree_view,
397 GtkTreePath *tree_path,
398 GtkTreeViewColumn *column,
400 static gboolean attach_button_pressed (GtkWidget *widget,
401 GdkEventButton *event,
403 static gboolean attach_key_pressed (GtkWidget *widget,
406 static void compose_send_cb (GtkAction *action, gpointer data);
407 static void compose_send_later_cb (GtkAction *action, gpointer data);
409 static void compose_save_cb (GtkAction *action,
412 static void compose_attach_cb (GtkAction *action,
414 static void compose_insert_file_cb (GtkAction *action,
416 static void compose_insert_sig_cb (GtkAction *action,
418 static void compose_replace_sig_cb (GtkAction *action,
421 static void compose_close_cb (GtkAction *action,
423 static void compose_print_cb (GtkAction *action,
426 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
428 static void compose_address_cb (GtkAction *action,
430 static void about_show_cb (GtkAction *action,
432 static void compose_template_activate_cb(GtkWidget *widget,
435 static void compose_ext_editor_cb (GtkAction *action,
438 static gint compose_delete_cb (GtkWidget *widget,
442 static void compose_undo_cb (GtkAction *action,
444 static void compose_redo_cb (GtkAction *action,
446 static void compose_cut_cb (GtkAction *action,
448 static void compose_copy_cb (GtkAction *action,
450 static void compose_paste_cb (GtkAction *action,
452 static void compose_paste_as_quote_cb (GtkAction *action,
454 static void compose_paste_no_wrap_cb (GtkAction *action,
456 static void compose_paste_wrap_cb (GtkAction *action,
458 static void compose_allsel_cb (GtkAction *action,
461 static void compose_advanced_action_cb (GtkAction *action,
464 static void compose_grab_focus_cb (GtkWidget *widget,
467 static void compose_changed_cb (GtkTextBuffer *textbuf,
470 static void compose_wrap_cb (GtkAction *action,
472 static void compose_wrap_all_cb (GtkAction *action,
474 static void compose_find_cb (GtkAction *action,
476 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
478 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
481 static void compose_toggle_ruler_cb (GtkToggleAction *action,
483 static void compose_toggle_sign_cb (GtkToggleAction *action,
485 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
487 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
488 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
489 static void activate_privacy_system (Compose *compose,
490 PrefsAccount *account,
492 static void compose_use_signing(Compose *compose, gboolean use_signing);
493 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
494 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
496 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
498 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
499 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
500 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
502 static void compose_attach_drag_received_cb (GtkWidget *widget,
503 GdkDragContext *drag_context,
506 GtkSelectionData *data,
510 static void compose_insert_drag_received_cb (GtkWidget *widget,
511 GdkDragContext *drag_context,
514 GtkSelectionData *data,
518 static void compose_header_drag_received_cb (GtkWidget *widget,
519 GdkDragContext *drag_context,
522 GtkSelectionData *data,
527 static gboolean compose_drag_drop (GtkWidget *widget,
528 GdkDragContext *drag_context,
530 guint time, gpointer user_data);
531 static gboolean completion_set_focus_to_subject
536 static void text_inserted (GtkTextBuffer *buffer,
541 static Compose *compose_generic_reply(MsgInfo *msginfo,
542 ComposeQuoteMode quote_mode,
546 gboolean followup_and_reply_to,
549 static void compose_headerentry_changed_cb (GtkWidget *entry,
550 ComposeHeaderEntry *headerentry);
551 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
553 ComposeHeaderEntry *headerentry);
554 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
555 ComposeHeaderEntry *headerentry);
557 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
559 static void compose_allow_user_actions (Compose *compose, gboolean allow);
561 static void compose_nothing_cb (GtkAction *action, gpointer data)
567 static void compose_check_all (GtkAction *action, gpointer data);
568 static void compose_highlight_all (GtkAction *action, gpointer data);
569 static void compose_check_backwards (GtkAction *action, gpointer data);
570 static void compose_check_forwards_go (GtkAction *action, gpointer data);
573 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
575 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
578 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
579 FolderItem *folder_item);
581 static void compose_attach_update_label(Compose *compose);
582 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
583 gboolean respect_default_to);
584 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
585 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
587 static GtkActionEntry compose_popup_entries[] =
589 {"Compose", NULL, "Compose", NULL, NULL, NULL },
590 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
591 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
592 {"Compose/---", NULL, "---", NULL, NULL, NULL },
593 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
596 static GtkActionEntry compose_entries[] =
598 {"Menu", NULL, "Menu", NULL, NULL, NULL },
600 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
601 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
603 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
605 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
606 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
607 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
609 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
610 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
611 {"Message/---", NULL, "---", NULL, NULL, NULL },
613 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
614 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
615 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
616 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
617 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
618 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
619 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
620 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
621 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
622 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
625 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
626 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
627 {"Edit/---", NULL, "---", NULL, NULL, NULL },
629 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
630 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
631 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
633 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
634 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
635 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
636 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
638 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
640 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
641 {"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*/
642 {"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*/
643 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
644 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
645 {"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*/
646 {"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*/
647 {"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*/
648 {"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*/
649 {"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*/
650 {"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*/
651 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
652 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
653 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
654 {"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*/
656 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
657 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
659 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
660 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
661 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
662 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
663 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
666 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
667 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
668 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
669 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
671 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
672 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
676 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
677 {"Options/---", NULL, "---", NULL, NULL, NULL },
678 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
679 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
681 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
682 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
684 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
685 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
686 #define ENC_ACTION(cs_char,c_char,string) \
687 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
689 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
690 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
691 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
692 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
693 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
694 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
695 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
696 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
697 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
700 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
702 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
703 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
704 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
705 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
708 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
711 static GtkToggleActionEntry compose_toggle_entries[] =
713 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
714 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
715 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
716 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
717 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
718 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
719 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
722 static GtkRadioActionEntry compose_radio_rm_entries[] =
724 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
725 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
726 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
727 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
730 static GtkRadioActionEntry compose_radio_prio_entries[] =
732 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
733 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
734 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
735 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
736 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
739 static GtkRadioActionEntry compose_radio_enc_entries[] =
741 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
776 static GtkTargetEntry compose_mime_types[] =
778 {"text/uri-list", 0, 0},
779 {"UTF8_STRING", 0, 0},
783 static gboolean compose_put_existing_to_front(MsgInfo *info)
785 const GList *compose_list = compose_get_compose_list();
786 const GList *elem = NULL;
789 for (elem = compose_list; elem != NULL && elem->data != NULL;
791 Compose *c = (Compose*)elem->data;
793 if (!c->targetinfo || !c->targetinfo->msgid ||
797 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
798 gtkut_window_popup(c->window);
806 static GdkColor quote_color1 =
807 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
808 static GdkColor quote_color2 =
809 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
810 static GdkColor quote_color3 =
811 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 static GdkColor quote_bgcolor1 =
814 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
815 static GdkColor quote_bgcolor2 =
816 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
817 static GdkColor quote_bgcolor3 =
818 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
820 static GdkColor signature_color = {
827 static GdkColor uri_color = {
834 static void compose_create_tags(GtkTextView *text, Compose *compose)
836 GtkTextBuffer *buffer;
837 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
838 #if !GTK_CHECK_VERSION(2, 24, 0)
845 buffer = gtk_text_view_get_buffer(text);
847 if (prefs_common.enable_color) {
848 /* grab the quote colors, converting from an int to a GdkColor */
849 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
851 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
853 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
855 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
857 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
859 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
861 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
863 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
866 signature_color = quote_color1 = quote_color2 = quote_color3 =
867 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
870 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
871 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
872 "foreground-gdk", "e_color1,
873 "paragraph-background-gdk", "e_bgcolor1,
875 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
876 "foreground-gdk", "e_color2,
877 "paragraph-background-gdk", "e_bgcolor2,
879 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
880 "foreground-gdk", "e_color3,
881 "paragraph-background-gdk", "e_bgcolor3,
884 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
885 "foreground-gdk", "e_color1,
887 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
888 "foreground-gdk", "e_color2,
890 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
891 "foreground-gdk", "e_color3,
895 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
896 "foreground-gdk", &signature_color,
899 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
900 "foreground-gdk", &uri_color,
902 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
903 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
905 #if !GTK_CHECK_VERSION(2, 24, 0)
906 color[0] = quote_color1;
907 color[1] = quote_color2;
908 color[2] = quote_color3;
909 color[3] = quote_bgcolor1;
910 color[4] = quote_bgcolor2;
911 color[5] = quote_bgcolor3;
912 color[6] = signature_color;
913 color[7] = uri_color;
915 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
916 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
918 for (i = 0; i < 8; i++) {
919 if (success[i] == FALSE) {
920 g_warning("Compose: color allocation failed.");
921 quote_color1 = quote_color2 = quote_color3 =
922 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
923 signature_color = uri_color = black;
929 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
932 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
935 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
937 return compose_generic_new(account, mailto, item, NULL, NULL);
940 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
942 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
945 #define SCROLL_TO_CURSOR(compose) { \
946 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
947 gtk_text_view_get_buffer( \
948 GTK_TEXT_VIEW(compose->text))); \
949 gtk_text_view_scroll_mark_onscreen( \
950 GTK_TEXT_VIEW(compose->text), \
954 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
957 if (folderidentifier) {
958 #if !GTK_CHECK_VERSION(2, 24, 0)
959 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
961 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
963 prefs_common.compose_save_to_history = add_history(
964 prefs_common.compose_save_to_history, folderidentifier);
965 #if !GTK_CHECK_VERSION(2, 24, 0)
966 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
967 prefs_common.compose_save_to_history);
969 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
970 prefs_common.compose_save_to_history);
974 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
975 if (folderidentifier)
976 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
978 gtk_entry_set_text(GTK_ENTRY(entry), "");
981 static gchar *compose_get_save_to(Compose *compose)
984 gchar *result = NULL;
985 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
986 result = gtk_editable_get_chars(entry, 0, -1);
989 #if !GTK_CHECK_VERSION(2, 24, 0)
990 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
992 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
994 prefs_common.compose_save_to_history = add_history(
995 prefs_common.compose_save_to_history, result);
996 #if !GTK_CHECK_VERSION(2, 24, 0)
997 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
998 prefs_common.compose_save_to_history);
1000 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1001 prefs_common.compose_save_to_history);
1007 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1008 GList *attach_files, GList *listAddress )
1011 GtkTextView *textview;
1012 GtkTextBuffer *textbuf;
1014 const gchar *subject_format = NULL;
1015 const gchar *body_format = NULL;
1016 gchar *mailto_from = NULL;
1017 PrefsAccount *mailto_account = NULL;
1018 MsgInfo* dummyinfo = NULL;
1019 gint cursor_pos = -1;
1020 MailField mfield = NO_FIELD_PRESENT;
1024 /* check if mailto defines a from */
1025 if (mailto && *mailto != '\0') {
1026 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1027 /* mailto defines a from, check if we can get account prefs from it,
1028 if not, the account prefs will be guessed using other ways, but we'll keep
1031 mailto_account = account_find_from_address(mailto_from, TRUE);
1032 if (mailto_account == NULL) {
1034 Xstrdup_a(tmp_from, mailto_from, return NULL);
1035 extract_address(tmp_from);
1036 mailto_account = account_find_from_address(tmp_from, TRUE);
1040 account = mailto_account;
1043 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1044 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1045 account = account_find_from_id(item->prefs->default_account);
1047 /* if no account prefs set, fallback to the current one */
1048 if (!account) account = cur_account;
1049 cm_return_val_if_fail(account != NULL, NULL);
1051 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1053 /* override from name if mailto asked for it */
1055 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1056 g_free(mailto_from);
1058 /* override from name according to folder properties */
1059 if (item && item->prefs &&
1060 item->prefs->compose_with_format &&
1061 item->prefs->compose_override_from_format &&
1062 *item->prefs->compose_override_from_format != '\0') {
1067 dummyinfo = compose_msginfo_new_from_compose(compose);
1069 /* decode \-escape sequences in the internal representation of the quote format */
1070 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1071 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1074 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1075 compose->gtkaspell);
1077 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1079 quote_fmt_scan_string(tmp);
1082 buf = quote_fmt_get_buffer();
1084 alertpanel_error(_("New message From format error."));
1086 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1087 quote_fmt_reset_vartable();
1092 compose->replyinfo = NULL;
1093 compose->fwdinfo = NULL;
1095 textview = GTK_TEXT_VIEW(compose->text);
1096 textbuf = gtk_text_view_get_buffer(textview);
1097 compose_create_tags(textview, compose);
1099 undo_block(compose->undostruct);
1101 compose_set_dictionaries_from_folder_prefs(compose, item);
1104 if (account->auto_sig)
1105 compose_insert_sig(compose, FALSE);
1106 gtk_text_buffer_get_start_iter(textbuf, &iter);
1107 gtk_text_buffer_place_cursor(textbuf, &iter);
1109 if (account->protocol != A_NNTP) {
1110 if (mailto && *mailto != '\0') {
1111 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1114 compose_set_folder_prefs(compose, item, TRUE);
1116 if (item && item->ret_rcpt) {
1117 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1120 if (mailto && *mailto != '\0') {
1121 if (!strchr(mailto, '@'))
1122 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1124 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1125 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1126 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1127 mfield = TO_FIELD_PRESENT;
1130 * CLAWS: just don't allow return receipt request, even if the user
1131 * may want to send an email. simple but foolproof.
1133 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1135 compose_add_field_list( compose, listAddress );
1137 if (item && item->prefs && item->prefs->compose_with_format) {
1138 subject_format = item->prefs->compose_subject_format;
1139 body_format = item->prefs->compose_body_format;
1140 } else if (account->compose_with_format) {
1141 subject_format = account->compose_subject_format;
1142 body_format = account->compose_body_format;
1143 } else if (prefs_common.compose_with_format) {
1144 subject_format = prefs_common.compose_subject_format;
1145 body_format = prefs_common.compose_body_format;
1148 if (subject_format || body_format) {
1151 && *subject_format != '\0' )
1153 gchar *subject = NULL;
1158 dummyinfo = compose_msginfo_new_from_compose(compose);
1160 /* decode \-escape sequences in the internal representation of the quote format */
1161 tmp = g_malloc(strlen(subject_format)+1);
1162 pref_get_unescaped_pref(tmp, subject_format);
1164 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1166 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1167 compose->gtkaspell);
1169 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1171 quote_fmt_scan_string(tmp);
1174 buf = quote_fmt_get_buffer();
1176 alertpanel_error(_("New message subject format error."));
1178 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1179 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1180 quote_fmt_reset_vartable();
1184 mfield = SUBJECT_FIELD_PRESENT;
1188 && *body_format != '\0' )
1191 GtkTextBuffer *buffer;
1192 GtkTextIter start, end;
1196 dummyinfo = compose_msginfo_new_from_compose(compose);
1198 text = GTK_TEXT_VIEW(compose->text);
1199 buffer = gtk_text_view_get_buffer(text);
1200 gtk_text_buffer_get_start_iter(buffer, &start);
1201 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1202 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1204 compose_quote_fmt(compose, dummyinfo,
1206 NULL, tmp, FALSE, TRUE,
1207 _("The body of the \"New message\" template has an error at line %d."));
1208 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1209 quote_fmt_reset_vartable();
1213 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1214 gtkaspell_highlight_all(compose->gtkaspell);
1216 mfield = BODY_FIELD_PRESENT;
1220 procmsg_msginfo_free( &dummyinfo );
1226 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1227 ainfo = (AttachInfo *) curr->data;
1228 compose_attach_append(compose, ainfo->file, ainfo->file,
1229 ainfo->content_type, ainfo->charset);
1233 compose_show_first_last_header(compose, TRUE);
1235 /* Set save folder */
1236 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1237 gchar *folderidentifier;
1239 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1240 folderidentifier = folder_item_get_identifier(item);
1241 compose_set_save_to(compose, folderidentifier);
1242 g_free(folderidentifier);
1245 /* Place cursor according to provided input (mfield) */
1247 case NO_FIELD_PRESENT:
1248 if (compose->header_last)
1249 gtk_widget_grab_focus(compose->header_last->entry);
1251 case TO_FIELD_PRESENT:
1252 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1254 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1257 gtk_widget_grab_focus(compose->subject_entry);
1259 case SUBJECT_FIELD_PRESENT:
1260 textview = GTK_TEXT_VIEW(compose->text);
1263 textbuf = gtk_text_view_get_buffer(textview);
1266 mark = gtk_text_buffer_get_insert(textbuf);
1267 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1268 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1270 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1271 * only defers where it comes to the variable body
1272 * is not null. If no body is present compose->text
1273 * will be null in which case you cannot place the
1274 * cursor inside the component so. An empty component
1275 * is therefore created before placing the cursor
1277 case BODY_FIELD_PRESENT:
1278 cursor_pos = quote_fmt_get_cursor_pos();
1279 if (cursor_pos == -1)
1280 gtk_widget_grab_focus(compose->header_last->entry);
1282 gtk_widget_grab_focus(compose->text);
1286 undo_unblock(compose->undostruct);
1288 if (prefs_common.auto_exteditor)
1289 compose_exec_ext_editor(compose);
1291 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1293 SCROLL_TO_CURSOR(compose);
1295 compose->modified = FALSE;
1296 compose_set_title(compose);
1298 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1303 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1304 gboolean override_pref, const gchar *system)
1306 const gchar *privacy = NULL;
1308 cm_return_if_fail(compose != NULL);
1309 cm_return_if_fail(account != NULL);
1311 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1314 if (account->default_privacy_system && strlen(account->default_privacy_system))
1315 privacy = account->default_privacy_system;
1319 GSList *privacy_avail = privacy_get_system_ids();
1320 if (privacy_avail && g_slist_length(privacy_avail)) {
1321 privacy = (gchar *)(privacy_avail->data);
1324 if (privacy != NULL) {
1326 g_free(compose->privacy_system);
1327 compose->privacy_system = NULL;
1328 g_free(compose->encdata);
1329 compose->encdata = NULL;
1331 if (compose->privacy_system == NULL)
1332 compose->privacy_system = g_strdup(privacy);
1333 else if (*(compose->privacy_system) == '\0') {
1334 g_free(compose->privacy_system);
1335 g_free(compose->encdata);
1336 compose->encdata = NULL;
1337 compose->privacy_system = g_strdup(privacy);
1339 compose_update_privacy_system_menu_item(compose, FALSE);
1340 compose_use_encryption(compose, TRUE);
1344 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1346 const gchar *privacy = NULL;
1348 if (account->default_privacy_system && strlen(account->default_privacy_system))
1349 privacy = account->default_privacy_system;
1353 GSList *privacy_avail = privacy_get_system_ids();
1354 if (privacy_avail && g_slist_length(privacy_avail)) {
1355 privacy = (gchar *)(privacy_avail->data);
1359 if (privacy != NULL) {
1361 g_free(compose->privacy_system);
1362 compose->privacy_system = NULL;
1363 g_free(compose->encdata);
1364 compose->encdata = NULL;
1366 if (compose->privacy_system == NULL)
1367 compose->privacy_system = g_strdup(privacy);
1368 compose_update_privacy_system_menu_item(compose, FALSE);
1369 compose_use_signing(compose, TRUE);
1373 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1377 Compose *compose = NULL;
1379 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1381 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1382 cm_return_val_if_fail(msginfo != NULL, NULL);
1384 list_len = g_slist_length(msginfo_list);
1388 case COMPOSE_REPLY_TO_ADDRESS:
1389 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1390 FALSE, prefs_common.default_reply_list, FALSE, body);
1392 case COMPOSE_REPLY_WITH_QUOTE:
1393 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1394 FALSE, prefs_common.default_reply_list, FALSE, body);
1396 case COMPOSE_REPLY_WITHOUT_QUOTE:
1397 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1398 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1400 case COMPOSE_REPLY_TO_SENDER:
1401 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1402 FALSE, FALSE, TRUE, body);
1404 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1405 compose = compose_followup_and_reply_to(msginfo,
1406 COMPOSE_QUOTE_CHECK,
1407 FALSE, FALSE, body);
1409 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1410 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1411 FALSE, FALSE, TRUE, body);
1413 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1414 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1415 FALSE, FALSE, TRUE, NULL);
1417 case COMPOSE_REPLY_TO_ALL:
1418 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1419 TRUE, FALSE, FALSE, body);
1421 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1422 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1423 TRUE, FALSE, FALSE, body);
1425 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1426 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1427 TRUE, FALSE, FALSE, NULL);
1429 case COMPOSE_REPLY_TO_LIST:
1430 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1431 FALSE, TRUE, FALSE, body);
1433 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1434 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1435 FALSE, TRUE, FALSE, body);
1437 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1438 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1439 FALSE, TRUE, FALSE, NULL);
1441 case COMPOSE_FORWARD:
1442 if (prefs_common.forward_as_attachment) {
1443 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1446 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1450 case COMPOSE_FORWARD_INLINE:
1451 /* check if we reply to more than one Message */
1452 if (list_len == 1) {
1453 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1456 /* more messages FALL THROUGH */
1457 case COMPOSE_FORWARD_AS_ATTACH:
1458 compose = compose_forward_multiple(NULL, msginfo_list);
1460 case COMPOSE_REDIRECT:
1461 compose = compose_redirect(NULL, msginfo, FALSE);
1464 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1467 if (compose == NULL) {
1468 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1472 compose->rmode = mode;
1473 switch (compose->rmode) {
1475 case COMPOSE_REPLY_WITH_QUOTE:
1476 case COMPOSE_REPLY_WITHOUT_QUOTE:
1477 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1478 debug_print("reply mode Normal\n");
1479 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1480 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1482 case COMPOSE_REPLY_TO_SENDER:
1483 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1484 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1485 debug_print("reply mode Sender\n");
1486 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1488 case COMPOSE_REPLY_TO_ALL:
1489 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1490 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1491 debug_print("reply mode All\n");
1492 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1494 case COMPOSE_REPLY_TO_LIST:
1495 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1496 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1497 debug_print("reply mode List\n");
1498 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1500 case COMPOSE_REPLY_TO_ADDRESS:
1501 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1509 static Compose *compose_reply(MsgInfo *msginfo,
1510 ComposeQuoteMode quote_mode,
1516 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1517 to_sender, FALSE, body);
1520 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1521 ComposeQuoteMode quote_mode,
1526 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1527 to_sender, TRUE, body);
1530 static void compose_extract_original_charset(Compose *compose)
1532 MsgInfo *info = NULL;
1533 if (compose->replyinfo) {
1534 info = compose->replyinfo;
1535 } else if (compose->fwdinfo) {
1536 info = compose->fwdinfo;
1537 } else if (compose->targetinfo) {
1538 info = compose->targetinfo;
1541 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1542 MimeInfo *partinfo = mimeinfo;
1543 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1544 partinfo = procmime_mimeinfo_next(partinfo);
1546 compose->orig_charset =
1547 g_strdup(procmime_mimeinfo_get_parameter(
1548 partinfo, "charset"));
1550 procmime_mimeinfo_free_all(&mimeinfo);
1554 #define SIGNAL_BLOCK(buffer) { \
1555 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1556 G_CALLBACK(compose_changed_cb), \
1558 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1559 G_CALLBACK(text_inserted), \
1563 #define SIGNAL_UNBLOCK(buffer) { \
1564 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1565 G_CALLBACK(compose_changed_cb), \
1567 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1568 G_CALLBACK(text_inserted), \
1572 static Compose *compose_generic_reply(MsgInfo *msginfo,
1573 ComposeQuoteMode quote_mode,
1574 gboolean to_all, gboolean to_ml,
1576 gboolean followup_and_reply_to,
1580 PrefsAccount *account = NULL;
1581 GtkTextView *textview;
1582 GtkTextBuffer *textbuf;
1583 gboolean quote = FALSE;
1584 const gchar *qmark = NULL;
1585 const gchar *body_fmt = NULL;
1586 gchar *s_system = NULL;
1588 cm_return_val_if_fail(msginfo != NULL, NULL);
1589 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1591 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1593 cm_return_val_if_fail(account != NULL, NULL);
1595 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1597 compose->updating = TRUE;
1599 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1600 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1602 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1603 if (!compose->replyinfo)
1604 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1606 compose_extract_original_charset(compose);
1608 if (msginfo->folder && msginfo->folder->ret_rcpt)
1609 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1611 /* Set save folder */
1612 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1613 gchar *folderidentifier;
1615 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1616 folderidentifier = folder_item_get_identifier(msginfo->folder);
1617 compose_set_save_to(compose, folderidentifier);
1618 g_free(folderidentifier);
1621 if (compose_parse_header(compose, msginfo) < 0) {
1622 compose->updating = FALSE;
1623 compose_destroy(compose);
1627 /* override from name according to folder properties */
1628 if (msginfo->folder && msginfo->folder->prefs &&
1629 msginfo->folder->prefs->reply_with_format &&
1630 msginfo->folder->prefs->reply_override_from_format &&
1631 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1636 /* decode \-escape sequences in the internal representation of the quote format */
1637 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1638 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1641 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1642 compose->gtkaspell);
1644 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1646 quote_fmt_scan_string(tmp);
1649 buf = quote_fmt_get_buffer();
1651 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1653 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1654 quote_fmt_reset_vartable();
1659 textview = (GTK_TEXT_VIEW(compose->text));
1660 textbuf = gtk_text_view_get_buffer(textview);
1661 compose_create_tags(textview, compose);
1663 undo_block(compose->undostruct);
1665 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1666 gtkaspell_block_check(compose->gtkaspell);
1669 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1670 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1671 /* use the reply format of folder (if enabled), or the account's one
1672 (if enabled) or fallback to the global reply format, which is always
1673 enabled (even if empty), and use the relevant quotemark */
1675 if (msginfo->folder && msginfo->folder->prefs &&
1676 msginfo->folder->prefs->reply_with_format) {
1677 qmark = msginfo->folder->prefs->reply_quotemark;
1678 body_fmt = msginfo->folder->prefs->reply_body_format;
1680 } else if (account->reply_with_format) {
1681 qmark = account->reply_quotemark;
1682 body_fmt = account->reply_body_format;
1685 qmark = prefs_common.quotemark;
1686 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1687 body_fmt = gettext(prefs_common.quotefmt);
1694 /* empty quotemark is not allowed */
1695 if (qmark == NULL || *qmark == '\0')
1697 compose_quote_fmt(compose, compose->replyinfo,
1698 body_fmt, qmark, body, FALSE, TRUE,
1699 _("The body of the \"Reply\" template has an error at line %d."));
1700 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1701 quote_fmt_reset_vartable();
1704 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1705 compose_force_encryption(compose, account, FALSE, s_system);
1708 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1709 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1710 compose_force_signing(compose, account, s_system);
1714 SIGNAL_BLOCK(textbuf);
1716 if (account->auto_sig)
1717 compose_insert_sig(compose, FALSE);
1719 compose_wrap_all(compose);
1722 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1723 gtkaspell_highlight_all(compose->gtkaspell);
1724 gtkaspell_unblock_check(compose->gtkaspell);
1726 SIGNAL_UNBLOCK(textbuf);
1728 gtk_widget_grab_focus(compose->text);
1730 undo_unblock(compose->undostruct);
1732 if (prefs_common.auto_exteditor)
1733 compose_exec_ext_editor(compose);
1735 compose->modified = FALSE;
1736 compose_set_title(compose);
1738 compose->updating = FALSE;
1739 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1740 SCROLL_TO_CURSOR(compose);
1742 if (compose->deferred_destroy) {
1743 compose_destroy(compose);
1751 #define INSERT_FW_HEADER(var, hdr) \
1752 if (msginfo->var && *msginfo->var) { \
1753 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1754 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1755 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1758 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1759 gboolean as_attach, const gchar *body,
1760 gboolean no_extedit,
1764 GtkTextView *textview;
1765 GtkTextBuffer *textbuf;
1766 gint cursor_pos = -1;
1769 cm_return_val_if_fail(msginfo != NULL, NULL);
1770 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1772 if (!account && !(account = compose_find_account(msginfo)))
1773 account = cur_account;
1775 if (!prefs_common.forward_as_attachment)
1776 mode = COMPOSE_FORWARD_INLINE;
1778 mode = COMPOSE_FORWARD;
1779 compose = compose_create(account, msginfo->folder, mode, batch);
1781 compose->updating = TRUE;
1782 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1783 if (!compose->fwdinfo)
1784 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1786 compose_extract_original_charset(compose);
1788 if (msginfo->subject && *msginfo->subject) {
1789 gchar *buf, *buf2, *p;
1791 buf = p = g_strdup(msginfo->subject);
1792 p += subject_get_prefix_length(p);
1793 memmove(buf, p, strlen(p) + 1);
1795 buf2 = g_strdup_printf("Fw: %s", buf);
1796 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1802 /* override from name according to folder properties */
1803 if (msginfo->folder && msginfo->folder->prefs &&
1804 msginfo->folder->prefs->forward_with_format &&
1805 msginfo->folder->prefs->forward_override_from_format &&
1806 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1810 MsgInfo *full_msginfo = NULL;
1813 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1815 full_msginfo = procmsg_msginfo_copy(msginfo);
1817 /* decode \-escape sequences in the internal representation of the quote format */
1818 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1819 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1822 gtkaspell_block_check(compose->gtkaspell);
1823 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1824 compose->gtkaspell);
1826 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1828 quote_fmt_scan_string(tmp);
1831 buf = quote_fmt_get_buffer();
1833 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1835 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1836 quote_fmt_reset_vartable();
1839 procmsg_msginfo_free(&full_msginfo);
1842 textview = GTK_TEXT_VIEW(compose->text);
1843 textbuf = gtk_text_view_get_buffer(textview);
1844 compose_create_tags(textview, compose);
1846 undo_block(compose->undostruct);
1850 msgfile = procmsg_get_message_file(msginfo);
1851 if (!is_file_exist(msgfile))
1852 g_warning("%s: file does not exist", msgfile);
1854 compose_attach_append(compose, msgfile, msgfile,
1855 "message/rfc822", NULL);
1859 const gchar *qmark = NULL;
1860 const gchar *body_fmt = NULL;
1861 MsgInfo *full_msginfo;
1863 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1865 full_msginfo = procmsg_msginfo_copy(msginfo);
1867 /* use the forward format of folder (if enabled), or the account's one
1868 (if enabled) or fallback to the global forward format, which is always
1869 enabled (even if empty), and use the relevant quotemark */
1870 if (msginfo->folder && msginfo->folder->prefs &&
1871 msginfo->folder->prefs->forward_with_format) {
1872 qmark = msginfo->folder->prefs->forward_quotemark;
1873 body_fmt = msginfo->folder->prefs->forward_body_format;
1875 } else if (account->forward_with_format) {
1876 qmark = account->forward_quotemark;
1877 body_fmt = account->forward_body_format;
1880 qmark = prefs_common.fw_quotemark;
1881 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1882 body_fmt = gettext(prefs_common.fw_quotefmt);
1887 /* empty quotemark is not allowed */
1888 if (qmark == NULL || *qmark == '\0')
1891 compose_quote_fmt(compose, full_msginfo,
1892 body_fmt, qmark, body, FALSE, TRUE,
1893 _("The body of the \"Forward\" template has an error at line %d."));
1894 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1895 quote_fmt_reset_vartable();
1896 compose_attach_parts(compose, msginfo);
1898 procmsg_msginfo_free(&full_msginfo);
1901 SIGNAL_BLOCK(textbuf);
1903 if (account->auto_sig)
1904 compose_insert_sig(compose, FALSE);
1906 compose_wrap_all(compose);
1909 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1910 gtkaspell_highlight_all(compose->gtkaspell);
1911 gtkaspell_unblock_check(compose->gtkaspell);
1913 SIGNAL_UNBLOCK(textbuf);
1915 cursor_pos = quote_fmt_get_cursor_pos();
1916 if (cursor_pos == -1)
1917 gtk_widget_grab_focus(compose->header_last->entry);
1919 gtk_widget_grab_focus(compose->text);
1921 if (!no_extedit && prefs_common.auto_exteditor)
1922 compose_exec_ext_editor(compose);
1925 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1926 gchar *folderidentifier;
1928 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1929 folderidentifier = folder_item_get_identifier(msginfo->folder);
1930 compose_set_save_to(compose, folderidentifier);
1931 g_free(folderidentifier);
1934 undo_unblock(compose->undostruct);
1936 compose->modified = FALSE;
1937 compose_set_title(compose);
1939 compose->updating = FALSE;
1940 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1941 SCROLL_TO_CURSOR(compose);
1943 if (compose->deferred_destroy) {
1944 compose_destroy(compose);
1948 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1953 #undef INSERT_FW_HEADER
1955 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1958 GtkTextView *textview;
1959 GtkTextBuffer *textbuf;
1963 gboolean single_mail = TRUE;
1965 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1967 if (g_slist_length(msginfo_list) > 1)
1968 single_mail = FALSE;
1970 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1971 if (((MsgInfo *)msginfo->data)->folder == NULL)
1974 /* guess account from first selected message */
1976 !(account = compose_find_account(msginfo_list->data)))
1977 account = cur_account;
1979 cm_return_val_if_fail(account != NULL, NULL);
1981 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1982 if (msginfo->data) {
1983 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1984 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1988 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1989 g_warning("no msginfo_list");
1993 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1995 compose->updating = TRUE;
1997 /* override from name according to folder properties */
1998 if (msginfo_list->data) {
1999 MsgInfo *msginfo = msginfo_list->data;
2001 if (msginfo->folder && msginfo->folder->prefs &&
2002 msginfo->folder->prefs->forward_with_format &&
2003 msginfo->folder->prefs->forward_override_from_format &&
2004 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2009 /* decode \-escape sequences in the internal representation of the quote format */
2010 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2011 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2014 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2015 compose->gtkaspell);
2017 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2019 quote_fmt_scan_string(tmp);
2022 buf = quote_fmt_get_buffer();
2024 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2026 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2027 quote_fmt_reset_vartable();
2033 textview = GTK_TEXT_VIEW(compose->text);
2034 textbuf = gtk_text_view_get_buffer(textview);
2035 compose_create_tags(textview, compose);
2037 undo_block(compose->undostruct);
2038 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2039 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2041 if (!is_file_exist(msgfile))
2042 g_warning("%s: file does not exist", msgfile);
2044 compose_attach_append(compose, msgfile, msgfile,
2045 "message/rfc822", NULL);
2050 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2051 if (info->subject && *info->subject) {
2052 gchar *buf, *buf2, *p;
2054 buf = p = g_strdup(info->subject);
2055 p += subject_get_prefix_length(p);
2056 memmove(buf, p, strlen(p) + 1);
2058 buf2 = g_strdup_printf("Fw: %s", buf);
2059 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2065 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2066 _("Fw: multiple emails"));
2069 SIGNAL_BLOCK(textbuf);
2071 if (account->auto_sig)
2072 compose_insert_sig(compose, FALSE);
2074 compose_wrap_all(compose);
2076 SIGNAL_UNBLOCK(textbuf);
2078 gtk_text_buffer_get_start_iter(textbuf, &iter);
2079 gtk_text_buffer_place_cursor(textbuf, &iter);
2081 if (prefs_common.auto_exteditor)
2082 compose_exec_ext_editor(compose);
2084 gtk_widget_grab_focus(compose->header_last->entry);
2085 undo_unblock(compose->undostruct);
2086 compose->modified = FALSE;
2087 compose_set_title(compose);
2089 compose->updating = FALSE;
2090 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2091 SCROLL_TO_CURSOR(compose);
2093 if (compose->deferred_destroy) {
2094 compose_destroy(compose);
2098 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2103 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2105 GtkTextIter start = *iter;
2106 GtkTextIter end_iter;
2107 int start_pos = gtk_text_iter_get_offset(&start);
2109 if (!compose->account->sig_sep)
2112 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2113 start_pos+strlen(compose->account->sig_sep));
2115 /* check sig separator */
2116 str = gtk_text_iter_get_text(&start, &end_iter);
2117 if (!strcmp(str, compose->account->sig_sep)) {
2119 /* check end of line (\n) */
2120 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2121 start_pos+strlen(compose->account->sig_sep));
2122 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2123 start_pos+strlen(compose->account->sig_sep)+1);
2124 tmp = gtk_text_iter_get_text(&start, &end_iter);
2125 if (!strcmp(tmp,"\n")) {
2137 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2139 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2140 Compose *compose = (Compose *)data;
2141 FolderItem *old_item = NULL;
2142 FolderItem *new_item = NULL;
2143 gchar *old_id, *new_id;
2145 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2146 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2149 old_item = hookdata->item;
2150 new_item = hookdata->item2;
2152 old_id = folder_item_get_identifier(old_item);
2153 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2155 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2156 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2157 compose->targetinfo->folder = new_item;
2160 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2161 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2162 compose->replyinfo->folder = new_item;
2165 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2166 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2167 compose->fwdinfo->folder = new_item;
2175 static void compose_colorize_signature(Compose *compose)
2177 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2179 GtkTextIter end_iter;
2180 gtk_text_buffer_get_start_iter(buffer, &iter);
2181 while (gtk_text_iter_forward_line(&iter))
2182 if (compose_is_sig_separator(compose, buffer, &iter)) {
2183 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2184 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2188 #define BLOCK_WRAP() { \
2189 prev_autowrap = compose->autowrap; \
2190 buffer = gtk_text_view_get_buffer( \
2191 GTK_TEXT_VIEW(compose->text)); \
2192 compose->autowrap = FALSE; \
2194 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2195 G_CALLBACK(compose_changed_cb), \
2197 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2198 G_CALLBACK(text_inserted), \
2201 #define UNBLOCK_WRAP() { \
2202 compose->autowrap = prev_autowrap; \
2203 if (compose->autowrap) { \
2204 gint old = compose->draft_timeout_tag; \
2205 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2206 compose_wrap_all(compose); \
2207 compose->draft_timeout_tag = old; \
2210 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2211 G_CALLBACK(compose_changed_cb), \
2213 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2214 G_CALLBACK(text_inserted), \
2218 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2220 Compose *compose = NULL;
2221 PrefsAccount *account = NULL;
2222 GtkTextView *textview;
2223 GtkTextBuffer *textbuf;
2227 gchar buf[BUFFSIZE];
2228 gboolean use_signing = FALSE;
2229 gboolean use_encryption = FALSE;
2230 gchar *privacy_system = NULL;
2231 int priority = PRIORITY_NORMAL;
2232 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2233 gboolean autowrap = prefs_common.autowrap;
2234 gboolean autoindent = prefs_common.auto_indent;
2235 HeaderEntry *manual_headers = NULL;
2237 cm_return_val_if_fail(msginfo != NULL, NULL);
2238 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2240 if (compose_put_existing_to_front(msginfo)) {
2244 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2245 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2246 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2247 gchar queueheader_buf[BUFFSIZE];
2250 /* Select Account from queue headers */
2251 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2252 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2253 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2254 account = account_find_from_id(id);
2256 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2257 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2258 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2259 account = account_find_from_id(id);
2261 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2262 sizeof(queueheader_buf), "NAID:")) {
2263 id = atoi(&queueheader_buf[strlen("NAID:")]);
2264 account = account_find_from_id(id);
2266 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2267 sizeof(queueheader_buf), "MAID:")) {
2268 id = atoi(&queueheader_buf[strlen("MAID:")]);
2269 account = account_find_from_id(id);
2271 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2272 sizeof(queueheader_buf), "S:")) {
2273 account = account_find_from_address(queueheader_buf, FALSE);
2275 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2276 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2277 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2278 use_signing = param;
2281 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2282 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2283 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2284 use_signing = param;
2287 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2288 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2289 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2290 use_encryption = param;
2292 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2293 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2294 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2295 use_encryption = param;
2297 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2298 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2299 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2302 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2303 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2304 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2307 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2308 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2309 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2311 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2312 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2313 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2315 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2316 sizeof(queueheader_buf), "X-Priority: ")) {
2317 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2320 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2321 sizeof(queueheader_buf), "RMID:")) {
2322 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2323 if (tokens[0] && tokens[1] && tokens[2]) {
2324 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2325 if (orig_item != NULL) {
2326 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2331 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2332 sizeof(queueheader_buf), "FMID:")) {
2333 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2334 if (tokens[0] && tokens[1] && tokens[2]) {
2335 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2336 if (orig_item != NULL) {
2337 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2342 /* Get manual headers */
2343 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2344 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2345 if (*listmh != '\0') {
2346 debug_print("Got manual headers: %s\n", listmh);
2347 manual_headers = procheader_entries_from_str(listmh);
2352 account = msginfo->folder->folder->account;
2355 if (!account && prefs_common.reedit_account_autosel) {
2356 gchar from[BUFFSIZE];
2357 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2358 extract_address(from);
2359 account = account_find_from_address(from, FALSE);
2363 account = cur_account;
2365 cm_return_val_if_fail(account != NULL, NULL);
2367 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2369 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2370 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2371 compose->autowrap = autowrap;
2372 compose->replyinfo = replyinfo;
2373 compose->fwdinfo = fwdinfo;
2375 compose->updating = TRUE;
2376 compose->priority = priority;
2378 if (privacy_system != NULL) {
2379 compose->privacy_system = privacy_system;
2380 compose_use_signing(compose, use_signing);
2381 compose_use_encryption(compose, use_encryption);
2382 compose_update_privacy_system_menu_item(compose, FALSE);
2384 activate_privacy_system(compose, account, FALSE);
2387 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2389 compose_extract_original_charset(compose);
2391 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2392 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2393 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2394 gchar queueheader_buf[BUFFSIZE];
2396 /* Set message save folder */
2397 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2398 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2399 compose_set_save_to(compose, &queueheader_buf[4]);
2401 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2402 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2409 if (compose_parse_header(compose, msginfo) < 0) {
2410 compose->updating = FALSE;
2411 compose_destroy(compose);
2414 compose_reedit_set_entry(compose, msginfo);
2416 textview = GTK_TEXT_VIEW(compose->text);
2417 textbuf = gtk_text_view_get_buffer(textview);
2418 compose_create_tags(textview, compose);
2420 mark = gtk_text_buffer_get_insert(textbuf);
2421 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2423 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2424 G_CALLBACK(compose_changed_cb),
2427 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2428 fp = procmime_get_first_encrypted_text_content(msginfo);
2430 compose_force_encryption(compose, account, TRUE, NULL);
2433 fp = procmime_get_first_text_content(msginfo);
2436 g_warning("Can't get text part");
2440 gboolean prev_autowrap;
2441 GtkTextBuffer *buffer;
2443 while (fgets(buf, sizeof(buf), fp) != NULL) {
2445 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2451 compose_attach_parts(compose, msginfo);
2453 compose_colorize_signature(compose);
2455 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2456 G_CALLBACK(compose_changed_cb),
2459 if (manual_headers != NULL) {
2460 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2461 procheader_entries_free(manual_headers);
2462 compose->updating = FALSE;
2463 compose_destroy(compose);
2466 procheader_entries_free(manual_headers);
2469 gtk_widget_grab_focus(compose->text);
2471 if (prefs_common.auto_exteditor) {
2472 compose_exec_ext_editor(compose);
2474 compose->modified = FALSE;
2475 compose_set_title(compose);
2477 compose->updating = FALSE;
2478 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2479 SCROLL_TO_CURSOR(compose);
2481 if (compose->deferred_destroy) {
2482 compose_destroy(compose);
2486 compose->sig_str = account_get_signature_str(compose->account);
2488 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2493 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2500 cm_return_val_if_fail(msginfo != NULL, NULL);
2503 account = account_get_reply_account(msginfo,
2504 prefs_common.reply_account_autosel);
2505 cm_return_val_if_fail(account != NULL, NULL);
2507 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2509 compose->updating = TRUE;
2511 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2512 compose->replyinfo = NULL;
2513 compose->fwdinfo = NULL;
2515 compose_show_first_last_header(compose, TRUE);
2517 gtk_widget_grab_focus(compose->header_last->entry);
2519 filename = procmsg_get_message_file(msginfo);
2521 if (filename == NULL) {
2522 compose->updating = FALSE;
2523 compose_destroy(compose);
2528 compose->redirect_filename = filename;
2530 /* Set save folder */
2531 item = msginfo->folder;
2532 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2533 gchar *folderidentifier;
2535 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2536 folderidentifier = folder_item_get_identifier(item);
2537 compose_set_save_to(compose, folderidentifier);
2538 g_free(folderidentifier);
2541 compose_attach_parts(compose, msginfo);
2543 if (msginfo->subject)
2544 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2546 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2548 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2549 _("The body of the \"Redirect\" template has an error at line %d."));
2550 quote_fmt_reset_vartable();
2551 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2553 compose_colorize_signature(compose);
2556 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2557 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2558 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2560 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2561 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2562 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2563 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2564 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2565 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2566 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2567 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2570 if (compose->toolbar->draft_btn)
2571 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2572 if (compose->toolbar->insert_btn)
2573 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2574 if (compose->toolbar->attach_btn)
2575 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2576 if (compose->toolbar->sig_btn)
2577 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2578 if (compose->toolbar->exteditor_btn)
2579 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2580 if (compose->toolbar->linewrap_current_btn)
2581 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2582 if (compose->toolbar->linewrap_all_btn)
2583 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2585 compose->modified = FALSE;
2586 compose_set_title(compose);
2587 compose->updating = FALSE;
2588 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2589 SCROLL_TO_CURSOR(compose);
2591 if (compose->deferred_destroy) {
2592 compose_destroy(compose);
2596 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2601 const GList *compose_get_compose_list(void)
2603 return compose_list;
2606 void compose_entry_append(Compose *compose, const gchar *address,
2607 ComposeEntryType type, ComposePrefType pref_type)
2609 const gchar *header;
2611 gboolean in_quote = FALSE;
2612 if (!address || *address == '\0') return;
2619 header = N_("Bcc:");
2621 case COMPOSE_REPLYTO:
2622 header = N_("Reply-To:");
2624 case COMPOSE_NEWSGROUPS:
2625 header = N_("Newsgroups:");
2627 case COMPOSE_FOLLOWUPTO:
2628 header = N_( "Followup-To:");
2630 case COMPOSE_INREPLYTO:
2631 header = N_( "In-Reply-To:");
2638 header = prefs_common_translated_header_name(header);
2640 cur = begin = (gchar *)address;
2642 /* we separate the line by commas, but not if we're inside a quoted
2644 while (*cur != '\0') {
2646 in_quote = !in_quote;
2647 if (*cur == ',' && !in_quote) {
2648 gchar *tmp = g_strdup(begin);
2650 tmp[cur-begin]='\0';
2653 while (*tmp == ' ' || *tmp == '\t')
2655 compose_add_header_entry(compose, header, tmp, pref_type);
2656 compose_entry_indicate(compose, tmp);
2663 gchar *tmp = g_strdup(begin);
2665 tmp[cur-begin]='\0';
2666 while (*tmp == ' ' || *tmp == '\t')
2668 compose_add_header_entry(compose, header, tmp, pref_type);
2669 compose_entry_indicate(compose, tmp);
2674 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2679 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2680 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2681 if (gtk_entry_get_text(entry) &&
2682 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2683 gtk_widget_modify_base(
2684 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2685 GTK_STATE_NORMAL, &default_header_bgcolor);
2686 gtk_widget_modify_text(
2687 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2688 GTK_STATE_NORMAL, &default_header_color);
2693 void compose_toolbar_cb(gint action, gpointer data)
2695 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2696 Compose *compose = (Compose*)toolbar_item->parent;
2698 cm_return_if_fail(compose != NULL);
2702 compose_send_cb(NULL, compose);
2705 compose_send_later_cb(NULL, compose);
2708 compose_draft(compose, COMPOSE_QUIT_EDITING);
2711 compose_insert_file_cb(NULL, compose);
2714 compose_attach_cb(NULL, compose);
2717 compose_insert_sig(compose, FALSE);
2720 compose_insert_sig(compose, TRUE);
2723 compose_ext_editor_cb(NULL, compose);
2725 case A_LINEWRAP_CURRENT:
2726 compose_beautify_paragraph(compose, NULL, TRUE);
2728 case A_LINEWRAP_ALL:
2729 compose_wrap_all_full(compose, TRUE);
2732 compose_address_cb(NULL, compose);
2735 case A_CHECK_SPELLING:
2736 compose_check_all(NULL, compose);
2744 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2749 gchar *subject = NULL;
2753 gchar **attach = NULL;
2754 gchar *inreplyto = NULL;
2755 MailField mfield = NO_FIELD_PRESENT;
2757 /* get mailto parts but skip from */
2758 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2761 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2762 mfield = TO_FIELD_PRESENT;
2765 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2767 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2769 if (!g_utf8_validate (subject, -1, NULL)) {
2770 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2771 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2774 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2776 mfield = SUBJECT_FIELD_PRESENT;
2779 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2780 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2783 gboolean prev_autowrap = compose->autowrap;
2785 compose->autowrap = FALSE;
2787 mark = gtk_text_buffer_get_insert(buffer);
2788 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2790 if (!g_utf8_validate (body, -1, NULL)) {
2791 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2792 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2795 gtk_text_buffer_insert(buffer, &iter, body, -1);
2797 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2799 compose->autowrap = prev_autowrap;
2800 if (compose->autowrap)
2801 compose_wrap_all(compose);
2802 mfield = BODY_FIELD_PRESENT;
2806 gint i = 0, att = 0;
2807 gchar *warn_files = NULL;
2808 while (attach[i] != NULL) {
2809 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2810 if (utf8_filename) {
2811 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2812 gchar *tmp = g_strdup_printf("%s%s\n",
2813 warn_files?warn_files:"",
2819 g_free(utf8_filename);
2821 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2826 alertpanel_notice(ngettext(
2827 "The following file has been attached: \n%s",
2828 "The following files have been attached: \n%s", att), warn_files);
2833 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2846 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2848 static HeaderEntry hentry[] = {
2849 {"Reply-To:", NULL, TRUE },
2850 {"Cc:", NULL, TRUE },
2851 {"References:", NULL, FALSE },
2852 {"Bcc:", NULL, TRUE },
2853 {"Newsgroups:", NULL, TRUE },
2854 {"Followup-To:", NULL, TRUE },
2855 {"List-Post:", NULL, FALSE },
2856 {"X-Priority:", NULL, FALSE },
2857 {NULL, NULL, FALSE }
2874 cm_return_val_if_fail(msginfo != NULL, -1);
2876 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2877 procheader_get_header_fields(fp, hentry);
2880 if (hentry[H_REPLY_TO].body != NULL) {
2881 if (hentry[H_REPLY_TO].body[0] != '\0') {
2883 conv_unmime_header(hentry[H_REPLY_TO].body,
2886 g_free(hentry[H_REPLY_TO].body);
2887 hentry[H_REPLY_TO].body = NULL;
2889 if (hentry[H_CC].body != NULL) {
2890 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2891 g_free(hentry[H_CC].body);
2892 hentry[H_CC].body = NULL;
2894 if (hentry[H_REFERENCES].body != NULL) {
2895 if (compose->mode == COMPOSE_REEDIT)
2896 compose->references = hentry[H_REFERENCES].body;
2898 compose->references = compose_parse_references
2899 (hentry[H_REFERENCES].body, msginfo->msgid);
2900 g_free(hentry[H_REFERENCES].body);
2902 hentry[H_REFERENCES].body = NULL;
2904 if (hentry[H_BCC].body != NULL) {
2905 if (compose->mode == COMPOSE_REEDIT)
2907 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2908 g_free(hentry[H_BCC].body);
2909 hentry[H_BCC].body = NULL;
2911 if (hentry[H_NEWSGROUPS].body != NULL) {
2912 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2913 hentry[H_NEWSGROUPS].body = NULL;
2915 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2916 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2917 compose->followup_to =
2918 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2921 g_free(hentry[H_FOLLOWUP_TO].body);
2922 hentry[H_FOLLOWUP_TO].body = NULL;
2924 if (hentry[H_LIST_POST].body != NULL) {
2925 gchar *to = NULL, *start = NULL;
2927 extract_address(hentry[H_LIST_POST].body);
2928 if (hentry[H_LIST_POST].body[0] != '\0') {
2929 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2931 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2932 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2935 g_free(compose->ml_post);
2936 compose->ml_post = to;
2939 g_free(hentry[H_LIST_POST].body);
2940 hentry[H_LIST_POST].body = NULL;
2943 /* CLAWS - X-Priority */
2944 if (compose->mode == COMPOSE_REEDIT)
2945 if (hentry[H_X_PRIORITY].body != NULL) {
2948 priority = atoi(hentry[H_X_PRIORITY].body);
2949 g_free(hentry[H_X_PRIORITY].body);
2951 hentry[H_X_PRIORITY].body = NULL;
2953 if (priority < PRIORITY_HIGHEST ||
2954 priority > PRIORITY_LOWEST)
2955 priority = PRIORITY_NORMAL;
2957 compose->priority = priority;
2960 if (compose->mode == COMPOSE_REEDIT) {
2961 if (msginfo->inreplyto && *msginfo->inreplyto)
2962 compose->inreplyto = g_strdup(msginfo->inreplyto);
2964 if (msginfo->msgid && *msginfo->msgid)
2965 compose->msgid = g_strdup(msginfo->msgid);
2967 if (msginfo->msgid && *msginfo->msgid)
2968 compose->inreplyto = g_strdup(msginfo->msgid);
2970 if (!compose->references) {
2971 if (msginfo->msgid && *msginfo->msgid) {
2972 if (msginfo->inreplyto && *msginfo->inreplyto)
2973 compose->references =
2974 g_strdup_printf("<%s>\n\t<%s>",
2978 compose->references =
2979 g_strconcat("<", msginfo->msgid, ">",
2981 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2982 compose->references =
2983 g_strconcat("<", msginfo->inreplyto, ">",
2992 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2997 cm_return_val_if_fail(msginfo != NULL, -1);
2999 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3000 procheader_get_header_fields(fp, entries);
3004 while (he != NULL && he->name != NULL) {
3006 GtkListStore *model = NULL;
3008 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3009 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3010 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3011 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3012 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3019 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3021 GSList *ref_id_list, *cur;
3025 ref_id_list = references_list_append(NULL, ref);
3026 if (!ref_id_list) return NULL;
3027 if (msgid && *msgid)
3028 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3033 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3034 /* "<" + Message-ID + ">" + CR+LF+TAB */
3035 len += strlen((gchar *)cur->data) + 5;
3037 if (len > MAX_REFERENCES_LEN) {
3038 /* remove second message-ID */
3039 if (ref_id_list && ref_id_list->next &&
3040 ref_id_list->next->next) {
3041 g_free(ref_id_list->next->data);
3042 ref_id_list = g_slist_remove
3043 (ref_id_list, ref_id_list->next->data);
3045 slist_free_strings_full(ref_id_list);
3052 new_ref = g_string_new("");
3053 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3054 if (new_ref->len > 0)
3055 g_string_append(new_ref, "\n\t");
3056 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3059 slist_free_strings_full(ref_id_list);
3061 new_ref_str = new_ref->str;
3062 g_string_free(new_ref, FALSE);
3067 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3068 const gchar *fmt, const gchar *qmark,
3069 const gchar *body, gboolean rewrap,
3070 gboolean need_unescape,
3071 const gchar *err_msg)
3073 MsgInfo* dummyinfo = NULL;
3074 gchar *quote_str = NULL;
3076 gboolean prev_autowrap;
3077 const gchar *trimmed_body = body;
3078 gint cursor_pos = -1;
3079 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3080 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3085 SIGNAL_BLOCK(buffer);
3088 dummyinfo = compose_msginfo_new_from_compose(compose);
3089 msginfo = dummyinfo;
3092 if (qmark != NULL) {
3094 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3095 compose->gtkaspell);
3097 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3099 quote_fmt_scan_string(qmark);
3102 buf = quote_fmt_get_buffer();
3104 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3106 Xstrdup_a(quote_str, buf, goto error)
3109 if (fmt && *fmt != '\0') {
3112 while (*trimmed_body == '\n')
3116 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3117 compose->gtkaspell);
3119 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3121 if (need_unescape) {
3124 /* decode \-escape sequences in the internal representation of the quote format */
3125 tmp = g_malloc(strlen(fmt)+1);
3126 pref_get_unescaped_pref(tmp, fmt);
3127 quote_fmt_scan_string(tmp);
3131 quote_fmt_scan_string(fmt);
3135 buf = quote_fmt_get_buffer();
3137 gint line = quote_fmt_get_line();
3138 alertpanel_error(err_msg, line);
3144 prev_autowrap = compose->autowrap;
3145 compose->autowrap = FALSE;
3147 mark = gtk_text_buffer_get_insert(buffer);
3148 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3149 if (g_utf8_validate(buf, -1, NULL)) {
3150 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3152 gchar *tmpout = NULL;
3153 tmpout = conv_codeset_strdup
3154 (buf, conv_get_locale_charset_str_no_utf8(),
3156 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3158 tmpout = g_malloc(strlen(buf)*2+1);
3159 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3161 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3165 cursor_pos = quote_fmt_get_cursor_pos();
3166 if (cursor_pos == -1)
3167 cursor_pos = gtk_text_iter_get_offset(&iter);
3168 compose->set_cursor_pos = cursor_pos;
3170 gtk_text_buffer_get_start_iter(buffer, &iter);
3171 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3172 gtk_text_buffer_place_cursor(buffer, &iter);
3174 compose->autowrap = prev_autowrap;
3175 if (compose->autowrap && rewrap)
3176 compose_wrap_all(compose);
3183 SIGNAL_UNBLOCK(buffer);
3185 procmsg_msginfo_free( &dummyinfo );
3190 /* if ml_post is of type addr@host and from is of type
3191 * addr-anything@host, return TRUE
3193 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3195 gchar *left_ml = NULL;
3196 gchar *right_ml = NULL;
3197 gchar *left_from = NULL;
3198 gchar *right_from = NULL;
3199 gboolean result = FALSE;
3201 if (!ml_post || !from)
3204 left_ml = g_strdup(ml_post);
3205 if (strstr(left_ml, "@")) {
3206 right_ml = strstr(left_ml, "@")+1;
3207 *(strstr(left_ml, "@")) = '\0';
3210 left_from = g_strdup(from);
3211 if (strstr(left_from, "@")) {
3212 right_from = strstr(left_from, "@")+1;
3213 *(strstr(left_from, "@")) = '\0';
3216 if (right_ml && right_from
3217 && !strncmp(left_from, left_ml, strlen(left_ml))
3218 && !strcmp(right_from, right_ml)) {
3227 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3228 gboolean respect_default_to)
3232 if (!folder || !folder->prefs)
3235 if (respect_default_to && folder->prefs->enable_default_to) {
3236 compose_entry_append(compose, folder->prefs->default_to,
3237 COMPOSE_TO, PREF_FOLDER);
3238 compose_entry_indicate(compose, folder->prefs->default_to);
3240 if (folder->prefs->enable_default_cc) {
3241 compose_entry_append(compose, folder->prefs->default_cc,
3242 COMPOSE_CC, PREF_FOLDER);
3243 compose_entry_indicate(compose, folder->prefs->default_cc);
3245 if (folder->prefs->enable_default_bcc) {
3246 compose_entry_append(compose, folder->prefs->default_bcc,
3247 COMPOSE_BCC, PREF_FOLDER);
3248 compose_entry_indicate(compose, folder->prefs->default_bcc);
3250 if (folder->prefs->enable_default_replyto) {
3251 compose_entry_append(compose, folder->prefs->default_replyto,
3252 COMPOSE_REPLYTO, PREF_FOLDER);
3253 compose_entry_indicate(compose, folder->prefs->default_replyto);
3257 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3262 if (!compose || !msginfo)
3265 if (msginfo->subject && *msginfo->subject) {
3266 buf = p = g_strdup(msginfo->subject);
3267 p += subject_get_prefix_length(p);
3268 memmove(buf, p, strlen(p) + 1);
3270 buf2 = g_strdup_printf("Re: %s", buf);
3271 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3276 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3279 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3280 gboolean to_all, gboolean to_ml,
3282 gboolean followup_and_reply_to)
3284 GSList *cc_list = NULL;
3287 gchar *replyto = NULL;
3288 gchar *ac_email = NULL;
3290 gboolean reply_to_ml = FALSE;
3291 gboolean default_reply_to = FALSE;
3293 cm_return_if_fail(compose->account != NULL);
3294 cm_return_if_fail(msginfo != NULL);
3296 reply_to_ml = to_ml && compose->ml_post;
3298 default_reply_to = msginfo->folder &&
3299 msginfo->folder->prefs->enable_default_reply_to;
3301 if (compose->account->protocol != A_NNTP) {
3302 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3304 if (reply_to_ml && !default_reply_to) {
3306 gboolean is_subscr = is_subscription(compose->ml_post,
3309 /* normal answer to ml post with a reply-to */
3310 compose_entry_append(compose,
3312 COMPOSE_TO, PREF_ML);
3313 if (compose->replyto)
3314 compose_entry_append(compose,
3316 COMPOSE_CC, PREF_ML);
3318 /* answer to subscription confirmation */
3319 if (compose->replyto)
3320 compose_entry_append(compose,
3322 COMPOSE_TO, PREF_ML);
3323 else if (msginfo->from)
3324 compose_entry_append(compose,
3326 COMPOSE_TO, PREF_ML);
3329 else if (!(to_all || to_sender) && default_reply_to) {
3330 compose_entry_append(compose,
3331 msginfo->folder->prefs->default_reply_to,
3332 COMPOSE_TO, PREF_FOLDER);
3333 compose_entry_indicate(compose,
3334 msginfo->folder->prefs->default_reply_to);
3340 compose_entry_append(compose, msginfo->from,
3341 COMPOSE_TO, PREF_NONE);
3343 Xstrdup_a(tmp1, msginfo->from, return);
3344 extract_address(tmp1);
3345 compose_entry_append(compose,
3346 (!account_find_from_address(tmp1, FALSE))
3349 COMPOSE_TO, PREF_NONE);
3350 if (compose->replyto)
3351 compose_entry_append(compose,
3353 COMPOSE_CC, PREF_NONE);
3355 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3356 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3357 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3358 if (compose->replyto) {
3359 compose_entry_append(compose,
3361 COMPOSE_TO, PREF_NONE);
3363 compose_entry_append(compose,
3364 msginfo->from ? msginfo->from : "",
3365 COMPOSE_TO, PREF_NONE);
3368 /* replying to own mail, use original recp */
3369 compose_entry_append(compose,
3370 msginfo->to ? msginfo->to : "",
3371 COMPOSE_TO, PREF_NONE);
3372 compose_entry_append(compose,
3373 msginfo->cc ? msginfo->cc : "",
3374 COMPOSE_CC, PREF_NONE);
3379 if (to_sender || (compose->followup_to &&
3380 !strncmp(compose->followup_to, "poster", 6)))
3381 compose_entry_append
3383 (compose->replyto ? compose->replyto :
3384 msginfo->from ? msginfo->from : ""),
3385 COMPOSE_TO, PREF_NONE);
3387 else if (followup_and_reply_to || to_all) {
3388 compose_entry_append
3390 (compose->replyto ? compose->replyto :
3391 msginfo->from ? msginfo->from : ""),
3392 COMPOSE_TO, PREF_NONE);
3394 compose_entry_append
3396 compose->followup_to ? compose->followup_to :
3397 compose->newsgroups ? compose->newsgroups : "",
3398 COMPOSE_NEWSGROUPS, PREF_NONE);
3400 compose_entry_append
3402 msginfo->cc ? msginfo->cc : "",
3403 COMPOSE_CC, PREF_NONE);
3406 compose_entry_append
3408 compose->followup_to ? compose->followup_to :
3409 compose->newsgroups ? compose->newsgroups : "",
3410 COMPOSE_NEWSGROUPS, PREF_NONE);
3412 compose_reply_set_subject(compose, msginfo);
3414 if (to_ml && compose->ml_post) return;
3415 if (!to_all || compose->account->protocol == A_NNTP) return;
3417 if (compose->replyto) {
3418 Xstrdup_a(replyto, compose->replyto, return);
3419 extract_address(replyto);
3421 if (msginfo->from) {
3422 Xstrdup_a(from, msginfo->from, return);
3423 extract_address(from);
3426 if (replyto && from)
3427 cc_list = address_list_append_with_comments(cc_list, from);
3428 if (to_all && msginfo->folder &&
3429 msginfo->folder->prefs->enable_default_reply_to)
3430 cc_list = address_list_append_with_comments(cc_list,
3431 msginfo->folder->prefs->default_reply_to);
3432 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3433 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3435 ac_email = g_utf8_strdown(compose->account->address, -1);
3438 for (cur = cc_list; cur != NULL; cur = cur->next) {
3439 gchar *addr = g_utf8_strdown(cur->data, -1);
3440 extract_address(addr);
3442 if (strcmp(ac_email, addr))
3443 compose_entry_append(compose, (gchar *)cur->data,
3444 COMPOSE_CC, PREF_NONE);
3446 debug_print("Cc address same as compose account's, ignoring\n");
3451 slist_free_strings_full(cc_list);
3457 #define SET_ENTRY(entry, str) \
3460 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3463 #define SET_ADDRESS(type, str) \
3466 compose_entry_append(compose, str, type, PREF_NONE); \
3469 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3471 cm_return_if_fail(msginfo != NULL);
3473 SET_ENTRY(subject_entry, msginfo->subject);
3474 SET_ENTRY(from_name, msginfo->from);
3475 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3476 SET_ADDRESS(COMPOSE_CC, compose->cc);
3477 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3478 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3479 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3480 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3482 compose_update_priority_menu_item(compose);
3483 compose_update_privacy_system_menu_item(compose, FALSE);
3484 compose_show_first_last_header(compose, TRUE);
3490 static void compose_insert_sig(Compose *compose, gboolean replace)
3492 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3493 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3495 GtkTextIter iter, iter_end;
3496 gint cur_pos, ins_pos;
3497 gboolean prev_autowrap;
3498 gboolean found = FALSE;
3499 gboolean exists = FALSE;
3501 cm_return_if_fail(compose->account != NULL);
3505 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3506 G_CALLBACK(compose_changed_cb),
3509 mark = gtk_text_buffer_get_insert(buffer);
3510 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3511 cur_pos = gtk_text_iter_get_offset (&iter);
3514 gtk_text_buffer_get_end_iter(buffer, &iter);
3516 exists = (compose->sig_str != NULL);
3519 GtkTextIter first_iter, start_iter, end_iter;
3521 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3523 if (!exists || compose->sig_str[0] == '\0')
3526 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3527 compose->signature_tag);
3530 /* include previous \n\n */
3531 gtk_text_iter_backward_chars(&first_iter, 1);
3532 start_iter = first_iter;
3533 end_iter = first_iter;
3535 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3536 compose->signature_tag);
3537 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3538 compose->signature_tag);
3540 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3546 g_free(compose->sig_str);
3547 compose->sig_str = account_get_signature_str(compose->account);
3549 cur_pos = gtk_text_iter_get_offset(&iter);
3551 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3552 g_free(compose->sig_str);
3553 compose->sig_str = NULL;
3555 if (compose->sig_inserted == FALSE)
3556 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3557 compose->sig_inserted = TRUE;
3559 cur_pos = gtk_text_iter_get_offset(&iter);
3560 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3562 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3563 gtk_text_iter_forward_chars(&iter, 1);
3564 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3565 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3567 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3568 cur_pos = gtk_text_buffer_get_char_count (buffer);
3571 /* put the cursor where it should be
3572 * either where the quote_fmt says, either where it was */
3573 if (compose->set_cursor_pos < 0)
3574 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3576 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3577 compose->set_cursor_pos);
3579 compose->set_cursor_pos = -1;
3580 gtk_text_buffer_place_cursor(buffer, &iter);
3581 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3582 G_CALLBACK(compose_changed_cb),
3588 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3591 GtkTextBuffer *buffer;
3594 const gchar *cur_encoding;
3595 gchar buf[BUFFSIZE];
3598 gboolean prev_autowrap;
3601 GString *file_contents = NULL;
3602 ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3604 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3606 /* get the size of the file we are about to insert */
3607 ret = g_stat(file, &file_stat);
3609 gchar *shortfile = g_path_get_basename(file);
3610 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3612 return COMPOSE_INSERT_NO_FILE;
3613 } else if (prefs_common.warn_large_insert == TRUE) {
3615 /* ask user for confirmation if the file is large */
3616 if (prefs_common.warn_large_insert_size < 0 ||
3617 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3621 msg = g_strdup_printf(_("You are about to insert a file of %s "
3622 "in the message body. Are you sure you want to do that?"),
3623 to_human_readable(file_stat.st_size));
3624 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3625 g_strconcat("+", _("_Insert"), NULL), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3628 /* do we ask for confirmation next time? */
3629 if (aval & G_ALERTDISABLE) {
3630 /* no confirmation next time, disable feature in preferences */
3631 aval &= ~G_ALERTDISABLE;
3632 prefs_common.warn_large_insert = FALSE;
3635 /* abort file insertion if user canceled action */
3636 if (aval != G_ALERTALTERNATE) {
3637 return COMPOSE_INSERT_NO_FILE;
3643 if ((fp = g_fopen(file, "rb")) == NULL) {
3644 FILE_OP_ERROR(file, "fopen");
3645 return COMPOSE_INSERT_READ_ERROR;
3648 prev_autowrap = compose->autowrap;
3649 compose->autowrap = FALSE;
3651 text = GTK_TEXT_VIEW(compose->text);
3652 buffer = gtk_text_view_get_buffer(text);
3653 mark = gtk_text_buffer_get_insert(buffer);
3654 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3656 g_signal_handlers_block_by_func(G_OBJECT(buffer),