2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2020 the Claws Mail team and Hiroyuki Yamamoto
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"
110 #include "file-utils.h"
113 #include "password.h"
114 #include "ldapserver.h"
128 #define N_ATTACH_COLS (N_COL_COLUMNS)
132 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
141 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
142 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
143 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
144 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
145 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
147 } ComposeCallAdvancedAction;
151 PRIORITY_HIGHEST = 1,
160 COMPOSE_INSERT_SUCCESS,
161 COMPOSE_INSERT_READ_ERROR,
162 COMPOSE_INSERT_INVALID_CHARACTER,
163 COMPOSE_INSERT_NO_FILE
164 } ComposeInsertResult;
168 COMPOSE_WRITE_FOR_SEND,
169 COMPOSE_WRITE_FOR_STORE
174 COMPOSE_QUOTE_FORCED,
181 SUBJECT_FIELD_PRESENT,
186 #define B64_LINE_SIZE 57
187 #define B64_BUFFSIZE 77
189 #define MAX_REFERENCES_LEN 999
191 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
192 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
194 #define COMPOSE_PRIVACY_WARNING() { \
195 alertpanel_error(_("You have opted to sign and/or encrypt this " \
196 "message but have not selected a privacy system.\n\n" \
197 "Signing and encrypting have been disabled for this " \
201 static GdkColor default_header_bgcolor = {
208 static GdkColor default_header_color = {
215 static GList *compose_list = NULL;
216 static GSList *extra_headers = NULL;
218 static Compose *compose_generic_new (PrefsAccount *account,
222 GList *listAddress );
224 static Compose *compose_create (PrefsAccount *account,
229 static void compose_entry_indicate (Compose *compose,
230 const gchar *address);
231 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
232 ComposeQuoteMode quote_mode,
236 static Compose *compose_forward_multiple (PrefsAccount *account,
237 GSList *msginfo_list);
238 static Compose *compose_reply (MsgInfo *msginfo,
239 ComposeQuoteMode quote_mode,
244 static Compose *compose_reply_mode (ComposeMode mode,
245 GSList *msginfo_list,
247 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
248 static void compose_update_privacy_systems_menu(Compose *compose);
250 static GtkWidget *compose_account_option_menu_create
252 static void compose_set_out_encoding (Compose *compose);
253 static void compose_set_template_menu (Compose *compose);
254 static void compose_destroy (Compose *compose);
256 static MailField compose_entries_set (Compose *compose,
258 ComposeEntryType to_type);
259 static gint compose_parse_header (Compose *compose,
261 static gint compose_parse_manual_headers (Compose *compose,
263 HeaderEntry *entries);
264 static gchar *compose_parse_references (const gchar *ref,
267 static gchar *compose_quote_fmt (Compose *compose,
273 gboolean need_unescape,
274 const gchar *err_msg);
276 static void compose_reply_set_entry (Compose *compose,
282 followup_and_reply_to);
283 static void compose_reedit_set_entry (Compose *compose,
286 static void compose_insert_sig (Compose *compose,
288 static ComposeInsertResult compose_insert_file (Compose *compose,
291 static gboolean compose_attach_append (Compose *compose,
294 const gchar *content_type,
295 const gchar *charset);
296 static void compose_attach_parts (Compose *compose,
299 static gboolean compose_beautify_paragraph (Compose *compose,
300 GtkTextIter *par_iter,
302 static void compose_wrap_all (Compose *compose);
303 static void compose_wrap_all_full (Compose *compose,
306 static void compose_set_title (Compose *compose);
307 static void compose_select_account (Compose *compose,
308 PrefsAccount *account,
311 static PrefsAccount *compose_current_mail_account(void);
312 /* static gint compose_send (Compose *compose); */
313 static gboolean compose_check_for_valid_recipient
315 static gboolean compose_check_entries (Compose *compose,
316 gboolean check_everything);
317 static gint compose_write_to_file (Compose *compose,
320 gboolean attach_parts);
321 static gint compose_write_body_to_file (Compose *compose,
323 static gint compose_remove_reedit_target (Compose *compose,
325 static void compose_remove_draft (Compose *compose);
326 static ComposeQueueResult compose_queue_sub (Compose *compose,
330 gboolean perform_checks,
331 gboolean remove_reedit_target);
332 static int compose_add_attachments (Compose *compose,
334 static gchar *compose_get_header (Compose *compose);
335 static gchar *compose_get_manual_headers_info (Compose *compose);
337 static void compose_convert_header (Compose *compose,
342 gboolean addr_field);
344 static void compose_attach_info_free (AttachInfo *ainfo);
345 static void compose_attach_remove_selected (GtkAction *action,
348 static void compose_template_apply (Compose *compose,
351 static void compose_attach_property (GtkAction *action,
353 static void compose_attach_property_create (gboolean *cancelled);
354 static void attach_property_ok (GtkWidget *widget,
355 gboolean *cancelled);
356 static void attach_property_cancel (GtkWidget *widget,
357 gboolean *cancelled);
358 static gint attach_property_delete_event (GtkWidget *widget,
360 gboolean *cancelled);
361 static gboolean attach_property_key_pressed (GtkWidget *widget,
363 gboolean *cancelled);
365 static void compose_exec_ext_editor (Compose *compose);
367 static gint compose_exec_ext_editor_real (const gchar *file,
368 GdkNativeWindow socket_wid);
369 static gboolean compose_ext_editor_kill (Compose *compose);
370 static gboolean compose_input_cb (GIOChannel *source,
371 GIOCondition condition,
373 static void compose_set_ext_editor_sensitive (Compose *compose,
375 static gboolean compose_get_ext_editor_cmd_valid();
376 static gboolean compose_get_ext_editor_uses_socket();
377 static gboolean compose_ext_editor_plug_removed_cb
380 #endif /* G_OS_UNIX */
382 static void compose_undo_state_changed (UndoMain *undostruct,
387 static void compose_create_header_entry (Compose *compose);
388 static void compose_add_header_entry (Compose *compose, const gchar *header,
389 gchar *text, ComposePrefType pref_type);
390 static void compose_remove_header_entries(Compose *compose);
392 static void compose_update_priority_menu_item(Compose * compose);
394 static void compose_spell_menu_changed (void *data);
395 static void compose_dict_changed (void *data);
397 static void compose_add_field_list ( Compose *compose,
398 GList *listAddress );
400 /* callback functions */
402 static void compose_notebook_size_alloc (GtkNotebook *notebook,
403 GtkAllocation *allocation,
405 static gboolean compose_edit_size_alloc (GtkEditable *widget,
406 GtkAllocation *allocation,
407 GtkSHRuler *shruler);
408 static void account_activated (GtkComboBox *optmenu,
410 static void attach_selected (GtkTreeView *tree_view,
411 GtkTreePath *tree_path,
412 GtkTreeViewColumn *column,
414 static gboolean attach_button_pressed (GtkWidget *widget,
415 GdkEventButton *event,
417 static gboolean attach_key_pressed (GtkWidget *widget,
420 static void compose_send_cb (GtkAction *action, gpointer data);
421 static void compose_send_later_cb (GtkAction *action, gpointer data);
423 static void compose_save_cb (GtkAction *action,
426 static void compose_attach_cb (GtkAction *action,
428 static void compose_insert_file_cb (GtkAction *action,
430 static void compose_insert_sig_cb (GtkAction *action,
432 static void compose_replace_sig_cb (GtkAction *action,
435 static void compose_close_cb (GtkAction *action,
437 static void compose_print_cb (GtkAction *action,
440 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
442 static void compose_address_cb (GtkAction *action,
444 static void about_show_cb (GtkAction *action,
446 static void compose_template_activate_cb(GtkWidget *widget,
449 static void compose_ext_editor_cb (GtkAction *action,
452 static gint compose_delete_cb (GtkWidget *widget,
456 static void compose_undo_cb (GtkAction *action,
458 static void compose_redo_cb (GtkAction *action,
460 static void compose_cut_cb (GtkAction *action,
462 static void compose_copy_cb (GtkAction *action,
464 static void compose_paste_cb (GtkAction *action,
466 static void compose_paste_as_quote_cb (GtkAction *action,
468 static void compose_paste_no_wrap_cb (GtkAction *action,
470 static void compose_paste_wrap_cb (GtkAction *action,
472 static void compose_allsel_cb (GtkAction *action,
475 static void compose_advanced_action_cb (GtkAction *action,
478 static void compose_grab_focus_cb (GtkWidget *widget,
481 static void compose_changed_cb (GtkTextBuffer *textbuf,
484 static void compose_wrap_cb (GtkAction *action,
486 static void compose_wrap_all_cb (GtkAction *action,
488 static void compose_find_cb (GtkAction *action,
490 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
492 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
495 static void compose_toggle_ruler_cb (GtkToggleAction *action,
497 static void compose_toggle_sign_cb (GtkToggleAction *action,
499 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
501 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
502 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
503 static void compose_activate_privacy_system (Compose *compose,
504 PrefsAccount *account,
506 static void compose_apply_folder_privacy_settings(Compose *compose, FolderItem *folder_item);
507 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
509 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
511 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
512 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
513 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
515 static void compose_attach_drag_received_cb (GtkWidget *widget,
516 GdkDragContext *drag_context,
519 GtkSelectionData *data,
523 static void compose_insert_drag_received_cb (GtkWidget *widget,
524 GdkDragContext *drag_context,
527 GtkSelectionData *data,
531 static void compose_header_drag_received_cb (GtkWidget *widget,
532 GdkDragContext *drag_context,
535 GtkSelectionData *data,
540 static gboolean compose_drag_drop (GtkWidget *widget,
541 GdkDragContext *drag_context,
543 guint time, gpointer user_data);
544 static gboolean completion_set_focus_to_subject
549 static void text_inserted (GtkTextBuffer *buffer,
554 static Compose *compose_generic_reply(MsgInfo *msginfo,
555 ComposeQuoteMode quote_mode,
559 gboolean followup_and_reply_to,
562 static void compose_headerentry_changed_cb (GtkWidget *entry,
563 ComposeHeaderEntry *headerentry);
564 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
566 ComposeHeaderEntry *headerentry);
567 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
568 ComposeHeaderEntry *headerentry);
570 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
572 static void compose_allow_user_actions (Compose *compose, gboolean allow);
574 static void compose_nothing_cb (GtkAction *action, gpointer data)
580 static void compose_check_all (GtkAction *action, gpointer data);
581 static void compose_highlight_all (GtkAction *action, gpointer data);
582 static void compose_check_backwards (GtkAction *action, gpointer data);
583 static void compose_check_forwards_go (GtkAction *action, gpointer data);
586 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
588 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
591 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
592 FolderItem *folder_item);
594 static void compose_attach_update_label(Compose *compose);
595 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
596 gboolean respect_default_to);
597 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
598 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
600 static GtkActionEntry compose_popup_entries[] =
602 {"Compose", NULL, "Compose", NULL, NULL, NULL },
603 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
604 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
605 {"Compose/---", NULL, "---", NULL, NULL, NULL },
606 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
609 static GtkActionEntry compose_entries[] =
611 {"Menu", NULL, "Menu", NULL, NULL, NULL },
613 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
614 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
616 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
618 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
619 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
620 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
622 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
623 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
624 {"Message/---", NULL, "---", NULL, NULL, NULL },
626 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
627 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
628 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
629 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
630 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
631 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
632 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
633 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
634 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
635 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
638 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
639 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
640 {"Edit/---", NULL, "---", NULL, NULL, NULL },
642 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
643 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
644 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
646 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
647 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
648 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
649 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
651 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
653 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
654 {"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*/
655 {"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*/
656 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
657 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
658 {"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*/
659 {"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*/
660 {"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*/
661 {"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*/
662 {"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*/
663 {"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*/
664 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
665 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
666 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
667 {"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*/
669 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
670 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
672 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
673 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
674 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
675 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
676 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
679 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
680 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
681 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
682 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
684 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
685 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
689 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
690 {"Options/---", NULL, "---", NULL, NULL, NULL },
691 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
692 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
694 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
695 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
697 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
698 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
699 #define ENC_ACTION(cs_char,c_char,string) \
700 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
702 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
703 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
704 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
705 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
706 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
707 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
708 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
709 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
710 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
713 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
715 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
716 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
717 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
718 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
721 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
724 static GtkToggleActionEntry compose_toggle_entries[] =
726 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
727 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
728 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
729 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
730 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
731 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
732 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
735 static GtkRadioActionEntry compose_radio_rm_entries[] =
737 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
738 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
739 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
740 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
743 static GtkRadioActionEntry compose_radio_prio_entries[] =
745 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
746 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
747 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
748 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
749 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
752 static GtkRadioActionEntry compose_radio_enc_entries[] =
754 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
777 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
778 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
779 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
780 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
781 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
782 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
783 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
784 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
785 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
786 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
789 static GtkTargetEntry compose_mime_types[] =
791 {"text/uri-list", 0, 0},
792 {"UTF8_STRING", 0, 0},
796 static gboolean compose_put_existing_to_front(MsgInfo *info)
798 const GList *compose_list = compose_get_compose_list();
799 const GList *elem = NULL;
802 for (elem = compose_list; elem != NULL && elem->data != NULL;
804 Compose *c = (Compose*)elem->data;
806 if (!c->targetinfo || !c->targetinfo->msgid ||
810 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
811 gtkut_window_popup(c->window);
819 static GdkColor quote_color1 =
820 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
821 static GdkColor quote_color2 =
822 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
823 static GdkColor quote_color3 =
824 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
826 static GdkColor quote_bgcolor1 =
827 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
828 static GdkColor quote_bgcolor2 =
829 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
830 static GdkColor quote_bgcolor3 =
831 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
833 static GdkColor signature_color = {
840 static GdkColor uri_color = {
847 static void compose_create_tags(GtkTextView *text, Compose *compose)
849 GtkTextBuffer *buffer;
850 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
852 buffer = gtk_text_view_get_buffer(text);
854 if (prefs_common.enable_color) {
855 /* grab the quote colors, converting from an int to a GdkColor */
856 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL1],
858 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL2],
860 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL3],
862 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL1_BG],
864 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL2_BG],
866 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL3_BG],
868 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_SIGNATURE],
870 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_URI],
873 signature_color = quote_color1 = quote_color2 = quote_color3 =
874 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
877 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
878 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
879 "foreground-gdk", "e_color1,
880 "paragraph-background-gdk", "e_bgcolor1,
882 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
883 "foreground-gdk", "e_color2,
884 "paragraph-background-gdk", "e_bgcolor2,
886 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
887 "foreground-gdk", "e_color3,
888 "paragraph-background-gdk", "e_bgcolor3,
891 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
892 "foreground-gdk", "e_color1,
894 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
895 "foreground-gdk", "e_color2,
897 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
898 "foreground-gdk", "e_color3,
902 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
903 "foreground-gdk", &signature_color,
906 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
907 "foreground-gdk", &uri_color,
909 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
910 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
913 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
916 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
919 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
921 return compose_generic_new(account, mailto, item, NULL, NULL);
924 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
926 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
929 #define SCROLL_TO_CURSOR(compose) { \
930 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
931 gtk_text_view_get_buffer( \
932 GTK_TEXT_VIEW(compose->text))); \
933 gtk_text_view_scroll_mark_onscreen( \
934 GTK_TEXT_VIEW(compose->text), \
938 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
941 if (folderidentifier) {
942 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
943 prefs_common.compose_save_to_history = add_history(
944 prefs_common.compose_save_to_history, folderidentifier);
945 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
946 prefs_common.compose_save_to_history);
949 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
950 if (folderidentifier)
951 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
953 gtk_entry_set_text(GTK_ENTRY(entry), "");
956 static gchar *compose_get_save_to(Compose *compose)
959 gchar *result = NULL;
960 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
961 result = gtk_editable_get_chars(entry, 0, -1);
964 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
965 prefs_common.compose_save_to_history = add_history(
966 prefs_common.compose_save_to_history, result);
967 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
968 prefs_common.compose_save_to_history);
973 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
974 GList *attach_files, GList *listAddress )
977 GtkTextView *textview;
978 GtkTextBuffer *textbuf;
980 const gchar *subject_format = NULL;
981 const gchar *body_format = NULL;
982 gchar *mailto_from = NULL;
983 PrefsAccount *mailto_account = NULL;
984 MsgInfo* dummyinfo = NULL;
985 gint cursor_pos = -1;
986 MailField mfield = NO_FIELD_PRESENT;
990 /* check if mailto defines a from */
991 if (mailto && *mailto != '\0') {
992 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
993 /* mailto defines a from, check if we can get account prefs from it,
994 if not, the account prefs will be guessed using other ways, but we'll keep
997 mailto_account = account_find_from_address(mailto_from, TRUE);
998 if (mailto_account == NULL) {
1000 Xstrdup_a(tmp_from, mailto_from, return NULL);
1001 extract_address(tmp_from);
1002 mailto_account = account_find_from_address(tmp_from, TRUE);
1006 account = mailto_account;
1009 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1010 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1011 account = account_find_from_id(item->prefs->default_account);
1013 /* if no account prefs set, fallback to the current one */
1014 if (!account) account = cur_account;
1015 cm_return_val_if_fail(account != NULL, NULL);
1017 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1018 compose_apply_folder_privacy_settings(compose, item);
1020 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1021 (account->default_encrypt || account->default_sign))
1022 COMPOSE_PRIVACY_WARNING();
1024 /* override from name if mailto asked for it */
1026 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1027 g_free(mailto_from);
1029 /* override from name according to folder properties */
1030 if (item && item->prefs &&
1031 item->prefs->compose_with_format &&
1032 item->prefs->compose_override_from_format &&
1033 *item->prefs->compose_override_from_format != '\0') {
1038 dummyinfo = compose_msginfo_new_from_compose(compose);
1040 /* decode \-escape sequences in the internal representation of the quote format */
1041 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1042 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1045 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1046 compose->gtkaspell);
1048 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1050 quote_fmt_scan_string(tmp);
1053 buf = quote_fmt_get_buffer();
1055 alertpanel_error(_("New message From format error."));
1057 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1058 quote_fmt_reset_vartable();
1059 quote_fmtlex_destroy();
1064 compose->replyinfo = NULL;
1065 compose->fwdinfo = NULL;
1067 textview = GTK_TEXT_VIEW(compose->text);
1068 textbuf = gtk_text_view_get_buffer(textview);
1069 compose_create_tags(textview, compose);
1071 undo_block(compose->undostruct);
1073 compose_set_dictionaries_from_folder_prefs(compose, item);
1076 if (account->auto_sig)
1077 compose_insert_sig(compose, FALSE);
1078 gtk_text_buffer_get_start_iter(textbuf, &iter);
1079 gtk_text_buffer_place_cursor(textbuf, &iter);
1081 if (account->protocol != A_NNTP) {
1082 if (mailto && *mailto != '\0') {
1083 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1086 compose_set_folder_prefs(compose, item, TRUE);
1088 if (item && item->ret_rcpt) {
1089 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1092 if (mailto && *mailto != '\0') {
1093 if (!strchr(mailto, '@'))
1094 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1096 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1097 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1098 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1099 mfield = TO_FIELD_PRESENT;
1102 * CLAWS: just don't allow return receipt request, even if the user
1103 * may want to send an email. simple but foolproof.
1105 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1107 compose_add_field_list( compose, listAddress );
1109 if (item && item->prefs && item->prefs->compose_with_format) {
1110 subject_format = item->prefs->compose_subject_format;
1111 body_format = item->prefs->compose_body_format;
1112 } else if (account->compose_with_format) {
1113 subject_format = account->compose_subject_format;
1114 body_format = account->compose_body_format;
1115 } else if (prefs_common.compose_with_format) {
1116 subject_format = prefs_common.compose_subject_format;
1117 body_format = prefs_common.compose_body_format;
1120 if (subject_format || body_format) {
1123 && *subject_format != '\0' )
1125 gchar *subject = NULL;
1130 dummyinfo = compose_msginfo_new_from_compose(compose);
1132 /* decode \-escape sequences in the internal representation of the quote format */
1133 tmp = g_malloc(strlen(subject_format)+1);
1134 pref_get_unescaped_pref(tmp, subject_format);
1136 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1138 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1139 compose->gtkaspell);
1141 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1143 quote_fmt_scan_string(tmp);
1146 buf = quote_fmt_get_buffer();
1148 alertpanel_error(_("New message subject format error."));
1150 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1151 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1152 quote_fmt_reset_vartable();
1153 quote_fmtlex_destroy();
1157 mfield = SUBJECT_FIELD_PRESENT;
1161 && *body_format != '\0' )
1164 GtkTextBuffer *buffer;
1165 GtkTextIter start, end;
1169 dummyinfo = compose_msginfo_new_from_compose(compose);
1171 text = GTK_TEXT_VIEW(compose->text);
1172 buffer = gtk_text_view_get_buffer(text);
1173 gtk_text_buffer_get_start_iter(buffer, &start);
1174 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1175 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1177 compose_quote_fmt(compose, dummyinfo,
1179 NULL, tmp, FALSE, TRUE,
1180 _("The body of the \"New message\" template has an error at line %d."));
1181 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1182 quote_fmt_reset_vartable();
1186 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1187 gtkaspell_highlight_all(compose->gtkaspell);
1189 mfield = BODY_FIELD_PRESENT;
1193 procmsg_msginfo_free( &dummyinfo );
1199 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1200 ainfo = (AttachInfo *) curr->data;
1202 compose_insert_file(compose, ainfo->file);
1204 compose_attach_append(compose, ainfo->file, ainfo->file,
1205 ainfo->content_type, ainfo->charset);
1209 compose_show_first_last_header(compose, TRUE);
1211 /* Set save folder */
1212 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1213 gchar *folderidentifier;
1215 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1216 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1217 folderidentifier = folder_item_get_identifier(item);
1218 compose_set_save_to(compose, folderidentifier);
1219 g_free(folderidentifier);
1222 /* Place cursor according to provided input (mfield) */
1224 case NO_FIELD_PRESENT:
1225 if (compose->header_last)
1226 gtk_widget_grab_focus(compose->header_last->entry);
1228 case TO_FIELD_PRESENT:
1229 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1231 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1234 gtk_widget_grab_focus(compose->subject_entry);
1236 case SUBJECT_FIELD_PRESENT:
1237 textview = GTK_TEXT_VIEW(compose->text);
1240 textbuf = gtk_text_view_get_buffer(textview);
1243 mark = gtk_text_buffer_get_insert(textbuf);
1244 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1245 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1247 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1248 * only defers where it comes to the variable body
1249 * is not null. If no body is present compose->text
1250 * will be null in which case you cannot place the
1251 * cursor inside the component so. An empty component
1252 * is therefore created before placing the cursor
1254 case BODY_FIELD_PRESENT:
1255 cursor_pos = quote_fmt_get_cursor_pos();
1256 if (cursor_pos == -1)
1257 gtk_widget_grab_focus(compose->header_last->entry);
1259 gtk_widget_grab_focus(compose->text);
1263 undo_unblock(compose->undostruct);
1265 if (prefs_common.auto_exteditor)
1266 compose_exec_ext_editor(compose);
1268 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1270 SCROLL_TO_CURSOR(compose);
1272 compose->modified = FALSE;
1273 compose_set_title(compose);
1275 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1280 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1281 gboolean override_pref, const gchar *system)
1283 const gchar *privacy = NULL;
1285 cm_return_if_fail(compose != NULL);
1286 cm_return_if_fail(account != NULL);
1288 if (privacy_system_can_encrypt(compose->privacy_system) == FALSE ||
1289 (override_pref == FALSE && account->default_encrypt_reply == FALSE))
1292 if (account->default_privacy_system && strlen(account->default_privacy_system))
1293 privacy = account->default_privacy_system;
1297 GSList *privacy_avail = privacy_get_system_ids();
1298 if (privacy_avail && g_slist_length(privacy_avail)) {
1299 privacy = (gchar *)(privacy_avail->data);
1301 g_slist_free_full(privacy_avail, g_free);
1303 if (privacy != NULL) {
1305 g_free(compose->privacy_system);
1306 compose->privacy_system = NULL;
1307 g_free(compose->encdata);
1308 compose->encdata = NULL;
1310 if (compose->privacy_system == NULL)
1311 compose->privacy_system = g_strdup(privacy);
1312 else if (*(compose->privacy_system) == '\0') {
1313 g_free(compose->privacy_system);
1314 g_free(compose->encdata);
1315 compose->encdata = NULL;
1316 compose->privacy_system = g_strdup(privacy);
1318 compose_update_privacy_system_menu_item(compose, FALSE);
1319 compose_use_encryption(compose, TRUE);
1323 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1325 const gchar *privacy = NULL;
1326 if (privacy_system_can_sign(compose->privacy_system) == FALSE)
1329 if (account->default_privacy_system && strlen(account->default_privacy_system))
1330 privacy = account->default_privacy_system;
1334 GSList *privacy_avail = privacy_get_system_ids();
1335 if (privacy_avail && g_slist_length(privacy_avail)) {
1336 privacy = (gchar *)(privacy_avail->data);
1340 if (privacy != NULL) {
1342 g_free(compose->privacy_system);
1343 compose->privacy_system = NULL;
1344 g_free(compose->encdata);
1345 compose->encdata = NULL;
1347 if (compose->privacy_system == NULL)
1348 compose->privacy_system = g_strdup(privacy);
1349 compose_update_privacy_system_menu_item(compose, FALSE);
1350 compose_use_signing(compose, TRUE);
1354 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1358 Compose *compose = NULL;
1360 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1362 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1363 cm_return_val_if_fail(msginfo != NULL, NULL);
1365 list_len = g_slist_length(msginfo_list);
1369 case COMPOSE_REPLY_TO_ADDRESS:
1370 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1371 FALSE, prefs_common.default_reply_list, FALSE, body);
1373 case COMPOSE_REPLY_WITH_QUOTE:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1375 FALSE, prefs_common.default_reply_list, FALSE, body);
1377 case COMPOSE_REPLY_WITHOUT_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1379 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1381 case COMPOSE_REPLY_TO_SENDER:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1383 FALSE, FALSE, TRUE, body);
1385 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1386 compose = compose_followup_and_reply_to(msginfo,
1387 COMPOSE_QUOTE_CHECK,
1388 FALSE, FALSE, body);
1390 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1391 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1392 FALSE, FALSE, TRUE, body);
1394 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1395 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1396 FALSE, FALSE, TRUE, NULL);
1398 case COMPOSE_REPLY_TO_ALL:
1399 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1400 TRUE, FALSE, FALSE, body);
1402 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1403 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1404 TRUE, FALSE, FALSE, body);
1406 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1407 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1408 TRUE, FALSE, FALSE, NULL);
1410 case COMPOSE_REPLY_TO_LIST:
1411 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1412 FALSE, TRUE, FALSE, body);
1414 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1415 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1416 FALSE, TRUE, FALSE, body);
1418 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1419 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1420 FALSE, TRUE, FALSE, NULL);
1422 case COMPOSE_FORWARD:
1423 if (prefs_common.forward_as_attachment) {
1424 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1427 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1431 case COMPOSE_FORWARD_INLINE:
1432 /* check if we reply to more than one Message */
1433 if (list_len == 1) {
1434 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1437 /* more messages FALL THROUGH */
1438 case COMPOSE_FORWARD_AS_ATTACH:
1439 compose = compose_forward_multiple(NULL, msginfo_list);
1441 case COMPOSE_REDIRECT:
1442 compose = compose_redirect(NULL, msginfo, FALSE);
1445 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1448 if (compose == NULL) {
1449 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1453 compose->rmode = mode;
1454 switch (compose->rmode) {
1456 case COMPOSE_REPLY_WITH_QUOTE:
1457 case COMPOSE_REPLY_WITHOUT_QUOTE:
1458 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1459 debug_print("reply mode Normal\n");
1460 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1461 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1463 case COMPOSE_REPLY_TO_SENDER:
1464 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1465 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1466 debug_print("reply mode Sender\n");
1467 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1469 case COMPOSE_REPLY_TO_ALL:
1470 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1471 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1472 debug_print("reply mode All\n");
1473 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1475 case COMPOSE_REPLY_TO_LIST:
1476 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1477 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1478 debug_print("reply mode List\n");
1479 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1481 case COMPOSE_REPLY_TO_ADDRESS:
1482 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1490 static Compose *compose_reply(MsgInfo *msginfo,
1491 ComposeQuoteMode quote_mode,
1497 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1498 to_sender, FALSE, body);
1501 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1502 ComposeQuoteMode quote_mode,
1507 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1508 to_sender, TRUE, body);
1511 static void compose_extract_original_charset(Compose *compose)
1513 MsgInfo *info = NULL;
1514 if (compose->replyinfo) {
1515 info = compose->replyinfo;
1516 } else if (compose->fwdinfo) {
1517 info = compose->fwdinfo;
1518 } else if (compose->targetinfo) {
1519 info = compose->targetinfo;
1522 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1523 MimeInfo *partinfo = mimeinfo;
1524 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1525 partinfo = procmime_mimeinfo_next(partinfo);
1527 compose->orig_charset =
1528 g_strdup(procmime_mimeinfo_get_parameter(
1529 partinfo, "charset"));
1531 procmime_mimeinfo_free_all(&mimeinfo);
1535 #define SIGNAL_BLOCK(buffer) { \
1536 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1537 G_CALLBACK(compose_changed_cb), \
1539 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1540 G_CALLBACK(text_inserted), \
1544 #define SIGNAL_UNBLOCK(buffer) { \
1545 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1546 G_CALLBACK(compose_changed_cb), \
1548 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1549 G_CALLBACK(text_inserted), \
1553 static Compose *compose_generic_reply(MsgInfo *msginfo,
1554 ComposeQuoteMode quote_mode,
1555 gboolean to_all, gboolean to_ml,
1557 gboolean followup_and_reply_to,
1561 PrefsAccount *account = NULL;
1562 GtkTextView *textview;
1563 GtkTextBuffer *textbuf;
1564 gboolean quote = FALSE;
1565 const gchar *qmark = NULL;
1566 const gchar *body_fmt = NULL;
1567 gchar *s_system = NULL;
1569 cm_return_val_if_fail(msginfo != NULL, NULL);
1570 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1572 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1574 cm_return_val_if_fail(account != NULL, NULL);
1576 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1577 compose_apply_folder_privacy_settings(compose, msginfo->folder);
1579 compose->updating = TRUE;
1581 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1584 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1585 if (!compose->replyinfo)
1586 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1588 compose_extract_original_charset(compose);
1590 if (msginfo->folder && msginfo->folder->ret_rcpt)
1591 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1593 /* Set save folder */
1594 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1595 gchar *folderidentifier;
1597 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1598 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1599 folderidentifier = folder_item_get_identifier(msginfo->folder);
1600 compose_set_save_to(compose, folderidentifier);
1601 g_free(folderidentifier);
1604 if (compose_parse_header(compose, msginfo) < 0) {
1605 compose->updating = FALSE;
1606 compose_destroy(compose);
1610 /* override from name according to folder properties */
1611 if (msginfo->folder && msginfo->folder->prefs &&
1612 msginfo->folder->prefs->reply_with_format &&
1613 msginfo->folder->prefs->reply_override_from_format &&
1614 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1619 /* decode \-escape sequences in the internal representation of the quote format */
1620 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1621 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1624 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1625 compose->gtkaspell);
1627 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1629 quote_fmt_scan_string(tmp);
1632 buf = quote_fmt_get_buffer();
1634 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1636 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1637 quote_fmt_reset_vartable();
1638 quote_fmtlex_destroy();
1643 textview = (GTK_TEXT_VIEW(compose->text));
1644 textbuf = gtk_text_view_get_buffer(textview);
1645 compose_create_tags(textview, compose);
1647 undo_block(compose->undostruct);
1649 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1650 gtkaspell_block_check(compose->gtkaspell);
1653 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1654 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1655 /* use the reply format of folder (if enabled), or the account's one
1656 (if enabled) or fallback to the global reply format, which is always
1657 enabled (even if empty), and use the relevant quotemark */
1659 if (msginfo->folder && msginfo->folder->prefs &&
1660 msginfo->folder->prefs->reply_with_format) {
1661 qmark = msginfo->folder->prefs->reply_quotemark;
1662 body_fmt = msginfo->folder->prefs->reply_body_format;
1664 } else if (account->reply_with_format) {
1665 qmark = account->reply_quotemark;
1666 body_fmt = account->reply_body_format;
1669 qmark = prefs_common.quotemark;
1670 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1671 body_fmt = gettext(prefs_common.quotefmt);
1678 /* empty quotemark is not allowed */
1679 if (qmark == NULL || *qmark == '\0')
1681 compose_quote_fmt(compose, compose->replyinfo,
1682 body_fmt, qmark, body, FALSE, TRUE,
1683 _("The body of the \"Reply\" template has an error at line %d."));
1684 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1685 quote_fmt_reset_vartable();
1688 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1689 compose_force_encryption(compose, account, FALSE, s_system);
1692 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1693 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1694 compose_force_signing(compose, account, s_system);
1698 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1699 ((account->default_encrypt || account->default_sign) ||
1700 (account->default_encrypt_reply && MSG_IS_ENCRYPTED(compose->replyinfo->flags)) ||
1701 (account->default_sign_reply && MSG_IS_SIGNED(compose->replyinfo->flags))))
1702 COMPOSE_PRIVACY_WARNING();
1704 SIGNAL_BLOCK(textbuf);
1706 if (account->auto_sig)
1707 compose_insert_sig(compose, FALSE);
1709 compose_wrap_all(compose);
1712 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1713 gtkaspell_highlight_all(compose->gtkaspell);
1714 gtkaspell_unblock_check(compose->gtkaspell);
1716 SIGNAL_UNBLOCK(textbuf);
1718 gtk_widget_grab_focus(compose->text);
1720 undo_unblock(compose->undostruct);
1722 if (prefs_common.auto_exteditor)
1723 compose_exec_ext_editor(compose);
1725 compose->modified = FALSE;
1726 compose_set_title(compose);
1728 compose->updating = FALSE;
1729 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1730 SCROLL_TO_CURSOR(compose);
1732 if (compose->deferred_destroy) {
1733 compose_destroy(compose);
1741 #define INSERT_FW_HEADER(var, hdr) \
1742 if (msginfo->var && *msginfo->var) { \
1743 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1744 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1745 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1748 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1749 gboolean as_attach, const gchar *body,
1750 gboolean no_extedit,
1754 GtkTextView *textview;
1755 GtkTextBuffer *textbuf;
1756 gint cursor_pos = -1;
1759 cm_return_val_if_fail(msginfo != NULL, NULL);
1760 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1762 if (!account && !(account = compose_find_account(msginfo)))
1763 account = cur_account;
1765 if (!prefs_common.forward_as_attachment)
1766 mode = COMPOSE_FORWARD_INLINE;
1768 mode = COMPOSE_FORWARD;
1769 compose = compose_create(account, msginfo->folder, mode, batch);
1770 compose_apply_folder_privacy_settings(compose, msginfo->folder);
1772 compose->updating = TRUE;
1773 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1774 if (!compose->fwdinfo)
1775 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1777 compose_extract_original_charset(compose);
1779 if (msginfo->subject && *msginfo->subject) {
1780 gchar *buf, *buf2, *p;
1782 buf = p = g_strdup(msginfo->subject);
1783 p += subject_get_prefix_length(p);
1784 memmove(buf, p, strlen(p) + 1);
1786 buf2 = g_strdup_printf("Fw: %s", buf);
1787 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1793 /* override from name according to folder properties */
1794 if (msginfo->folder && msginfo->folder->prefs &&
1795 msginfo->folder->prefs->forward_with_format &&
1796 msginfo->folder->prefs->forward_override_from_format &&
1797 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1801 MsgInfo *full_msginfo = NULL;
1804 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1806 full_msginfo = procmsg_msginfo_copy(msginfo);
1808 /* decode \-escape sequences in the internal representation of the quote format */
1809 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1810 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1813 gtkaspell_block_check(compose->gtkaspell);
1814 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1815 compose->gtkaspell);
1817 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1819 quote_fmt_scan_string(tmp);
1822 buf = quote_fmt_get_buffer();
1824 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1826 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1827 quote_fmt_reset_vartable();
1828 quote_fmtlex_destroy();
1831 procmsg_msginfo_free(&full_msginfo);
1834 textview = GTK_TEXT_VIEW(compose->text);
1835 textbuf = gtk_text_view_get_buffer(textview);
1836 compose_create_tags(textview, compose);
1838 undo_block(compose->undostruct);
1842 msgfile = procmsg_get_message_file(msginfo);
1843 if (!is_file_exist(msgfile))
1844 g_warning("%s: file does not exist", msgfile);
1846 compose_attach_append(compose, msgfile, msgfile,
1847 "message/rfc822", NULL);
1851 const gchar *qmark = NULL;
1852 const gchar *body_fmt = NULL;
1853 MsgInfo *full_msginfo;
1855 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1857 full_msginfo = procmsg_msginfo_copy(msginfo);
1859 /* use the forward format of folder (if enabled), or the account's one
1860 (if enabled) or fallback to the global forward format, which is always
1861 enabled (even if empty), and use the relevant quotemark */
1862 if (msginfo->folder && msginfo->folder->prefs &&
1863 msginfo->folder->prefs->forward_with_format) {
1864 qmark = msginfo->folder->prefs->forward_quotemark;
1865 body_fmt = msginfo->folder->prefs->forward_body_format;
1867 } else if (account->forward_with_format) {
1868 qmark = account->forward_quotemark;
1869 body_fmt = account->forward_body_format;
1872 qmark = prefs_common.fw_quotemark;
1873 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1874 body_fmt = gettext(prefs_common.fw_quotefmt);
1879 /* empty quotemark is not allowed */
1880 if (qmark == NULL || *qmark == '\0')
1883 compose_quote_fmt(compose, full_msginfo,
1884 body_fmt, qmark, body, FALSE, TRUE,
1885 _("The body of the \"Forward\" template has an error at line %d."));
1886 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1887 quote_fmt_reset_vartable();
1888 compose_attach_parts(compose, msginfo);
1890 procmsg_msginfo_free(&full_msginfo);
1893 SIGNAL_BLOCK(textbuf);
1895 if (account->auto_sig)
1896 compose_insert_sig(compose, FALSE);
1898 compose_wrap_all(compose);
1901 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1902 gtkaspell_highlight_all(compose->gtkaspell);
1903 gtkaspell_unblock_check(compose->gtkaspell);
1905 SIGNAL_UNBLOCK(textbuf);
1907 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1908 (account->default_encrypt || account->default_sign))
1909 COMPOSE_PRIVACY_WARNING();
1911 cursor_pos = quote_fmt_get_cursor_pos();
1912 if (cursor_pos == -1)
1913 gtk_widget_grab_focus(compose->header_last->entry);
1915 gtk_widget_grab_focus(compose->text);
1917 if (!no_extedit && prefs_common.auto_exteditor)
1918 compose_exec_ext_editor(compose);
1921 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1922 gchar *folderidentifier;
1924 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1925 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1926 folderidentifier = folder_item_get_identifier(msginfo->folder);
1927 compose_set_save_to(compose, folderidentifier);
1928 g_free(folderidentifier);
1931 undo_unblock(compose->undostruct);
1933 compose->modified = FALSE;
1934 compose_set_title(compose);
1936 compose->updating = FALSE;
1937 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1938 SCROLL_TO_CURSOR(compose);
1940 if (compose->deferred_destroy) {
1941 compose_destroy(compose);
1945 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1950 #undef INSERT_FW_HEADER
1952 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1955 GtkTextView *textview;
1956 GtkTextBuffer *textbuf;
1960 gboolean single_mail = TRUE;
1962 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1964 if (g_slist_length(msginfo_list) > 1)
1965 single_mail = FALSE;
1967 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1968 if (((MsgInfo *)msginfo->data)->folder == NULL)
1971 /* guess account from first selected message */
1973 !(account = compose_find_account(msginfo_list->data)))
1974 account = cur_account;
1976 cm_return_val_if_fail(account != NULL, NULL);
1978 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1979 if (msginfo->data) {
1980 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1981 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1985 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1986 g_warning("no msginfo_list");
1990 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1991 compose_apply_folder_privacy_settings(compose, ((MsgInfo *)msginfo_list->data)->folder);
1992 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1993 (account->default_encrypt || account->default_sign))
1994 COMPOSE_PRIVACY_WARNING();
1996 compose->updating = TRUE;
1998 /* override from name according to folder properties */
1999 if (msginfo_list->data) {
2000 MsgInfo *msginfo = msginfo_list->data;
2002 if (msginfo->folder && msginfo->folder->prefs &&
2003 msginfo->folder->prefs->forward_with_format &&
2004 msginfo->folder->prefs->forward_override_from_format &&
2005 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2010 /* decode \-escape sequences in the internal representation of the quote format */
2011 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2012 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2015 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2016 compose->gtkaspell);
2018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2020 quote_fmt_scan_string(tmp);
2023 buf = quote_fmt_get_buffer();
2025 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2027 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2028 quote_fmt_reset_vartable();
2029 quote_fmtlex_destroy();
2035 textview = GTK_TEXT_VIEW(compose->text);
2036 textbuf = gtk_text_view_get_buffer(textview);
2037 compose_create_tags(textview, compose);
2039 undo_block(compose->undostruct);
2040 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2041 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2043 if (!is_file_exist(msgfile))
2044 g_warning("%s: file does not exist", msgfile);
2046 compose_attach_append(compose, msgfile, msgfile,
2047 "message/rfc822", NULL);
2052 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2053 if (info->subject && *info->subject) {
2054 gchar *buf, *buf2, *p;
2056 buf = p = g_strdup(info->subject);
2057 p += subject_get_prefix_length(p);
2058 memmove(buf, p, strlen(p) + 1);
2060 buf2 = g_strdup_printf("Fw: %s", buf);
2061 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2067 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2068 _("Fw: multiple emails"));
2071 SIGNAL_BLOCK(textbuf);
2073 if (account->auto_sig)
2074 compose_insert_sig(compose, FALSE);
2076 compose_wrap_all(compose);
2078 SIGNAL_UNBLOCK(textbuf);
2080 gtk_text_buffer_get_start_iter(textbuf, &iter);
2081 gtk_text_buffer_place_cursor(textbuf, &iter);
2083 if (prefs_common.auto_exteditor)
2084 compose_exec_ext_editor(compose);
2086 gtk_widget_grab_focus(compose->header_last->entry);
2087 undo_unblock(compose->undostruct);
2088 compose->modified = FALSE;
2089 compose_set_title(compose);
2091 compose->updating = FALSE;
2092 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2093 SCROLL_TO_CURSOR(compose);
2095 if (compose->deferred_destroy) {
2096 compose_destroy(compose);
2100 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2105 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2107 GtkTextIter start = *iter;
2108 GtkTextIter end_iter;
2109 int start_pos = gtk_text_iter_get_offset(&start);
2111 if (!compose->account->sig_sep)
2114 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2115 start_pos+strlen(compose->account->sig_sep));
2117 /* check sig separator */
2118 str = gtk_text_iter_get_text(&start, &end_iter);
2119 if (!strcmp(str, compose->account->sig_sep)) {
2121 /* check end of line (\n) */
2122 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2123 start_pos+strlen(compose->account->sig_sep));
2124 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2125 start_pos+strlen(compose->account->sig_sep)+1);
2126 tmp = gtk_text_iter_get_text(&start, &end_iter);
2127 if (!strcmp(tmp,"\n")) {
2139 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2141 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2142 Compose *compose = (Compose *)data;
2143 FolderItem *old_item = NULL;
2144 FolderItem *new_item = NULL;
2145 gchar *old_id, *new_id;
2147 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2148 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2151 old_item = hookdata->item;
2152 new_item = hookdata->item2;
2154 old_id = folder_item_get_identifier(old_item);
2155 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2157 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2158 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2159 compose->targetinfo->folder = new_item;
2162 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2163 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2164 compose->replyinfo->folder = new_item;
2167 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2168 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2169 compose->fwdinfo->folder = new_item;
2177 static void compose_colorize_signature(Compose *compose)
2179 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2181 GtkTextIter end_iter;
2182 gtk_text_buffer_get_start_iter(buffer, &iter);
2183 while (gtk_text_iter_forward_line(&iter))
2184 if (compose_is_sig_separator(compose, buffer, &iter)) {
2185 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2186 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2190 #define BLOCK_WRAP() { \
2191 prev_autowrap = compose->autowrap; \
2192 buffer = gtk_text_view_get_buffer( \
2193 GTK_TEXT_VIEW(compose->text)); \
2194 compose->autowrap = FALSE; \
2196 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2197 G_CALLBACK(compose_changed_cb), \
2199 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2200 G_CALLBACK(text_inserted), \
2203 #define UNBLOCK_WRAP() { \
2204 compose->autowrap = prev_autowrap; \
2205 if (compose->autowrap) { \
2206 gint old = compose->draft_timeout_tag; \
2207 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2208 compose_wrap_all(compose); \
2209 compose->draft_timeout_tag = old; \
2212 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2213 G_CALLBACK(compose_changed_cb), \
2215 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2216 G_CALLBACK(text_inserted), \
2220 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2222 Compose *compose = NULL;
2223 PrefsAccount *account = NULL;
2224 GtkTextView *textview;
2225 GtkTextBuffer *textbuf;
2229 gboolean use_signing = FALSE;
2230 gboolean use_encryption = FALSE;
2231 gchar *privacy_system = NULL;
2232 int priority = PRIORITY_NORMAL;
2233 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2234 gboolean autowrap = prefs_common.autowrap;
2235 gboolean autoindent = prefs_common.auto_indent;
2236 HeaderEntry *manual_headers = NULL;
2238 cm_return_val_if_fail(msginfo != NULL, NULL);
2239 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2241 if (compose_put_existing_to_front(msginfo)) {
2245 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2246 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2247 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2248 gchar *queueheader_buf = NULL;
2251 /* Select Account from queue headers */
2252 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2253 "X-Claws-Account-Id:")) {
2254 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2255 account = account_find_from_id(id);
2256 g_free(queueheader_buf);
2258 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2259 "X-Sylpheed-Account-Id:")) {
2260 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2261 account = account_find_from_id(id);
2262 g_free(queueheader_buf);
2264 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2266 id = atoi(&queueheader_buf[strlen("NAID:")]);
2267 account = account_find_from_id(id);
2268 g_free(queueheader_buf);
2270 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2272 id = atoi(&queueheader_buf[strlen("MAID:")]);
2273 account = account_find_from_id(id);
2274 g_free(queueheader_buf);
2276 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2278 account = account_find_from_address(queueheader_buf, FALSE);
2279 g_free(queueheader_buf);
2281 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2283 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2284 use_signing = param;
2285 g_free(queueheader_buf);
2287 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2288 "X-Sylpheed-Sign:")) {
2289 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2290 use_signing = param;
2291 g_free(queueheader_buf);
2293 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2294 "X-Claws-Encrypt:")) {
2295 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2296 use_encryption = param;
2297 g_free(queueheader_buf);
2299 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2300 "X-Sylpheed-Encrypt:")) {
2301 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2302 use_encryption = param;
2303 g_free(queueheader_buf);
2305 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2306 "X-Claws-Auto-Wrapping:")) {
2307 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2309 g_free(queueheader_buf);
2311 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2312 "X-Claws-Auto-Indent:")) {
2313 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2315 g_free(queueheader_buf);
2317 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2318 "X-Claws-Privacy-System:")) {
2319 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2320 g_free(queueheader_buf);
2322 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2323 "X-Sylpheed-Privacy-System:")) {
2324 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2325 g_free(queueheader_buf);
2327 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2329 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2331 g_free(queueheader_buf);
2333 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2335 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2336 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2337 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2338 if (orig_item != NULL) {
2339 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2343 g_free(queueheader_buf);
2345 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2347 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2348 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2349 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2350 if (orig_item != NULL) {
2351 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2355 g_free(queueheader_buf);
2357 /* Get manual headers */
2358 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2359 "X-Claws-Manual-Headers:")) {
2360 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2361 if (listmh && *listmh != '\0') {
2362 debug_print("Got manual headers: %s\n", listmh);
2363 manual_headers = procheader_entries_from_str(listmh);
2366 g_free(queueheader_buf);
2369 account = msginfo->folder->folder->account;
2372 if (!account && prefs_common.reedit_account_autosel) {
2374 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2375 extract_address(from);
2376 account = account_find_from_address(from, FALSE);
2381 account = cur_account;
2383 cm_return_val_if_fail(account != NULL, NULL);
2385 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2387 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2388 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2389 compose->autowrap = autowrap;
2390 compose->replyinfo = replyinfo;
2391 compose->fwdinfo = fwdinfo;
2393 compose->updating = TRUE;
2394 compose->priority = priority;
2396 if (privacy_system != NULL) {
2397 compose->privacy_system = privacy_system;
2398 compose_use_signing(compose, use_signing);
2399 compose_use_encryption(compose, use_encryption);
2400 compose_update_privacy_system_menu_item(compose, FALSE);
2402 compose_activate_privacy_system(compose, account, FALSE);
2404 compose_apply_folder_privacy_settings(compose, msginfo->folder);
2405 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
2406 (account->default_encrypt || account->default_sign))
2407 COMPOSE_PRIVACY_WARNING();
2409 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2410 compose->targetinfo->tags = g_slist_copy(msginfo->tags);
2412 compose_extract_original_charset(compose);
2414 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2415 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2416 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2417 gchar *queueheader_buf = NULL;
2419 /* Set message save folder */
2420 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2421 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2422 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2423 compose_set_save_to(compose, &queueheader_buf[4]);
2424 g_free(queueheader_buf);
2426 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2427 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2429 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2431 g_free(queueheader_buf);
2435 if (compose_parse_header(compose, msginfo) < 0) {
2436 compose->updating = FALSE;
2437 compose_destroy(compose);
2440 compose_reedit_set_entry(compose, msginfo);
2442 textview = GTK_TEXT_VIEW(compose->text);
2443 textbuf = gtk_text_view_get_buffer(textview);
2444 compose_create_tags(textview, compose);
2446 mark = gtk_text_buffer_get_insert(textbuf);
2447 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2449 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2450 G_CALLBACK(compose_changed_cb),
2453 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2454 fp = procmime_get_first_encrypted_text_content(msginfo);
2456 compose_force_encryption(compose, account, TRUE, NULL);
2459 fp = procmime_get_first_text_content(msginfo);
2462 g_warning("Can't get text part");
2466 gchar buf[BUFFSIZE];
2467 gboolean prev_autowrap;
2468 GtkTextBuffer *buffer;
2470 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
2472 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2478 compose_attach_parts(compose, msginfo);
2480 compose_colorize_signature(compose);
2482 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2483 G_CALLBACK(compose_changed_cb),
2486 if (manual_headers != NULL) {
2487 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2488 procheader_entries_free(manual_headers);
2489 compose->updating = FALSE;
2490 compose_destroy(compose);
2493 procheader_entries_free(manual_headers);
2496 gtk_widget_grab_focus(compose->text);
2498 if (prefs_common.auto_exteditor) {
2499 compose_exec_ext_editor(compose);
2501 compose->modified = FALSE;
2502 compose_set_title(compose);
2504 compose->updating = FALSE;
2505 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2506 SCROLL_TO_CURSOR(compose);
2508 if (compose->deferred_destroy) {
2509 compose_destroy(compose);
2513 compose->sig_str = account_get_signature_str(compose->account);
2515 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2520 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2527 cm_return_val_if_fail(msginfo != NULL, NULL);
2530 account = account_get_reply_account(msginfo,
2531 prefs_common.reply_account_autosel);
2532 cm_return_val_if_fail(account != NULL, NULL);
2534 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2536 compose->updating = TRUE;
2538 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2539 compose->replyinfo = NULL;
2540 compose->fwdinfo = NULL;
2542 compose_show_first_last_header(compose, TRUE);
2544 gtk_widget_grab_focus(compose->header_last->entry);
2546 filename = procmsg_get_message_file(msginfo);
2548 if (filename == NULL) {
2549 compose->updating = FALSE;
2550 compose_destroy(compose);
2555 compose->redirect_filename = filename;
2557 /* Set save folder */
2558 item = msginfo->folder;
2559 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2560 gchar *folderidentifier;
2562 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2563 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2564 folderidentifier = folder_item_get_identifier(item);
2565 compose_set_save_to(compose, folderidentifier);
2566 g_free(folderidentifier);
2569 compose_attach_parts(compose, msginfo);
2571 if (msginfo->subject)
2572 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2574 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2576 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2577 _("The body of the \"Redirect\" template has an error at line %d."));
2578 quote_fmt_reset_vartable();
2579 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2581 compose_colorize_signature(compose);
2584 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2585 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2586 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2589 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2590 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2592 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2594 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2596 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2598 if (compose->toolbar->draft_btn)
2599 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2600 if (compose->toolbar->insert_btn)
2601 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2602 if (compose->toolbar->attach_btn)
2603 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2604 if (compose->toolbar->sig_btn)
2605 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2606 if (compose->toolbar->exteditor_btn)
2607 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2608 if (compose->toolbar->linewrap_current_btn)
2609 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2610 if (compose->toolbar->linewrap_all_btn)
2611 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2612 if (compose->toolbar->privacy_sign_btn)
2613 gtk_widget_set_sensitive(compose->toolbar->privacy_sign_btn, FALSE);
2614 if (compose->toolbar->privacy_encrypt_btn)
2615 gtk_widget_set_sensitive(compose->toolbar->privacy_encrypt_btn, FALSE);
2617 compose->modified = FALSE;
2618 compose_set_title(compose);
2619 compose->updating = FALSE;
2620 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2621 SCROLL_TO_CURSOR(compose);
2623 if (compose->deferred_destroy) {
2624 compose_destroy(compose);
2628 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2633 const GList *compose_get_compose_list(void)
2635 return compose_list;
2638 void compose_entry_append(Compose *compose, const gchar *address,
2639 ComposeEntryType type, ComposePrefType pref_type)
2641 const gchar *header;
2643 gboolean in_quote = FALSE;
2644 if (!address || *address == '\0') return;
2651 header = N_("Bcc:");
2653 case COMPOSE_REPLYTO:
2654 header = N_("Reply-To:");
2656 case COMPOSE_NEWSGROUPS:
2657 header = N_("Newsgroups:");
2659 case COMPOSE_FOLLOWUPTO:
2660 header = N_( "Followup-To:");
2662 case COMPOSE_INREPLYTO:
2663 header = N_( "In-Reply-To:");
2670 header = prefs_common_translated_header_name(header);
2672 cur = begin = (gchar *)address;
2674 /* we separate the line by commas, but not if we're inside a quoted
2676 while (*cur != '\0') {
2678 in_quote = !in_quote;
2679 if (*cur == ',' && !in_quote) {
2680 gchar *tmp = g_strdup(begin);
2682 tmp[cur-begin]='\0';
2685 while (*tmp == ' ' || *tmp == '\t')
2687 compose_add_header_entry(compose, header, tmp, pref_type);
2688 compose_entry_indicate(compose, tmp);
2695 gchar *tmp = g_strdup(begin);
2697 tmp[cur-begin]='\0';
2698 while (*tmp == ' ' || *tmp == '\t')
2700 compose_add_header_entry(compose, header, tmp, pref_type);
2701 compose_entry_indicate(compose, tmp);
2706 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2711 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2712 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2713 if (gtk_entry_get_text(entry) &&
2714 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2715 gtk_widget_modify_base(
2716 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2717 GTK_STATE_NORMAL, &default_header_bgcolor);
2718 gtk_widget_modify_text(
2719 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2720 GTK_STATE_NORMAL, &default_header_color);
2725 void compose_toolbar_cb(gint action, gpointer data)
2727 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2728 Compose *compose = (Compose*)toolbar_item->parent;
2730 cm_return_if_fail(compose != NULL);
2734 compose_send_cb(NULL, compose);
2737 compose_send_later_cb(NULL, compose);
2740 compose_draft(compose, COMPOSE_QUIT_EDITING);
2743 compose_insert_file_cb(NULL, compose);
2746 compose_attach_cb(NULL, compose);
2749 compose_insert_sig(compose, FALSE);
2752 compose_insert_sig(compose, TRUE);
2755 compose_ext_editor_cb(NULL, compose);
2757 case A_LINEWRAP_CURRENT:
2758 compose_beautify_paragraph(compose, NULL, TRUE);
2760 case A_LINEWRAP_ALL:
2761 compose_wrap_all_full(compose, TRUE);
2764 compose_address_cb(NULL, compose);
2767 case A_CHECK_SPELLING:
2768 compose_check_all(NULL, compose);
2771 case A_PRIVACY_SIGN:
2773 case A_PRIVACY_ENCRYPT:
2780 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2785 gchar *subject = NULL;
2789 gchar **attach = NULL;
2790 gchar *inreplyto = NULL;
2791 MailField mfield = NO_FIELD_PRESENT;
2793 /* get mailto parts but skip from */
2794 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2797 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2798 mfield = TO_FIELD_PRESENT;
2801 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2803 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2805 if (!g_utf8_validate (subject, -1, NULL)) {
2806 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2807 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2810 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2812 mfield = SUBJECT_FIELD_PRESENT;
2815 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2816 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2819 gboolean prev_autowrap = compose->autowrap;
2821 compose->autowrap = FALSE;
2823 mark = gtk_text_buffer_get_insert(buffer);
2824 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2826 if (!g_utf8_validate (body, -1, NULL)) {
2827 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2828 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2831 gtk_text_buffer_insert(buffer, &iter, body, -1);
2833 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2835 compose->autowrap = prev_autowrap;
2836 if (compose->autowrap)
2837 compose_wrap_all(compose);
2838 mfield = BODY_FIELD_PRESENT;
2842 gint i = 0, att = 0;
2843 gchar *warn_files = NULL;
2844 while (attach[i] != NULL) {
2845 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2846 if (utf8_filename) {
2847 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2848 gchar *tmp = g_strdup_printf("%s%s\n",
2849 warn_files?warn_files:"",
2855 g_free(utf8_filename);
2857 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2862 alertpanel_notice(ngettext(
2863 "The following file has been attached: \n%s",
2864 "The following files have been attached: \n%s", att), warn_files);
2869 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2882 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2884 static HeaderEntry hentry[] = {
2885 {"Reply-To:", NULL, TRUE },
2886 {"Cc:", NULL, TRUE },
2887 {"References:", NULL, FALSE },
2888 {"Bcc:", NULL, TRUE },
2889 {"Newsgroups:", NULL, TRUE },
2890 {"Followup-To:", NULL, TRUE },
2891 {"List-Post:", NULL, FALSE },
2892 {"X-Priority:", NULL, FALSE },
2893 {NULL, NULL, FALSE }
2910 cm_return_val_if_fail(msginfo != NULL, -1);
2912 if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL) return -1;
2913 procheader_get_header_fields(fp, hentry);
2916 if (hentry[H_REPLY_TO].body != NULL) {
2917 if (hentry[H_REPLY_TO].body[0] != '\0') {
2919 conv_unmime_header(hentry[H_REPLY_TO].body,
2922 g_free(hentry[H_REPLY_TO].body);
2923 hentry[H_REPLY_TO].body = NULL;
2925 if (hentry[H_CC].body != NULL) {
2926 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2927 g_free(hentry[H_CC].body);
2928 hentry[H_CC].body = NULL;
2930 if (hentry[H_REFERENCES].body != NULL) {
2931 if (compose->mode == COMPOSE_REEDIT)
2932 compose->references = hentry[H_REFERENCES].body;
2934 compose->references = compose_parse_references
2935 (hentry[H_REFERENCES].body, msginfo->msgid);
2936 g_free(hentry[H_REFERENCES].body);
2938 hentry[H_REFERENCES].body = NULL;
2940 if (hentry[H_BCC].body != NULL) {
2941 if (compose->mode == COMPOSE_REEDIT)
2943 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2944 g_free(hentry[H_BCC].body);
2945 hentry[H_BCC].body = NULL;
2947 if (hentry[H_NEWSGROUPS].body != NULL) {
2948 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2949 hentry[H_NEWSGROUPS].body = NULL;
2951 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2952 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2953 compose->followup_to =
2954 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2957 g_free(hentry[H_FOLLOWUP_TO].body);
2958 hentry[H_FOLLOWUP_TO].body = NULL;
2960 if (hentry[H_LIST_POST].body != NULL) {
2961 gchar *to = NULL, *start = NULL;
2963 extract_address(hentry[H_LIST_POST].body);
2964 if (hentry[H_LIST_POST].body[0] != '\0') {
2965 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2967 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2968 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2971 g_free(compose->ml_post);
2972 compose->ml_post = to;
2975 g_free(hentry[H_LIST_POST].body);
2976 hentry[H_LIST_POST].body = NULL;
2979 /* CLAWS - X-Priority */
2980 if (compose->mode == COMPOSE_REEDIT)
2981 if (hentry[H_X_PRIORITY].body != NULL) {
2984 priority = atoi(hentry[H_X_PRIORITY].body);
2985 g_free(hentry[H_X_PRIORITY].body);
2987 hentry[H_X_PRIORITY].body = NULL;
2989 if (priority < PRIORITY_HIGHEST ||
2990 priority > PRIORITY_LOWEST)
2991 priority = PRIORITY_NORMAL;
2993 compose->priority = priority;
2996 if (compose->mode == COMPOSE_REEDIT) {
2997 if (msginfo->inreplyto && *msginfo->inreplyto)
2998 compose->inreplyto = g_strdup(msginfo->inreplyto);
3000 if (msginfo->msgid && *msginfo->msgid &&
3001 compose->folder != NULL &&
3002 compose->folder->stype == F_DRAFT)
3003 compose->msgid = g_strdup(msginfo->msgid);
3005 if (msginfo->msgid && *msginfo->msgid)
3006 compose->inreplyto = g_strdup(msginfo->msgid);
3008 if (!compose->references) {
3009 if (msginfo->msgid && *msginfo->msgid) {
3010 if (msginfo->inreplyto && *msginfo->inreplyto)
3011 compose->references =
3012 g_strdup_printf("<%s>\n\t<%s>",
3016 compose->references =
3017 g_strconcat("<", msginfo->msgid, ">",
3019 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3020 compose->references =
3021 g_strconcat("<", msginfo->inreplyto, ">",
3030 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3035 cm_return_val_if_fail(msginfo != NULL, -1);
3037 if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL) return -1;
3038 procheader_get_header_fields(fp, entries);
3042 while (he != NULL && he->name != NULL) {
3044 GtkListStore *model = NULL;
3046 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3047 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3048 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3049 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3050 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3057 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3059 GSList *ref_id_list, *cur;
3063 ref_id_list = references_list_append(NULL, ref);
3064 if (!ref_id_list) return NULL;
3065 if (msgid && *msgid)
3066 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3071 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3072 /* "<" + Message-ID + ">" + CR+LF+TAB */
3073 len += strlen((gchar *)cur->data) + 5;
3075 if (len > MAX_REFERENCES_LEN) {
3076 /* remove second message-ID */
3077 if (ref_id_list && ref_id_list->next &&
3078 ref_id_list->next->next) {
3079 g_free(ref_id_list->next->data);
3080 ref_id_list = g_slist_remove
3081 (ref_id_list, ref_id_list->next->data);
3083 slist_free_strings_full(ref_id_list);
3090 new_ref = g_string_new("");
3091 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3092 if (new_ref->len > 0)
3093 g_string_append(new_ref, "\n\t");
3094 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3097 slist_free_strings_full(ref_id_list);
3099 new_ref_str = new_ref->str;
3100 g_string_free(new_ref, FALSE);
3105 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3106 const gchar *fmt, const gchar *qmark,
3107 const gchar *body, gboolean rewrap,
3108 gboolean need_unescape,
3109 const gchar *err_msg)
3111 MsgInfo* dummyinfo = NULL;
3112 gchar *quote_str = NULL;
3114 gboolean prev_autowrap;
3115 const gchar *trimmed_body = body;
3116 gint cursor_pos = -1;
3117 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3118 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3123 SIGNAL_BLOCK(buffer);
3126 dummyinfo = compose_msginfo_new_from_compose(compose);
3127 msginfo = dummyinfo;
3130 if (qmark != NULL) {
3132 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3133 compose->gtkaspell);
3135 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3137 quote_fmt_scan_string(qmark);
3140 buf = quote_fmt_get_buffer();
3143 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3145 Xstrdup_a(quote_str, buf, goto error)