2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
111 #include "password.h"
112 #include "ldapserver.h"
126 #define N_ATTACH_COLS (N_COL_COLUMNS)
130 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
135 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
136 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
137 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
141 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
142 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
143 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
144 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
145 } ComposeCallAdvancedAction;
149 PRIORITY_HIGHEST = 1,
158 COMPOSE_INSERT_SUCCESS,
159 COMPOSE_INSERT_READ_ERROR,
160 COMPOSE_INSERT_INVALID_CHARACTER,
161 COMPOSE_INSERT_NO_FILE
162 } ComposeInsertResult;
166 COMPOSE_WRITE_FOR_SEND,
167 COMPOSE_WRITE_FOR_STORE
172 COMPOSE_QUOTE_FORCED,
179 SUBJECT_FIELD_PRESENT,
184 #define B64_LINE_SIZE 57
185 #define B64_BUFFSIZE 77
187 #define MAX_REFERENCES_LEN 999
189 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
190 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
192 static GdkColor default_header_bgcolor = {
199 static GdkColor default_header_color = {
206 static GList *compose_list = NULL;
207 static GSList *extra_headers = NULL;
209 static Compose *compose_generic_new (PrefsAccount *account,
213 GList *listAddress );
215 static Compose *compose_create (PrefsAccount *account,
220 static void compose_entry_indicate (Compose *compose,
221 const gchar *address);
222 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
223 ComposeQuoteMode quote_mode,
227 static Compose *compose_forward_multiple (PrefsAccount *account,
228 GSList *msginfo_list);
229 static Compose *compose_reply (MsgInfo *msginfo,
230 ComposeQuoteMode quote_mode,
235 static Compose *compose_reply_mode (ComposeMode mode,
236 GSList *msginfo_list,
238 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
239 static void compose_update_privacy_systems_menu(Compose *compose);
241 static GtkWidget *compose_account_option_menu_create
243 static void compose_set_out_encoding (Compose *compose);
244 static void compose_set_template_menu (Compose *compose);
245 static void compose_destroy (Compose *compose);
247 static MailField compose_entries_set (Compose *compose,
249 ComposeEntryType to_type);
250 static gint compose_parse_header (Compose *compose,
252 static gint compose_parse_manual_headers (Compose *compose,
254 HeaderEntry *entries);
255 static gchar *compose_parse_references (const gchar *ref,
258 static gchar *compose_quote_fmt (Compose *compose,
264 gboolean need_unescape,
265 const gchar *err_msg);
267 static void compose_reply_set_entry (Compose *compose,
273 followup_and_reply_to);
274 static void compose_reedit_set_entry (Compose *compose,
277 static void compose_insert_sig (Compose *compose,
279 static ComposeInsertResult compose_insert_file (Compose *compose,
282 static gboolean compose_attach_append (Compose *compose,
285 const gchar *content_type,
286 const gchar *charset);
287 static void compose_attach_parts (Compose *compose,
290 static gboolean compose_beautify_paragraph (Compose *compose,
291 GtkTextIter *par_iter,
293 static void compose_wrap_all (Compose *compose);
294 static void compose_wrap_all_full (Compose *compose,
297 static void compose_set_title (Compose *compose);
298 static void compose_select_account (Compose *compose,
299 PrefsAccount *account,
302 static PrefsAccount *compose_current_mail_account(void);
303 /* static gint compose_send (Compose *compose); */
304 static gboolean compose_check_for_valid_recipient
306 static gboolean compose_check_entries (Compose *compose,
307 gboolean check_everything);
308 static gint compose_write_to_file (Compose *compose,
311 gboolean attach_parts);
312 static gint compose_write_body_to_file (Compose *compose,
314 static gint compose_remove_reedit_target (Compose *compose,
316 static void compose_remove_draft (Compose *compose);
317 static gint compose_queue_sub (Compose *compose,
321 gboolean perform_checks,
322 gboolean remove_reedit_target);
323 static int compose_add_attachments (Compose *compose,
325 static gchar *compose_get_header (Compose *compose);
326 static gchar *compose_get_manual_headers_info (Compose *compose);
328 static void compose_convert_header (Compose *compose,
333 gboolean addr_field);
335 static void compose_attach_info_free (AttachInfo *ainfo);
336 static void compose_attach_remove_selected (GtkAction *action,
339 static void compose_template_apply (Compose *compose,
342 static void compose_attach_property (GtkAction *action,
344 static void compose_attach_property_create (gboolean *cancelled);
345 static void attach_property_ok (GtkWidget *widget,
346 gboolean *cancelled);
347 static void attach_property_cancel (GtkWidget *widget,
348 gboolean *cancelled);
349 static gint attach_property_delete_event (GtkWidget *widget,
351 gboolean *cancelled);
352 static gboolean attach_property_key_pressed (GtkWidget *widget,
354 gboolean *cancelled);
356 static void compose_exec_ext_editor (Compose *compose);
358 static gint compose_exec_ext_editor_real (const gchar *file,
359 GdkNativeWindow socket_wid);
360 static gboolean compose_ext_editor_kill (Compose *compose);
361 static gboolean compose_input_cb (GIOChannel *source,
362 GIOCondition condition,
364 static void compose_set_ext_editor_sensitive (Compose *compose,
366 static gboolean compose_get_ext_editor_cmd_valid();
367 static gboolean compose_get_ext_editor_uses_socket();
368 static gboolean compose_ext_editor_plug_removed_cb
371 #endif /* G_OS_UNIX */
373 static void compose_undo_state_changed (UndoMain *undostruct,
378 static void compose_create_header_entry (Compose *compose);
379 static void compose_add_header_entry (Compose *compose, const gchar *header,
380 gchar *text, ComposePrefType pref_type);
381 static void compose_remove_header_entries(Compose *compose);
383 static void compose_update_priority_menu_item(Compose * compose);
385 static void compose_spell_menu_changed (void *data);
386 static void compose_dict_changed (void *data);
388 static void compose_add_field_list ( Compose *compose,
389 GList *listAddress );
391 /* callback functions */
393 static void compose_notebook_size_alloc (GtkNotebook *notebook,
394 GtkAllocation *allocation,
396 static gboolean compose_edit_size_alloc (GtkEditable *widget,
397 GtkAllocation *allocation,
398 GtkSHRuler *shruler);
399 static void account_activated (GtkComboBox *optmenu,
401 static void attach_selected (GtkTreeView *tree_view,
402 GtkTreePath *tree_path,
403 GtkTreeViewColumn *column,
405 static gboolean attach_button_pressed (GtkWidget *widget,
406 GdkEventButton *event,
408 static gboolean attach_key_pressed (GtkWidget *widget,
411 static void compose_send_cb (GtkAction *action, gpointer data);
412 static void compose_send_later_cb (GtkAction *action, gpointer data);
414 static void compose_save_cb (GtkAction *action,
417 static void compose_attach_cb (GtkAction *action,
419 static void compose_insert_file_cb (GtkAction *action,
421 static void compose_insert_sig_cb (GtkAction *action,
423 static void compose_replace_sig_cb (GtkAction *action,
426 static void compose_close_cb (GtkAction *action,
428 static void compose_print_cb (GtkAction *action,
431 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
433 static void compose_address_cb (GtkAction *action,
435 static void about_show_cb (GtkAction *action,
437 static void compose_template_activate_cb(GtkWidget *widget,
440 static void compose_ext_editor_cb (GtkAction *action,
443 static gint compose_delete_cb (GtkWidget *widget,
447 static void compose_undo_cb (GtkAction *action,
449 static void compose_redo_cb (GtkAction *action,
451 static void compose_cut_cb (GtkAction *action,
453 static void compose_copy_cb (GtkAction *action,
455 static void compose_paste_cb (GtkAction *action,
457 static void compose_paste_as_quote_cb (GtkAction *action,
459 static void compose_paste_no_wrap_cb (GtkAction *action,
461 static void compose_paste_wrap_cb (GtkAction *action,
463 static void compose_allsel_cb (GtkAction *action,
466 static void compose_advanced_action_cb (GtkAction *action,
469 static void compose_grab_focus_cb (GtkWidget *widget,
472 static void compose_changed_cb (GtkTextBuffer *textbuf,
475 static void compose_wrap_cb (GtkAction *action,
477 static void compose_wrap_all_cb (GtkAction *action,
479 static void compose_find_cb (GtkAction *action,
481 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
483 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
486 static void compose_toggle_ruler_cb (GtkToggleAction *action,
488 static void compose_toggle_sign_cb (GtkToggleAction *action,
490 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
492 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
493 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
494 static void activate_privacy_system (Compose *compose,
495 PrefsAccount *account,
497 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
499 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
501 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
502 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
503 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
505 static void compose_attach_drag_received_cb (GtkWidget *widget,
506 GdkDragContext *drag_context,
509 GtkSelectionData *data,
513 static void compose_insert_drag_received_cb (GtkWidget *widget,
514 GdkDragContext *drag_context,
517 GtkSelectionData *data,
521 static void compose_header_drag_received_cb (GtkWidget *widget,
522 GdkDragContext *drag_context,
525 GtkSelectionData *data,
530 static gboolean compose_drag_drop (GtkWidget *widget,
531 GdkDragContext *drag_context,
533 guint time, gpointer user_data);
534 static gboolean completion_set_focus_to_subject
539 static void text_inserted (GtkTextBuffer *buffer,
544 static Compose *compose_generic_reply(MsgInfo *msginfo,
545 ComposeQuoteMode quote_mode,
549 gboolean followup_and_reply_to,
552 static void compose_headerentry_changed_cb (GtkWidget *entry,
553 ComposeHeaderEntry *headerentry);
554 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
556 ComposeHeaderEntry *headerentry);
557 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
558 ComposeHeaderEntry *headerentry);
560 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
562 static void compose_allow_user_actions (Compose *compose, gboolean allow);
564 static void compose_nothing_cb (GtkAction *action, gpointer data)
570 static void compose_check_all (GtkAction *action, gpointer data);
571 static void compose_highlight_all (GtkAction *action, gpointer data);
572 static void compose_check_backwards (GtkAction *action, gpointer data);
573 static void compose_check_forwards_go (GtkAction *action, gpointer data);
576 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
578 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
581 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
582 FolderItem *folder_item);
584 static void compose_attach_update_label(Compose *compose);
585 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
586 gboolean respect_default_to);
587 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
588 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
590 static GtkActionEntry compose_popup_entries[] =
592 {"Compose", NULL, "Compose", NULL, NULL, NULL },
593 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
594 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
595 {"Compose/---", NULL, "---", NULL, NULL, NULL },
596 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
599 static GtkActionEntry compose_entries[] =
601 {"Menu", NULL, "Menu", NULL, NULL, NULL },
603 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
604 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
606 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
608 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
609 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
610 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
612 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
613 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
614 {"Message/---", NULL, "---", NULL, NULL, NULL },
616 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
617 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
618 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
619 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
620 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
621 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
622 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
623 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
624 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
625 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
628 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
629 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
630 {"Edit/---", NULL, "---", NULL, NULL, NULL },
632 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
633 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
634 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
636 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
637 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
638 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
639 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
641 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
643 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
644 {"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*/
645 {"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*/
646 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
647 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
648 {"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*/
649 {"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*/
650 {"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*/
651 {"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*/
652 {"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*/
653 {"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*/
654 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
655 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
656 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
657 {"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*/
659 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
660 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
662 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
663 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
664 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
665 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
666 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
669 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
670 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
671 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
672 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
674 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
675 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
679 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
680 {"Options/---", NULL, "---", NULL, NULL, NULL },
681 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
682 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
684 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
685 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
687 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
688 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
689 #define ENC_ACTION(cs_char,c_char,string) \
690 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
692 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
693 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
694 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
695 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
696 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
697 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
698 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
699 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
700 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
703 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
705 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
706 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
707 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
708 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
711 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
714 static GtkToggleActionEntry compose_toggle_entries[] =
716 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
717 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
718 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
719 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
720 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
721 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
722 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
725 static GtkRadioActionEntry compose_radio_rm_entries[] =
727 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
728 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
729 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
730 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
733 static GtkRadioActionEntry compose_radio_prio_entries[] =
735 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
736 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
737 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
738 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
739 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
742 static GtkRadioActionEntry compose_radio_enc_entries[] =
744 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
779 static GtkTargetEntry compose_mime_types[] =
781 {"text/uri-list", 0, 0},
782 {"UTF8_STRING", 0, 0},
786 static gboolean compose_put_existing_to_front(MsgInfo *info)
788 const GList *compose_list = compose_get_compose_list();
789 const GList *elem = NULL;
792 for (elem = compose_list; elem != NULL && elem->data != NULL;
794 Compose *c = (Compose*)elem->data;
796 if (!c->targetinfo || !c->targetinfo->msgid ||
800 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
801 gtkut_window_popup(c->window);
809 static GdkColor quote_color1 =
810 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
811 static GdkColor quote_color2 =
812 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 static GdkColor quote_color3 =
814 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor1 =
817 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
818 static GdkColor quote_bgcolor2 =
819 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
820 static GdkColor quote_bgcolor3 =
821 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
823 static GdkColor signature_color = {
830 static GdkColor uri_color = {
837 static void compose_create_tags(GtkTextView *text, Compose *compose)
839 GtkTextBuffer *buffer;
840 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
841 #if !GTK_CHECK_VERSION(2, 24, 0)
848 buffer = gtk_text_view_get_buffer(text);
850 if (prefs_common.enable_color) {
851 /* grab the quote colors, converting from an int to a GdkColor */
852 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
854 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
856 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
858 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
860 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
862 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
864 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
866 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
869 signature_color = quote_color1 = quote_color2 = quote_color3 =
870 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
873 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
874 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
875 "foreground-gdk", "e_color1,
876 "paragraph-background-gdk", "e_bgcolor1,
878 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
879 "foreground-gdk", "e_color2,
880 "paragraph-background-gdk", "e_bgcolor2,
882 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
883 "foreground-gdk", "e_color3,
884 "paragraph-background-gdk", "e_bgcolor3,
887 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
888 "foreground-gdk", "e_color1,
890 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
891 "foreground-gdk", "e_color2,
893 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
894 "foreground-gdk", "e_color3,
898 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
899 "foreground-gdk", &signature_color,
902 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
903 "foreground-gdk", &uri_color,
905 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
906 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
908 #if !GTK_CHECK_VERSION(2, 24, 0)
909 color[0] = quote_color1;
910 color[1] = quote_color2;
911 color[2] = quote_color3;
912 color[3] = quote_bgcolor1;
913 color[4] = quote_bgcolor2;
914 color[5] = quote_bgcolor3;
915 color[6] = signature_color;
916 color[7] = uri_color;
918 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
919 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
921 for (i = 0; i < 8; i++) {
922 if (success[i] == FALSE) {
923 g_warning("Compose: color allocation failed.");
924 quote_color1 = quote_color2 = quote_color3 =
925 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
926 signature_color = uri_color = black;
932 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
935 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
938 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
940 return compose_generic_new(account, mailto, item, NULL, NULL);
943 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
945 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
948 #define SCROLL_TO_CURSOR(compose) { \
949 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
950 gtk_text_view_get_buffer( \
951 GTK_TEXT_VIEW(compose->text))); \
952 gtk_text_view_scroll_mark_onscreen( \
953 GTK_TEXT_VIEW(compose->text), \
957 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
960 if (folderidentifier) {
961 #if !GTK_CHECK_VERSION(2, 24, 0)
962 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
964 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
966 prefs_common.compose_save_to_history = add_history(
967 prefs_common.compose_save_to_history, folderidentifier);
968 #if !GTK_CHECK_VERSION(2, 24, 0)
969 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
970 prefs_common.compose_save_to_history);
972 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
973 prefs_common.compose_save_to_history);
977 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
978 if (folderidentifier)
979 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
981 gtk_entry_set_text(GTK_ENTRY(entry), "");
984 static gchar *compose_get_save_to(Compose *compose)
987 gchar *result = NULL;
988 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
989 result = gtk_editable_get_chars(entry, 0, -1);
992 #if !GTK_CHECK_VERSION(2, 24, 0)
993 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
995 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
997 prefs_common.compose_save_to_history = add_history(
998 prefs_common.compose_save_to_history, result);
999 #if !GTK_CHECK_VERSION(2, 24, 0)
1000 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
1001 prefs_common.compose_save_to_history);
1003 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1004 prefs_common.compose_save_to_history);
1010 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1011 GList *attach_files, GList *listAddress )
1014 GtkTextView *textview;
1015 GtkTextBuffer *textbuf;
1017 const gchar *subject_format = NULL;
1018 const gchar *body_format = NULL;
1019 gchar *mailto_from = NULL;
1020 PrefsAccount *mailto_account = NULL;
1021 MsgInfo* dummyinfo = NULL;
1022 gint cursor_pos = -1;
1023 MailField mfield = NO_FIELD_PRESENT;
1027 /* check if mailto defines a from */
1028 if (mailto && *mailto != '\0') {
1029 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1030 /* mailto defines a from, check if we can get account prefs from it,
1031 if not, the account prefs will be guessed using other ways, but we'll keep
1034 mailto_account = account_find_from_address(mailto_from, TRUE);
1035 if (mailto_account == NULL) {
1037 Xstrdup_a(tmp_from, mailto_from, return NULL);
1038 extract_address(tmp_from);
1039 mailto_account = account_find_from_address(tmp_from, TRUE);
1043 account = mailto_account;
1046 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1047 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1048 account = account_find_from_id(item->prefs->default_account);
1050 /* if no account prefs set, fallback to the current one */
1051 if (!account) account = cur_account;
1052 cm_return_val_if_fail(account != NULL, NULL);
1054 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1056 /* override from name if mailto asked for it */
1058 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1059 g_free(mailto_from);
1061 /* override from name according to folder properties */
1062 if (item && item->prefs &&
1063 item->prefs->compose_with_format &&
1064 item->prefs->compose_override_from_format &&
1065 *item->prefs->compose_override_from_format != '\0') {
1070 dummyinfo = compose_msginfo_new_from_compose(compose);
1072 /* decode \-escape sequences in the internal representation of the quote format */
1073 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1074 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1077 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1078 compose->gtkaspell);
1080 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1082 quote_fmt_scan_string(tmp);
1085 buf = quote_fmt_get_buffer();
1087 alertpanel_error(_("New message From format error."));
1089 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1090 quote_fmt_reset_vartable();
1091 quote_fmtlex_destroy();
1096 compose->replyinfo = NULL;
1097 compose->fwdinfo = NULL;
1099 textview = GTK_TEXT_VIEW(compose->text);
1100 textbuf = gtk_text_view_get_buffer(textview);
1101 compose_create_tags(textview, compose);
1103 undo_block(compose->undostruct);
1105 compose_set_dictionaries_from_folder_prefs(compose, item);
1108 if (account->auto_sig)
1109 compose_insert_sig(compose, FALSE);
1110 gtk_text_buffer_get_start_iter(textbuf, &iter);
1111 gtk_text_buffer_place_cursor(textbuf, &iter);
1113 if (account->protocol != A_NNTP) {
1114 if (mailto && *mailto != '\0') {
1115 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1118 compose_set_folder_prefs(compose, item, TRUE);
1120 if (item && item->ret_rcpt) {
1121 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1124 if (mailto && *mailto != '\0') {
1125 if (!strchr(mailto, '@'))
1126 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1128 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1129 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1130 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1131 mfield = TO_FIELD_PRESENT;
1134 * CLAWS: just don't allow return receipt request, even if the user
1135 * may want to send an email. simple but foolproof.
1137 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1139 compose_add_field_list( compose, listAddress );
1141 if (item && item->prefs && item->prefs->compose_with_format) {
1142 subject_format = item->prefs->compose_subject_format;
1143 body_format = item->prefs->compose_body_format;
1144 } else if (account->compose_with_format) {
1145 subject_format = account->compose_subject_format;
1146 body_format = account->compose_body_format;
1147 } else if (prefs_common.compose_with_format) {
1148 subject_format = prefs_common.compose_subject_format;
1149 body_format = prefs_common.compose_body_format;
1152 if (subject_format || body_format) {
1155 && *subject_format != '\0' )
1157 gchar *subject = NULL;
1162 dummyinfo = compose_msginfo_new_from_compose(compose);
1164 /* decode \-escape sequences in the internal representation of the quote format */
1165 tmp = g_malloc(strlen(subject_format)+1);
1166 pref_get_unescaped_pref(tmp, subject_format);
1168 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1170 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1171 compose->gtkaspell);
1173 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1175 quote_fmt_scan_string(tmp);
1178 buf = quote_fmt_get_buffer();
1180 alertpanel_error(_("New message subject format error."));
1182 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1183 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1184 quote_fmt_reset_vartable();
1185 quote_fmtlex_destroy();
1189 mfield = SUBJECT_FIELD_PRESENT;
1193 && *body_format != '\0' )
1196 GtkTextBuffer *buffer;
1197 GtkTextIter start, end;
1201 dummyinfo = compose_msginfo_new_from_compose(compose);
1203 text = GTK_TEXT_VIEW(compose->text);
1204 buffer = gtk_text_view_get_buffer(text);
1205 gtk_text_buffer_get_start_iter(buffer, &start);
1206 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1207 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1209 compose_quote_fmt(compose, dummyinfo,
1211 NULL, tmp, FALSE, TRUE,
1212 _("The body of the \"New message\" template has an error at line %d."));
1213 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1214 quote_fmt_reset_vartable();
1218 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1219 gtkaspell_highlight_all(compose->gtkaspell);
1221 mfield = BODY_FIELD_PRESENT;
1225 procmsg_msginfo_free( &dummyinfo );
1231 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1232 ainfo = (AttachInfo *) curr->data;
1234 compose_insert_file(compose, ainfo->file);
1236 compose_attach_append(compose, ainfo->file, ainfo->file,
1237 ainfo->content_type, ainfo->charset);
1241 compose_show_first_last_header(compose, TRUE);
1243 /* Set save folder */
1244 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1245 gchar *folderidentifier;
1247 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1248 folderidentifier = folder_item_get_identifier(item);
1249 compose_set_save_to(compose, folderidentifier);
1250 g_free(folderidentifier);
1253 /* Place cursor according to provided input (mfield) */
1255 case NO_FIELD_PRESENT:
1256 if (compose->header_last)
1257 gtk_widget_grab_focus(compose->header_last->entry);
1259 case TO_FIELD_PRESENT:
1260 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1262 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1265 gtk_widget_grab_focus(compose->subject_entry);
1267 case SUBJECT_FIELD_PRESENT:
1268 textview = GTK_TEXT_VIEW(compose->text);
1271 textbuf = gtk_text_view_get_buffer(textview);
1274 mark = gtk_text_buffer_get_insert(textbuf);
1275 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1276 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1278 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1279 * only defers where it comes to the variable body
1280 * is not null. If no body is present compose->text
1281 * will be null in which case you cannot place the
1282 * cursor inside the component so. An empty component
1283 * is therefore created before placing the cursor
1285 case BODY_FIELD_PRESENT:
1286 cursor_pos = quote_fmt_get_cursor_pos();
1287 if (cursor_pos == -1)
1288 gtk_widget_grab_focus(compose->header_last->entry);
1290 gtk_widget_grab_focus(compose->text);
1294 undo_unblock(compose->undostruct);
1296 if (prefs_common.auto_exteditor)
1297 compose_exec_ext_editor(compose);
1299 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1301 SCROLL_TO_CURSOR(compose);
1303 compose->modified = FALSE;
1304 compose_set_title(compose);
1306 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1311 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1312 gboolean override_pref, const gchar *system)
1314 const gchar *privacy = NULL;
1316 cm_return_if_fail(compose != NULL);
1317 cm_return_if_fail(account != NULL);
1319 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1322 if (account->default_privacy_system && strlen(account->default_privacy_system))
1323 privacy = account->default_privacy_system;
1327 GSList *privacy_avail = privacy_get_system_ids();
1328 if (privacy_avail && g_slist_length(privacy_avail)) {
1329 privacy = (gchar *)(privacy_avail->data);
1331 g_slist_free_full(privacy_avail, g_free);
1333 if (privacy != NULL) {
1335 g_free(compose->privacy_system);
1336 compose->privacy_system = NULL;
1337 g_free(compose->encdata);
1338 compose->encdata = NULL;
1340 if (compose->privacy_system == NULL)
1341 compose->privacy_system = g_strdup(privacy);
1342 else if (*(compose->privacy_system) == '\0') {
1343 g_free(compose->privacy_system);
1344 g_free(compose->encdata);
1345 compose->encdata = NULL;
1346 compose->privacy_system = g_strdup(privacy);
1348 compose_update_privacy_system_menu_item(compose, FALSE);
1349 compose_use_encryption(compose, TRUE);
1353 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1355 const gchar *privacy = NULL;
1357 if (account->default_privacy_system && strlen(account->default_privacy_system))
1358 privacy = account->default_privacy_system;
1362 GSList *privacy_avail = privacy_get_system_ids();
1363 if (privacy_avail && g_slist_length(privacy_avail)) {
1364 privacy = (gchar *)(privacy_avail->data);
1368 if (privacy != NULL) {
1370 g_free(compose->privacy_system);
1371 compose->privacy_system = NULL;
1372 g_free(compose->encdata);
1373 compose->encdata = NULL;
1375 if (compose->privacy_system == NULL)
1376 compose->privacy_system = g_strdup(privacy);
1377 compose_update_privacy_system_menu_item(compose, FALSE);
1378 compose_use_signing(compose, TRUE);
1382 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1386 Compose *compose = NULL;
1388 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1390 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1391 cm_return_val_if_fail(msginfo != NULL, NULL);
1393 list_len = g_slist_length(msginfo_list);
1397 case COMPOSE_REPLY_TO_ADDRESS:
1398 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1399 FALSE, prefs_common.default_reply_list, FALSE, body);
1401 case COMPOSE_REPLY_WITH_QUOTE:
1402 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1403 FALSE, prefs_common.default_reply_list, FALSE, body);
1405 case COMPOSE_REPLY_WITHOUT_QUOTE:
1406 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1407 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1409 case COMPOSE_REPLY_TO_SENDER:
1410 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1411 FALSE, FALSE, TRUE, body);
1413 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1414 compose = compose_followup_and_reply_to(msginfo,
1415 COMPOSE_QUOTE_CHECK,
1416 FALSE, FALSE, body);
1418 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1419 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1420 FALSE, FALSE, TRUE, body);
1422 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1423 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1424 FALSE, FALSE, TRUE, NULL);
1426 case COMPOSE_REPLY_TO_ALL:
1427 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1428 TRUE, FALSE, FALSE, body);
1430 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1431 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1432 TRUE, FALSE, FALSE, body);
1434 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1435 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1436 TRUE, FALSE, FALSE, NULL);
1438 case COMPOSE_REPLY_TO_LIST:
1439 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1440 FALSE, TRUE, FALSE, body);
1442 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1443 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1444 FALSE, TRUE, FALSE, body);
1446 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1447 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1448 FALSE, TRUE, FALSE, NULL);
1450 case COMPOSE_FORWARD:
1451 if (prefs_common.forward_as_attachment) {
1452 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1455 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1459 case COMPOSE_FORWARD_INLINE:
1460 /* check if we reply to more than one Message */
1461 if (list_len == 1) {
1462 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1465 /* more messages FALL THROUGH */
1466 case COMPOSE_FORWARD_AS_ATTACH:
1467 compose = compose_forward_multiple(NULL, msginfo_list);
1469 case COMPOSE_REDIRECT:
1470 compose = compose_redirect(NULL, msginfo, FALSE);
1473 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1476 if (compose == NULL) {
1477 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1481 compose->rmode = mode;
1482 switch (compose->rmode) {
1484 case COMPOSE_REPLY_WITH_QUOTE:
1485 case COMPOSE_REPLY_WITHOUT_QUOTE:
1486 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1487 debug_print("reply mode Normal\n");
1488 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1489 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1491 case COMPOSE_REPLY_TO_SENDER:
1492 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1493 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1494 debug_print("reply mode Sender\n");
1495 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1497 case COMPOSE_REPLY_TO_ALL:
1498 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1499 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1500 debug_print("reply mode All\n");
1501 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1503 case COMPOSE_REPLY_TO_LIST:
1504 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1505 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1506 debug_print("reply mode List\n");
1507 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1509 case COMPOSE_REPLY_TO_ADDRESS:
1510 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1518 static Compose *compose_reply(MsgInfo *msginfo,
1519 ComposeQuoteMode quote_mode,
1525 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1526 to_sender, FALSE, body);
1529 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1530 ComposeQuoteMode quote_mode,
1535 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1536 to_sender, TRUE, body);
1539 static void compose_extract_original_charset(Compose *compose)
1541 MsgInfo *info = NULL;
1542 if (compose->replyinfo) {
1543 info = compose->replyinfo;
1544 } else if (compose->fwdinfo) {
1545 info = compose->fwdinfo;
1546 } else if (compose->targetinfo) {
1547 info = compose->targetinfo;
1550 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1551 MimeInfo *partinfo = mimeinfo;
1552 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1553 partinfo = procmime_mimeinfo_next(partinfo);
1555 compose->orig_charset =
1556 g_strdup(procmime_mimeinfo_get_parameter(
1557 partinfo, "charset"));
1559 procmime_mimeinfo_free_all(&mimeinfo);
1563 #define SIGNAL_BLOCK(buffer) { \
1564 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1565 G_CALLBACK(compose_changed_cb), \
1567 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1568 G_CALLBACK(text_inserted), \
1572 #define SIGNAL_UNBLOCK(buffer) { \
1573 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1574 G_CALLBACK(compose_changed_cb), \
1576 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1577 G_CALLBACK(text_inserted), \
1581 static Compose *compose_generic_reply(MsgInfo *msginfo,
1582 ComposeQuoteMode quote_mode,
1583 gboolean to_all, gboolean to_ml,
1585 gboolean followup_and_reply_to,
1589 PrefsAccount *account = NULL;
1590 GtkTextView *textview;
1591 GtkTextBuffer *textbuf;
1592 gboolean quote = FALSE;
1593 const gchar *qmark = NULL;
1594 const gchar *body_fmt = NULL;
1595 gchar *s_system = NULL;
1597 cm_return_val_if_fail(msginfo != NULL, NULL);
1598 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1600 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1602 cm_return_val_if_fail(account != NULL, NULL);
1604 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1606 compose->updating = TRUE;
1608 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1609 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1611 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1612 if (!compose->replyinfo)
1613 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1615 compose_extract_original_charset(compose);
1617 if (msginfo->folder && msginfo->folder->ret_rcpt)
1618 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1620 /* Set save folder */
1621 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1622 gchar *folderidentifier;
1624 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1625 folderidentifier = folder_item_get_identifier(msginfo->folder);
1626 compose_set_save_to(compose, folderidentifier);
1627 g_free(folderidentifier);
1630 if (compose_parse_header(compose, msginfo) < 0) {
1631 compose->updating = FALSE;
1632 compose_destroy(compose);
1636 /* override from name according to folder properties */
1637 if (msginfo->folder && msginfo->folder->prefs &&
1638 msginfo->folder->prefs->reply_with_format &&
1639 msginfo->folder->prefs->reply_override_from_format &&
1640 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1645 /* decode \-escape sequences in the internal representation of the quote format */
1646 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1647 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1650 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1651 compose->gtkaspell);
1653 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1655 quote_fmt_scan_string(tmp);
1658 buf = quote_fmt_get_buffer();
1660 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1662 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1663 quote_fmt_reset_vartable();
1664 quote_fmtlex_destroy();
1669 textview = (GTK_TEXT_VIEW(compose->text));
1670 textbuf = gtk_text_view_get_buffer(textview);
1671 compose_create_tags(textview, compose);
1673 undo_block(compose->undostruct);
1675 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1676 gtkaspell_block_check(compose->gtkaspell);
1679 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1680 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1681 /* use the reply format of folder (if enabled), or the account's one
1682 (if enabled) or fallback to the global reply format, which is always
1683 enabled (even if empty), and use the relevant quotemark */
1685 if (msginfo->folder && msginfo->folder->prefs &&
1686 msginfo->folder->prefs->reply_with_format) {
1687 qmark = msginfo->folder->prefs->reply_quotemark;
1688 body_fmt = msginfo->folder->prefs->reply_body_format;
1690 } else if (account->reply_with_format) {
1691 qmark = account->reply_quotemark;
1692 body_fmt = account->reply_body_format;
1695 qmark = prefs_common.quotemark;
1696 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1697 body_fmt = gettext(prefs_common.quotefmt);
1704 /* empty quotemark is not allowed */
1705 if (qmark == NULL || *qmark == '\0')
1707 compose_quote_fmt(compose, compose->replyinfo,
1708 body_fmt, qmark, body, FALSE, TRUE,
1709 _("The body of the \"Reply\" template has an error at line %d."));
1710 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1711 quote_fmt_reset_vartable();
1714 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1715 compose_force_encryption(compose, account, FALSE, s_system);
1718 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1719 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1720 compose_force_signing(compose, account, s_system);
1724 SIGNAL_BLOCK(textbuf);
1726 if (account->auto_sig)
1727 compose_insert_sig(compose, FALSE);
1729 compose_wrap_all(compose);
1732 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1733 gtkaspell_highlight_all(compose->gtkaspell);
1734 gtkaspell_unblock_check(compose->gtkaspell);
1736 SIGNAL_UNBLOCK(textbuf);
1738 gtk_widget_grab_focus(compose->text);
1740 undo_unblock(compose->undostruct);
1742 if (prefs_common.auto_exteditor)
1743 compose_exec_ext_editor(compose);
1745 compose->modified = FALSE;
1746 compose_set_title(compose);
1748 compose->updating = FALSE;
1749 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1750 SCROLL_TO_CURSOR(compose);
1752 if (compose->deferred_destroy) {
1753 compose_destroy(compose);
1761 #define INSERT_FW_HEADER(var, hdr) \
1762 if (msginfo->var && *msginfo->var) { \
1763 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1764 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1765 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1768 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1769 gboolean as_attach, const gchar *body,
1770 gboolean no_extedit,
1774 GtkTextView *textview;
1775 GtkTextBuffer *textbuf;
1776 gint cursor_pos = -1;
1779 cm_return_val_if_fail(msginfo != NULL, NULL);
1780 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1782 if (!account && !(account = compose_find_account(msginfo)))
1783 account = cur_account;
1785 if (!prefs_common.forward_as_attachment)
1786 mode = COMPOSE_FORWARD_INLINE;
1788 mode = COMPOSE_FORWARD;
1789 compose = compose_create(account, msginfo->folder, mode, batch);
1791 compose->updating = TRUE;
1792 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1793 if (!compose->fwdinfo)
1794 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1796 compose_extract_original_charset(compose);
1798 if (msginfo->subject && *msginfo->subject) {
1799 gchar *buf, *buf2, *p;
1801 buf = p = g_strdup(msginfo->subject);
1802 p += subject_get_prefix_length(p);
1803 memmove(buf, p, strlen(p) + 1);
1805 buf2 = g_strdup_printf("Fw: %s", buf);
1806 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1812 /* override from name according to folder properties */
1813 if (msginfo->folder && msginfo->folder->prefs &&
1814 msginfo->folder->prefs->forward_with_format &&
1815 msginfo->folder->prefs->forward_override_from_format &&
1816 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1820 MsgInfo *full_msginfo = NULL;
1823 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1825 full_msginfo = procmsg_msginfo_copy(msginfo);
1827 /* decode \-escape sequences in the internal representation of the quote format */
1828 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1829 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1832 gtkaspell_block_check(compose->gtkaspell);
1833 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1834 compose->gtkaspell);
1836 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1838 quote_fmt_scan_string(tmp);
1841 buf = quote_fmt_get_buffer();
1843 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1845 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1846 quote_fmt_reset_vartable();
1847 quote_fmtlex_destroy();
1850 procmsg_msginfo_free(&full_msginfo);
1853 textview = GTK_TEXT_VIEW(compose->text);
1854 textbuf = gtk_text_view_get_buffer(textview);
1855 compose_create_tags(textview, compose);
1857 undo_block(compose->undostruct);
1861 msgfile = procmsg_get_message_file(msginfo);
1862 if (!is_file_exist(msgfile))
1863 g_warning("%s: file does not exist", msgfile);
1865 compose_attach_append(compose, msgfile, msgfile,
1866 "message/rfc822", NULL);
1870 const gchar *qmark = NULL;
1871 const gchar *body_fmt = NULL;
1872 MsgInfo *full_msginfo;
1874 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1876 full_msginfo = procmsg_msginfo_copy(msginfo);
1878 /* use the forward format of folder (if enabled), or the account's one
1879 (if enabled) or fallback to the global forward format, which is always
1880 enabled (even if empty), and use the relevant quotemark */
1881 if (msginfo->folder && msginfo->folder->prefs &&
1882 msginfo->folder->prefs->forward_with_format) {
1883 qmark = msginfo->folder->prefs->forward_quotemark;
1884 body_fmt = msginfo->folder->prefs->forward_body_format;
1886 } else if (account->forward_with_format) {
1887 qmark = account->forward_quotemark;
1888 body_fmt = account->forward_body_format;
1891 qmark = prefs_common.fw_quotemark;
1892 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1893 body_fmt = gettext(prefs_common.fw_quotefmt);
1898 /* empty quotemark is not allowed */
1899 if (qmark == NULL || *qmark == '\0')
1902 compose_quote_fmt(compose, full_msginfo,
1903 body_fmt, qmark, body, FALSE, TRUE,
1904 _("The body of the \"Forward\" template has an error at line %d."));
1905 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1906 quote_fmt_reset_vartable();
1907 compose_attach_parts(compose, msginfo);
1909 procmsg_msginfo_free(&full_msginfo);
1912 SIGNAL_BLOCK(textbuf);
1914 if (account->auto_sig)
1915 compose_insert_sig(compose, FALSE);
1917 compose_wrap_all(compose);
1920 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1921 gtkaspell_highlight_all(compose->gtkaspell);
1922 gtkaspell_unblock_check(compose->gtkaspell);
1924 SIGNAL_UNBLOCK(textbuf);
1926 cursor_pos = quote_fmt_get_cursor_pos();
1927 if (cursor_pos == -1)
1928 gtk_widget_grab_focus(compose->header_last->entry);
1930 gtk_widget_grab_focus(compose->text);
1932 if (!no_extedit && prefs_common.auto_exteditor)
1933 compose_exec_ext_editor(compose);
1936 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1937 gchar *folderidentifier;
1939 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1940 folderidentifier = folder_item_get_identifier(msginfo->folder);
1941 compose_set_save_to(compose, folderidentifier);
1942 g_free(folderidentifier);
1945 undo_unblock(compose->undostruct);
1947 compose->modified = FALSE;
1948 compose_set_title(compose);
1950 compose->updating = FALSE;
1951 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1952 SCROLL_TO_CURSOR(compose);
1954 if (compose->deferred_destroy) {
1955 compose_destroy(compose);
1959 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1964 #undef INSERT_FW_HEADER
1966 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1969 GtkTextView *textview;
1970 GtkTextBuffer *textbuf;
1974 gboolean single_mail = TRUE;
1976 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1978 if (g_slist_length(msginfo_list) > 1)
1979 single_mail = FALSE;
1981 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1982 if (((MsgInfo *)msginfo->data)->folder == NULL)
1985 /* guess account from first selected message */
1987 !(account = compose_find_account(msginfo_list->data)))
1988 account = cur_account;
1990 cm_return_val_if_fail(account != NULL, NULL);
1992 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1993 if (msginfo->data) {
1994 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1995 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1999 if (msginfo_list == NULL || msginfo_list->data == NULL) {
2000 g_warning("no msginfo_list");
2004 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
2006 compose->updating = TRUE;
2008 /* override from name according to folder properties */
2009 if (msginfo_list->data) {
2010 MsgInfo *msginfo = msginfo_list->data;
2012 if (msginfo->folder && msginfo->folder->prefs &&
2013 msginfo->folder->prefs->forward_with_format &&
2014 msginfo->folder->prefs->forward_override_from_format &&
2015 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2020 /* decode \-escape sequences in the internal representation of the quote format */
2021 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2022 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2025 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2026 compose->gtkaspell);
2028 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2030 quote_fmt_scan_string(tmp);
2033 buf = quote_fmt_get_buffer();
2035 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2037 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2038 quote_fmt_reset_vartable();
2039 quote_fmtlex_destroy();
2045 textview = GTK_TEXT_VIEW(compose->text);
2046 textbuf = gtk_text_view_get_buffer(textview);
2047 compose_create_tags(textview, compose);
2049 undo_block(compose->undostruct);
2050 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2051 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2053 if (!is_file_exist(msgfile))
2054 g_warning("%s: file does not exist", msgfile);
2056 compose_attach_append(compose, msgfile, msgfile,
2057 "message/rfc822", NULL);
2062 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2063 if (info->subject && *info->subject) {
2064 gchar *buf, *buf2, *p;
2066 buf = p = g_strdup(info->subject);
2067 p += subject_get_prefix_length(p);
2068 memmove(buf, p, strlen(p) + 1);
2070 buf2 = g_strdup_printf("Fw: %s", buf);
2071 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2077 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2078 _("Fw: multiple emails"));
2081 SIGNAL_BLOCK(textbuf);
2083 if (account->auto_sig)
2084 compose_insert_sig(compose, FALSE);
2086 compose_wrap_all(compose);
2088 SIGNAL_UNBLOCK(textbuf);
2090 gtk_text_buffer_get_start_iter(textbuf, &iter);
2091 gtk_text_buffer_place_cursor(textbuf, &iter);
2093 if (prefs_common.auto_exteditor)
2094 compose_exec_ext_editor(compose);
2096 gtk_widget_grab_focus(compose->header_last->entry);
2097 undo_unblock(compose->undostruct);
2098 compose->modified = FALSE;
2099 compose_set_title(compose);
2101 compose->updating = FALSE;
2102 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2103 SCROLL_TO_CURSOR(compose);
2105 if (compose->deferred_destroy) {
2106 compose_destroy(compose);
2110 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2115 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2117 GtkTextIter start = *iter;
2118 GtkTextIter end_iter;
2119 int start_pos = gtk_text_iter_get_offset(&start);
2121 if (!compose->account->sig_sep)
2124 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2125 start_pos+strlen(compose->account->sig_sep));
2127 /* check sig separator */
2128 str = gtk_text_iter_get_text(&start, &end_iter);
2129 if (!strcmp(str, compose->account->sig_sep)) {
2131 /* check end of line (\n) */
2132 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2133 start_pos+strlen(compose->account->sig_sep));
2134 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2135 start_pos+strlen(compose->account->sig_sep)+1);
2136 tmp = gtk_text_iter_get_text(&start, &end_iter);
2137 if (!strcmp(tmp,"\n")) {
2149 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2151 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2152 Compose *compose = (Compose *)data;
2153 FolderItem *old_item = NULL;
2154 FolderItem *new_item = NULL;
2155 gchar *old_id, *new_id;
2157 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2158 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2161 old_item = hookdata->item;
2162 new_item = hookdata->item2;
2164 old_id = folder_item_get_identifier(old_item);
2165 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2167 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2168 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2169 compose->targetinfo->folder = new_item;
2172 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2173 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2174 compose->replyinfo->folder = new_item;
2177 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2178 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2179 compose->fwdinfo->folder = new_item;
2187 static void compose_colorize_signature(Compose *compose)
2189 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2191 GtkTextIter end_iter;
2192 gtk_text_buffer_get_start_iter(buffer, &iter);
2193 while (gtk_text_iter_forward_line(&iter))
2194 if (compose_is_sig_separator(compose, buffer, &iter)) {
2195 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2196 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2200 #define BLOCK_WRAP() { \
2201 prev_autowrap = compose->autowrap; \
2202 buffer = gtk_text_view_get_buffer( \
2203 GTK_TEXT_VIEW(compose->text)); \
2204 compose->autowrap = FALSE; \
2206 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2207 G_CALLBACK(compose_changed_cb), \
2209 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2210 G_CALLBACK(text_inserted), \
2213 #define UNBLOCK_WRAP() { \
2214 compose->autowrap = prev_autowrap; \
2215 if (compose->autowrap) { \
2216 gint old = compose->draft_timeout_tag; \
2217 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2218 compose_wrap_all(compose); \
2219 compose->draft_timeout_tag = old; \
2222 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2223 G_CALLBACK(compose_changed_cb), \
2225 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2226 G_CALLBACK(text_inserted), \
2230 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2232 Compose *compose = NULL;
2233 PrefsAccount *account = NULL;
2234 GtkTextView *textview;
2235 GtkTextBuffer *textbuf;
2239 gboolean use_signing = FALSE;
2240 gboolean use_encryption = FALSE;
2241 gchar *privacy_system = NULL;
2242 int priority = PRIORITY_NORMAL;
2243 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2244 gboolean autowrap = prefs_common.autowrap;
2245 gboolean autoindent = prefs_common.auto_indent;
2246 HeaderEntry *manual_headers = NULL;
2248 cm_return_val_if_fail(msginfo != NULL, NULL);
2249 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2251 if (compose_put_existing_to_front(msginfo)) {
2255 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2256 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2257 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2258 gchar *queueheader_buf = NULL;
2261 /* Select Account from queue headers */
2262 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2263 "X-Claws-Account-Id:")) {
2264 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2265 account = account_find_from_id(id);
2266 g_free(queueheader_buf);
2268 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2269 "X-Sylpheed-Account-Id:")) {
2270 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2271 account = account_find_from_id(id);
2272 g_free(queueheader_buf);
2274 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2276 id = atoi(&queueheader_buf[strlen("NAID:")]);
2277 account = account_find_from_id(id);
2278 g_free(queueheader_buf);
2280 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2282 id = atoi(&queueheader_buf[strlen("MAID:")]);
2283 account = account_find_from_id(id);
2284 g_free(queueheader_buf);
2286 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2288 account = account_find_from_address(queueheader_buf, FALSE);
2289 g_free(queueheader_buf);
2291 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2293 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2294 use_signing = param;
2295 g_free(queueheader_buf);
2297 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2298 "X-Sylpheed-Sign:")) {
2299 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2300 use_signing = param;
2301 g_free(queueheader_buf);
2303 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2304 "X-Claws-Encrypt:")) {
2305 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2306 use_encryption = param;
2307 g_free(queueheader_buf);
2309 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2310 "X-Sylpheed-Encrypt:")) {
2311 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2312 use_encryption = param;
2313 g_free(queueheader_buf);
2315 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2316 "X-Claws-Auto-Wrapping:")) {
2317 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2319 g_free(queueheader_buf);
2321 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2322 "X-Claws-Auto-Indent:")) {
2323 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2325 g_free(queueheader_buf);
2327 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2328 "X-Claws-Privacy-System:")) {
2329 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2330 g_free(queueheader_buf);
2332 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2333 "X-Sylpheed-Privacy-System:")) {
2334 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2335 g_free(queueheader_buf);
2337 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2339 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2341 g_free(queueheader_buf);
2343 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2345 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2346 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2347 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2348 if (orig_item != NULL) {
2349 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2353 g_free(queueheader_buf);
2355 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2357 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2358 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2359 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2360 if (orig_item != NULL) {
2361 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2365 g_free(queueheader_buf);
2367 /* Get manual headers */
2368 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2369 "X-Claws-Manual-Headers:")) {
2370 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2371 if (listmh && *listmh != '\0') {
2372 debug_print("Got manual headers: %s\n", listmh);
2373 manual_headers = procheader_entries_from_str(listmh);
2376 g_free(queueheader_buf);
2379 account = msginfo->folder->folder->account;
2382 if (!account && prefs_common.reedit_account_autosel) {
2384 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2385 extract_address(from);
2386 account = account_find_from_address(from, FALSE);
2391 account = cur_account;
2393 cm_return_val_if_fail(account != NULL, NULL);
2395 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2397 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2399 compose->autowrap = autowrap;
2400 compose->replyinfo = replyinfo;
2401 compose->fwdinfo = fwdinfo;
2403 compose->updating = TRUE;
2404 compose->priority = priority;
2406 if (privacy_system != NULL) {
2407 compose->privacy_system = privacy_system;
2408 compose_use_signing(compose, use_signing);
2409 compose_use_encryption(compose, use_encryption);
2410 compose_update_privacy_system_menu_item(compose, FALSE);
2412 activate_privacy_system(compose, account, FALSE);
2415 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2417 compose_extract_original_charset(compose);
2419 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2420 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2421 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2422 gchar *queueheader_buf = NULL;
2424 /* Set message save folder */
2425 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2426 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2427 compose_set_save_to(compose, &queueheader_buf[4]);
2428 g_free(queueheader_buf);
2430 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2431 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2433 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2435 g_free(queueheader_buf);
2439 if (compose_parse_header(compose, msginfo) < 0) {
2440 compose->updating = FALSE;
2441 compose_destroy(compose);
2444 compose_reedit_set_entry(compose, msginfo);
2446 textview = GTK_TEXT_VIEW(compose->text);
2447 textbuf = gtk_text_view_get_buffer(textview);
2448 compose_create_tags(textview, compose);
2450 mark = gtk_text_buffer_get_insert(textbuf);
2451 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2453 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2454 G_CALLBACK(compose_changed_cb),
2457 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2458 fp = procmime_get_first_encrypted_text_content(msginfo);
2460 compose_force_encryption(compose, account, TRUE, NULL);
2463 fp = procmime_get_first_text_content(msginfo);
2466 g_warning("Can't get text part");
2470 gchar buf[BUFFSIZE];
2471 gboolean prev_autowrap;
2472 GtkTextBuffer *buffer;
2474 while (fgets(buf, sizeof(buf), fp) != NULL) {
2476 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2482 compose_attach_parts(compose, msginfo);
2484 compose_colorize_signature(compose);
2486 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2487 G_CALLBACK(compose_changed_cb),
2490 if (manual_headers != NULL) {
2491 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2492 procheader_entries_free(manual_headers);
2493 compose->updating = FALSE;
2494 compose_destroy(compose);
2497 procheader_entries_free(manual_headers);
2500 gtk_widget_grab_focus(compose->text);
2502 if (prefs_common.auto_exteditor) {
2503 compose_exec_ext_editor(compose);
2505 compose->modified = FALSE;
2506 compose_set_title(compose);
2508 compose->updating = FALSE;
2509 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2510 SCROLL_TO_CURSOR(compose);
2512 if (compose->deferred_destroy) {
2513 compose_destroy(compose);
2517 compose->sig_str = account_get_signature_str(compose->account);
2519 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2524 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2531 cm_return_val_if_fail(msginfo != NULL, NULL);
2534 account = account_get_reply_account(msginfo,
2535 prefs_common.reply_account_autosel);
2536 cm_return_val_if_fail(account != NULL, NULL);
2538 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2540 compose->updating = TRUE;
2542 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2543 compose->replyinfo = NULL;
2544 compose->fwdinfo = NULL;
2546 compose_show_first_last_header(compose, TRUE);
2548 gtk_widget_grab_focus(compose->header_last->entry);
2550 filename = procmsg_get_message_file(msginfo);
2552 if (filename == NULL) {
2553 compose->updating = FALSE;
2554 compose_destroy(compose);
2559 compose->redirect_filename = filename;
2561 /* Set save folder */
2562 item = msginfo->folder;
2563 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2564 gchar *folderidentifier;
2566 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2567 folderidentifier = folder_item_get_identifier(item);
2568 compose_set_save_to(compose, folderidentifier);
2569 g_free(folderidentifier);
2572 compose_attach_parts(compose, msginfo);
2574 if (msginfo->subject)
2575 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2577 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2579 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2580 _("The body of the \"Redirect\" template has an error at line %d."));
2581 quote_fmt_reset_vartable();
2582 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2584 compose_colorize_signature(compose);
2587 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2588 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2589 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2592 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2594 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2596 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2597 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2598 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2599 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2601 if (compose->toolbar->draft_btn)
2602 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2603 if (compose->toolbar->insert_btn)
2604 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2605 if (compose->toolbar->attach_btn)
2606 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2607 if (compose->toolbar->sig_btn)
2608 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2609 if (compose->toolbar->exteditor_btn)
2610 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2611 if (compose->toolbar->linewrap_current_btn)
2612 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2613 if (compose->toolbar->linewrap_all_btn)
2614 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2616 compose->modified = FALSE;
2617 compose_set_title(compose);
2618 compose->updating = FALSE;
2619 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2620 SCROLL_TO_CURSOR(compose);
2622 if (compose->deferred_destroy) {
2623 compose_destroy(compose);
2627 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2632 const GList *compose_get_compose_list(void)
2634 return compose_list;
2637 void compose_entry_append(Compose *compose, const gchar *address,
2638 ComposeEntryType type, ComposePrefType pref_type)
2640 const gchar *header;
2642 gboolean in_quote = FALSE;
2643 if (!address || *address == '\0') return;
2650 header = N_("Bcc:");
2652 case COMPOSE_REPLYTO:
2653 header = N_("Reply-To:");
2655 case COMPOSE_NEWSGROUPS:
2656 header = N_("Newsgroups:");
2658 case COMPOSE_FOLLOWUPTO:
2659 header = N_( "Followup-To:");
2661 case COMPOSE_INREPLYTO:
2662 header = N_( "In-Reply-To:");
2669 header = prefs_common_translated_header_name(header);
2671 cur = begin = (gchar *)address;
2673 /* we separate the line by commas, but not if we're inside a quoted
2675 while (*cur != '\0') {
2677 in_quote = !in_quote;
2678 if (*cur == ',' && !in_quote) {
2679 gchar *tmp = g_strdup(begin);
2681 tmp[cur-begin]='\0';
2684 while (*tmp == ' ' || *tmp == '\t')
2686 compose_add_header_entry(compose, header, tmp, pref_type);
2687 compose_entry_indicate(compose, tmp);
2694 gchar *tmp = g_strdup(begin);
2696 tmp[cur-begin]='\0';
2697 while (*tmp == ' ' || *tmp == '\t')
2699 compose_add_header_entry(compose, header, tmp, pref_type);
2700 compose_entry_indicate(compose, tmp);
2705 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2710 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2711 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2712 if (gtk_entry_get_text(entry) &&
2713 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2714 gtk_widget_modify_base(
2715 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2716 GTK_STATE_NORMAL, &default_header_bgcolor);
2717 gtk_widget_modify_text(
2718 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2719 GTK_STATE_NORMAL, &default_header_color);
2724 void compose_toolbar_cb(gint action, gpointer data)
2726 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2727 Compose *compose = (Compose*)toolbar_item->parent;
2729 cm_return_if_fail(compose != NULL);
2733 compose_send_cb(NULL, compose);
2736 compose_send_later_cb(NULL, compose);
2739 compose_draft(compose, COMPOSE_QUIT_EDITING);
2742 compose_insert_file_cb(NULL, compose);
2745 compose_attach_cb(NULL, compose);
2748 compose_insert_sig(compose, FALSE);
2751 compose_insert_sig(compose, TRUE);
2754 compose_ext_editor_cb(NULL, compose);
2756 case A_LINEWRAP_CURRENT:
2757 compose_beautify_paragraph(compose, NULL, TRUE);
2759 case A_LINEWRAP_ALL:
2760 compose_wrap_all_full(compose, TRUE);
2763 compose_address_cb(NULL, compose);
2766 case A_CHECK_SPELLING:
2767 compose_check_all(NULL, compose);
2770 case A_PRIVACY_SIGN:
2772 case A_PRIVACY_ENCRYPT:
2779 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2784 gchar *subject = NULL;
2788 gchar **attach = NULL;
2789 gchar *inreplyto = NULL;
2790 MailField mfield = NO_FIELD_PRESENT;
2792 /* get mailto parts but skip from */
2793 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2796 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2797 mfield = TO_FIELD_PRESENT;
2800 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2802 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2804 if (!g_utf8_validate (subject, -1, NULL)) {
2805 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2806 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2809 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2811 mfield = SUBJECT_FIELD_PRESENT;
2814 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2815 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2818 gboolean prev_autowrap = compose->autowrap;
2820 compose->autowrap = FALSE;
2822 mark = gtk_text_buffer_get_insert(buffer);
2823 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2825 if (!g_utf8_validate (body, -1, NULL)) {
2826 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2827 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2830 gtk_text_buffer_insert(buffer, &iter, body, -1);
2832 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2834 compose->autowrap = prev_autowrap;
2835 if (compose->autowrap)
2836 compose_wrap_all(compose);
2837 mfield = BODY_FIELD_PRESENT;
2841 gint i = 0, att = 0;
2842 gchar *warn_files = NULL;
2843 while (attach[i] != NULL) {
2844 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2845 if (utf8_filename) {
2846 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2847 gchar *tmp = g_strdup_printf("%s%s\n",
2848 warn_files?warn_files:"",
2854 g_free(utf8_filename);
2856 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2861 alertpanel_notice(ngettext(
2862 "The following file has been attached: \n%s",
2863 "The following files have been attached: \n%s", att), warn_files);
2868 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2881 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2883 static HeaderEntry hentry[] = {
2884 {"Reply-To:", NULL, TRUE },
2885 {"Cc:", NULL, TRUE },
2886 {"References:", NULL, FALSE },
2887 {"Bcc:", NULL, TRUE },
2888 {"Newsgroups:", NULL, TRUE },
2889 {"Followup-To:", NULL, TRUE },
2890 {"List-Post:", NULL, FALSE },
2891 {"X-Priority:", NULL, FALSE },
2892 {NULL, NULL, FALSE }
2909 cm_return_val_if_fail(msginfo != NULL, -1);
2911 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2912 procheader_get_header_fields(fp, hentry);
2915 if (hentry[H_REPLY_TO].body != NULL) {
2916 if (hentry[H_REPLY_TO].body[0] != '\0') {
2918 conv_unmime_header(hentry[H_REPLY_TO].body,
2921 g_free(hentry[H_REPLY_TO].body);
2922 hentry[H_REPLY_TO].body = NULL;
2924 if (hentry[H_CC].body != NULL) {
2925 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2926 g_free(hentry[H_CC].body);
2927 hentry[H_CC].body = NULL;
2929 if (hentry[H_REFERENCES].body != NULL) {
2930 if (compose->mode == COMPOSE_REEDIT)
2931 compose->references = hentry[H_REFERENCES].body;
2933 compose->references = compose_parse_references
2934 (hentry[H_REFERENCES].body, msginfo->msgid);
2935 g_free(hentry[H_REFERENCES].body);
2937 hentry[H_REFERENCES].body = NULL;
2939 if (hentry[H_BCC].body != NULL) {
2940 if (compose->mode == COMPOSE_REEDIT)
2942 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2943 g_free(hentry[H_BCC].body);
2944 hentry[H_BCC].body = NULL;
2946 if (hentry[H_NEWSGROUPS].body != NULL) {
2947 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2948 hentry[H_NEWSGROUPS].body = NULL;
2950 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2951 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2952 compose->followup_to =
2953 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2956 g_free(hentry[H_FOLLOWUP_TO].body);
2957 hentry[H_FOLLOWUP_TO].body = NULL;
2959 if (hentry[H_LIST_POST].body != NULL) {
2960 gchar *to = NULL, *start = NULL;
2962 extract_address(hentry[H_LIST_POST].body);
2963 if (hentry[H_LIST_POST].body[0] != '\0') {
2964 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2966 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2967 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2970 g_free(compose->ml_post);
2971 compose->ml_post = to;
2974 g_free(hentry[H_LIST_POST].body);
2975 hentry[H_LIST_POST].body = NULL;
2978 /* CLAWS - X-Priority */
2979 if (compose->mode == COMPOSE_REEDIT)
2980 if (hentry[H_X_PRIORITY].body != NULL) {
2983 priority = atoi(hentry[H_X_PRIORITY].body);
2984 g_free(hentry[H_X_PRIORITY].body);
2986 hentry[H_X_PRIORITY].body = NULL;
2988 if (priority < PRIORITY_HIGHEST ||
2989 priority > PRIORITY_LOWEST)
2990 priority = PRIORITY_NORMAL;
2992 compose->priority = priority;
2995 if (compose->mode == COMPOSE_REEDIT) {
2996 if (msginfo->inreplyto && *msginfo->inreplyto)
2997 compose->inreplyto = g_strdup(msginfo->inreplyto);
2999 if (msginfo->msgid && *msginfo->msgid &&
3000 compose->folder != NULL &&
3001 compose->folder->stype == F_DRAFT)
3002 compose->msgid = g_strdup(msginfo->msgid);
3004 if (msginfo->msgid && *msginfo->msgid)
3005 compose->inreplyto = g_strdup(msginfo->msgid);
3007 if (!compose->references) {
3008 if (msginfo->msgid && *msginfo->msgid) {
3009 if (msginfo->inreplyto && *msginfo->inreplyto)
3010 compose->references =
3011 g_strdup_printf("<%s>\n\t<%s>",
3015 compose->references =
3016 g_strconcat("<", msginfo->msgid, ">",
3018 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3019 compose->references =
3020 g_strconcat("<", msginfo->inreplyto, ">",
3029 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3034 cm_return_val_if_fail(msginfo != NULL, -1);
3036 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3037 procheader_get_header_fields(fp, entries);
3041 while (he != NULL && he->name != NULL) {
3043 GtkListStore *model = NULL;
3045 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3046 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3047 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3048 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3049 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3056 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3058 GSList *ref_id_list, *cur;
3062 ref_id_list = references_list_append(NULL, ref);
3063 if (!ref_id_list) return NULL;
3064 if (msgid && *msgid)
3065 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3070 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3071 /* "<" + Message-ID + ">" + CR+LF+TAB */
3072 len += strlen((gchar *)cur->data) + 5;
3074 if (len > MAX_REFERENCES_LEN) {
3075 /* remove second message-ID */
3076 if (ref_id_list && ref_id_list->next &&
3077 ref_id_list->next->next) {
3078 g_free(ref_id_list->next->data);
3079 ref_id_list = g_slist_remove
3080 (ref_id_list, ref_id_list->next->data);
3082 slist_free_strings_full(ref_id_list);
3089 new_ref = g_string_new("");
3090 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3091 if (new_ref->len > 0)
3092 g_string_append(new_ref, "\n\t");
3093 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3096 slist_free_strings_full(ref_id_list);
3098 new_ref_str = new_ref->str;
3099 g_string_free(new_ref, FALSE);
3104 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3105 const gchar *fmt, const gchar *qmark,
3106 const gchar *body, gboolean rewrap,
3107 gboolean need_unescape,
3108 const gchar *err_msg)
3110 MsgInfo* dummyinfo = NULL;
3111 gchar *quote_str = NULL;
3113 gboolean prev_autowrap;
3114 const gchar *trimmed_body = body;
3115 gint cursor_pos = -1;
3116 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3117 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3122 SIGNAL_BLOCK(buffer);
3125 dummyinfo = compose_msginfo_new_from_compose(compose);
3126 msginfo = dummyinfo;
3129 if (qmark != NULL) {
3131 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3132 compose->gtkaspell);
3134 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3136 quote_fmt_scan_string(qmark);
3139 buf = quote_fmt_get_buffer();
3142 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3144 Xstrdup_a(quote_str, buf, goto error)
3147 if (fmt && *fmt != '\0') {
3150 while (*trimmed_body == '\n')
3154 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,