2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2018 the Claws Mail team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
110 #include "file-utils.h"
113 #include "password.h"
114 #include "ldapserver.h"
128 #define N_ATTACH_COLS (N_COL_COLUMNS)
132 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
141 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
142 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
143 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
144 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
145 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
146 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
147 } ComposeCallAdvancedAction;
151 PRIORITY_HIGHEST = 1,
160 COMPOSE_INSERT_SUCCESS,
161 COMPOSE_INSERT_READ_ERROR,
162 COMPOSE_INSERT_INVALID_CHARACTER,
163 COMPOSE_INSERT_NO_FILE
164 } ComposeInsertResult;
168 COMPOSE_WRITE_FOR_SEND,
169 COMPOSE_WRITE_FOR_STORE
174 COMPOSE_QUOTE_FORCED,
181 SUBJECT_FIELD_PRESENT,
186 #define B64_LINE_SIZE 57
187 #define B64_BUFFSIZE 77
189 #define MAX_REFERENCES_LEN 999
191 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
192 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
194 static GdkColor default_header_bgcolor = {
201 static GdkColor default_header_color = {
208 static GList *compose_list = NULL;
209 static GSList *extra_headers = NULL;
211 static Compose *compose_generic_new (PrefsAccount *account,
215 GList *listAddress );
217 static Compose *compose_create (PrefsAccount *account,
222 static void compose_entry_indicate (Compose *compose,
223 const gchar *address);
224 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
225 ComposeQuoteMode quote_mode,
229 static Compose *compose_forward_multiple (PrefsAccount *account,
230 GSList *msginfo_list);
231 static Compose *compose_reply (MsgInfo *msginfo,
232 ComposeQuoteMode quote_mode,
237 static Compose *compose_reply_mode (ComposeMode mode,
238 GSList *msginfo_list,
240 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
241 static void compose_update_privacy_systems_menu(Compose *compose);
243 static GtkWidget *compose_account_option_menu_create
245 static void compose_set_out_encoding (Compose *compose);
246 static void compose_set_template_menu (Compose *compose);
247 static void compose_destroy (Compose *compose);
249 static MailField compose_entries_set (Compose *compose,
251 ComposeEntryType to_type);
252 static gint compose_parse_header (Compose *compose,
254 static gint compose_parse_manual_headers (Compose *compose,
256 HeaderEntry *entries);
257 static gchar *compose_parse_references (const gchar *ref,
260 static gchar *compose_quote_fmt (Compose *compose,
266 gboolean need_unescape,
267 const gchar *err_msg);
269 static void compose_reply_set_entry (Compose *compose,
275 followup_and_reply_to);
276 static void compose_reedit_set_entry (Compose *compose,
279 static void compose_insert_sig (Compose *compose,
281 static ComposeInsertResult compose_insert_file (Compose *compose,
284 static gboolean compose_attach_append (Compose *compose,
287 const gchar *content_type,
288 const gchar *charset);
289 static void compose_attach_parts (Compose *compose,
292 static gboolean compose_beautify_paragraph (Compose *compose,
293 GtkTextIter *par_iter,
295 static void compose_wrap_all (Compose *compose);
296 static void compose_wrap_all_full (Compose *compose,
299 static void compose_set_title (Compose *compose);
300 static void compose_select_account (Compose *compose,
301 PrefsAccount *account,
304 static PrefsAccount *compose_current_mail_account(void);
305 /* static gint compose_send (Compose *compose); */
306 static gboolean compose_check_for_valid_recipient
308 static gboolean compose_check_entries (Compose *compose,
309 gboolean check_everything);
310 static gint compose_write_to_file (Compose *compose,
313 gboolean attach_parts);
314 static gint compose_write_body_to_file (Compose *compose,
316 static gint compose_remove_reedit_target (Compose *compose,
318 static void compose_remove_draft (Compose *compose);
319 static ComposeQueueResult compose_queue_sub (Compose *compose,
323 gboolean perform_checks,
324 gboolean remove_reedit_target);
325 static int compose_add_attachments (Compose *compose,
327 static gchar *compose_get_header (Compose *compose);
328 static gchar *compose_get_manual_headers_info (Compose *compose);
330 static void compose_convert_header (Compose *compose,
335 gboolean addr_field);
337 static void compose_attach_info_free (AttachInfo *ainfo);
338 static void compose_attach_remove_selected (GtkAction *action,
341 static void compose_template_apply (Compose *compose,
344 static void compose_attach_property (GtkAction *action,
346 static void compose_attach_property_create (gboolean *cancelled);
347 static void attach_property_ok (GtkWidget *widget,
348 gboolean *cancelled);
349 static void attach_property_cancel (GtkWidget *widget,
350 gboolean *cancelled);
351 static gint attach_property_delete_event (GtkWidget *widget,
353 gboolean *cancelled);
354 static gboolean attach_property_key_pressed (GtkWidget *widget,
356 gboolean *cancelled);
358 static void compose_exec_ext_editor (Compose *compose);
360 static gint compose_exec_ext_editor_real (const gchar *file,
361 GdkNativeWindow socket_wid);
362 static gboolean compose_ext_editor_kill (Compose *compose);
363 static gboolean compose_input_cb (GIOChannel *source,
364 GIOCondition condition,
366 static void compose_set_ext_editor_sensitive (Compose *compose,
368 static gboolean compose_get_ext_editor_cmd_valid();
369 static gboolean compose_get_ext_editor_uses_socket();
370 static gboolean compose_ext_editor_plug_removed_cb
373 #endif /* G_OS_UNIX */
375 static void compose_undo_state_changed (UndoMain *undostruct,
380 static void compose_create_header_entry (Compose *compose);
381 static void compose_add_header_entry (Compose *compose, const gchar *header,
382 gchar *text, ComposePrefType pref_type);
383 static void compose_remove_header_entries(Compose *compose);
385 static void compose_update_priority_menu_item(Compose * compose);
387 static void compose_spell_menu_changed (void *data);
388 static void compose_dict_changed (void *data);
390 static void compose_add_field_list ( Compose *compose,
391 GList *listAddress );
393 /* callback functions */
395 static void compose_notebook_size_alloc (GtkNotebook *notebook,
396 GtkAllocation *allocation,
398 static gboolean compose_edit_size_alloc (GtkEditable *widget,
399 GtkAllocation *allocation,
400 GtkSHRuler *shruler);
401 static void account_activated (GtkComboBox *optmenu,
403 static void attach_selected (GtkTreeView *tree_view,
404 GtkTreePath *tree_path,
405 GtkTreeViewColumn *column,
407 static gboolean attach_button_pressed (GtkWidget *widget,
408 GdkEventButton *event,
410 static gboolean attach_key_pressed (GtkWidget *widget,
413 static void compose_send_cb (GtkAction *action, gpointer data);
414 static void compose_send_later_cb (GtkAction *action, gpointer data);
416 static void compose_save_cb (GtkAction *action,
419 static void compose_attach_cb (GtkAction *action,
421 static void compose_insert_file_cb (GtkAction *action,
423 static void compose_insert_sig_cb (GtkAction *action,
425 static void compose_replace_sig_cb (GtkAction *action,
428 static void compose_close_cb (GtkAction *action,
430 static void compose_print_cb (GtkAction *action,
433 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
435 static void compose_address_cb (GtkAction *action,
437 static void about_show_cb (GtkAction *action,
439 static void compose_template_activate_cb(GtkWidget *widget,
442 static void compose_ext_editor_cb (GtkAction *action,
445 static gint compose_delete_cb (GtkWidget *widget,
449 static void compose_undo_cb (GtkAction *action,
451 static void compose_redo_cb (GtkAction *action,
453 static void compose_cut_cb (GtkAction *action,
455 static void compose_copy_cb (GtkAction *action,
457 static void compose_paste_cb (GtkAction *action,
459 static void compose_paste_as_quote_cb (GtkAction *action,
461 static void compose_paste_no_wrap_cb (GtkAction *action,
463 static void compose_paste_wrap_cb (GtkAction *action,
465 static void compose_allsel_cb (GtkAction *action,
468 static void compose_advanced_action_cb (GtkAction *action,
471 static void compose_grab_focus_cb (GtkWidget *widget,
474 static void compose_changed_cb (GtkTextBuffer *textbuf,
477 static void compose_wrap_cb (GtkAction *action,
479 static void compose_wrap_all_cb (GtkAction *action,
481 static void compose_find_cb (GtkAction *action,
483 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
485 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
488 static void compose_toggle_ruler_cb (GtkToggleAction *action,
490 static void compose_toggle_sign_cb (GtkToggleAction *action,
492 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
494 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
495 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
496 static void compose_activate_privacy_system (Compose *compose,
497 PrefsAccount *account,
499 static void compose_apply_folder_privacy_settings(Compose *compose, FolderItem *folder_item);
500 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
502 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
504 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
505 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
506 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
508 static void compose_attach_drag_received_cb (GtkWidget *widget,
509 GdkDragContext *drag_context,
512 GtkSelectionData *data,
516 static void compose_insert_drag_received_cb (GtkWidget *widget,
517 GdkDragContext *drag_context,
520 GtkSelectionData *data,
524 static void compose_header_drag_received_cb (GtkWidget *widget,
525 GdkDragContext *drag_context,
528 GtkSelectionData *data,
533 static gboolean compose_drag_drop (GtkWidget *widget,
534 GdkDragContext *drag_context,
536 guint time, gpointer user_data);
537 static gboolean completion_set_focus_to_subject
542 static void text_inserted (GtkTextBuffer *buffer,
547 static Compose *compose_generic_reply(MsgInfo *msginfo,
548 ComposeQuoteMode quote_mode,
552 gboolean followup_and_reply_to,
555 static void compose_headerentry_changed_cb (GtkWidget *entry,
556 ComposeHeaderEntry *headerentry);
557 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
559 ComposeHeaderEntry *headerentry);
560 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
561 ComposeHeaderEntry *headerentry);
563 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
565 static void compose_allow_user_actions (Compose *compose, gboolean allow);
567 static void compose_nothing_cb (GtkAction *action, gpointer data)
573 static void compose_check_all (GtkAction *action, gpointer data);
574 static void compose_highlight_all (GtkAction *action, gpointer data);
575 static void compose_check_backwards (GtkAction *action, gpointer data);
576 static void compose_check_forwards_go (GtkAction *action, gpointer data);
579 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
581 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
584 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
585 FolderItem *folder_item);
587 static void compose_attach_update_label(Compose *compose);
588 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
589 gboolean respect_default_to);
590 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
591 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
593 static GtkActionEntry compose_popup_entries[] =
595 {"Compose", NULL, "Compose", NULL, NULL, NULL },
596 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
597 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
598 {"Compose/---", NULL, "---", NULL, NULL, NULL },
599 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
602 static GtkActionEntry compose_entries[] =
604 {"Menu", NULL, "Menu", NULL, NULL, NULL },
606 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
607 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
609 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
611 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
612 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
613 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
615 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
616 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
617 {"Message/---", NULL, "---", NULL, NULL, NULL },
619 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
620 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
621 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
622 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
623 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
624 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
625 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
626 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
627 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
628 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
631 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
632 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
633 {"Edit/---", NULL, "---", NULL, NULL, NULL },
635 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
636 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
637 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
639 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
640 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
641 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
642 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
644 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
646 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
647 {"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*/
648 {"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*/
649 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
650 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
651 {"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*/
652 {"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*/
653 {"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*/
654 {"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*/
655 {"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*/
656 {"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*/
657 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
658 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
659 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
660 {"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*/
662 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
663 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
665 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
666 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
667 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
668 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
669 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
672 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
673 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
674 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
675 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
677 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
678 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
682 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
683 {"Options/---", NULL, "---", NULL, NULL, NULL },
684 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
685 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
687 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
688 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
690 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
691 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
692 #define ENC_ACTION(cs_char,c_char,string) \
693 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
695 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
696 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
697 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
698 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
699 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
700 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
701 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
702 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
703 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
706 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
708 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
709 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
710 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
711 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
714 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
717 static GtkToggleActionEntry compose_toggle_entries[] =
719 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
720 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
721 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
722 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
723 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
724 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
725 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
728 static GtkRadioActionEntry compose_radio_rm_entries[] =
730 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
731 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
732 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
733 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
736 static GtkRadioActionEntry compose_radio_prio_entries[] =
738 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
739 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
740 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
741 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
742 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
745 static GtkRadioActionEntry compose_radio_enc_entries[] =
747 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
775 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
776 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
777 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
778 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
779 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
782 static GtkTargetEntry compose_mime_types[] =
784 {"text/uri-list", 0, 0},
785 {"UTF8_STRING", 0, 0},
789 static gboolean compose_put_existing_to_front(MsgInfo *info)
791 const GList *compose_list = compose_get_compose_list();
792 const GList *elem = NULL;
795 for (elem = compose_list; elem != NULL && elem->data != NULL;
797 Compose *c = (Compose*)elem->data;
799 if (!c->targetinfo || !c->targetinfo->msgid ||
803 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
804 gtkut_window_popup(c->window);
812 static GdkColor quote_color1 =
813 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
814 static GdkColor quote_color2 =
815 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_color3 =
817 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
819 static GdkColor quote_bgcolor1 =
820 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
821 static GdkColor quote_bgcolor2 =
822 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
823 static GdkColor quote_bgcolor3 =
824 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
826 static GdkColor signature_color = {
833 static GdkColor uri_color = {
840 static void compose_create_tags(GtkTextView *text, Compose *compose)
842 GtkTextBuffer *buffer;
843 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
845 buffer = gtk_text_view_get_buffer(text);
847 if (prefs_common.enable_color) {
848 /* grab the quote colors, converting from an int to a GdkColor */
849 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL1],
851 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL2],
853 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL3],
855 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL1_BG],
857 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL2_BG],
859 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_QUOTE_LEVEL3_BG],
861 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_SIGNATURE],
863 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_URI],
866 signature_color = quote_color1 = quote_color2 = quote_color3 =
867 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
870 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
871 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
872 "foreground-gdk", "e_color1,
873 "paragraph-background-gdk", "e_bgcolor1,
875 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
876 "foreground-gdk", "e_color2,
877 "paragraph-background-gdk", "e_bgcolor2,
879 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
880 "foreground-gdk", "e_color3,
881 "paragraph-background-gdk", "e_bgcolor3,
884 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
885 "foreground-gdk", "e_color1,
887 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
888 "foreground-gdk", "e_color2,
890 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
891 "foreground-gdk", "e_color3,
895 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
896 "foreground-gdk", &signature_color,
899 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
900 "foreground-gdk", &uri_color,
902 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
903 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
906 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
909 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
912 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
914 return compose_generic_new(account, mailto, item, NULL, NULL);
917 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
919 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
922 #define SCROLL_TO_CURSOR(compose) { \
923 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
924 gtk_text_view_get_buffer( \
925 GTK_TEXT_VIEW(compose->text))); \
926 gtk_text_view_scroll_mark_onscreen( \
927 GTK_TEXT_VIEW(compose->text), \
931 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
934 if (folderidentifier) {
935 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
936 prefs_common.compose_save_to_history = add_history(
937 prefs_common.compose_save_to_history, folderidentifier);
938 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
939 prefs_common.compose_save_to_history);
942 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
943 if (folderidentifier)
944 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
946 gtk_entry_set_text(GTK_ENTRY(entry), "");
949 static gchar *compose_get_save_to(Compose *compose)
952 gchar *result = NULL;
953 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
954 result = gtk_editable_get_chars(entry, 0, -1);
957 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
958 prefs_common.compose_save_to_history = add_history(
959 prefs_common.compose_save_to_history, result);
960 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
961 prefs_common.compose_save_to_history);
966 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
967 GList *attach_files, GList *listAddress )
970 GtkTextView *textview;
971 GtkTextBuffer *textbuf;
973 const gchar *subject_format = NULL;
974 const gchar *body_format = NULL;
975 gchar *mailto_from = NULL;
976 PrefsAccount *mailto_account = NULL;
977 MsgInfo* dummyinfo = NULL;
978 gint cursor_pos = -1;
979 MailField mfield = NO_FIELD_PRESENT;
983 /* check if mailto defines a from */
984 if (mailto && *mailto != '\0') {
985 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
986 /* mailto defines a from, check if we can get account prefs from it,
987 if not, the account prefs will be guessed using other ways, but we'll keep
990 mailto_account = account_find_from_address(mailto_from, TRUE);
991 if (mailto_account == NULL) {
993 Xstrdup_a(tmp_from, mailto_from, return NULL);
994 extract_address(tmp_from);
995 mailto_account = account_find_from_address(tmp_from, TRUE);
999 account = mailto_account;
1002 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1003 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1004 account = account_find_from_id(item->prefs->default_account);
1006 /* if no account prefs set, fallback to the current one */
1007 if (!account) account = cur_account;
1008 cm_return_val_if_fail(account != NULL, NULL);
1010 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1011 compose_apply_folder_privacy_settings(compose, item);
1013 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1014 (account->default_encrypt || account->default_sign))
1015 alertpanel_error(_("You have opted to sign and/or encrypt this "
1016 "message but have not selected a privacy system.\n\n"
1017 "Signing and encrypting have been disabled for this "
1020 /* override from name if mailto asked for it */
1022 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1023 g_free(mailto_from);
1025 /* override from name according to folder properties */
1026 if (item && item->prefs &&
1027 item->prefs->compose_with_format &&
1028 item->prefs->compose_override_from_format &&
1029 *item->prefs->compose_override_from_format != '\0') {
1034 dummyinfo = compose_msginfo_new_from_compose(compose);
1036 /* decode \-escape sequences in the internal representation of the quote format */
1037 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1038 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1041 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1042 compose->gtkaspell);
1044 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1046 quote_fmt_scan_string(tmp);
1049 buf = quote_fmt_get_buffer();
1051 alertpanel_error(_("New message From format error."));
1053 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1054 quote_fmt_reset_vartable();
1055 quote_fmtlex_destroy();
1060 compose->replyinfo = NULL;
1061 compose->fwdinfo = NULL;
1063 textview = GTK_TEXT_VIEW(compose->text);
1064 textbuf = gtk_text_view_get_buffer(textview);
1065 compose_create_tags(textview, compose);
1067 undo_block(compose->undostruct);
1069 compose_set_dictionaries_from_folder_prefs(compose, item);
1072 if (account->auto_sig)
1073 compose_insert_sig(compose, FALSE);
1074 gtk_text_buffer_get_start_iter(textbuf, &iter);
1075 gtk_text_buffer_place_cursor(textbuf, &iter);
1077 if (account->protocol != A_NNTP) {
1078 if (mailto && *mailto != '\0') {
1079 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1082 compose_set_folder_prefs(compose, item, TRUE);
1084 if (item && item->ret_rcpt) {
1085 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1088 if (mailto && *mailto != '\0') {
1089 if (!strchr(mailto, '@'))
1090 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1092 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1093 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1094 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1095 mfield = TO_FIELD_PRESENT;
1098 * CLAWS: just don't allow return receipt request, even if the user
1099 * may want to send an email. simple but foolproof.
1101 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1103 compose_add_field_list( compose, listAddress );
1105 if (item && item->prefs && item->prefs->compose_with_format) {
1106 subject_format = item->prefs->compose_subject_format;
1107 body_format = item->prefs->compose_body_format;
1108 } else if (account->compose_with_format) {
1109 subject_format = account->compose_subject_format;
1110 body_format = account->compose_body_format;
1111 } else if (prefs_common.compose_with_format) {
1112 subject_format = prefs_common.compose_subject_format;
1113 body_format = prefs_common.compose_body_format;
1116 if (subject_format || body_format) {
1119 && *subject_format != '\0' )
1121 gchar *subject = NULL;
1126 dummyinfo = compose_msginfo_new_from_compose(compose);
1128 /* decode \-escape sequences in the internal representation of the quote format */
1129 tmp = g_malloc(strlen(subject_format)+1);
1130 pref_get_unescaped_pref(tmp, subject_format);
1132 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1134 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1135 compose->gtkaspell);
1137 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1139 quote_fmt_scan_string(tmp);
1142 buf = quote_fmt_get_buffer();
1144 alertpanel_error(_("New message subject format error."));
1146 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1147 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1148 quote_fmt_reset_vartable();
1149 quote_fmtlex_destroy();
1153 mfield = SUBJECT_FIELD_PRESENT;
1157 && *body_format != '\0' )
1160 GtkTextBuffer *buffer;
1161 GtkTextIter start, end;
1165 dummyinfo = compose_msginfo_new_from_compose(compose);
1167 text = GTK_TEXT_VIEW(compose->text);
1168 buffer = gtk_text_view_get_buffer(text);
1169 gtk_text_buffer_get_start_iter(buffer, &start);
1170 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1171 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1173 compose_quote_fmt(compose, dummyinfo,
1175 NULL, tmp, FALSE, TRUE,
1176 _("The body of the \"New message\" template has an error at line %d."));
1177 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1178 quote_fmt_reset_vartable();
1182 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1183 gtkaspell_highlight_all(compose->gtkaspell);
1185 mfield = BODY_FIELD_PRESENT;
1189 procmsg_msginfo_free( &dummyinfo );
1195 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1196 ainfo = (AttachInfo *) curr->data;
1198 compose_insert_file(compose, ainfo->file);
1200 compose_attach_append(compose, ainfo->file, ainfo->file,
1201 ainfo->content_type, ainfo->charset);
1205 compose_show_first_last_header(compose, TRUE);
1207 /* Set save folder */
1208 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1209 gchar *folderidentifier;
1211 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1212 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1213 folderidentifier = folder_item_get_identifier(item);
1214 compose_set_save_to(compose, folderidentifier);
1215 g_free(folderidentifier);
1218 /* Place cursor according to provided input (mfield) */
1220 case NO_FIELD_PRESENT:
1221 if (compose->header_last)
1222 gtk_widget_grab_focus(compose->header_last->entry);
1224 case TO_FIELD_PRESENT:
1225 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1227 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1230 gtk_widget_grab_focus(compose->subject_entry);
1232 case SUBJECT_FIELD_PRESENT:
1233 textview = GTK_TEXT_VIEW(compose->text);
1236 textbuf = gtk_text_view_get_buffer(textview);
1239 mark = gtk_text_buffer_get_insert(textbuf);
1240 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1241 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1243 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1244 * only defers where it comes to the variable body
1245 * is not null. If no body is present compose->text
1246 * will be null in which case you cannot place the
1247 * cursor inside the component so. An empty component
1248 * is therefore created before placing the cursor
1250 case BODY_FIELD_PRESENT:
1251 cursor_pos = quote_fmt_get_cursor_pos();
1252 if (cursor_pos == -1)
1253 gtk_widget_grab_focus(compose->header_last->entry);
1255 gtk_widget_grab_focus(compose->text);
1259 undo_unblock(compose->undostruct);
1261 if (prefs_common.auto_exteditor)
1262 compose_exec_ext_editor(compose);
1264 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1266 SCROLL_TO_CURSOR(compose);
1268 compose->modified = FALSE;
1269 compose_set_title(compose);
1271 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1276 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1277 gboolean override_pref, const gchar *system)
1279 const gchar *privacy = NULL;
1281 cm_return_if_fail(compose != NULL);
1282 cm_return_if_fail(account != NULL);
1284 if (privacy_system_can_encrypt(compose->privacy_system) == FALSE ||
1285 (override_pref == FALSE && account->default_encrypt_reply == FALSE))
1288 if (account->default_privacy_system && strlen(account->default_privacy_system))
1289 privacy = account->default_privacy_system;
1293 GSList *privacy_avail = privacy_get_system_ids();
1294 if (privacy_avail && g_slist_length(privacy_avail)) {
1295 privacy = (gchar *)(privacy_avail->data);
1297 g_slist_free_full(privacy_avail, g_free);
1299 if (privacy != NULL) {
1301 g_free(compose->privacy_system);
1302 compose->privacy_system = NULL;
1303 g_free(compose->encdata);
1304 compose->encdata = NULL;
1306 if (compose->privacy_system == NULL)
1307 compose->privacy_system = g_strdup(privacy);
1308 else if (*(compose->privacy_system) == '\0') {
1309 g_free(compose->privacy_system);
1310 g_free(compose->encdata);
1311 compose->encdata = NULL;
1312 compose->privacy_system = g_strdup(privacy);
1314 compose_update_privacy_system_menu_item(compose, FALSE);
1315 compose_use_encryption(compose, TRUE);
1319 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1321 const gchar *privacy = NULL;
1322 if (privacy_system_can_sign(compose->privacy_system) == FALSE)
1325 if (account->default_privacy_system && strlen(account->default_privacy_system))
1326 privacy = account->default_privacy_system;
1330 GSList *privacy_avail = privacy_get_system_ids();
1331 if (privacy_avail && g_slist_length(privacy_avail)) {
1332 privacy = (gchar *)(privacy_avail->data);
1336 if (privacy != NULL) {
1338 g_free(compose->privacy_system);
1339 compose->privacy_system = NULL;
1340 g_free(compose->encdata);
1341 compose->encdata = NULL;
1343 if (compose->privacy_system == NULL)
1344 compose->privacy_system = g_strdup(privacy);
1345 compose_update_privacy_system_menu_item(compose, FALSE);
1346 compose_use_signing(compose, TRUE);
1350 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1354 Compose *compose = NULL;
1356 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1358 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1359 cm_return_val_if_fail(msginfo != NULL, NULL);
1361 list_len = g_slist_length(msginfo_list);
1365 case COMPOSE_REPLY_TO_ADDRESS:
1366 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1367 FALSE, prefs_common.default_reply_list, FALSE, body);
1369 case COMPOSE_REPLY_WITH_QUOTE:
1370 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1371 FALSE, prefs_common.default_reply_list, FALSE, body);
1373 case COMPOSE_REPLY_WITHOUT_QUOTE:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1375 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1377 case COMPOSE_REPLY_TO_SENDER:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1379 FALSE, FALSE, TRUE, body);
1381 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1382 compose = compose_followup_and_reply_to(msginfo,
1383 COMPOSE_QUOTE_CHECK,
1384 FALSE, FALSE, body);
1386 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1387 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1388 FALSE, FALSE, TRUE, body);
1390 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1391 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1392 FALSE, FALSE, TRUE, NULL);
1394 case COMPOSE_REPLY_TO_ALL:
1395 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1396 TRUE, FALSE, FALSE, body);
1398 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1399 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1400 TRUE, FALSE, FALSE, body);
1402 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1403 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1404 TRUE, FALSE, FALSE, NULL);
1406 case COMPOSE_REPLY_TO_LIST:
1407 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1408 FALSE, TRUE, FALSE, body);
1410 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1411 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1412 FALSE, TRUE, FALSE, body);
1414 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1415 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1416 FALSE, TRUE, FALSE, NULL);
1418 case COMPOSE_FORWARD:
1419 if (prefs_common.forward_as_attachment) {
1420 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1423 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1427 case COMPOSE_FORWARD_INLINE:
1428 /* check if we reply to more than one Message */
1429 if (list_len == 1) {
1430 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1433 /* more messages FALL THROUGH */
1434 case COMPOSE_FORWARD_AS_ATTACH:
1435 compose = compose_forward_multiple(NULL, msginfo_list);
1437 case COMPOSE_REDIRECT:
1438 compose = compose_redirect(NULL, msginfo, FALSE);
1441 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1444 if (compose == NULL) {
1445 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1449 compose->rmode = mode;
1450 switch (compose->rmode) {
1452 case COMPOSE_REPLY_WITH_QUOTE:
1453 case COMPOSE_REPLY_WITHOUT_QUOTE:
1454 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1455 debug_print("reply mode Normal\n");
1456 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1457 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1459 case COMPOSE_REPLY_TO_SENDER:
1460 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1461 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1462 debug_print("reply mode Sender\n");
1463 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1465 case COMPOSE_REPLY_TO_ALL:
1466 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1467 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1468 debug_print("reply mode All\n");
1469 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1471 case COMPOSE_REPLY_TO_LIST:
1472 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1473 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1474 debug_print("reply mode List\n");
1475 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1477 case COMPOSE_REPLY_TO_ADDRESS:
1478 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1486 static Compose *compose_reply(MsgInfo *msginfo,
1487 ComposeQuoteMode quote_mode,
1493 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1494 to_sender, FALSE, body);
1497 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1498 ComposeQuoteMode quote_mode,
1503 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1504 to_sender, TRUE, body);
1507 static void compose_extract_original_charset(Compose *compose)
1509 MsgInfo *info = NULL;
1510 if (compose->replyinfo) {
1511 info = compose->replyinfo;
1512 } else if (compose->fwdinfo) {
1513 info = compose->fwdinfo;
1514 } else if (compose->targetinfo) {
1515 info = compose->targetinfo;
1518 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1519 MimeInfo *partinfo = mimeinfo;
1520 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1521 partinfo = procmime_mimeinfo_next(partinfo);
1523 compose->orig_charset =
1524 g_strdup(procmime_mimeinfo_get_parameter(
1525 partinfo, "charset"));
1527 procmime_mimeinfo_free_all(&mimeinfo);
1531 #define SIGNAL_BLOCK(buffer) { \
1532 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1533 G_CALLBACK(compose_changed_cb), \
1535 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1536 G_CALLBACK(text_inserted), \
1540 #define SIGNAL_UNBLOCK(buffer) { \
1541 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1542 G_CALLBACK(compose_changed_cb), \
1544 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1545 G_CALLBACK(text_inserted), \
1549 static Compose *compose_generic_reply(MsgInfo *msginfo,
1550 ComposeQuoteMode quote_mode,
1551 gboolean to_all, gboolean to_ml,
1553 gboolean followup_and_reply_to,
1557 PrefsAccount *account = NULL;
1558 GtkTextView *textview;
1559 GtkTextBuffer *textbuf;
1560 gboolean quote = FALSE;
1561 const gchar *qmark = NULL;
1562 const gchar *body_fmt = NULL;
1563 gchar *s_system = NULL;
1565 cm_return_val_if_fail(msginfo != NULL, NULL);
1566 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1568 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1570 cm_return_val_if_fail(account != NULL, NULL);
1572 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1573 compose_apply_folder_privacy_settings(compose, msginfo->folder);
1575 compose->updating = TRUE;
1577 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1578 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1580 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1581 if (!compose->replyinfo)
1582 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1584 compose_extract_original_charset(compose);
1586 if (msginfo->folder && msginfo->folder->ret_rcpt)
1587 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1589 /* Set save folder */
1590 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1591 gchar *folderidentifier;
1593 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1594 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1595 folderidentifier = folder_item_get_identifier(msginfo->folder);
1596 compose_set_save_to(compose, folderidentifier);
1597 g_free(folderidentifier);
1600 if (compose_parse_header(compose, msginfo) < 0) {
1601 compose->updating = FALSE;
1602 compose_destroy(compose);
1606 /* override from name according to folder properties */
1607 if (msginfo->folder && msginfo->folder->prefs &&
1608 msginfo->folder->prefs->reply_with_format &&
1609 msginfo->folder->prefs->reply_override_from_format &&
1610 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1615 /* decode \-escape sequences in the internal representation of the quote format */
1616 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1617 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1620 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1621 compose->gtkaspell);
1623 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1625 quote_fmt_scan_string(tmp);
1628 buf = quote_fmt_get_buffer();
1630 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1632 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1633 quote_fmt_reset_vartable();
1634 quote_fmtlex_destroy();
1639 textview = (GTK_TEXT_VIEW(compose->text));
1640 textbuf = gtk_text_view_get_buffer(textview);
1641 compose_create_tags(textview, compose);
1643 undo_block(compose->undostruct);
1645 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1646 gtkaspell_block_check(compose->gtkaspell);
1649 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1650 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1651 /* use the reply format of folder (if enabled), or the account's one
1652 (if enabled) or fallback to the global reply format, which is always
1653 enabled (even if empty), and use the relevant quotemark */
1655 if (msginfo->folder && msginfo->folder->prefs &&
1656 msginfo->folder->prefs->reply_with_format) {
1657 qmark = msginfo->folder->prefs->reply_quotemark;
1658 body_fmt = msginfo->folder->prefs->reply_body_format;
1660 } else if (account->reply_with_format) {
1661 qmark = account->reply_quotemark;
1662 body_fmt = account->reply_body_format;
1665 qmark = prefs_common.quotemark;
1666 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1667 body_fmt = gettext(prefs_common.quotefmt);
1674 /* empty quotemark is not allowed */
1675 if (qmark == NULL || *qmark == '\0')
1677 compose_quote_fmt(compose, compose->replyinfo,
1678 body_fmt, qmark, body, FALSE, TRUE,
1679 _("The body of the \"Reply\" template has an error at line %d."));
1680 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1681 quote_fmt_reset_vartable();
1684 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1685 compose_force_encryption(compose, account, FALSE, s_system);
1688 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1689 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1690 compose_force_signing(compose, account, s_system);
1694 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1695 ((account->default_encrypt_reply && MSG_IS_ENCRYPTED(compose->replyinfo->flags)) ||
1696 (account->default_sign_reply && MSG_IS_SIGNED(compose->replyinfo->flags))))
1697 alertpanel_error(_("You have opted to sign and/or encrypt this "
1698 "message but have not selected a privacy system.\n\n"
1699 "Signing and encrypting have been disabled for this "
1702 SIGNAL_BLOCK(textbuf);
1704 if (account->auto_sig)
1705 compose_insert_sig(compose, FALSE);
1707 compose_wrap_all(compose);
1710 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1711 gtkaspell_highlight_all(compose->gtkaspell);
1712 gtkaspell_unblock_check(compose->gtkaspell);
1714 SIGNAL_UNBLOCK(textbuf);
1716 gtk_widget_grab_focus(compose->text);
1718 undo_unblock(compose->undostruct);
1720 if (prefs_common.auto_exteditor)
1721 compose_exec_ext_editor(compose);
1723 compose->modified = FALSE;
1724 compose_set_title(compose);
1726 compose->updating = FALSE;
1727 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1728 SCROLL_TO_CURSOR(compose);
1730 if (compose->deferred_destroy) {
1731 compose_destroy(compose);
1739 #define INSERT_FW_HEADER(var, hdr) \
1740 if (msginfo->var && *msginfo->var) { \
1741 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1742 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1743 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1746 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1747 gboolean as_attach, const gchar *body,
1748 gboolean no_extedit,
1752 GtkTextView *textview;
1753 GtkTextBuffer *textbuf;
1754 gint cursor_pos = -1;
1757 cm_return_val_if_fail(msginfo != NULL, NULL);
1758 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1760 if (!account && !(account = compose_find_account(msginfo)))
1761 account = cur_account;
1763 if (!prefs_common.forward_as_attachment)
1764 mode = COMPOSE_FORWARD_INLINE;
1766 mode = COMPOSE_FORWARD;
1767 compose = compose_create(account, msginfo->folder, mode, batch);
1768 compose_apply_folder_privacy_settings(compose, msginfo->folder);
1770 compose->updating = TRUE;
1771 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1772 if (!compose->fwdinfo)
1773 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1775 compose_extract_original_charset(compose);
1777 if (msginfo->subject && *msginfo->subject) {
1778 gchar *buf, *buf2, *p;
1780 buf = p = g_strdup(msginfo->subject);
1781 p += subject_get_prefix_length(p);
1782 memmove(buf, p, strlen(p) + 1);
1784 buf2 = g_strdup_printf("Fw: %s", buf);
1785 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1791 /* override from name according to folder properties */
1792 if (msginfo->folder && msginfo->folder->prefs &&
1793 msginfo->folder->prefs->forward_with_format &&
1794 msginfo->folder->prefs->forward_override_from_format &&
1795 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1799 MsgInfo *full_msginfo = NULL;
1802 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1804 full_msginfo = procmsg_msginfo_copy(msginfo);
1806 /* decode \-escape sequences in the internal representation of the quote format */
1807 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1808 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1811 gtkaspell_block_check(compose->gtkaspell);
1812 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1813 compose->gtkaspell);
1815 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1817 quote_fmt_scan_string(tmp);
1820 buf = quote_fmt_get_buffer();
1822 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1824 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1825 quote_fmt_reset_vartable();
1826 quote_fmtlex_destroy();
1829 procmsg_msginfo_free(&full_msginfo);
1832 textview = GTK_TEXT_VIEW(compose->text);
1833 textbuf = gtk_text_view_get_buffer(textview);
1834 compose_create_tags(textview, compose);
1836 undo_block(compose->undostruct);
1840 msgfile = procmsg_get_message_file(msginfo);
1841 if (!is_file_exist(msgfile))
1842 g_warning("%s: file does not exist", msgfile);
1844 compose_attach_append(compose, msgfile, msgfile,
1845 "message/rfc822", NULL);
1849 const gchar *qmark = NULL;
1850 const gchar *body_fmt = NULL;
1851 MsgInfo *full_msginfo;
1853 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1855 full_msginfo = procmsg_msginfo_copy(msginfo);
1857 /* use the forward format of folder (if enabled), or the account's one
1858 (if enabled) or fallback to the global forward format, which is always
1859 enabled (even if empty), and use the relevant quotemark */
1860 if (msginfo->folder && msginfo->folder->prefs &&
1861 msginfo->folder->prefs->forward_with_format) {
1862 qmark = msginfo->folder->prefs->forward_quotemark;
1863 body_fmt = msginfo->folder->prefs->forward_body_format;
1865 } else if (account->forward_with_format) {
1866 qmark = account->forward_quotemark;
1867 body_fmt = account->forward_body_format;
1870 qmark = prefs_common.fw_quotemark;
1871 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1872 body_fmt = gettext(prefs_common.fw_quotefmt);
1877 /* empty quotemark is not allowed */
1878 if (qmark == NULL || *qmark == '\0')
1881 compose_quote_fmt(compose, full_msginfo,
1882 body_fmt, qmark, body, FALSE, TRUE,
1883 _("The body of the \"Forward\" template has an error at line %d."));
1884 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1885 quote_fmt_reset_vartable();
1886 compose_attach_parts(compose, msginfo);
1888 procmsg_msginfo_free(&full_msginfo);
1891 SIGNAL_BLOCK(textbuf);
1893 if (account->auto_sig)
1894 compose_insert_sig(compose, FALSE);
1896 compose_wrap_all(compose);
1899 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1900 gtkaspell_highlight_all(compose->gtkaspell);
1901 gtkaspell_unblock_check(compose->gtkaspell);
1903 SIGNAL_UNBLOCK(textbuf);
1905 cursor_pos = quote_fmt_get_cursor_pos();
1906 if (cursor_pos == -1)
1907 gtk_widget_grab_focus(compose->header_last->entry);
1909 gtk_widget_grab_focus(compose->text);
1911 if (!no_extedit && prefs_common.auto_exteditor)
1912 compose_exec_ext_editor(compose);
1915 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1916 gchar *folderidentifier;
1918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1919 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1920 folderidentifier = folder_item_get_identifier(msginfo->folder);
1921 compose_set_save_to(compose, folderidentifier);
1922 g_free(folderidentifier);
1925 undo_unblock(compose->undostruct);
1927 compose->modified = FALSE;
1928 compose_set_title(compose);
1930 compose->updating = FALSE;
1931 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1932 SCROLL_TO_CURSOR(compose);
1934 if (compose->deferred_destroy) {
1935 compose_destroy(compose);
1939 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1944 #undef INSERT_FW_HEADER
1946 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1949 GtkTextView *textview;
1950 GtkTextBuffer *textbuf;
1954 gboolean single_mail = TRUE;
1956 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1958 if (g_slist_length(msginfo_list) > 1)
1959 single_mail = FALSE;
1961 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1962 if (((MsgInfo *)msginfo->data)->folder == NULL)
1965 /* guess account from first selected message */
1967 !(account = compose_find_account(msginfo_list->data)))
1968 account = cur_account;
1970 cm_return_val_if_fail(account != NULL, NULL);
1972 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1973 if (msginfo->data) {
1974 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1975 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1979 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1980 g_warning("no msginfo_list");
1984 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1985 compose_apply_folder_privacy_settings(compose, ((MsgInfo *)msginfo_list->data)->folder);
1987 compose->updating = TRUE;
1989 /* override from name according to folder properties */
1990 if (msginfo_list->data) {
1991 MsgInfo *msginfo = msginfo_list->data;
1993 if (msginfo->folder && msginfo->folder->prefs &&
1994 msginfo->folder->prefs->forward_with_format &&
1995 msginfo->folder->prefs->forward_override_from_format &&
1996 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2001 /* decode \-escape sequences in the internal representation of the quote format */
2002 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2003 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2006 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2007 compose->gtkaspell);
2009 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2011 quote_fmt_scan_string(tmp);
2014 buf = quote_fmt_get_buffer();
2016 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2018 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2019 quote_fmt_reset_vartable();
2020 quote_fmtlex_destroy();
2026 textview = GTK_TEXT_VIEW(compose->text);
2027 textbuf = gtk_text_view_get_buffer(textview);
2028 compose_create_tags(textview, compose);
2030 undo_block(compose->undostruct);
2031 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2032 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2034 if (!is_file_exist(msgfile))
2035 g_warning("%s: file does not exist", msgfile);
2037 compose_attach_append(compose, msgfile, msgfile,
2038 "message/rfc822", NULL);
2043 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2044 if (info->subject && *info->subject) {
2045 gchar *buf, *buf2, *p;
2047 buf = p = g_strdup(info->subject);
2048 p += subject_get_prefix_length(p);
2049 memmove(buf, p, strlen(p) + 1);
2051 buf2 = g_strdup_printf("Fw: %s", buf);
2052 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2058 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2059 _("Fw: multiple emails"));
2062 SIGNAL_BLOCK(textbuf);
2064 if (account->auto_sig)
2065 compose_insert_sig(compose, FALSE);
2067 compose_wrap_all(compose);
2069 SIGNAL_UNBLOCK(textbuf);
2071 gtk_text_buffer_get_start_iter(textbuf, &iter);
2072 gtk_text_buffer_place_cursor(textbuf, &iter);
2074 if (prefs_common.auto_exteditor)
2075 compose_exec_ext_editor(compose);
2077 gtk_widget_grab_focus(compose->header_last->entry);
2078 undo_unblock(compose->undostruct);
2079 compose->modified = FALSE;
2080 compose_set_title(compose);
2082 compose->updating = FALSE;
2083 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2084 SCROLL_TO_CURSOR(compose);
2086 if (compose->deferred_destroy) {
2087 compose_destroy(compose);
2091 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2096 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2098 GtkTextIter start = *iter;
2099 GtkTextIter end_iter;
2100 int start_pos = gtk_text_iter_get_offset(&start);
2102 if (!compose->account->sig_sep)
2105 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2106 start_pos+strlen(compose->account->sig_sep));
2108 /* check sig separator */
2109 str = gtk_text_iter_get_text(&start, &end_iter);
2110 if (!strcmp(str, compose->account->sig_sep)) {
2112 /* check end of line (\n) */
2113 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2114 start_pos+strlen(compose->account->sig_sep));
2115 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2116 start_pos+strlen(compose->account->sig_sep)+1);
2117 tmp = gtk_text_iter_get_text(&start, &end_iter);
2118 if (!strcmp(tmp,"\n")) {
2130 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2132 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2133 Compose *compose = (Compose *)data;
2134 FolderItem *old_item = NULL;
2135 FolderItem *new_item = NULL;
2136 gchar *old_id, *new_id;
2138 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2139 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2142 old_item = hookdata->item;
2143 new_item = hookdata->item2;
2145 old_id = folder_item_get_identifier(old_item);
2146 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2148 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2149 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2150 compose->targetinfo->folder = new_item;
2153 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2154 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2155 compose->replyinfo->folder = new_item;
2158 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2159 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2160 compose->fwdinfo->folder = new_item;
2168 static void compose_colorize_signature(Compose *compose)
2170 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2172 GtkTextIter end_iter;
2173 gtk_text_buffer_get_start_iter(buffer, &iter);
2174 while (gtk_text_iter_forward_line(&iter))
2175 if (compose_is_sig_separator(compose, buffer, &iter)) {
2176 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2177 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2181 #define BLOCK_WRAP() { \
2182 prev_autowrap = compose->autowrap; \
2183 buffer = gtk_text_view_get_buffer( \
2184 GTK_TEXT_VIEW(compose->text)); \
2185 compose->autowrap = FALSE; \
2187 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2188 G_CALLBACK(compose_changed_cb), \
2190 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2191 G_CALLBACK(text_inserted), \
2194 #define UNBLOCK_WRAP() { \
2195 compose->autowrap = prev_autowrap; \
2196 if (compose->autowrap) { \
2197 gint old = compose->draft_timeout_tag; \
2198 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2199 compose_wrap_all(compose); \
2200 compose->draft_timeout_tag = old; \
2203 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2204 G_CALLBACK(compose_changed_cb), \
2206 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2207 G_CALLBACK(text_inserted), \
2211 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2213 Compose *compose = NULL;
2214 PrefsAccount *account = NULL;
2215 GtkTextView *textview;
2216 GtkTextBuffer *textbuf;
2220 gboolean use_signing = FALSE;
2221 gboolean use_encryption = FALSE;
2222 gchar *privacy_system = NULL;
2223 int priority = PRIORITY_NORMAL;
2224 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2225 gboolean autowrap = prefs_common.autowrap;
2226 gboolean autoindent = prefs_common.auto_indent;
2227 HeaderEntry *manual_headers = NULL;
2229 cm_return_val_if_fail(msginfo != NULL, NULL);
2230 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2232 if (compose_put_existing_to_front(msginfo)) {
2236 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2237 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2238 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2239 gchar *queueheader_buf = NULL;
2242 /* Select Account from queue headers */
2243 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2244 "X-Claws-Account-Id:")) {
2245 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2246 account = account_find_from_id(id);
2247 g_free(queueheader_buf);
2249 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2250 "X-Sylpheed-Account-Id:")) {
2251 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2252 account = account_find_from_id(id);
2253 g_free(queueheader_buf);
2255 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2257 id = atoi(&queueheader_buf[strlen("NAID:")]);
2258 account = account_find_from_id(id);
2259 g_free(queueheader_buf);
2261 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2263 id = atoi(&queueheader_buf[strlen("MAID:")]);
2264 account = account_find_from_id(id);
2265 g_free(queueheader_buf);
2267 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2269 account = account_find_from_address(queueheader_buf, FALSE);
2270 g_free(queueheader_buf);
2272 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2274 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2275 use_signing = param;
2276 g_free(queueheader_buf);
2278 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2279 "X-Sylpheed-Sign:")) {
2280 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2281 use_signing = param;
2282 g_free(queueheader_buf);
2284 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2285 "X-Claws-Encrypt:")) {
2286 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2287 use_encryption = param;
2288 g_free(queueheader_buf);
2290 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2291 "X-Sylpheed-Encrypt:")) {
2292 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2293 use_encryption = param;
2294 g_free(queueheader_buf);
2296 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2297 "X-Claws-Auto-Wrapping:")) {
2298 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2300 g_free(queueheader_buf);
2302 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2303 "X-Claws-Auto-Indent:")) {
2304 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2306 g_free(queueheader_buf);
2308 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2309 "X-Claws-Privacy-System:")) {
2310 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2311 g_free(queueheader_buf);
2313 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2314 "X-Sylpheed-Privacy-System:")) {
2315 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2316 g_free(queueheader_buf);
2318 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2320 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2322 g_free(queueheader_buf);
2324 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2326 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2327 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2328 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2329 if (orig_item != NULL) {
2330 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2334 g_free(queueheader_buf);
2336 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2338 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2339 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2340 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2341 if (orig_item != NULL) {
2342 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2346 g_free(queueheader_buf);
2348 /* Get manual headers */
2349 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2350 "X-Claws-Manual-Headers:")) {
2351 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2352 if (listmh && *listmh != '\0') {
2353 debug_print("Got manual headers: %s\n", listmh);
2354 manual_headers = procheader_entries_from_str(listmh);
2357 g_free(queueheader_buf);
2360 account = msginfo->folder->folder->account;
2363 if (!account && prefs_common.reedit_account_autosel) {
2365 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2366 extract_address(from);
2367 account = account_find_from_address(from, FALSE);
2372 account = cur_account;
2374 cm_return_val_if_fail(account != NULL, NULL);
2376 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2378 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2379 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2380 compose->autowrap = autowrap;
2381 compose->replyinfo = replyinfo;
2382 compose->fwdinfo = fwdinfo;
2384 compose->updating = TRUE;
2385 compose->priority = priority;
2387 if (privacy_system != NULL) {
2388 compose->privacy_system = privacy_system;
2389 compose_use_signing(compose, use_signing);
2390 compose_use_encryption(compose, use_encryption);
2391 compose_update_privacy_system_menu_item(compose, FALSE);
2393 compose_activate_privacy_system(compose, account, FALSE);
2395 compose_apply_folder_privacy_settings(compose, msginfo->folder);
2397 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2398 compose->targetinfo->tags = g_slist_copy(msginfo->tags);
2400 compose_extract_original_charset(compose);
2402 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2403 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2404 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2405 gchar *queueheader_buf = NULL;
2407 /* Set message save folder */
2408 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2409 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2410 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2411 compose_set_save_to(compose, &queueheader_buf[4]);
2412 g_free(queueheader_buf);
2414 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2415 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2417 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2419 g_free(queueheader_buf);
2423 if (compose_parse_header(compose, msginfo) < 0) {
2424 compose->updating = FALSE;
2425 compose_destroy(compose);
2428 compose_reedit_set_entry(compose, msginfo);
2430 textview = GTK_TEXT_VIEW(compose->text);
2431 textbuf = gtk_text_view_get_buffer(textview);
2432 compose_create_tags(textview, compose);
2434 mark = gtk_text_buffer_get_insert(textbuf);
2435 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2437 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2438 G_CALLBACK(compose_changed_cb),
2441 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2442 fp = procmime_get_first_encrypted_text_content(msginfo);
2444 compose_force_encryption(compose, account, TRUE, NULL);
2447 fp = procmime_get_first_text_content(msginfo);
2450 g_warning("Can't get text part");
2454 gchar buf[BUFFSIZE];
2455 gboolean prev_autowrap;
2456 GtkTextBuffer *buffer;
2458 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
2460 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2466 compose_attach_parts(compose, msginfo);
2468 compose_colorize_signature(compose);
2470 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2471 G_CALLBACK(compose_changed_cb),
2474 if (manual_headers != NULL) {
2475 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2476 procheader_entries_free(manual_headers);
2477 compose->updating = FALSE;
2478 compose_destroy(compose);
2481 procheader_entries_free(manual_headers);
2484 gtk_widget_grab_focus(compose->text);
2486 if (prefs_common.auto_exteditor) {
2487 compose_exec_ext_editor(compose);
2489 compose->modified = FALSE;
2490 compose_set_title(compose);
2492 compose->updating = FALSE;
2493 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2494 SCROLL_TO_CURSOR(compose);
2496 if (compose->deferred_destroy) {
2497 compose_destroy(compose);
2501 compose->sig_str = account_get_signature_str(compose->account);
2503 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2508 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2515 cm_return_val_if_fail(msginfo != NULL, NULL);
2518 account = account_get_reply_account(msginfo,
2519 prefs_common.reply_account_autosel);
2520 cm_return_val_if_fail(account != NULL, NULL);
2522 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2523 compose_apply_folder_privacy_settings(compose, msginfo->folder);
2525 compose->updating = TRUE;
2527 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2528 compose->replyinfo = NULL;
2529 compose->fwdinfo = NULL;
2531 compose_show_first_last_header(compose, TRUE);
2533 gtk_widget_grab_focus(compose->header_last->entry);
2535 filename = procmsg_get_message_file(msginfo);
2537 if (filename == NULL) {
2538 compose->updating = FALSE;
2539 compose_destroy(compose);
2544 compose->redirect_filename = filename;
2546 /* Set save folder */
2547 item = msginfo->folder;
2548 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2549 gchar *folderidentifier;
2551 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2552 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2553 folderidentifier = folder_item_get_identifier(item);
2554 compose_set_save_to(compose, folderidentifier);
2555 g_free(folderidentifier);
2558 compose_attach_parts(compose, msginfo);
2560 if (msginfo->subject)
2561 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2563 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2565 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2566 _("The body of the \"Redirect\" template has an error at line %d."));
2567 quote_fmt_reset_vartable();
2568 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2570 compose_colorize_signature(compose);
2573 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2574 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2575 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2577 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2578 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2579 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2581 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2587 if (compose->toolbar->draft_btn)
2588 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2589 if (compose->toolbar->insert_btn)
2590 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2591 if (compose->toolbar->attach_btn)
2592 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2593 if (compose->toolbar->sig_btn)
2594 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2595 if (compose->toolbar->exteditor_btn)
2596 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2597 if (compose->toolbar->linewrap_current_btn)
2598 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2599 if (compose->toolbar->linewrap_all_btn)
2600 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2602 compose->modified = FALSE;
2603 compose_set_title(compose);
2604 compose->updating = FALSE;
2605 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2606 SCROLL_TO_CURSOR(compose);
2608 if (compose->deferred_destroy) {
2609 compose_destroy(compose);
2613 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2618 const GList *compose_get_compose_list(void)
2620 return compose_list;
2623 void compose_entry_append(Compose *compose, const gchar *address,
2624 ComposeEntryType type, ComposePrefType pref_type)
2626 const gchar *header;
2628 gboolean in_quote = FALSE;
2629 if (!address || *address == '\0') return;
2636 header = N_("Bcc:");
2638 case COMPOSE_REPLYTO:
2639 header = N_("Reply-To:");
2641 case COMPOSE_NEWSGROUPS:
2642 header = N_("Newsgroups:");
2644 case COMPOSE_FOLLOWUPTO:
2645 header = N_( "Followup-To:");
2647 case COMPOSE_INREPLYTO:
2648 header = N_( "In-Reply-To:");
2655 header = prefs_common_translated_header_name(header);
2657 cur = begin = (gchar *)address;
2659 /* we separate the line by commas, but not if we're inside a quoted
2661 while (*cur != '\0') {
2663 in_quote = !in_quote;
2664 if (*cur == ',' && !in_quote) {
2665 gchar *tmp = g_strdup(begin);
2667 tmp[cur-begin]='\0';
2670 while (*tmp == ' ' || *tmp == '\t')
2672 compose_add_header_entry(compose, header, tmp, pref_type);
2673 compose_entry_indicate(compose, tmp);
2680 gchar *tmp = g_strdup(begin);
2682 tmp[cur-begin]='\0';
2683 while (*tmp == ' ' || *tmp == '\t')
2685 compose_add_header_entry(compose, header, tmp, pref_type);
2686 compose_entry_indicate(compose, tmp);
2691 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2696 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2697 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2698 if (gtk_entry_get_text(entry) &&
2699 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2700 gtk_widget_modify_base(
2701 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2702 GTK_STATE_NORMAL, &default_header_bgcolor);
2703 gtk_widget_modify_text(
2704 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2705 GTK_STATE_NORMAL, &default_header_color);
2710 void compose_toolbar_cb(gint action, gpointer data)
2712 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2713 Compose *compose = (Compose*)toolbar_item->parent;
2715 cm_return_if_fail(compose != NULL);
2719 compose_send_cb(NULL, compose);
2722 compose_send_later_cb(NULL, compose);
2725 compose_draft(compose, COMPOSE_QUIT_EDITING);
2728 compose_insert_file_cb(NULL, compose);
2731 compose_attach_cb(NULL, compose);
2734 compose_insert_sig(compose, FALSE);
2737 compose_insert_sig(compose, TRUE);
2740 compose_ext_editor_cb(NULL, compose);
2742 case A_LINEWRAP_CURRENT:
2743 compose_beautify_paragraph(compose, NULL, TRUE);
2745 case A_LINEWRAP_ALL:
2746 compose_wrap_all_full(compose, TRUE);
2749 compose_address_cb(NULL, compose);
2752 case A_CHECK_SPELLING:
2753 compose_check_all(NULL, compose);
2756 case A_PRIVACY_SIGN:
2758 case A_PRIVACY_ENCRYPT:
2765 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2770 gchar *subject = NULL;
2774 gchar **attach = NULL;
2775 gchar *inreplyto = NULL;
2776 MailField mfield = NO_FIELD_PRESENT;
2778 /* get mailto parts but skip from */
2779 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2782 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2783 mfield = TO_FIELD_PRESENT;
2786 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2788 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2790 if (!g_utf8_validate (subject, -1, NULL)) {
2791 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2792 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2795 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2797 mfield = SUBJECT_FIELD_PRESENT;
2800 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2801 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2804 gboolean prev_autowrap = compose->autowrap;
2806 compose->autowrap = FALSE;
2808 mark = gtk_text_buffer_get_insert(buffer);
2809 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2811 if (!g_utf8_validate (body, -1, NULL)) {
2812 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2813 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2816 gtk_text_buffer_insert(buffer, &iter, body, -1);
2818 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2820 compose->autowrap = prev_autowrap;
2821 if (compose->autowrap)
2822 compose_wrap_all(compose);
2823 mfield = BODY_FIELD_PRESENT;
2827 gint i = 0, att = 0;
2828 gchar *warn_files = NULL;
2829 while (attach[i] != NULL) {
2830 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2831 if (utf8_filename) {
2832 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2833 gchar *tmp = g_strdup_printf("%s%s\n",
2834 warn_files?warn_files:"",
2840 g_free(utf8_filename);
2842 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2847 alertpanel_notice(ngettext(
2848 "The following file has been attached: \n%s",
2849 "The following files have been attached: \n%s", att), warn_files);
2854 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2867 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2869 static HeaderEntry hentry[] = {
2870 {"Reply-To:", NULL, TRUE },
2871 {"Cc:", NULL, TRUE },
2872 {"References:", NULL, FALSE },
2873 {"Bcc:", NULL, TRUE },
2874 {"Newsgroups:", NULL, TRUE },
2875 {"Followup-To:", NULL, TRUE },
2876 {"List-Post:", NULL, FALSE },
2877 {"X-Priority:", NULL, FALSE },
2878 {NULL, NULL, FALSE }
2895 cm_return_val_if_fail(msginfo != NULL, -1);
2897 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2898 procheader_get_header_fields(fp, hentry);
2901 if (hentry[H_REPLY_TO].body != NULL) {
2902 if (hentry[H_REPLY_TO].body[0] != '\0') {
2904 conv_unmime_header(hentry[H_REPLY_TO].body,
2907 g_free(hentry[H_REPLY_TO].body);
2908 hentry[H_REPLY_TO].body = NULL;
2910 if (hentry[H_CC].body != NULL) {
2911 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2912 g_free(hentry[H_CC].body);
2913 hentry[H_CC].body = NULL;
2915 if (hentry[H_REFERENCES].body != NULL) {
2916 if (compose->mode == COMPOSE_REEDIT)
2917 compose->references = hentry[H_REFERENCES].body;
2919 compose->references = compose_parse_references
2920 (hentry[H_REFERENCES].body, msginfo->msgid);
2921 g_free(hentry[H_REFERENCES].body);
2923 hentry[H_REFERENCES].body = NULL;
2925 if (hentry[H_BCC].body != NULL) {
2926 if (compose->mode == COMPOSE_REEDIT)
2928 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2929 g_free(hentry[H_BCC].body);
2930 hentry[H_BCC].body = NULL;
2932 if (hentry[H_NEWSGROUPS].body != NULL) {
2933 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2934 hentry[H_NEWSGROUPS].body = NULL;
2936 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2937 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2938 compose->followup_to =
2939 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2942 g_free(hentry[H_FOLLOWUP_TO].body);
2943 hentry[H_FOLLOWUP_TO].body = NULL;
2945 if (hentry[H_LIST_POST].body != NULL) {
2946 gchar *to = NULL, *start = NULL;
2948 extract_address(hentry[H_LIST_POST].body);
2949 if (hentry[H_LIST_POST].body[0] != '\0') {
2950 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2952 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2953 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2956 g_free(compose->ml_post);
2957 compose->ml_post = to;
2960 g_free(hentry[H_LIST_POST].body);
2961 hentry[H_LIST_POST].body = NULL;
2964 /* CLAWS - X-Priority */
2965 if (compose->mode == COMPOSE_REEDIT)
2966 if (hentry[H_X_PRIORITY].body != NULL) {
2969 priority = atoi(hentry[H_X_PRIORITY].body);
2970 g_free(hentry[H_X_PRIORITY].body);
2972 hentry[H_X_PRIORITY].body = NULL;
2974 if (priority < PRIORITY_HIGHEST ||
2975 priority > PRIORITY_LOWEST)
2976 priority = PRIORITY_NORMAL;
2978 compose->priority = priority;
2981 if (compose->mode == COMPOSE_REEDIT) {
2982 if (msginfo->inreplyto && *msginfo->inreplyto)
2983 compose->inreplyto = g_strdup(msginfo->inreplyto);
2985 if (msginfo->msgid && *msginfo->msgid &&
2986 compose->folder != NULL &&
2987 compose->folder->stype == F_DRAFT)
2988 compose->msgid = g_strdup(msginfo->msgid);
2990 if (msginfo->msgid && *msginfo->msgid)
2991 compose->inreplyto = g_strdup(msginfo->msgid);
2993 if (!compose->references) {
2994 if (msginfo->msgid && *msginfo->msgid) {
2995 if (msginfo->inreplyto && *msginfo->inreplyto)
2996 compose->references =
2997 g_strdup_printf("<%s>\n\t<%s>",
3001 compose->references =
3002 g_strconcat("<", msginfo->msgid, ">",
3004 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3005 compose->references =
3006 g_strconcat("<", msginfo->inreplyto, ">",
3015 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3020 cm_return_val_if_fail(msginfo != NULL, -1);
3022 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3023 procheader_get_header_fields(fp, entries);
3027 while (he != NULL && he->name != NULL) {
3029 GtkListStore *model = NULL;
3031 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3032 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3033 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3034 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3035 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3042 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3044 GSList *ref_id_list, *cur;
3048 ref_id_list = references_list_append(NULL, ref);
3049 if (!ref_id_list) return NULL;
3050 if (msgid && *msgid)
3051 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3056 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3057 /* "<" + Message-ID + ">" + CR+LF+TAB */
3058 len += strlen((gchar *)cur->data) + 5;
3060 if (len > MAX_REFERENCES_LEN) {
3061 /* remove second message-ID */
3062 if (ref_id_list && ref_id_list->next &&
3063 ref_id_list->next->next) {
3064 g_free(ref_id_list->next->data);
3065 ref_id_list = g_slist_remove
3066 (ref_id_list, ref_id_list->next->data);
3068 slist_free_strings_full(ref_id_list);
3075 new_ref = g_string_new("");
3076 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3077 if (new_ref->len > 0)
3078 g_string_append(new_ref, "\n\t");
3079 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3082 slist_free_strings_full(ref_id_list);
3084 new_ref_str = new_ref->str;
3085 g_string_free(new_ref, FALSE);
3090 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3091 const gchar *fmt, const gchar *qmark,
3092 const gchar *body, gboolean rewrap,
3093 gboolean need_unescape,
3094 const gchar *err_msg)
3096 MsgInfo* dummyinfo = NULL;
3097 gchar *quote_str = NULL;
3099 gboolean prev_autowrap;
3100 const gchar *trimmed_body = body;
3101 gint cursor_pos = -1;
3102 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3103 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3108 SIGNAL_BLOCK(buffer);
3111 dummyinfo = compose_msginfo_new_from_compose(compose);
3112 msginfo = dummyinfo;
3115 if (qmark != NULL) {
3117 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3118 compose->gtkaspell);
3120 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3122 quote_fmt_scan_string(qmark);
3125 buf = quote_fmt_get_buffer();
3128 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3130 Xstrdup_a(quote_str, buf, goto error)
3133 if (fmt && *fmt != '\0') {
3136 while (*trimmed_body == '\n')
3140 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3141 compose->gtkaspell);
3143 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3145 if (need_unescape) {