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_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
945 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
947 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), FALSE);
948 gtk_entry_set_text(GTK_ENTRY(entry), "");
952 static gchar *compose_get_save_to(Compose *compose)
955 gchar *result = NULL;
956 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
957 result = gtk_editable_get_chars(entry, 0, -1);
960 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
961 prefs_common.compose_save_to_history = add_history(
962 prefs_common.compose_save_to_history, result);
963 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
964 prefs_common.compose_save_to_history);
969 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
970 GList *attach_files, GList *listAddress )
973 GtkTextView *textview;
974 GtkTextBuffer *textbuf;
976 const gchar *subject_format = NULL;
977 const gchar *body_format = NULL;
978 gchar *mailto_from = NULL;
979 PrefsAccount *mailto_account = NULL;
980 MsgInfo* dummyinfo = NULL;
981 gint cursor_pos = -1;
982 MailField mfield = NO_FIELD_PRESENT;
986 /* check if mailto defines a from */
987 if (mailto && *mailto != '\0') {
988 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
989 /* mailto defines a from, check if we can get account prefs from it,
990 if not, the account prefs will be guessed using other ways, but we'll keep
993 mailto_account = account_find_from_address(mailto_from, TRUE);
994 if (mailto_account == NULL) {
996 Xstrdup_a(tmp_from, mailto_from, return NULL);
997 extract_address(tmp_from);
998 mailto_account = account_find_from_address(tmp_from, TRUE);
1002 account = mailto_account;
1005 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1006 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1007 account = account_find_from_id(item->prefs->default_account);
1009 /* if no account prefs set, fallback to the current one */
1010 if (!account) account = cur_account;
1011 cm_return_val_if_fail(account != NULL, NULL);
1013 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1014 compose_apply_folder_privacy_settings(compose, item);
1016 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1017 (account->default_encrypt || account->default_sign))
1018 alertpanel_error(_("You have opted to sign and/or encrypt this "
1019 "message but have not selected a privacy system.\n\n"
1020 "Signing and encrypting have been disabled for this "
1023 /* override from name if mailto asked for it */
1025 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1026 g_free(mailto_from);
1028 /* override from name according to folder properties */
1029 if (item && item->prefs &&
1030 item->prefs->compose_with_format &&
1031 item->prefs->compose_override_from_format &&
1032 *item->prefs->compose_override_from_format != '\0') {
1037 dummyinfo = compose_msginfo_new_from_compose(compose);
1039 /* decode \-escape sequences in the internal representation of the quote format */
1040 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1041 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1044 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1045 compose->gtkaspell);
1047 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1049 quote_fmt_scan_string(tmp);
1052 buf = quote_fmt_get_buffer();
1054 alertpanel_error(_("New message From format error."));
1056 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1057 quote_fmt_reset_vartable();
1058 quote_fmtlex_destroy();
1063 compose->replyinfo = NULL;
1064 compose->fwdinfo = NULL;
1066 textview = GTK_TEXT_VIEW(compose->text);
1067 textbuf = gtk_text_view_get_buffer(textview);
1068 compose_create_tags(textview, compose);
1070 undo_block(compose->undostruct);
1072 compose_set_dictionaries_from_folder_prefs(compose, item);
1075 if (account->auto_sig)
1076 compose_insert_sig(compose, FALSE);
1077 gtk_text_buffer_get_start_iter(textbuf, &iter);
1078 gtk_text_buffer_place_cursor(textbuf, &iter);
1080 if (account->protocol != A_NNTP) {
1081 if (mailto && *mailto != '\0') {
1082 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1085 compose_set_folder_prefs(compose, item, TRUE);
1087 if (item && item->ret_rcpt) {
1088 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1091 if (mailto && *mailto != '\0') {
1092 if (!strchr(mailto, '@'))
1093 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1095 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1096 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1097 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1098 mfield = TO_FIELD_PRESENT;
1101 * CLAWS: just don't allow return receipt request, even if the user
1102 * may want to send an email. simple but foolproof.
1104 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1106 compose_add_field_list( compose, listAddress );
1108 if (item && item->prefs && item->prefs->compose_with_format) {
1109 subject_format = item->prefs->compose_subject_format;
1110 body_format = item->prefs->compose_body_format;
1111 } else if (account->compose_with_format) {
1112 subject_format = account->compose_subject_format;
1113 body_format = account->compose_body_format;
1114 } else if (prefs_common.compose_with_format) {
1115 subject_format = prefs_common.compose_subject_format;
1116 body_format = prefs_common.compose_body_format;
1119 if (subject_format || body_format) {
1122 && *subject_format != '\0' )
1124 gchar *subject = NULL;
1129 dummyinfo = compose_msginfo_new_from_compose(compose);
1131 /* decode \-escape sequences in the internal representation of the quote format */
1132 tmp = g_malloc(strlen(subject_format)+1);
1133 pref_get_unescaped_pref(tmp, subject_format);
1135 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1137 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1138 compose->gtkaspell);
1140 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1142 quote_fmt_scan_string(tmp);
1145 buf = quote_fmt_get_buffer();
1147 alertpanel_error(_("New message subject format error."));
1149 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1150 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1151 quote_fmt_reset_vartable();
1152 quote_fmtlex_destroy();
1156 mfield = SUBJECT_FIELD_PRESENT;
1160 && *body_format != '\0' )
1163 GtkTextBuffer *buffer;
1164 GtkTextIter start, end;
1168 dummyinfo = compose_msginfo_new_from_compose(compose);
1170 text = GTK_TEXT_VIEW(compose->text);
1171 buffer = gtk_text_view_get_buffer(text);
1172 gtk_text_buffer_get_start_iter(buffer, &start);
1173 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1174 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1176 compose_quote_fmt(compose, dummyinfo,
1178 NULL, tmp, FALSE, TRUE,
1179 _("The body of the \"New message\" template has an error at line %d."));
1180 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1181 quote_fmt_reset_vartable();
1185 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1186 gtkaspell_highlight_all(compose->gtkaspell);
1188 mfield = BODY_FIELD_PRESENT;
1192 procmsg_msginfo_free( &dummyinfo );
1198 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1199 ainfo = (AttachInfo *) curr->data;
1201 compose_insert_file(compose, ainfo->file);
1203 compose_attach_append(compose, ainfo->file, ainfo->file,
1204 ainfo->content_type, ainfo->charset);
1208 compose_show_first_last_header(compose, TRUE);
1210 /* Set save folder */
1211 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1212 gchar *folderidentifier;
1214 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1215 folderidentifier = folder_item_get_identifier(item);
1216 compose_set_save_to(compose, folderidentifier);
1217 g_free(folderidentifier);
1220 /* Place cursor according to provided input (mfield) */
1222 case NO_FIELD_PRESENT:
1223 if (compose->header_last)
1224 gtk_widget_grab_focus(compose->header_last->entry);
1226 case TO_FIELD_PRESENT:
1227 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1229 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1232 gtk_widget_grab_focus(compose->subject_entry);
1234 case SUBJECT_FIELD_PRESENT:
1235 textview = GTK_TEXT_VIEW(compose->text);
1238 textbuf = gtk_text_view_get_buffer(textview);
1241 mark = gtk_text_buffer_get_insert(textbuf);
1242 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1243 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1245 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1246 * only defers where it comes to the variable body
1247 * is not null. If no body is present compose->text
1248 * will be null in which case you cannot place the
1249 * cursor inside the component so. An empty component
1250 * is therefore created before placing the cursor
1252 case BODY_FIELD_PRESENT:
1253 cursor_pos = quote_fmt_get_cursor_pos();
1254 if (cursor_pos == -1)
1255 gtk_widget_grab_focus(compose->header_last->entry);
1257 gtk_widget_grab_focus(compose->text);
1261 undo_unblock(compose->undostruct);
1263 if (prefs_common.auto_exteditor)
1264 compose_exec_ext_editor(compose);
1266 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1268 SCROLL_TO_CURSOR(compose);
1270 compose->modified = FALSE;
1271 compose_set_title(compose);
1273 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1278 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1279 gboolean override_pref, const gchar *system)
1281 const gchar *privacy = NULL;
1283 cm_return_if_fail(compose != NULL);
1284 cm_return_if_fail(account != NULL);
1286 if (privacy_system_can_encrypt(compose->privacy_system) == FALSE ||
1287 (override_pref == FALSE && account->default_encrypt_reply == FALSE))
1290 if (account->default_privacy_system && strlen(account->default_privacy_system))
1291 privacy = account->default_privacy_system;
1295 GSList *privacy_avail = privacy_get_system_ids();
1296 if (privacy_avail && g_slist_length(privacy_avail)) {
1297 privacy = (gchar *)(privacy_avail->data);
1299 g_slist_free_full(privacy_avail, g_free);
1301 if (privacy != NULL) {
1303 g_free(compose->privacy_system);
1304 compose->privacy_system = NULL;
1305 g_free(compose->encdata);
1306 compose->encdata = NULL;
1308 if (compose->privacy_system == NULL)
1309 compose->privacy_system = g_strdup(privacy);
1310 else if (*(compose->privacy_system) == '\0') {
1311 g_free(compose->privacy_system);
1312 g_free(compose->encdata);
1313 compose->encdata = NULL;
1314 compose->privacy_system = g_strdup(privacy);
1316 compose_update_privacy_system_menu_item(compose, FALSE);
1317 compose_use_encryption(compose, TRUE);
1321 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1323 const gchar *privacy = NULL;
1324 if (privacy_system_can_sign(compose->privacy_system) == FALSE)
1327 if (account->default_privacy_system && strlen(account->default_privacy_system))
1328 privacy = account->default_privacy_system;
1332 GSList *privacy_avail = privacy_get_system_ids();
1333 if (privacy_avail && g_slist_length(privacy_avail)) {
1334 privacy = (gchar *)(privacy_avail->data);
1338 if (privacy != NULL) {
1340 g_free(compose->privacy_system);
1341 compose->privacy_system = NULL;
1342 g_free(compose->encdata);
1343 compose->encdata = NULL;
1345 if (compose->privacy_system == NULL)
1346 compose->privacy_system = g_strdup(privacy);
1347 compose_update_privacy_system_menu_item(compose, FALSE);
1348 compose_use_signing(compose, TRUE);
1352 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1356 Compose *compose = NULL;
1358 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1360 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1361 cm_return_val_if_fail(msginfo != NULL, NULL);
1363 list_len = g_slist_length(msginfo_list);
1367 case COMPOSE_REPLY_TO_ADDRESS:
1368 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1369 FALSE, prefs_common.default_reply_list, FALSE, body);
1371 case COMPOSE_REPLY_WITH_QUOTE:
1372 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1373 FALSE, prefs_common.default_reply_list, FALSE, body);
1375 case COMPOSE_REPLY_WITHOUT_QUOTE:
1376 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1377 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1379 case COMPOSE_REPLY_TO_SENDER:
1380 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1381 FALSE, FALSE, TRUE, body);
1383 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1384 compose = compose_followup_and_reply_to(msginfo,
1385 COMPOSE_QUOTE_CHECK,
1386 FALSE, FALSE, body);
1388 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1389 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1390 FALSE, FALSE, TRUE, body);
1392 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1393 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1394 FALSE, FALSE, TRUE, NULL);
1396 case COMPOSE_REPLY_TO_ALL:
1397 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1398 TRUE, FALSE, FALSE, body);
1400 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1401 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1402 TRUE, FALSE, FALSE, body);
1404 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1405 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1406 TRUE, FALSE, FALSE, NULL);
1408 case COMPOSE_REPLY_TO_LIST:
1409 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1410 FALSE, TRUE, FALSE, body);
1412 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1413 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1414 FALSE, TRUE, FALSE, body);
1416 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1417 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1418 FALSE, TRUE, FALSE, NULL);
1420 case COMPOSE_FORWARD:
1421 if (prefs_common.forward_as_attachment) {
1422 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1425 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1429 case COMPOSE_FORWARD_INLINE:
1430 /* check if we reply to more than one Message */
1431 if (list_len == 1) {
1432 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1435 /* more messages FALL THROUGH */
1436 case COMPOSE_FORWARD_AS_ATTACH:
1437 compose = compose_forward_multiple(NULL, msginfo_list);
1439 case COMPOSE_REDIRECT:
1440 compose = compose_redirect(NULL, msginfo, FALSE);
1443 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1446 if (compose == NULL) {
1447 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1451 compose->rmode = mode;
1452 switch (compose->rmode) {
1454 case COMPOSE_REPLY_WITH_QUOTE:
1455 case COMPOSE_REPLY_WITHOUT_QUOTE:
1456 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1457 debug_print("reply mode Normal\n");
1458 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1459 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1461 case COMPOSE_REPLY_TO_SENDER:
1462 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1463 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1464 debug_print("reply mode Sender\n");
1465 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1467 case COMPOSE_REPLY_TO_ALL:
1468 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1469 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1470 debug_print("reply mode All\n");
1471 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1473 case COMPOSE_REPLY_TO_LIST:
1474 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1475 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1476 debug_print("reply mode List\n");
1477 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1479 case COMPOSE_REPLY_TO_ADDRESS:
1480 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1488 static Compose *compose_reply(MsgInfo *msginfo,
1489 ComposeQuoteMode quote_mode,
1495 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1496 to_sender, FALSE, body);
1499 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1500 ComposeQuoteMode quote_mode,
1505 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1506 to_sender, TRUE, body);
1509 static void compose_extract_original_charset(Compose *compose)
1511 MsgInfo *info = NULL;
1512 if (compose->replyinfo) {
1513 info = compose->replyinfo;
1514 } else if (compose->fwdinfo) {
1515 info = compose->fwdinfo;
1516 } else if (compose->targetinfo) {
1517 info = compose->targetinfo;
1520 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1521 MimeInfo *partinfo = mimeinfo;
1522 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1523 partinfo = procmime_mimeinfo_next(partinfo);
1525 compose->orig_charset =
1526 g_strdup(procmime_mimeinfo_get_parameter(
1527 partinfo, "charset"));
1529 procmime_mimeinfo_free_all(&mimeinfo);
1533 #define SIGNAL_BLOCK(buffer) { \
1534 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1535 G_CALLBACK(compose_changed_cb), \
1537 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1538 G_CALLBACK(text_inserted), \
1542 #define SIGNAL_UNBLOCK(buffer) { \
1543 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1544 G_CALLBACK(compose_changed_cb), \
1546 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1547 G_CALLBACK(text_inserted), \
1551 static Compose *compose_generic_reply(MsgInfo *msginfo,
1552 ComposeQuoteMode quote_mode,
1553 gboolean to_all, gboolean to_ml,
1555 gboolean followup_and_reply_to,
1559 PrefsAccount *account = NULL;
1560 GtkTextView *textview;
1561 GtkTextBuffer *textbuf;
1562 gboolean quote = FALSE;
1563 const gchar *qmark = NULL;
1564 const gchar *body_fmt = NULL;
1565 gchar *s_system = NULL;
1567 cm_return_val_if_fail(msginfo != NULL, NULL);
1568 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1570 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1572 cm_return_val_if_fail(account != NULL, NULL);
1574 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1575 compose_apply_folder_privacy_settings(compose, msginfo->folder);
1577 compose->updating = TRUE;
1579 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1582 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1583 if (!compose->replyinfo)
1584 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1586 compose_extract_original_charset(compose);
1588 if (msginfo->folder && msginfo->folder->ret_rcpt)
1589 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1591 /* Set save folder */
1592 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1593 gchar *folderidentifier;
1595 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1596 folderidentifier = folder_item_get_identifier(msginfo->folder);
1597 compose_set_save_to(compose, folderidentifier);
1598 g_free(folderidentifier);
1601 if (compose_parse_header(compose, msginfo) < 0) {
1602 compose->updating = FALSE;
1603 compose_destroy(compose);
1607 /* override from name according to folder properties */
1608 if (msginfo->folder && msginfo->folder->prefs &&
1609 msginfo->folder->prefs->reply_with_format &&
1610 msginfo->folder->prefs->reply_override_from_format &&
1611 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1616 /* decode \-escape sequences in the internal representation of the quote format */
1617 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1618 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1621 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1622 compose->gtkaspell);
1624 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1626 quote_fmt_scan_string(tmp);
1629 buf = quote_fmt_get_buffer();
1631 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1633 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1634 quote_fmt_reset_vartable();
1635 quote_fmtlex_destroy();
1640 textview = (GTK_TEXT_VIEW(compose->text));
1641 textbuf = gtk_text_view_get_buffer(textview);
1642 compose_create_tags(textview, compose);
1644 undo_block(compose->undostruct);
1646 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1647 gtkaspell_block_check(compose->gtkaspell);
1650 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1651 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1652 /* use the reply format of folder (if enabled), or the account's one
1653 (if enabled) or fallback to the global reply format, which is always
1654 enabled (even if empty), and use the relevant quotemark */
1656 if (msginfo->folder && msginfo->folder->prefs &&
1657 msginfo->folder->prefs->reply_with_format) {
1658 qmark = msginfo->folder->prefs->reply_quotemark;
1659 body_fmt = msginfo->folder->prefs->reply_body_format;
1661 } else if (account->reply_with_format) {
1662 qmark = account->reply_quotemark;
1663 body_fmt = account->reply_body_format;
1666 qmark = prefs_common.quotemark;
1667 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1668 body_fmt = gettext(prefs_common.quotefmt);
1675 /* empty quotemark is not allowed */
1676 if (qmark == NULL || *qmark == '\0')
1678 compose_quote_fmt(compose, compose->replyinfo,
1679 body_fmt, qmark, body, FALSE, TRUE,
1680 _("The body of the \"Reply\" template has an error at line %d."));
1681 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1682 quote_fmt_reset_vartable();
1685 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1686 compose_force_encryption(compose, account, FALSE, s_system);
1689 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1690 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1691 compose_force_signing(compose, account, s_system);
1695 if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1696 ((account->default_encrypt_reply && MSG_IS_ENCRYPTED(compose->replyinfo->flags)) ||
1697 (account->default_sign_reply && MSG_IS_SIGNED(compose->replyinfo->flags))))
1698 alertpanel_error(_("You have opted to sign and/or encrypt this "
1699 "message but have not selected a privacy system.\n\n"
1700 "Signing and encrypting have been disabled for this "
1703 SIGNAL_BLOCK(textbuf);
1705 if (account->auto_sig)
1706 compose_insert_sig(compose, FALSE);
1708 compose_wrap_all(compose);
1711 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1712 gtkaspell_highlight_all(compose->gtkaspell);
1713 gtkaspell_unblock_check(compose->gtkaspell);
1715 SIGNAL_UNBLOCK(textbuf);
1717 gtk_widget_grab_focus(compose->text);
1719 undo_unblock(compose->undostruct);
1721 if (prefs_common.auto_exteditor)
1722 compose_exec_ext_editor(compose);
1724 compose->modified = FALSE;
1725 compose_set_title(compose);
1727 compose->updating = FALSE;
1728 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1729 SCROLL_TO_CURSOR(compose);
1731 if (compose->deferred_destroy) {
1732 compose_destroy(compose);
1740 #define INSERT_FW_HEADER(var, hdr) \
1741 if (msginfo->var && *msginfo->var) { \
1742 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1743 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1744 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1747 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1748 gboolean as_attach, const gchar *body,
1749 gboolean no_extedit,
1753 GtkTextView *textview;
1754 GtkTextBuffer *textbuf;
1755 gint cursor_pos = -1;
1758 cm_return_val_if_fail(msginfo != NULL, NULL);
1759 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1761 if (!account && !(account = compose_find_account(msginfo)))
1762 account = cur_account;
1764 if (!prefs_common.forward_as_attachment)
1765 mode = COMPOSE_FORWARD_INLINE;
1767 mode = COMPOSE_FORWARD;
1768 compose = compose_create(account, msginfo->folder, mode, batch);
1769 compose_apply_folder_privacy_settings(compose, msginfo->folder);
1771 compose->updating = TRUE;
1772 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1773 if (!compose->fwdinfo)
1774 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1776 compose_extract_original_charset(compose);
1778 if (msginfo->subject && *msginfo->subject) {
1779 gchar *buf, *buf2, *p;
1781 buf = p = g_strdup(msginfo->subject);
1782 p += subject_get_prefix_length(p);
1783 memmove(buf, p, strlen(p) + 1);
1785 buf2 = g_strdup_printf("Fw: %s", buf);
1786 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1792 /* override from name according to folder properties */
1793 if (msginfo->folder && msginfo->folder->prefs &&
1794 msginfo->folder->prefs->forward_with_format &&
1795 msginfo->folder->prefs->forward_override_from_format &&
1796 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1800 MsgInfo *full_msginfo = NULL;
1803 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1805 full_msginfo = procmsg_msginfo_copy(msginfo);
1807 /* decode \-escape sequences in the internal representation of the quote format */
1808 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1809 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1812 gtkaspell_block_check(compose->gtkaspell);
1813 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1814 compose->gtkaspell);
1816 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1818 quote_fmt_scan_string(tmp);
1821 buf = quote_fmt_get_buffer();
1823 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1825 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1826 quote_fmt_reset_vartable();
1827 quote_fmtlex_destroy();
1830 procmsg_msginfo_free(&full_msginfo);
1833 textview = GTK_TEXT_VIEW(compose->text);
1834 textbuf = gtk_text_view_get_buffer(textview);
1835 compose_create_tags(textview, compose);
1837 undo_block(compose->undostruct);
1841 msgfile = procmsg_get_message_file(msginfo);
1842 if (!is_file_exist(msgfile))
1843 g_warning("%s: file does not exist", msgfile);
1845 compose_attach_append(compose, msgfile, msgfile,
1846 "message/rfc822", NULL);
1850 const gchar *qmark = NULL;
1851 const gchar *body_fmt = NULL;
1852 MsgInfo *full_msginfo;
1854 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1856 full_msginfo = procmsg_msginfo_copy(msginfo);
1858 /* use the forward format of folder (if enabled), or the account's one
1859 (if enabled) or fallback to the global forward format, which is always
1860 enabled (even if empty), and use the relevant quotemark */
1861 if (msginfo->folder && msginfo->folder->prefs &&
1862 msginfo->folder->prefs->forward_with_format) {
1863 qmark = msginfo->folder->prefs->forward_quotemark;
1864 body_fmt = msginfo->folder->prefs->forward_body_format;
1866 } else if (account->forward_with_format) {
1867 qmark = account->forward_quotemark;
1868 body_fmt = account->forward_body_format;
1871 qmark = prefs_common.fw_quotemark;
1872 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1873 body_fmt = gettext(prefs_common.fw_quotefmt);
1878 /* empty quotemark is not allowed */
1879 if (qmark == NULL || *qmark == '\0')
1882 compose_quote_fmt(compose, full_msginfo,
1883 body_fmt, qmark, body, FALSE, TRUE,
1884 _("The body of the \"Forward\" template has an error at line %d."));
1885 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1886 quote_fmt_reset_vartable();
1887 compose_attach_parts(compose, msginfo);
1889 procmsg_msginfo_free(&full_msginfo);
1892 SIGNAL_BLOCK(textbuf);
1894 if (account->auto_sig)
1895 compose_insert_sig(compose, FALSE);
1897 compose_wrap_all(compose);
1900 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1901 gtkaspell_highlight_all(compose->gtkaspell);
1902 gtkaspell_unblock_check(compose->gtkaspell);
1904 SIGNAL_UNBLOCK(textbuf);
1906 cursor_pos = quote_fmt_get_cursor_pos();
1907 if (cursor_pos == -1)
1908 gtk_widget_grab_focus(compose->header_last->entry);
1910 gtk_widget_grab_focus(compose->text);
1912 if (!no_extedit && prefs_common.auto_exteditor)
1913 compose_exec_ext_editor(compose);
1916 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1917 gchar *folderidentifier;
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_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2410 compose_set_save_to(compose, &queueheader_buf[4]);
2411 g_free(queueheader_buf);
2413 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2414 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2416 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2418 g_free(queueheader_buf);
2422 if (compose_parse_header(compose, msginfo) < 0) {
2423 compose->updating = FALSE;
2424 compose_destroy(compose);
2427 compose_reedit_set_entry(compose, msginfo);
2429 textview = GTK_TEXT_VIEW(compose->text);
2430 textbuf = gtk_text_view_get_buffer(textview);
2431 compose_create_tags(textview, compose);
2433 mark = gtk_text_buffer_get_insert(textbuf);
2434 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2436 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2437 G_CALLBACK(compose_changed_cb),
2440 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2441 fp = procmime_get_first_encrypted_text_content(msginfo);
2443 compose_force_encryption(compose, account, TRUE, NULL);
2446 fp = procmime_get_first_text_content(msginfo);
2449 g_warning("Can't get text part");
2453 gchar buf[BUFFSIZE];
2454 gboolean prev_autowrap;
2455 GtkTextBuffer *buffer;
2457 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
2459 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2465 compose_attach_parts(compose, msginfo);
2467 compose_colorize_signature(compose);
2469 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2470 G_CALLBACK(compose_changed_cb),
2473 if (manual_headers != NULL) {
2474 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2475 procheader_entries_free(manual_headers);
2476 compose->updating = FALSE;
2477 compose_destroy(compose);
2480 procheader_entries_free(manual_headers);
2483 gtk_widget_grab_focus(compose->text);
2485 if (prefs_common.auto_exteditor) {
2486 compose_exec_ext_editor(compose);
2488 compose->modified = FALSE;
2489 compose_set_title(compose);
2491 compose->updating = FALSE;
2492 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2493 SCROLL_TO_CURSOR(compose);
2495 if (compose->deferred_destroy) {
2496 compose_destroy(compose);
2500 compose->sig_str = account_get_signature_str(compose->account);
2502 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2507 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2514 cm_return_val_if_fail(msginfo != NULL, NULL);
2517 account = account_get_reply_account(msginfo,
2518 prefs_common.reply_account_autosel);
2519 cm_return_val_if_fail(account != NULL, NULL);
2521 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2522 compose_apply_folder_privacy_settings(compose, msginfo->folder);
2524 compose->updating = TRUE;
2526 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2527 compose->replyinfo = NULL;
2528 compose->fwdinfo = NULL;
2530 compose_show_first_last_header(compose, TRUE);
2532 gtk_widget_grab_focus(compose->header_last->entry);
2534 filename = procmsg_get_message_file(msginfo);
2536 if (filename == NULL) {
2537 compose->updating = FALSE;
2538 compose_destroy(compose);
2543 compose->redirect_filename = filename;
2545 /* Set save folder */
2546 item = msginfo->folder;
2547 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2548 gchar *folderidentifier;
2550 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2551 folderidentifier = folder_item_get_identifier(item);
2552 compose_set_save_to(compose, folderidentifier);
2553 g_free(folderidentifier);
2556 compose_attach_parts(compose, msginfo);
2558 if (msginfo->subject)
2559 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2561 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2563 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2564 _("The body of the \"Redirect\" template has an error at line %d."));
2565 quote_fmt_reset_vartable();
2566 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2568 compose_colorize_signature(compose);
2571 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2572 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2573 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2575 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2576 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2577 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2578 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2579 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2581 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2585 if (compose->toolbar->draft_btn)
2586 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2587 if (compose->toolbar->insert_btn)
2588 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2589 if (compose->toolbar->attach_btn)
2590 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2591 if (compose->toolbar->sig_btn)
2592 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2593 if (compose->toolbar->exteditor_btn)
2594 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2595 if (compose->toolbar->linewrap_current_btn)
2596 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2597 if (compose->toolbar->linewrap_all_btn)
2598 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2600 compose->modified = FALSE;
2601 compose_set_title(compose);
2602 compose->updating = FALSE;
2603 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2604 SCROLL_TO_CURSOR(compose);
2606 if (compose->deferred_destroy) {
2607 compose_destroy(compose);
2611 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2616 const GList *compose_get_compose_list(void)
2618 return compose_list;
2621 void compose_entry_append(Compose *compose, const gchar *address,
2622 ComposeEntryType type, ComposePrefType pref_type)
2624 const gchar *header;
2626 gboolean in_quote = FALSE;
2627 if (!address || *address == '\0') return;
2634 header = N_("Bcc:");
2636 case COMPOSE_REPLYTO:
2637 header = N_("Reply-To:");
2639 case COMPOSE_NEWSGROUPS:
2640 header = N_("Newsgroups:");
2642 case COMPOSE_FOLLOWUPTO:
2643 header = N_( "Followup-To:");
2645 case COMPOSE_INREPLYTO:
2646 header = N_( "In-Reply-To:");
2653 header = prefs_common_translated_header_name(header);
2655 cur = begin = (gchar *)address;
2657 /* we separate the line by commas, but not if we're inside a quoted
2659 while (*cur != '\0') {
2661 in_quote = !in_quote;
2662 if (*cur == ',' && !in_quote) {
2663 gchar *tmp = g_strdup(begin);
2665 tmp[cur-begin]='\0';
2668 while (*tmp == ' ' || *tmp == '\t')
2670 compose_add_header_entry(compose, header, tmp, pref_type);
2671 compose_entry_indicate(compose, tmp);
2678 gchar *tmp = g_strdup(begin);
2680 tmp[cur-begin]='\0';
2681 while (*tmp == ' ' || *tmp == '\t')
2683 compose_add_header_entry(compose, header, tmp, pref_type);
2684 compose_entry_indicate(compose, tmp);
2689 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2694 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2695 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2696 if (gtk_entry_get_text(entry) &&
2697 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2698 gtk_widget_modify_base(
2699 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2700 GTK_STATE_NORMAL, &default_header_bgcolor);
2701 gtk_widget_modify_text(
2702 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2703 GTK_STATE_NORMAL, &default_header_color);
2708 void compose_toolbar_cb(gint action, gpointer data)
2710 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2711 Compose *compose = (Compose*)toolbar_item->parent;
2713 cm_return_if_fail(compose != NULL);
2717 compose_send_cb(NULL, compose);
2720 compose_send_later_cb(NULL, compose);
2723 compose_draft(compose, COMPOSE_QUIT_EDITING);
2726 compose_insert_file_cb(NULL, compose);
2729 compose_attach_cb(NULL, compose);
2732 compose_insert_sig(compose, FALSE);
2735 compose_insert_sig(compose, TRUE);
2738 compose_ext_editor_cb(NULL, compose);
2740 case A_LINEWRAP_CURRENT:
2741 compose_beautify_paragraph(compose, NULL, TRUE);
2743 case A_LINEWRAP_ALL:
2744 compose_wrap_all_full(compose, TRUE);
2747 compose_address_cb(NULL, compose);
2750 case A_CHECK_SPELLING:
2751 compose_check_all(NULL, compose);
2754 case A_PRIVACY_SIGN:
2756 case A_PRIVACY_ENCRYPT:
2763 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2768 gchar *subject = NULL;
2772 gchar **attach = NULL;
2773 gchar *inreplyto = NULL;
2774 MailField mfield = NO_FIELD_PRESENT;
2776 /* get mailto parts but skip from */
2777 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2780 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2781 mfield = TO_FIELD_PRESENT;
2784 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2786 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2788 if (!g_utf8_validate (subject, -1, NULL)) {
2789 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2790 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2793 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2795 mfield = SUBJECT_FIELD_PRESENT;
2798 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2799 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2802 gboolean prev_autowrap = compose->autowrap;
2804 compose->autowrap = FALSE;
2806 mark = gtk_text_buffer_get_insert(buffer);
2807 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2809 if (!g_utf8_validate (body, -1, NULL)) {
2810 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2811 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2814 gtk_text_buffer_insert(buffer, &iter, body, -1);
2816 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2818 compose->autowrap = prev_autowrap;
2819 if (compose->autowrap)
2820 compose_wrap_all(compose);
2821 mfield = BODY_FIELD_PRESENT;
2825 gint i = 0, att = 0;
2826 gchar *warn_files = NULL;
2827 while (attach[i] != NULL) {
2828 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2829 if (utf8_filename) {
2830 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2831 gchar *tmp = g_strdup_printf("%s%s\n",
2832 warn_files?warn_files:"",
2838 g_free(utf8_filename);
2840 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2845 alertpanel_notice(ngettext(
2846 "The following file has been attached: \n%s",
2847 "The following files have been attached: \n%s", att), warn_files);
2852 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2865 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2867 static HeaderEntry hentry[] = {
2868 {"Reply-To:", NULL, TRUE },
2869 {"Cc:", NULL, TRUE },
2870 {"References:", NULL, FALSE },
2871 {"Bcc:", NULL, TRUE },
2872 {"Newsgroups:", NULL, TRUE },
2873 {"Followup-To:", NULL, TRUE },
2874 {"List-Post:", NULL, FALSE },
2875 {"X-Priority:", NULL, FALSE },
2876 {NULL, NULL, FALSE }
2893 cm_return_val_if_fail(msginfo != NULL, -1);
2895 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2896 procheader_get_header_fields(fp, hentry);
2899 if (hentry[H_REPLY_TO].body != NULL) {
2900 if (hentry[H_REPLY_TO].body[0] != '\0') {
2902 conv_unmime_header(hentry[H_REPLY_TO].body,
2905 g_free(hentry[H_REPLY_TO].body);
2906 hentry[H_REPLY_TO].body = NULL;
2908 if (hentry[H_CC].body != NULL) {
2909 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2910 g_free(hentry[H_CC].body);
2911 hentry[H_CC].body = NULL;
2913 if (hentry[H_REFERENCES].body != NULL) {
2914 if (compose->mode == COMPOSE_REEDIT)
2915 compose->references = hentry[H_REFERENCES].body;
2917 compose->references = compose_parse_references
2918 (hentry[H_REFERENCES].body, msginfo->msgid);
2919 g_free(hentry[H_REFERENCES].body);
2921 hentry[H_REFERENCES].body = NULL;
2923 if (hentry[H_BCC].body != NULL) {
2924 if (compose->mode == COMPOSE_REEDIT)
2926 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2927 g_free(hentry[H_BCC].body);
2928 hentry[H_BCC].body = NULL;
2930 if (hentry[H_NEWSGROUPS].body != NULL) {
2931 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2932 hentry[H_NEWSGROUPS].body = NULL;
2934 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2935 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2936 compose->followup_to =
2937 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2940 g_free(hentry[H_FOLLOWUP_TO].body);
2941 hentry[H_FOLLOWUP_TO].body = NULL;
2943 if (hentry[H_LIST_POST].body != NULL) {
2944 gchar *to = NULL, *start = NULL;
2946 extract_address(hentry[H_LIST_POST].body);
2947 if (hentry[H_LIST_POST].body[0] != '\0') {
2948 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2950 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2951 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2954 g_free(compose->ml_post);
2955 compose->ml_post = to;
2958 g_free(hentry[H_LIST_POST].body);
2959 hentry[H_LIST_POST].body = NULL;
2962 /* CLAWS - X-Priority */
2963 if (compose->mode == COMPOSE_REEDIT)
2964 if (hentry[H_X_PRIORITY].body != NULL) {
2967 priority = atoi(hentry[H_X_PRIORITY].body);
2968 g_free(hentry[H_X_PRIORITY].body);
2970 hentry[H_X_PRIORITY].body = NULL;
2972 if (priority < PRIORITY_HIGHEST ||
2973 priority > PRIORITY_LOWEST)
2974 priority = PRIORITY_NORMAL;
2976 compose->priority = priority;
2979 if (compose->mode == COMPOSE_REEDIT) {
2980 if (msginfo->inreplyto && *msginfo->inreplyto)
2981 compose->inreplyto = g_strdup(msginfo->inreplyto);
2983 if (msginfo->msgid && *msginfo->msgid &&
2984 compose->folder != NULL &&
2985 compose->folder->stype == F_DRAFT)
2986 compose->msgid = g_strdup(msginfo->msgid);
2988 if (msginfo->msgid && *msginfo->msgid)
2989 compose->inreplyto = g_strdup(msginfo->msgid);
2991 if (!compose->references) {
2992 if (msginfo->msgid && *msginfo->msgid) {
2993 if (msginfo->inreplyto && *msginfo->inreplyto)
2994 compose->references =
2995 g_strdup_printf("<%s>\n\t<%s>",
2999 compose->references =
3000 g_strconcat("<", msginfo->msgid, ">",
3002 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3003 compose->references =
3004 g_strconcat("<", msginfo->inreplyto, ">",
3013 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3018 cm_return_val_if_fail(msginfo != NULL, -1);
3020 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3021 procheader_get_header_fields(fp, entries);
3025 while (he != NULL && he->name != NULL) {
3027 GtkListStore *model = NULL;
3029 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3030 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3031 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3032 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3033 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3040 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3042 GSList *ref_id_list, *cur;
3046 ref_id_list = references_list_append(NULL, ref);
3047 if (!ref_id_list) return NULL;
3048 if (msgid && *msgid)
3049 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3054 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3055 /* "<" + Message-ID + ">" + CR+LF+TAB */
3056 len += strlen((gchar *)cur->data) + 5;
3058 if (len > MAX_REFERENCES_LEN) {
3059 /* remove second message-ID */
3060 if (ref_id_list && ref_id_list->next &&
3061 ref_id_list->next->next) {
3062 g_free(ref_id_list->next->data);
3063 ref_id_list = g_slist_remove
3064 (ref_id_list, ref_id_list->next->data);
3066 slist_free_strings_full(ref_id_list);
3073 new_ref = g_string_new("");
3074 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3075 if (new_ref->len > 0)
3076 g_string_append(new_ref, "\n\t");
3077 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3080 slist_free_strings_full(ref_id_list);
3082 new_ref_str = new_ref->str;
3083 g_string_free(new_ref, FALSE);
3088 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3089 const gchar *fmt, const gchar *qmark,
3090 const gchar *body, gboolean rewrap,
3091 gboolean need_unescape,
3092 const gchar *err_msg)
3094 MsgInfo* dummyinfo = NULL;
3095 gchar *quote_str = NULL;
3097 gboolean prev_autowrap;
3098 const gchar *trimmed_body = body;
3099 gint cursor_pos = -1;
3100 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3101 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3106 SIGNAL_BLOCK(buffer);
3109 dummyinfo = compose_msginfo_new_from_compose(compose);
3110 msginfo = dummyinfo;
3113 if (qmark != NULL) {
3115 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3116 compose->gtkaspell);
3118 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3120 quote_fmt_scan_string(qmark);
3123 buf = quote_fmt_get_buffer();
3126 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3128 Xstrdup_a(quote_str, buf, goto error)
3131 if (fmt && *fmt != '\0') {
3134 while (*trimmed_body == '\n')
3138 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3139 compose->gtkaspell);
3141 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3143 if (need_unescape) {
3146 /* decode \-escape sequences in the internal representation of the quote format */
3147 tmp = g_malloc(strlen(fmt)+1);
3148 pref_get_unescaped_pref(tmp, fmt);
3149 quote_fmt_scan_string(tmp);
3153 quote_fmt_scan_string(fmt);
3157 buf = quote_fmt_get_buffer();
3160 gint line = quote_fmt_get_line();
3161 alertpanel_error(err_msg, line);
3169 prev_autowrap = compose->autowrap;
3170 compose->autowrap = FALSE;
3172 mark = gtk_text_buffer_get_insert(buffer);
3173 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3174 if (g_utf8_validate(buf, -1, NULL)) {
3175 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3177 gchar *tmpout = NULL;
3178 tmpout = conv_codeset_strdup
3179 (buf, conv_get_locale_charset_str_no_utf8(),
3181 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3183 tmpout = g_malloc(strlen(buf)*2+1);
3184 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3186 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3190 cursor_pos = quote_fmt_get_cursor_pos();
3191 if (cursor_pos == -1)
3192 cursor_pos = gtk_text_iter_get_offset(&iter);
3193 compose->set_cursor_pos = cursor_pos;
3195 gtk_text_buffer_get_start_iter(buffer, &iter);
3196 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3197 gtk_text_buffer_place_cursor(buffer, &iter);
3199 compose->autowrap = prev_autowrap;
3200 if (compose->autowrap && rewrap)
3201 compose_wrap_all(compose);
3208 SIGNAL_UNBLOCK(buffer);
3210 procmsg_msginfo_free( &dummyinfo );
3215 /* if ml_post is of type addr@host and from is of type
3216 * addr-anything@host, return TRUE
3218 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3220 gchar *left_ml = NULL;
3221 gchar *right_ml = NULL;
3222 gchar *left_from = NULL;
3223 gchar *right_from = NULL;
3224 gboolean result = FALSE;
3226 if (!ml_post || !from)
3229 left_ml = g_strdup(ml_post);
3230 if (strstr(left_ml, "@")) {
3231 right_ml = strstr(left_ml, "@")+1;
3232 *(strstr(left_ml, "@")) = '\0';
3235 left_from = g_strdup(from);
3236 if (strstr(left_from, "@")) {
3237 right_from = strstr(left_from, "@")+1;
3238 *(strstr(left_from, "@")) = '\0';
3241 if (right_ml && right_from
3242 && !strncmp(left_from, left_ml, strlen(left_ml))
3243 && !strcmp(right_from, right_ml)) {
3252 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3253 gboolean respect_default_to)
3257 if (!folder || !folder->prefs)
3260 if (respect_default_to && folder->prefs->enable_default_to) {
3261 compose_entry_append(compose, folder->prefs->default_to,
3262 COMPOSE_TO, PREF_FOLDER);
3263 compose_entry_indicate(compose, folder->prefs->default_to);
3265 if (folder->prefs->enable_default_cc) {
3266 compose_entry_append(compose, folder->prefs->default_cc,
3267 COMPOSE_CC, PREF_FOLDER);
3268 compose_entry_indicate(compose, folder->prefs->default_cc);
3270 if (folder->prefs->enable_default_bcc) {
3271 compose_entry_append(compose, folder->prefs->default_bcc,
3272 COMPOSE_BCC, PREF_FOLDER);
3273 compose_entry_indicate(compose, folder->prefs->default_bcc);
3275 if (folder->prefs->enable_default_replyto) {
3276 compose_entry_append(compose, folder->prefs->default_replyto,
3277 COMPOSE_REPLYTO, PREF_FOLDER);
3278 compose_entry_indicate(compose, folder->prefs->default_replyto);
3282 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3287 if (!compose || !msginfo)
3290 if (msginfo->subject && *msginfo->subject) {
3291 buf = p = g_strdup(msginfo->subject);
3292 p += subject_get_prefix_length(p);
3293 memmove(buf, p, strlen(p) + 1);
3295 buf2 = g_strdup_printf("Re: %s", buf);
3296 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3301 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3304 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3305 gboolean to_all, gboolean to_ml,
3307 gboolean followup_and_reply_to)
3309 GSList *cc_list = NULL;
3312 gchar *replyto = NULL;
3313 gchar *ac_email = NULL;
3315 gboolean reply_to_ml = FALSE;
3316 gboolean default_reply_to = FALSE;
3318 cm_return_if_fail(compose->account != NULL);
3319 cm_return_if_fail(msginfo != NULL);
3321 reply_to_ml = to_ml && compose->ml_post;
3323 default_reply_to = msginfo->folder &&
3324 msginfo->folder->prefs->enable_default_reply_to;
3326 if (compose->account->protocol != A_NNTP) {
3327 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3329 if (reply_to_ml && !default_reply_to) {
3331 gboolean is_subscr = is_subscription(compose->ml_post,
3334 /* normal answer to ml post with a reply-to */
3335 compose_entry_append(compose,
3337 COMPOSE_TO, PREF_ML);
3338 if (compose->replyto)
3339 compose_entry_append(compose,
3341 COMPOSE_CC, PREF_ML);
3343 /* answer to subscription confirmation */
3344 if (compose->replyto)
3345 compose_entry_append(compose,
3347 COMPOSE_TO, PREF_ML);
3348 else if (msginfo->from)
3349 compose_entry_append(compose,
3351 COMPOSE_TO, PREF_ML);
3354 else if (!(to_all || to_sender) && default_reply_to) {
3355 compose_entry_append(compose,
3356 msginfo->folder->prefs->default_reply_to,
3357 COMPOSE_TO, PREF_FOLDER);
3358 compose_entry_indicate(compose,
3359 msginfo->folder->prefs->default_reply_to);
3365 compose_entry_append(compose, msginfo->from,
3366 COMPOSE_TO, PREF_NONE);
3368 Xstrdup_a(tmp1, msginfo->from, return);
3369 extract_address(tmp1);
3370 compose_entry_append(compose,
3371 (!account_find_from_address(tmp1, FALSE))
3374 COMPOSE_TO, PREF_NONE);
3375 if (compose->replyto)
3376 compose_entry_append(compose,
3378 COMPOSE_CC, PREF_NONE);
3380 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3381 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3382 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3383 if (compose->replyto) {
3384 compose_entry_append(compose,
3386 COMPOSE_TO, PREF_NONE);
3388 compose_entry_append(compose,
3389 msginfo->from ? msginfo->from : "",
3390 COMPOSE_TO, PREF_NONE);
3393 /* replying to own mail, use original recp */
3394 compose_entry_append(compose,
3395 msginfo->to ? msginfo->to : "",
3396 COMPOSE_TO, PREF_NONE);
3397 compose_entry_append(compose,
3398 msginfo->cc ? msginfo->cc : "",
3399 COMPOSE_CC, PREF_NONE);
3404 if (to_sender || (compose->followup_to &&
3405 !strncmp(compose->followup_to, "poster", 6)))
3406 compose_entry_append
3408 (compose->replyto ? compose->replyto :
3409 msginfo->from ? msginfo->from : ""),
3410 COMPOSE_TO, PREF_NONE);
3412 else if (followup_and_reply_to || to_all) {
3413 compose_entry_append
3415 (compose->replyto ? compose->replyto :
3416 msginfo->from ? msginfo->from : ""),
3417 COMPOSE_TO, PREF_NONE);
3419 compose_entry_append
3421 compose->followup_to ? compose->followup_to :
3422 compose->newsgroups ? compose->newsgroups : "",
3423 COMPOSE_NEWSGROUPS, PREF_NONE);
3425 compose_entry_append
3427 msginfo->cc ? msginfo->cc : "",
3428 COMPOSE_CC, PREF_NONE);
3431 compose_entry_append
3433 compose->followup_to ? compose->followup_to :
3434 compose->newsgroups ? compose->newsgroups : "",
3435 COMPOSE_NEWSGROUPS, PREF_NONE);
3437 compose_reply_set_subject(compose, msginfo);
3439 if (to_ml && compose->ml_post) return;
3440 if (!to_all || compose->account->protocol == A_NNTP) return;
3442 if (compose->replyto) {
3443 Xstrdup_a(replyto, compose->replyto, return);
3444 extract_address(replyto);
3446 if (msginfo->from) {
3447 Xstrdup_a(from, msginfo->from, return);
3448 extract_address(from);
3451 if (replyto && from)
3452 cc_list = address_list_append_with_comments(cc_list, from);
3453 if (to_all && msginfo->folder &&
3454 msginfo->folder->prefs->enable_default_reply_to)
3455 cc_list = address_list_append_with_comments(cc_list,
3456 msginfo->folder->prefs->default_reply_to);
3457 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3458 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3460 ac_email = g_utf8_strdown(compose->account->address, -1);
3463 for (cur = cc_list; cur != NULL; cur = cur->next) {
3464 gchar *addr = g_utf8_strdown(cur->data, -1);
3465 extract_address(addr);
3467 if (strcmp(ac_email, addr))
3468 compose_entry_append(compose, (gchar *)cur->data,
3469 COMPOSE_CC, PREF_NONE);
3471 debug_print("Cc address same as compose account's, ignoring\n");
3476 slist_free_strings_full(cc_list);
3482 #define SET_ENTRY(entry, str) \
3485 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3488 #define SET_ADDRESS(type, str) \
3491 compose_entry_append(compose, str, type, PREF_NONE); \
3494 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3496 cm_return_if_fail(msginfo != NULL);
3498 SET_ENTRY(subject_entry, msginfo->subject);
3499 SET_ENTRY(from_name, msginfo->from);
3500 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3501 SET_ADDRESS(COMPOSE_CC, compose->cc);
3502 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3503 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3504 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3505 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3507 compose_update_priority_menu_item(compose);
3508 compose_update_privacy_system_menu_item(compose, FALSE);
3509 compose_show_first_last_header(compose, TRUE);
3515 static void compose_insert_sig(Compose *compose, gboolean replace)
3517 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3518 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3520 GtkTextIter iter, iter_end;
3521 gint cur_pos, ins_pos;
3522 gboolean prev_autowrap;
3523 gboolean found = FALSE;
3524 gboolean exists = FALSE;
3526 cm_return_if_fail(compose->account != NULL);
3530 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3531 G_CALLBACK(compose_changed_cb),
3534 mark = gtk_text_buffer_get_insert(buffer);
3535 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3536 cur_pos = gtk_text_iter_get_offset (&iter);
3539 gtk_text_buffer_get_end_iter(buffer, &iter);
3541 exists = (compose->sig_str != NULL);
3544 GtkTextIter first_iter, start_iter, end_iter;
3546 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3548 if (!exists || compose->sig_str[0] == '\0')
3551 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3552 compose->signature_tag);
3555 /* include previous \n\n */
3556 gtk_text_iter_backward_chars(&first_iter, 1);
3557 start_iter = first_iter;
3558 end_iter = first_iter;
3560 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3561 compose->signature_tag);
3562 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3563 compose->signature_tag);
3565 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3571 g_free(compose->sig_str);
3572 compose->sig_str = account_get_signature_str(compose->account);
3574 cur_pos = gtk_text_iter_get_offset(&iter);
3576 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3577 g_free(compose->sig_str);
3578 compose->sig_str = NULL;
3580 if (compose->sig_inserted == FALSE)
3581 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3582 compose->sig_inserted = TRUE;
3584 cur_pos = gtk_text_iter_get_offset(&iter);
3585 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3587 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3588 gtk_text_iter_forward_chars(&iter, 1);
3589 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3590 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3592 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3593 cur_pos = gtk_text_buffer_get_char_count (buffer);
3596 /* put the cursor where it should be
3597 * either where the quote_fmt says, either where it was */
3598 if (compose->set_cursor_pos < 0)
3599 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3601 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3602 compose->set_cursor_pos);
3604 compose->set_cursor_pos = -1;
3605 gtk_text_buffer_place_cursor(buffer, &iter);
3606 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3607 G_CALLBACK(compose_changed_cb),
3613 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3616 GtkTextBuffer *buffer;
3619 const gchar *cur_encoding;
3620 gchar buf[BUFFSIZE];
3623 gboolean prev_autowrap;
3627 GError *error = NULL;
3633 GString *file_contents = NULL;
3634 ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3636 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3638 /* get the size of the file we are about to insert */
3640 f = g_file_new_for_path(file);