2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
186 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
188 static GdkColor default_header_bgcolor = {
195 static GdkColor default_header_color = {
202 static GList *compose_list = NULL;
203 static GSList *extra_headers = NULL;
205 static Compose *compose_generic_new (PrefsAccount *account,
209 GList *listAddress );
211 static Compose *compose_create (PrefsAccount *account,
216 static void compose_entry_indicate (Compose *compose,
217 const gchar *address);
218 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
219 ComposeQuoteMode quote_mode,
223 static Compose *compose_forward_multiple (PrefsAccount *account,
224 GSList *msginfo_list);
225 static Compose *compose_reply (MsgInfo *msginfo,
226 ComposeQuoteMode quote_mode,
231 static Compose *compose_reply_mode (ComposeMode mode,
232 GSList *msginfo_list,
234 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
235 static void compose_update_privacy_systems_menu(Compose *compose);
237 static GtkWidget *compose_account_option_menu_create
239 static void compose_set_out_encoding (Compose *compose);
240 static void compose_set_template_menu (Compose *compose);
241 static void compose_destroy (Compose *compose);
243 static MailField compose_entries_set (Compose *compose,
245 ComposeEntryType to_type);
246 static gint compose_parse_header (Compose *compose,
248 static gint compose_parse_manual_headers (Compose *compose,
250 HeaderEntry *entries);
251 static gchar *compose_parse_references (const gchar *ref,
254 static gchar *compose_quote_fmt (Compose *compose,
260 gboolean need_unescape,
261 const gchar *err_msg);
263 static void compose_reply_set_entry (Compose *compose,
269 followup_and_reply_to);
270 static void compose_reedit_set_entry (Compose *compose,
273 static void compose_insert_sig (Compose *compose,
275 static ComposeInsertResult compose_insert_file (Compose *compose,
278 static gboolean compose_attach_append (Compose *compose,
281 const gchar *content_type,
282 const gchar *charset);
283 static void compose_attach_parts (Compose *compose,
286 static gboolean compose_beautify_paragraph (Compose *compose,
287 GtkTextIter *par_iter,
289 static void compose_wrap_all (Compose *compose);
290 static void compose_wrap_all_full (Compose *compose,
293 static void compose_set_title (Compose *compose);
294 static void compose_select_account (Compose *compose,
295 PrefsAccount *account,
298 static PrefsAccount *compose_current_mail_account(void);
299 /* static gint compose_send (Compose *compose); */
300 static gboolean compose_check_for_valid_recipient
302 static gboolean compose_check_entries (Compose *compose,
303 gboolean check_everything);
304 static gint compose_write_to_file (Compose *compose,
307 gboolean attach_parts);
308 static gint compose_write_body_to_file (Compose *compose,
310 static gint compose_remove_reedit_target (Compose *compose,
312 static void compose_remove_draft (Compose *compose);
313 static gint compose_queue_sub (Compose *compose,
317 gboolean perform_checks,
318 gboolean remove_reedit_target);
319 static int compose_add_attachments (Compose *compose,
321 static gchar *compose_get_header (Compose *compose);
322 static gchar *compose_get_manual_headers_info (Compose *compose);
324 static void compose_convert_header (Compose *compose,
329 gboolean addr_field);
331 static void compose_attach_info_free (AttachInfo *ainfo);
332 static void compose_attach_remove_selected (GtkAction *action,
335 static void compose_template_apply (Compose *compose,
338 static void compose_attach_property (GtkAction *action,
340 static void compose_attach_property_create (gboolean *cancelled);
341 static void attach_property_ok (GtkWidget *widget,
342 gboolean *cancelled);
343 static void attach_property_cancel (GtkWidget *widget,
344 gboolean *cancelled);
345 static gint attach_property_delete_event (GtkWidget *widget,
347 gboolean *cancelled);
348 static gboolean attach_property_key_pressed (GtkWidget *widget,
350 gboolean *cancelled);
352 static void compose_exec_ext_editor (Compose *compose);
354 static gint compose_exec_ext_editor_real (const gchar *file,
355 GdkNativeWindow socket_wid);
356 static gboolean compose_ext_editor_kill (Compose *compose);
357 static gboolean compose_input_cb (GIOChannel *source,
358 GIOCondition condition,
360 static void compose_set_ext_editor_sensitive (Compose *compose,
362 static gboolean compose_get_ext_editor_cmd_valid();
363 static gboolean compose_get_ext_editor_uses_socket();
364 static gboolean compose_ext_editor_plug_removed_cb
367 #endif /* G_OS_UNIX */
369 static void compose_undo_state_changed (UndoMain *undostruct,
374 static void compose_create_header_entry (Compose *compose);
375 static void compose_add_header_entry (Compose *compose, const gchar *header,
376 gchar *text, ComposePrefType pref_type);
377 static void compose_remove_header_entries(Compose *compose);
379 static void compose_update_priority_menu_item(Compose * compose);
381 static void compose_spell_menu_changed (void *data);
382 static void compose_dict_changed (void *data);
384 static void compose_add_field_list ( Compose *compose,
385 GList *listAddress );
387 /* callback functions */
389 static void compose_notebook_size_alloc (GtkNotebook *notebook,
390 GtkAllocation *allocation,
392 static gboolean compose_edit_size_alloc (GtkEditable *widget,
393 GtkAllocation *allocation,
394 GtkSHRuler *shruler);
395 static void account_activated (GtkComboBox *optmenu,
397 static void attach_selected (GtkTreeView *tree_view,
398 GtkTreePath *tree_path,
399 GtkTreeViewColumn *column,
401 static gboolean attach_button_pressed (GtkWidget *widget,
402 GdkEventButton *event,
404 static gboolean attach_key_pressed (GtkWidget *widget,
407 static void compose_send_cb (GtkAction *action, gpointer data);
408 static void compose_send_later_cb (GtkAction *action, gpointer data);
410 static void compose_save_cb (GtkAction *action,
413 static void compose_attach_cb (GtkAction *action,
415 static void compose_insert_file_cb (GtkAction *action,
417 static void compose_insert_sig_cb (GtkAction *action,
419 static void compose_replace_sig_cb (GtkAction *action,
422 static void compose_close_cb (GtkAction *action,
424 static void compose_print_cb (GtkAction *action,
427 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
429 static void compose_address_cb (GtkAction *action,
431 static void about_show_cb (GtkAction *action,
433 static void compose_template_activate_cb(GtkWidget *widget,
436 static void compose_ext_editor_cb (GtkAction *action,
439 static gint compose_delete_cb (GtkWidget *widget,
443 static void compose_undo_cb (GtkAction *action,
445 static void compose_redo_cb (GtkAction *action,
447 static void compose_cut_cb (GtkAction *action,
449 static void compose_copy_cb (GtkAction *action,
451 static void compose_paste_cb (GtkAction *action,
453 static void compose_paste_as_quote_cb (GtkAction *action,
455 static void compose_paste_no_wrap_cb (GtkAction *action,
457 static void compose_paste_wrap_cb (GtkAction *action,
459 static void compose_allsel_cb (GtkAction *action,
462 static void compose_advanced_action_cb (GtkAction *action,
465 static void compose_grab_focus_cb (GtkWidget *widget,
468 static void compose_changed_cb (GtkTextBuffer *textbuf,
471 static void compose_wrap_cb (GtkAction *action,
473 static void compose_wrap_all_cb (GtkAction *action,
475 static void compose_find_cb (GtkAction *action,
477 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
479 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
482 static void compose_toggle_ruler_cb (GtkToggleAction *action,
484 static void compose_toggle_sign_cb (GtkToggleAction *action,
486 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
488 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
489 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
490 static void activate_privacy_system (Compose *compose,
491 PrefsAccount *account,
493 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
495 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
497 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
498 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
499 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
501 static void compose_attach_drag_received_cb (GtkWidget *widget,
502 GdkDragContext *drag_context,
505 GtkSelectionData *data,
509 static void compose_insert_drag_received_cb (GtkWidget *widget,
510 GdkDragContext *drag_context,
513 GtkSelectionData *data,
517 static void compose_header_drag_received_cb (GtkWidget *widget,
518 GdkDragContext *drag_context,
521 GtkSelectionData *data,
526 static gboolean compose_drag_drop (GtkWidget *widget,
527 GdkDragContext *drag_context,
529 guint time, gpointer user_data);
530 static gboolean completion_set_focus_to_subject
535 static void text_inserted (GtkTextBuffer *buffer,
540 static Compose *compose_generic_reply(MsgInfo *msginfo,
541 ComposeQuoteMode quote_mode,
545 gboolean followup_and_reply_to,
548 static void compose_headerentry_changed_cb (GtkWidget *entry,
549 ComposeHeaderEntry *headerentry);
550 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
552 ComposeHeaderEntry *headerentry);
553 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
554 ComposeHeaderEntry *headerentry);
556 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
558 static void compose_allow_user_actions (Compose *compose, gboolean allow);
560 static void compose_nothing_cb (GtkAction *action, gpointer data)
566 static void compose_check_all (GtkAction *action, gpointer data);
567 static void compose_highlight_all (GtkAction *action, gpointer data);
568 static void compose_check_backwards (GtkAction *action, gpointer data);
569 static void compose_check_forwards_go (GtkAction *action, gpointer data);
572 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
574 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
577 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
578 FolderItem *folder_item);
580 static void compose_attach_update_label(Compose *compose);
581 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
582 gboolean respect_default_to);
583 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
584 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
586 static GtkActionEntry compose_popup_entries[] =
588 {"Compose", NULL, "Compose", NULL, NULL, NULL },
589 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
590 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
591 {"Compose/---", NULL, "---", NULL, NULL, NULL },
592 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
595 static GtkActionEntry compose_entries[] =
597 {"Menu", NULL, "Menu", NULL, NULL, NULL },
599 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
600 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
602 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
604 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
605 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
606 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
608 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
609 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
610 {"Message/---", NULL, "---", NULL, NULL, NULL },
612 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
613 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
614 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
615 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
616 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
617 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
618 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
619 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
620 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
621 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
624 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
625 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
626 {"Edit/---", NULL, "---", NULL, NULL, NULL },
628 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
629 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
630 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
632 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
633 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
634 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
635 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
637 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
639 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
640 {"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*/
641 {"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*/
642 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
643 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
644 {"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*/
645 {"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*/
646 {"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*/
647 {"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*/
648 {"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*/
649 {"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*/
650 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
651 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
652 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
653 {"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*/
655 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
656 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
658 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
659 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
660 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
661 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
662 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
665 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
666 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
667 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
668 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
670 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
671 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
675 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
676 {"Options/---", NULL, "---", NULL, NULL, NULL },
677 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
678 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
681 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
683 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
684 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
685 #define ENC_ACTION(cs_char,c_char,string) \
686 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
688 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
689 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
690 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
691 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
692 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
693 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
694 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
695 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
696 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
699 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
701 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
702 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
703 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
704 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
707 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
710 static GtkToggleActionEntry compose_toggle_entries[] =
712 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
713 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
714 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
715 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
716 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
717 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
718 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
721 static GtkRadioActionEntry compose_radio_rm_entries[] =
723 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
724 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
725 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
726 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
729 static GtkRadioActionEntry compose_radio_prio_entries[] =
731 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
732 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
733 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
734 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
735 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
738 static GtkRadioActionEntry compose_radio_enc_entries[] =
740 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
775 static GtkTargetEntry compose_mime_types[] =
777 {"text/uri-list", 0, 0},
778 {"UTF8_STRING", 0, 0},
782 static gboolean compose_put_existing_to_front(MsgInfo *info)
784 const GList *compose_list = compose_get_compose_list();
785 const GList *elem = NULL;
788 for (elem = compose_list; elem != NULL && elem->data != NULL;
790 Compose *c = (Compose*)elem->data;
792 if (!c->targetinfo || !c->targetinfo->msgid ||
796 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
797 gtkut_window_popup(c->window);
805 static GdkColor quote_color1 =
806 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
807 static GdkColor quote_color2 =
808 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 static GdkColor quote_color3 =
810 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
812 static GdkColor quote_bgcolor1 =
813 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
814 static GdkColor quote_bgcolor2 =
815 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor3 =
817 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
819 static GdkColor signature_color = {
826 static GdkColor uri_color = {
833 static void compose_create_tags(GtkTextView *text, Compose *compose)
835 GtkTextBuffer *buffer;
836 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
837 #if !GTK_CHECK_VERSION(2, 24, 0)
844 buffer = gtk_text_view_get_buffer(text);
846 if (prefs_common.enable_color) {
847 /* grab the quote colors, converting from an int to a GdkColor */
848 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
850 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
852 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
854 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
856 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
858 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
860 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
862 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
865 signature_color = quote_color1 = quote_color2 = quote_color3 =
866 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
869 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
870 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
871 "foreground-gdk", "e_color1,
872 "paragraph-background-gdk", "e_bgcolor1,
874 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
875 "foreground-gdk", "e_color2,
876 "paragraph-background-gdk", "e_bgcolor2,
878 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
879 "foreground-gdk", "e_color3,
880 "paragraph-background-gdk", "e_bgcolor3,
883 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
884 "foreground-gdk", "e_color1,
886 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
887 "foreground-gdk", "e_color2,
889 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
890 "foreground-gdk", "e_color3,
894 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
895 "foreground-gdk", &signature_color,
898 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
899 "foreground-gdk", &uri_color,
901 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
902 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
904 #if !GTK_CHECK_VERSION(2, 24, 0)
905 color[0] = quote_color1;
906 color[1] = quote_color2;
907 color[2] = quote_color3;
908 color[3] = quote_bgcolor1;
909 color[4] = quote_bgcolor2;
910 color[5] = quote_bgcolor3;
911 color[6] = signature_color;
912 color[7] = uri_color;
914 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
915 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
917 for (i = 0; i < 8; i++) {
918 if (success[i] == FALSE) {
919 g_warning("Compose: color allocation failed.");
920 quote_color1 = quote_color2 = quote_color3 =
921 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
922 signature_color = uri_color = black;
928 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
931 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
934 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
936 return compose_generic_new(account, mailto, item, NULL, NULL);
939 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
941 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
944 #define SCROLL_TO_CURSOR(compose) { \
945 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
946 gtk_text_view_get_buffer( \
947 GTK_TEXT_VIEW(compose->text))); \
948 gtk_text_view_scroll_mark_onscreen( \
949 GTK_TEXT_VIEW(compose->text), \
953 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
956 if (folderidentifier) {
957 #if !GTK_CHECK_VERSION(2, 24, 0)
958 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
960 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
962 prefs_common.compose_save_to_history = add_history(
963 prefs_common.compose_save_to_history, folderidentifier);
964 #if !GTK_CHECK_VERSION(2, 24, 0)
965 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
966 prefs_common.compose_save_to_history);
968 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
969 prefs_common.compose_save_to_history);
973 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
974 if (folderidentifier)
975 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
977 gtk_entry_set_text(GTK_ENTRY(entry), "");
980 static gchar *compose_get_save_to(Compose *compose)
983 gchar *result = NULL;
984 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
985 result = gtk_editable_get_chars(entry, 0, -1);
988 #if !GTK_CHECK_VERSION(2, 24, 0)
989 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
991 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
993 prefs_common.compose_save_to_history = add_history(
994 prefs_common.compose_save_to_history, result);
995 #if !GTK_CHECK_VERSION(2, 24, 0)
996 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
997 prefs_common.compose_save_to_history);
999 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1000 prefs_common.compose_save_to_history);
1006 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1007 GList *attach_files, GList *listAddress )
1010 GtkTextView *textview;
1011 GtkTextBuffer *textbuf;
1013 const gchar *subject_format = NULL;
1014 const gchar *body_format = NULL;
1015 gchar *mailto_from = NULL;
1016 PrefsAccount *mailto_account = NULL;
1017 MsgInfo* dummyinfo = NULL;
1018 gint cursor_pos = -1;
1019 MailField mfield = NO_FIELD_PRESENT;
1023 /* check if mailto defines a from */
1024 if (mailto && *mailto != '\0') {
1025 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1026 /* mailto defines a from, check if we can get account prefs from it,
1027 if not, the account prefs will be guessed using other ways, but we'll keep
1030 mailto_account = account_find_from_address(mailto_from, TRUE);
1031 if (mailto_account == NULL) {
1033 Xstrdup_a(tmp_from, mailto_from, return NULL);
1034 extract_address(tmp_from);
1035 mailto_account = account_find_from_address(tmp_from, TRUE);
1039 account = mailto_account;
1042 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1043 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1044 account = account_find_from_id(item->prefs->default_account);
1046 /* if no account prefs set, fallback to the current one */
1047 if (!account) account = cur_account;
1048 cm_return_val_if_fail(account != NULL, NULL);
1050 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1052 /* override from name if mailto asked for it */
1054 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1055 g_free(mailto_from);
1057 /* override from name according to folder properties */
1058 if (item && item->prefs &&
1059 item->prefs->compose_with_format &&
1060 item->prefs->compose_override_from_format &&
1061 *item->prefs->compose_override_from_format != '\0') {
1066 dummyinfo = compose_msginfo_new_from_compose(compose);
1068 /* decode \-escape sequences in the internal representation of the quote format */
1069 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1070 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1073 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1074 compose->gtkaspell);
1076 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1078 quote_fmt_scan_string(tmp);
1081 buf = quote_fmt_get_buffer();
1083 alertpanel_error(_("New message From format error."));
1085 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1086 quote_fmt_reset_vartable();
1087 quote_fmtlex_destroy();
1092 compose->replyinfo = NULL;
1093 compose->fwdinfo = NULL;
1095 textview = GTK_TEXT_VIEW(compose->text);
1096 textbuf = gtk_text_view_get_buffer(textview);
1097 compose_create_tags(textview, compose);
1099 undo_block(compose->undostruct);
1101 compose_set_dictionaries_from_folder_prefs(compose, item);
1104 if (account->auto_sig)
1105 compose_insert_sig(compose, FALSE);
1106 gtk_text_buffer_get_start_iter(textbuf, &iter);
1107 gtk_text_buffer_place_cursor(textbuf, &iter);
1109 if (account->protocol != A_NNTP) {
1110 if (mailto && *mailto != '\0') {
1111 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1114 compose_set_folder_prefs(compose, item, TRUE);
1116 if (item && item->ret_rcpt) {
1117 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1120 if (mailto && *mailto != '\0') {
1121 if (!strchr(mailto, '@'))
1122 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1124 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1125 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1126 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1127 mfield = TO_FIELD_PRESENT;
1130 * CLAWS: just don't allow return receipt request, even if the user
1131 * may want to send an email. simple but foolproof.
1133 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1135 compose_add_field_list( compose, listAddress );
1137 if (item && item->prefs && item->prefs->compose_with_format) {
1138 subject_format = item->prefs->compose_subject_format;
1139 body_format = item->prefs->compose_body_format;
1140 } else if (account->compose_with_format) {
1141 subject_format = account->compose_subject_format;
1142 body_format = account->compose_body_format;
1143 } else if (prefs_common.compose_with_format) {
1144 subject_format = prefs_common.compose_subject_format;
1145 body_format = prefs_common.compose_body_format;
1148 if (subject_format || body_format) {
1151 && *subject_format != '\0' )
1153 gchar *subject = NULL;
1158 dummyinfo = compose_msginfo_new_from_compose(compose);
1160 /* decode \-escape sequences in the internal representation of the quote format */
1161 tmp = g_malloc(strlen(subject_format)+1);
1162 pref_get_unescaped_pref(tmp, subject_format);
1164 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1166 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1167 compose->gtkaspell);
1169 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1171 quote_fmt_scan_string(tmp);
1174 buf = quote_fmt_get_buffer();
1176 alertpanel_error(_("New message subject format error."));
1178 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1179 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1180 quote_fmt_reset_vartable();
1181 quote_fmtlex_destroy();
1185 mfield = SUBJECT_FIELD_PRESENT;
1189 && *body_format != '\0' )
1192 GtkTextBuffer *buffer;
1193 GtkTextIter start, end;
1197 dummyinfo = compose_msginfo_new_from_compose(compose);
1199 text = GTK_TEXT_VIEW(compose->text);
1200 buffer = gtk_text_view_get_buffer(text);
1201 gtk_text_buffer_get_start_iter(buffer, &start);
1202 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1203 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1205 compose_quote_fmt(compose, dummyinfo,
1207 NULL, tmp, FALSE, TRUE,
1208 _("The body of the \"New message\" template has an error at line %d."));
1209 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1210 quote_fmt_reset_vartable();
1214 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1215 gtkaspell_highlight_all(compose->gtkaspell);
1217 mfield = BODY_FIELD_PRESENT;
1221 procmsg_msginfo_free( &dummyinfo );
1227 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1228 ainfo = (AttachInfo *) curr->data;
1230 compose_insert_file(compose, ainfo->file);
1232 compose_attach_append(compose, ainfo->file, ainfo->file,
1233 ainfo->content_type, ainfo->charset);
1237 compose_show_first_last_header(compose, TRUE);
1239 /* Set save folder */
1240 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1241 gchar *folderidentifier;
1243 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1244 folderidentifier = folder_item_get_identifier(item);
1245 compose_set_save_to(compose, folderidentifier);
1246 g_free(folderidentifier);
1249 /* Place cursor according to provided input (mfield) */
1251 case NO_FIELD_PRESENT:
1252 if (compose->header_last)
1253 gtk_widget_grab_focus(compose->header_last->entry);
1255 case TO_FIELD_PRESENT:
1256 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1258 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1261 gtk_widget_grab_focus(compose->subject_entry);
1263 case SUBJECT_FIELD_PRESENT:
1264 textview = GTK_TEXT_VIEW(compose->text);
1267 textbuf = gtk_text_view_get_buffer(textview);
1270 mark = gtk_text_buffer_get_insert(textbuf);
1271 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1272 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1274 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1275 * only defers where it comes to the variable body
1276 * is not null. If no body is present compose->text
1277 * will be null in which case you cannot place the
1278 * cursor inside the component so. An empty component
1279 * is therefore created before placing the cursor
1281 case BODY_FIELD_PRESENT:
1282 cursor_pos = quote_fmt_get_cursor_pos();
1283 if (cursor_pos == -1)
1284 gtk_widget_grab_focus(compose->header_last->entry);
1286 gtk_widget_grab_focus(compose->text);
1290 undo_unblock(compose->undostruct);
1292 if (prefs_common.auto_exteditor)
1293 compose_exec_ext_editor(compose);
1295 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1297 SCROLL_TO_CURSOR(compose);
1299 compose->modified = FALSE;
1300 compose_set_title(compose);
1302 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1307 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1308 gboolean override_pref, const gchar *system)
1310 const gchar *privacy = NULL;
1312 cm_return_if_fail(compose != NULL);
1313 cm_return_if_fail(account != NULL);
1315 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1318 if (account->default_privacy_system && strlen(account->default_privacy_system))
1319 privacy = account->default_privacy_system;
1323 GSList *privacy_avail = privacy_get_system_ids();
1324 if (privacy_avail && g_slist_length(privacy_avail)) {
1325 privacy = (gchar *)(privacy_avail->data);
1327 g_slist_free_full(privacy_avail, g_free);
1329 if (privacy != NULL) {
1331 g_free(compose->privacy_system);
1332 compose->privacy_system = NULL;
1333 g_free(compose->encdata);
1334 compose->encdata = NULL;
1336 if (compose->privacy_system == NULL)
1337 compose->privacy_system = g_strdup(privacy);
1338 else if (*(compose->privacy_system) == '\0') {
1339 g_free(compose->privacy_system);
1340 g_free(compose->encdata);
1341 compose->encdata = NULL;
1342 compose->privacy_system = g_strdup(privacy);
1344 compose_update_privacy_system_menu_item(compose, FALSE);
1345 compose_use_encryption(compose, TRUE);
1349 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1351 const gchar *privacy = NULL;
1353 if (account->default_privacy_system && strlen(account->default_privacy_system))
1354 privacy = account->default_privacy_system;
1358 GSList *privacy_avail = privacy_get_system_ids();
1359 if (privacy_avail && g_slist_length(privacy_avail)) {
1360 privacy = (gchar *)(privacy_avail->data);
1364 if (privacy != NULL) {
1366 g_free(compose->privacy_system);
1367 compose->privacy_system = NULL;
1368 g_free(compose->encdata);
1369 compose->encdata = NULL;
1371 if (compose->privacy_system == NULL)
1372 compose->privacy_system = g_strdup(privacy);
1373 compose_update_privacy_system_menu_item(compose, FALSE);
1374 compose_use_signing(compose, TRUE);
1378 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1382 Compose *compose = NULL;
1384 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1386 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1387 cm_return_val_if_fail(msginfo != NULL, NULL);
1389 list_len = g_slist_length(msginfo_list);
1393 case COMPOSE_REPLY_TO_ADDRESS:
1394 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1395 FALSE, prefs_common.default_reply_list, FALSE, body);
1397 case COMPOSE_REPLY_WITH_QUOTE:
1398 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1399 FALSE, prefs_common.default_reply_list, FALSE, body);
1401 case COMPOSE_REPLY_WITHOUT_QUOTE:
1402 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1403 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1405 case COMPOSE_REPLY_TO_SENDER:
1406 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1407 FALSE, FALSE, TRUE, body);
1409 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1410 compose = compose_followup_and_reply_to(msginfo,
1411 COMPOSE_QUOTE_CHECK,
1412 FALSE, FALSE, body);
1414 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1415 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1416 FALSE, FALSE, TRUE, body);
1418 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1419 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1420 FALSE, FALSE, TRUE, NULL);
1422 case COMPOSE_REPLY_TO_ALL:
1423 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1424 TRUE, FALSE, FALSE, body);
1426 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1427 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1428 TRUE, FALSE, FALSE, body);
1430 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1431 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1432 TRUE, FALSE, FALSE, NULL);
1434 case COMPOSE_REPLY_TO_LIST:
1435 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1436 FALSE, TRUE, FALSE, body);
1438 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1439 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1440 FALSE, TRUE, FALSE, body);
1442 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1443 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1444 FALSE, TRUE, FALSE, NULL);
1446 case COMPOSE_FORWARD:
1447 if (prefs_common.forward_as_attachment) {
1448 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1451 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1455 case COMPOSE_FORWARD_INLINE:
1456 /* check if we reply to more than one Message */
1457 if (list_len == 1) {
1458 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1461 /* more messages FALL THROUGH */
1462 case COMPOSE_FORWARD_AS_ATTACH:
1463 compose = compose_forward_multiple(NULL, msginfo_list);
1465 case COMPOSE_REDIRECT:
1466 compose = compose_redirect(NULL, msginfo, FALSE);
1469 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1472 if (compose == NULL) {
1473 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1477 compose->rmode = mode;
1478 switch (compose->rmode) {
1480 case COMPOSE_REPLY_WITH_QUOTE:
1481 case COMPOSE_REPLY_WITHOUT_QUOTE:
1482 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1483 debug_print("reply mode Normal\n");
1484 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1485 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1487 case COMPOSE_REPLY_TO_SENDER:
1488 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1489 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1490 debug_print("reply mode Sender\n");
1491 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1493 case COMPOSE_REPLY_TO_ALL:
1494 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1495 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1496 debug_print("reply mode All\n");
1497 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1499 case COMPOSE_REPLY_TO_LIST:
1500 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1501 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1502 debug_print("reply mode List\n");
1503 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1505 case COMPOSE_REPLY_TO_ADDRESS:
1506 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1514 static Compose *compose_reply(MsgInfo *msginfo,
1515 ComposeQuoteMode quote_mode,
1521 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1522 to_sender, FALSE, body);
1525 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1526 ComposeQuoteMode quote_mode,
1531 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1532 to_sender, TRUE, body);
1535 static void compose_extract_original_charset(Compose *compose)
1537 MsgInfo *info = NULL;
1538 if (compose->replyinfo) {
1539 info = compose->replyinfo;
1540 } else if (compose->fwdinfo) {
1541 info = compose->fwdinfo;
1542 } else if (compose->targetinfo) {
1543 info = compose->targetinfo;
1546 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1547 MimeInfo *partinfo = mimeinfo;
1548 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1549 partinfo = procmime_mimeinfo_next(partinfo);
1551 compose->orig_charset =
1552 g_strdup(procmime_mimeinfo_get_parameter(
1553 partinfo, "charset"));
1555 procmime_mimeinfo_free_all(&mimeinfo);
1559 #define SIGNAL_BLOCK(buffer) { \
1560 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1561 G_CALLBACK(compose_changed_cb), \
1563 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1564 G_CALLBACK(text_inserted), \
1568 #define SIGNAL_UNBLOCK(buffer) { \
1569 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1570 G_CALLBACK(compose_changed_cb), \
1572 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1573 G_CALLBACK(text_inserted), \
1577 static Compose *compose_generic_reply(MsgInfo *msginfo,
1578 ComposeQuoteMode quote_mode,
1579 gboolean to_all, gboolean to_ml,
1581 gboolean followup_and_reply_to,
1585 PrefsAccount *account = NULL;
1586 GtkTextView *textview;
1587 GtkTextBuffer *textbuf;
1588 gboolean quote = FALSE;
1589 const gchar *qmark = NULL;
1590 const gchar *body_fmt = NULL;
1591 gchar *s_system = NULL;
1593 cm_return_val_if_fail(msginfo != NULL, NULL);
1594 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1596 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1598 cm_return_val_if_fail(account != NULL, NULL);
1600 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1602 compose->updating = TRUE;
1604 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1605 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1607 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1608 if (!compose->replyinfo)
1609 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1611 compose_extract_original_charset(compose);
1613 if (msginfo->folder && msginfo->folder->ret_rcpt)
1614 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1616 /* Set save folder */
1617 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1618 gchar *folderidentifier;
1620 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1621 folderidentifier = folder_item_get_identifier(msginfo->folder);
1622 compose_set_save_to(compose, folderidentifier);
1623 g_free(folderidentifier);
1626 if (compose_parse_header(compose, msginfo) < 0) {
1627 compose->updating = FALSE;
1628 compose_destroy(compose);
1632 /* override from name according to folder properties */
1633 if (msginfo->folder && msginfo->folder->prefs &&
1634 msginfo->folder->prefs->reply_with_format &&
1635 msginfo->folder->prefs->reply_override_from_format &&
1636 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1641 /* decode \-escape sequences in the internal representation of the quote format */
1642 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1643 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1646 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1647 compose->gtkaspell);
1649 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1651 quote_fmt_scan_string(tmp);
1654 buf = quote_fmt_get_buffer();
1656 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1658 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1659 quote_fmt_reset_vartable();
1660 quote_fmtlex_destroy();
1665 textview = (GTK_TEXT_VIEW(compose->text));
1666 textbuf = gtk_text_view_get_buffer(textview);
1667 compose_create_tags(textview, compose);
1669 undo_block(compose->undostruct);
1671 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1672 gtkaspell_block_check(compose->gtkaspell);
1675 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1676 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1677 /* use the reply format of folder (if enabled), or the account's one
1678 (if enabled) or fallback to the global reply format, which is always
1679 enabled (even if empty), and use the relevant quotemark */
1681 if (msginfo->folder && msginfo->folder->prefs &&
1682 msginfo->folder->prefs->reply_with_format) {
1683 qmark = msginfo->folder->prefs->reply_quotemark;
1684 body_fmt = msginfo->folder->prefs->reply_body_format;
1686 } else if (account->reply_with_format) {
1687 qmark = account->reply_quotemark;
1688 body_fmt = account->reply_body_format;
1691 qmark = prefs_common.quotemark;
1692 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1693 body_fmt = gettext(prefs_common.quotefmt);
1700 /* empty quotemark is not allowed */
1701 if (qmark == NULL || *qmark == '\0')
1703 compose_quote_fmt(compose, compose->replyinfo,
1704 body_fmt, qmark, body, FALSE, TRUE,
1705 _("The body of the \"Reply\" template has an error at line %d."));
1706 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1707 quote_fmt_reset_vartable();
1710 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1711 compose_force_encryption(compose, account, FALSE, s_system);
1714 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1715 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1716 compose_force_signing(compose, account, s_system);
1720 SIGNAL_BLOCK(textbuf);
1722 if (account->auto_sig)
1723 compose_insert_sig(compose, FALSE);
1725 compose_wrap_all(compose);
1728 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1729 gtkaspell_highlight_all(compose->gtkaspell);
1730 gtkaspell_unblock_check(compose->gtkaspell);
1732 SIGNAL_UNBLOCK(textbuf);
1734 gtk_widget_grab_focus(compose->text);
1736 undo_unblock(compose->undostruct);
1738 if (prefs_common.auto_exteditor)
1739 compose_exec_ext_editor(compose);
1741 compose->modified = FALSE;
1742 compose_set_title(compose);
1744 compose->updating = FALSE;
1745 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1746 SCROLL_TO_CURSOR(compose);
1748 if (compose->deferred_destroy) {
1749 compose_destroy(compose);
1757 #define INSERT_FW_HEADER(var, hdr) \
1758 if (msginfo->var && *msginfo->var) { \
1759 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1760 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1761 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1764 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1765 gboolean as_attach, const gchar *body,
1766 gboolean no_extedit,
1770 GtkTextView *textview;
1771 GtkTextBuffer *textbuf;
1772 gint cursor_pos = -1;
1775 cm_return_val_if_fail(msginfo != NULL, NULL);
1776 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1778 if (!account && !(account = compose_find_account(msginfo)))
1779 account = cur_account;
1781 if (!prefs_common.forward_as_attachment)
1782 mode = COMPOSE_FORWARD_INLINE;
1784 mode = COMPOSE_FORWARD;
1785 compose = compose_create(account, msginfo->folder, mode, batch);
1787 compose->updating = TRUE;
1788 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1789 if (!compose->fwdinfo)
1790 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1792 compose_extract_original_charset(compose);
1794 if (msginfo->subject && *msginfo->subject) {
1795 gchar *buf, *buf2, *p;
1797 buf = p = g_strdup(msginfo->subject);
1798 p += subject_get_prefix_length(p);
1799 memmove(buf, p, strlen(p) + 1);
1801 buf2 = g_strdup_printf("Fw: %s", buf);
1802 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1808 /* override from name according to folder properties */
1809 if (msginfo->folder && msginfo->folder->prefs &&
1810 msginfo->folder->prefs->forward_with_format &&
1811 msginfo->folder->prefs->forward_override_from_format &&
1812 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1816 MsgInfo *full_msginfo = NULL;
1819 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1821 full_msginfo = procmsg_msginfo_copy(msginfo);
1823 /* decode \-escape sequences in the internal representation of the quote format */
1824 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1825 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1828 gtkaspell_block_check(compose->gtkaspell);
1829 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1830 compose->gtkaspell);
1832 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1834 quote_fmt_scan_string(tmp);
1837 buf = quote_fmt_get_buffer();
1839 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1841 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1842 quote_fmt_reset_vartable();
1843 quote_fmtlex_destroy();
1846 procmsg_msginfo_free(&full_msginfo);
1849 textview = GTK_TEXT_VIEW(compose->text);
1850 textbuf = gtk_text_view_get_buffer(textview);
1851 compose_create_tags(textview, compose);
1853 undo_block(compose->undostruct);
1857 msgfile = procmsg_get_message_file(msginfo);
1858 if (!is_file_exist(msgfile))
1859 g_warning("%s: file does not exist", msgfile);
1861 compose_attach_append(compose, msgfile, msgfile,
1862 "message/rfc822", NULL);
1866 const gchar *qmark = NULL;
1867 const gchar *body_fmt = NULL;
1868 MsgInfo *full_msginfo;
1870 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1872 full_msginfo = procmsg_msginfo_copy(msginfo);
1874 /* use the forward format of folder (if enabled), or the account's one
1875 (if enabled) or fallback to the global forward format, which is always
1876 enabled (even if empty), and use the relevant quotemark */
1877 if (msginfo->folder && msginfo->folder->prefs &&
1878 msginfo->folder->prefs->forward_with_format) {
1879 qmark = msginfo->folder->prefs->forward_quotemark;
1880 body_fmt = msginfo->folder->prefs->forward_body_format;
1882 } else if (account->forward_with_format) {
1883 qmark = account->forward_quotemark;
1884 body_fmt = account->forward_body_format;
1887 qmark = prefs_common.fw_quotemark;
1888 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1889 body_fmt = gettext(prefs_common.fw_quotefmt);
1894 /* empty quotemark is not allowed */
1895 if (qmark == NULL || *qmark == '\0')
1898 compose_quote_fmt(compose, full_msginfo,
1899 body_fmt, qmark, body, FALSE, TRUE,
1900 _("The body of the \"Forward\" template has an error at line %d."));
1901 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1902 quote_fmt_reset_vartable();
1903 compose_attach_parts(compose, msginfo);
1905 procmsg_msginfo_free(&full_msginfo);
1908 SIGNAL_BLOCK(textbuf);
1910 if (account->auto_sig)
1911 compose_insert_sig(compose, FALSE);
1913 compose_wrap_all(compose);
1916 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1917 gtkaspell_highlight_all(compose->gtkaspell);
1918 gtkaspell_unblock_check(compose->gtkaspell);
1920 SIGNAL_UNBLOCK(textbuf);
1922 cursor_pos = quote_fmt_get_cursor_pos();
1923 if (cursor_pos == -1)
1924 gtk_widget_grab_focus(compose->header_last->entry);
1926 gtk_widget_grab_focus(compose->text);
1928 if (!no_extedit && prefs_common.auto_exteditor)
1929 compose_exec_ext_editor(compose);
1932 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1933 gchar *folderidentifier;
1935 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1936 folderidentifier = folder_item_get_identifier(msginfo->folder);
1937 compose_set_save_to(compose, folderidentifier);
1938 g_free(folderidentifier);
1941 undo_unblock(compose->undostruct);
1943 compose->modified = FALSE;
1944 compose_set_title(compose);
1946 compose->updating = FALSE;
1947 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1948 SCROLL_TO_CURSOR(compose);
1950 if (compose->deferred_destroy) {
1951 compose_destroy(compose);
1955 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1960 #undef INSERT_FW_HEADER
1962 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1965 GtkTextView *textview;
1966 GtkTextBuffer *textbuf;
1970 gboolean single_mail = TRUE;
1972 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1974 if (g_slist_length(msginfo_list) > 1)
1975 single_mail = FALSE;
1977 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1978 if (((MsgInfo *)msginfo->data)->folder == NULL)
1981 /* guess account from first selected message */
1983 !(account = compose_find_account(msginfo_list->data)))
1984 account = cur_account;
1986 cm_return_val_if_fail(account != NULL, NULL);
1988 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1989 if (msginfo->data) {
1990 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1991 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1995 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1996 g_warning("no msginfo_list");
2000 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
2002 compose->updating = TRUE;
2004 /* override from name according to folder properties */
2005 if (msginfo_list->data) {
2006 MsgInfo *msginfo = msginfo_list->data;
2008 if (msginfo->folder && msginfo->folder->prefs &&
2009 msginfo->folder->prefs->forward_with_format &&
2010 msginfo->folder->prefs->forward_override_from_format &&
2011 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2016 /* decode \-escape sequences in the internal representation of the quote format */
2017 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2018 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2021 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2022 compose->gtkaspell);
2024 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2026 quote_fmt_scan_string(tmp);
2029 buf = quote_fmt_get_buffer();
2031 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2033 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2034 quote_fmt_reset_vartable();
2035 quote_fmtlex_destroy();
2041 textview = GTK_TEXT_VIEW(compose->text);
2042 textbuf = gtk_text_view_get_buffer(textview);
2043 compose_create_tags(textview, compose);
2045 undo_block(compose->undostruct);
2046 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2047 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2049 if (!is_file_exist(msgfile))
2050 g_warning("%s: file does not exist", msgfile);
2052 compose_attach_append(compose, msgfile, msgfile,
2053 "message/rfc822", NULL);
2058 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2059 if (info->subject && *info->subject) {
2060 gchar *buf, *buf2, *p;
2062 buf = p = g_strdup(info->subject);
2063 p += subject_get_prefix_length(p);
2064 memmove(buf, p, strlen(p) + 1);
2066 buf2 = g_strdup_printf("Fw: %s", buf);
2067 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2073 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2074 _("Fw: multiple emails"));
2077 SIGNAL_BLOCK(textbuf);
2079 if (account->auto_sig)
2080 compose_insert_sig(compose, FALSE);
2082 compose_wrap_all(compose);
2084 SIGNAL_UNBLOCK(textbuf);
2086 gtk_text_buffer_get_start_iter(textbuf, &iter);
2087 gtk_text_buffer_place_cursor(textbuf, &iter);
2089 if (prefs_common.auto_exteditor)
2090 compose_exec_ext_editor(compose);
2092 gtk_widget_grab_focus(compose->header_last->entry);
2093 undo_unblock(compose->undostruct);
2094 compose->modified = FALSE;
2095 compose_set_title(compose);
2097 compose->updating = FALSE;
2098 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2099 SCROLL_TO_CURSOR(compose);
2101 if (compose->deferred_destroy) {
2102 compose_destroy(compose);
2106 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2111 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2113 GtkTextIter start = *iter;
2114 GtkTextIter end_iter;
2115 int start_pos = gtk_text_iter_get_offset(&start);
2117 if (!compose->account->sig_sep)
2120 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2121 start_pos+strlen(compose->account->sig_sep));
2123 /* check sig separator */
2124 str = gtk_text_iter_get_text(&start, &end_iter);
2125 if (!strcmp(str, compose->account->sig_sep)) {
2127 /* check end of line (\n) */
2128 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2129 start_pos+strlen(compose->account->sig_sep));
2130 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2131 start_pos+strlen(compose->account->sig_sep)+1);
2132 tmp = gtk_text_iter_get_text(&start, &end_iter);
2133 if (!strcmp(tmp,"\n")) {
2145 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2147 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2148 Compose *compose = (Compose *)data;
2149 FolderItem *old_item = NULL;
2150 FolderItem *new_item = NULL;
2151 gchar *old_id, *new_id;
2153 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2154 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2157 old_item = hookdata->item;
2158 new_item = hookdata->item2;
2160 old_id = folder_item_get_identifier(old_item);
2161 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2163 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2164 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2165 compose->targetinfo->folder = new_item;
2168 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2169 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2170 compose->replyinfo->folder = new_item;
2173 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2174 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2175 compose->fwdinfo->folder = new_item;
2183 static void compose_colorize_signature(Compose *compose)
2185 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2187 GtkTextIter end_iter;
2188 gtk_text_buffer_get_start_iter(buffer, &iter);
2189 while (gtk_text_iter_forward_line(&iter))
2190 if (compose_is_sig_separator(compose, buffer, &iter)) {
2191 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2192 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2196 #define BLOCK_WRAP() { \
2197 prev_autowrap = compose->autowrap; \
2198 buffer = gtk_text_view_get_buffer( \
2199 GTK_TEXT_VIEW(compose->text)); \
2200 compose->autowrap = FALSE; \
2202 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2203 G_CALLBACK(compose_changed_cb), \
2205 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2206 G_CALLBACK(text_inserted), \
2209 #define UNBLOCK_WRAP() { \
2210 compose->autowrap = prev_autowrap; \
2211 if (compose->autowrap) { \
2212 gint old = compose->draft_timeout_tag; \
2213 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2214 compose_wrap_all(compose); \
2215 compose->draft_timeout_tag = old; \
2218 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2219 G_CALLBACK(compose_changed_cb), \
2221 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2222 G_CALLBACK(text_inserted), \
2226 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2228 Compose *compose = NULL;
2229 PrefsAccount *account = NULL;
2230 GtkTextView *textview;
2231 GtkTextBuffer *textbuf;
2235 gboolean use_signing = FALSE;
2236 gboolean use_encryption = FALSE;
2237 gchar *privacy_system = NULL;
2238 int priority = PRIORITY_NORMAL;
2239 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2240 gboolean autowrap = prefs_common.autowrap;
2241 gboolean autoindent = prefs_common.auto_indent;
2242 HeaderEntry *manual_headers = NULL;
2244 cm_return_val_if_fail(msginfo != NULL, NULL);
2245 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2247 if (compose_put_existing_to_front(msginfo)) {
2251 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2252 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2253 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2254 gchar *queueheader_buf = NULL;
2257 /* Select Account from queue headers */
2258 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2259 "X-Claws-Account-Id:")) {
2260 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2261 account = account_find_from_id(id);
2262 g_free(queueheader_buf);
2264 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2265 "X-Sylpheed-Account-Id:")) {
2266 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2267 account = account_find_from_id(id);
2268 g_free(queueheader_buf);
2270 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2272 id = atoi(&queueheader_buf[strlen("NAID:")]);
2273 account = account_find_from_id(id);
2274 g_free(queueheader_buf);
2276 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2278 id = atoi(&queueheader_buf[strlen("MAID:")]);
2279 account = account_find_from_id(id);
2280 g_free(queueheader_buf);
2282 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2284 account = account_find_from_address(queueheader_buf, FALSE);
2285 g_free(queueheader_buf);
2287 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2289 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2290 use_signing = param;
2291 g_free(queueheader_buf);
2293 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2294 "X-Sylpheed-Sign:")) {
2295 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2296 use_signing = param;
2297 g_free(queueheader_buf);
2299 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2300 "X-Claws-Encrypt:")) {
2301 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2302 use_encryption = param;
2303 g_free(queueheader_buf);
2305 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2306 "X-Sylpheed-Encrypt:")) {
2307 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2308 use_encryption = param;
2309 g_free(queueheader_buf);
2311 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2312 "X-Claws-Auto-Wrapping:")) {
2313 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2315 g_free(queueheader_buf);
2317 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2318 "X-Claws-Auto-Indent:")) {
2319 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2321 g_free(queueheader_buf);
2323 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2324 "X-Claws-Privacy-System:")) {
2325 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2326 g_free(queueheader_buf);
2328 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2329 "X-Sylpheed-Privacy-System:")) {
2330 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2331 g_free(queueheader_buf);
2333 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2335 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2337 g_free(queueheader_buf);
2339 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2341 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2342 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2343 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2344 if (orig_item != NULL) {
2345 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2349 g_free(queueheader_buf);
2351 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2353 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2354 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2355 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2356 if (orig_item != NULL) {
2357 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2361 g_free(queueheader_buf);
2363 /* Get manual headers */
2364 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2365 "X-Claws-Manual-Headers:")) {
2366 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2367 if (listmh && *listmh != '\0') {
2368 debug_print("Got manual headers: %s\n", listmh);
2369 manual_headers = procheader_entries_from_str(listmh);
2372 g_free(queueheader_buf);
2375 account = msginfo->folder->folder->account;
2378 if (!account && prefs_common.reedit_account_autosel) {
2380 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2381 extract_address(from);
2382 account = account_find_from_address(from, FALSE);
2387 account = cur_account;
2389 cm_return_val_if_fail(account != NULL, NULL);
2391 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2393 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2394 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2395 compose->autowrap = autowrap;
2396 compose->replyinfo = replyinfo;
2397 compose->fwdinfo = fwdinfo;
2399 compose->updating = TRUE;
2400 compose->priority = priority;
2402 if (privacy_system != NULL) {
2403 compose->privacy_system = privacy_system;
2404 compose_use_signing(compose, use_signing);
2405 compose_use_encryption(compose, use_encryption);
2406 compose_update_privacy_system_menu_item(compose, FALSE);
2408 activate_privacy_system(compose, account, FALSE);
2411 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2413 compose_extract_original_charset(compose);
2415 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2416 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2417 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2418 gchar *queueheader_buf = NULL;
2420 /* Set message save folder */
2421 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2422 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2423 compose_set_save_to(compose, &queueheader_buf[4]);
2424 g_free(queueheader_buf);
2426 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2427 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2429 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2431 g_free(queueheader_buf);
2435 if (compose_parse_header(compose, msginfo) < 0) {
2436 compose->updating = FALSE;
2437 compose_destroy(compose);
2440 compose_reedit_set_entry(compose, msginfo);
2442 textview = GTK_TEXT_VIEW(compose->text);
2443 textbuf = gtk_text_view_get_buffer(textview);
2444 compose_create_tags(textview, compose);
2446 mark = gtk_text_buffer_get_insert(textbuf);
2447 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2449 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2450 G_CALLBACK(compose_changed_cb),
2453 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2454 fp = procmime_get_first_encrypted_text_content(msginfo);
2456 compose_force_encryption(compose, account, TRUE, NULL);
2459 fp = procmime_get_first_text_content(msginfo);
2462 g_warning("Can't get text part");
2466 gchar buf[BUFFSIZE];
2467 gboolean prev_autowrap;
2468 GtkTextBuffer *buffer;
2470 while (fgets(buf, sizeof(buf), fp) != NULL) {
2472 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2478 compose_attach_parts(compose, msginfo);
2480 compose_colorize_signature(compose);
2482 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2483 G_CALLBACK(compose_changed_cb),
2486 if (manual_headers != NULL) {
2487 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2488 procheader_entries_free(manual_headers);
2489 compose->updating = FALSE;
2490 compose_destroy(compose);
2493 procheader_entries_free(manual_headers);
2496 gtk_widget_grab_focus(compose->text);
2498 if (prefs_common.auto_exteditor) {
2499 compose_exec_ext_editor(compose);
2501 compose->modified = FALSE;
2502 compose_set_title(compose);
2504 compose->updating = FALSE;
2505 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2506 SCROLL_TO_CURSOR(compose);
2508 if (compose->deferred_destroy) {
2509 compose_destroy(compose);
2513 compose->sig_str = account_get_signature_str(compose->account);
2515 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2520 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2527 cm_return_val_if_fail(msginfo != NULL, NULL);
2530 account = account_get_reply_account(msginfo,
2531 prefs_common.reply_account_autosel);
2532 cm_return_val_if_fail(account != NULL, NULL);
2534 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2536 compose->updating = TRUE;
2538 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2539 compose->replyinfo = NULL;
2540 compose->fwdinfo = NULL;
2542 compose_show_first_last_header(compose, TRUE);
2544 gtk_widget_grab_focus(compose->header_last->entry);
2546 filename = procmsg_get_message_file(msginfo);
2548 if (filename == NULL) {
2549 compose->updating = FALSE;
2550 compose_destroy(compose);
2555 compose->redirect_filename = filename;
2557 /* Set save folder */
2558 item = msginfo->folder;
2559 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2560 gchar *folderidentifier;
2562 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2563 folderidentifier = folder_item_get_identifier(item);
2564 compose_set_save_to(compose, folderidentifier);
2565 g_free(folderidentifier);
2568 compose_attach_parts(compose, msginfo);
2570 if (msginfo->subject)
2571 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2573 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2575 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2576 _("The body of the \"Redirect\" template has an error at line %d."));
2577 quote_fmt_reset_vartable();
2578 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2580 compose_colorize_signature(compose);
2583 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2584 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2585 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2589 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2590 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2592 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2594 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2597 if (compose->toolbar->draft_btn)
2598 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2599 if (compose->toolbar->insert_btn)
2600 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2601 if (compose->toolbar->attach_btn)
2602 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2603 if (compose->toolbar->sig_btn)
2604 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2605 if (compose->toolbar->exteditor_btn)
2606 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2607 if (compose->toolbar->linewrap_current_btn)
2608 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2609 if (compose->toolbar->linewrap_all_btn)
2610 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2612 compose->modified = FALSE;
2613 compose_set_title(compose);
2614 compose->updating = FALSE;
2615 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2616 SCROLL_TO_CURSOR(compose);
2618 if (compose->deferred_destroy) {
2619 compose_destroy(compose);
2623 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2628 const GList *compose_get_compose_list(void)
2630 return compose_list;
2633 void compose_entry_append(Compose *compose, const gchar *address,
2634 ComposeEntryType type, ComposePrefType pref_type)
2636 const gchar *header;
2638 gboolean in_quote = FALSE;
2639 if (!address || *address == '\0') return;
2646 header = N_("Bcc:");
2648 case COMPOSE_REPLYTO:
2649 header = N_("Reply-To:");
2651 case COMPOSE_NEWSGROUPS:
2652 header = N_("Newsgroups:");
2654 case COMPOSE_FOLLOWUPTO:
2655 header = N_( "Followup-To:");
2657 case COMPOSE_INREPLYTO:
2658 header = N_( "In-Reply-To:");
2665 header = prefs_common_translated_header_name(header);
2667 cur = begin = (gchar *)address;
2669 /* we separate the line by commas, but not if we're inside a quoted
2671 while (*cur != '\0') {
2673 in_quote = !in_quote;
2674 if (*cur == ',' && !in_quote) {
2675 gchar *tmp = g_strdup(begin);
2677 tmp[cur-begin]='\0';
2680 while (*tmp == ' ' || *tmp == '\t')
2682 compose_add_header_entry(compose, header, tmp, pref_type);
2683 compose_entry_indicate(compose, tmp);
2690 gchar *tmp = g_strdup(begin);
2692 tmp[cur-begin]='\0';
2693 while (*tmp == ' ' || *tmp == '\t')
2695 compose_add_header_entry(compose, header, tmp, pref_type);
2696 compose_entry_indicate(compose, tmp);
2701 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2706 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2707 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2708 if (gtk_entry_get_text(entry) &&
2709 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2710 gtk_widget_modify_base(
2711 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2712 GTK_STATE_NORMAL, &default_header_bgcolor);
2713 gtk_widget_modify_text(
2714 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2715 GTK_STATE_NORMAL, &default_header_color);
2720 void compose_toolbar_cb(gint action, gpointer data)
2722 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2723 Compose *compose = (Compose*)toolbar_item->parent;
2725 cm_return_if_fail(compose != NULL);
2729 compose_send_cb(NULL, compose);
2732 compose_send_later_cb(NULL, compose);
2735 compose_draft(compose, COMPOSE_QUIT_EDITING);
2738 compose_insert_file_cb(NULL, compose);
2741 compose_attach_cb(NULL, compose);
2744 compose_insert_sig(compose, FALSE);
2747 compose_insert_sig(compose, TRUE);
2750 compose_ext_editor_cb(NULL, compose);
2752 case A_LINEWRAP_CURRENT:
2753 compose_beautify_paragraph(compose, NULL, TRUE);
2755 case A_LINEWRAP_ALL:
2756 compose_wrap_all_full(compose, TRUE);
2759 compose_address_cb(NULL, compose);
2762 case A_CHECK_SPELLING:
2763 compose_check_all(NULL, compose);
2766 case A_PRIVACY_SIGN:
2768 case A_PRIVACY_ENCRYPT:
2775 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2780 gchar *subject = NULL;
2784 gchar **attach = NULL;
2785 gchar *inreplyto = NULL;
2786 MailField mfield = NO_FIELD_PRESENT;
2788 /* get mailto parts but skip from */
2789 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2792 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2793 mfield = TO_FIELD_PRESENT;
2796 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2798 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2800 if (!g_utf8_validate (subject, -1, NULL)) {
2801 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2802 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2805 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2807 mfield = SUBJECT_FIELD_PRESENT;
2810 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2811 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2814 gboolean prev_autowrap = compose->autowrap;
2816 compose->autowrap = FALSE;
2818 mark = gtk_text_buffer_get_insert(buffer);
2819 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2821 if (!g_utf8_validate (body, -1, NULL)) {
2822 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2823 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2826 gtk_text_buffer_insert(buffer, &iter, body, -1);
2828 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2830 compose->autowrap = prev_autowrap;
2831 if (compose->autowrap)
2832 compose_wrap_all(compose);
2833 mfield = BODY_FIELD_PRESENT;
2837 gint i = 0, att = 0;
2838 gchar *warn_files = NULL;
2839 while (attach[i] != NULL) {
2840 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2841 if (utf8_filename) {
2842 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2843 gchar *tmp = g_strdup_printf("%s%s\n",
2844 warn_files?warn_files:"",
2850 g_free(utf8_filename);
2852 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2857 alertpanel_notice(ngettext(
2858 "The following file has been attached: \n%s",
2859 "The following files have been attached: \n%s", att), warn_files);
2864 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2877 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2879 static HeaderEntry hentry[] = {
2880 {"Reply-To:", NULL, TRUE },
2881 {"Cc:", NULL, TRUE },
2882 {"References:", NULL, FALSE },
2883 {"Bcc:", NULL, TRUE },
2884 {"Newsgroups:", NULL, TRUE },
2885 {"Followup-To:", NULL, TRUE },
2886 {"List-Post:", NULL, FALSE },
2887 {"X-Priority:", NULL, FALSE },
2888 {NULL, NULL, FALSE }
2905 cm_return_val_if_fail(msginfo != NULL, -1);
2907 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2908 procheader_get_header_fields(fp, hentry);
2911 if (hentry[H_REPLY_TO].body != NULL) {
2912 if (hentry[H_REPLY_TO].body[0] != '\0') {
2914 conv_unmime_header(hentry[H_REPLY_TO].body,
2917 g_free(hentry[H_REPLY_TO].body);
2918 hentry[H_REPLY_TO].body = NULL;
2920 if (hentry[H_CC].body != NULL) {
2921 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2922 g_free(hentry[H_CC].body);
2923 hentry[H_CC].body = NULL;
2925 if (hentry[H_REFERENCES].body != NULL) {
2926 if (compose->mode == COMPOSE_REEDIT)
2927 compose->references = hentry[H_REFERENCES].body;
2929 compose->references = compose_parse_references
2930 (hentry[H_REFERENCES].body, msginfo->msgid);
2931 g_free(hentry[H_REFERENCES].body);
2933 hentry[H_REFERENCES].body = NULL;
2935 if (hentry[H_BCC].body != NULL) {
2936 if (compose->mode == COMPOSE_REEDIT)
2938 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2939 g_free(hentry[H_BCC].body);
2940 hentry[H_BCC].body = NULL;
2942 if (hentry[H_NEWSGROUPS].body != NULL) {
2943 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2944 hentry[H_NEWSGROUPS].body = NULL;
2946 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2947 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2948 compose->followup_to =
2949 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2952 g_free(hentry[H_FOLLOWUP_TO].body);
2953 hentry[H_FOLLOWUP_TO].body = NULL;
2955 if (hentry[H_LIST_POST].body != NULL) {
2956 gchar *to = NULL, *start = NULL;
2958 extract_address(hentry[H_LIST_POST].body);
2959 if (hentry[H_LIST_POST].body[0] != '\0') {
2960 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2962 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2963 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2966 g_free(compose->ml_post);
2967 compose->ml_post = to;
2970 g_free(hentry[H_LIST_POST].body);
2971 hentry[H_LIST_POST].body = NULL;
2974 /* CLAWS - X-Priority */
2975 if (compose->mode == COMPOSE_REEDIT)
2976 if (hentry[H_X_PRIORITY].body != NULL) {
2979 priority = atoi(hentry[H_X_PRIORITY].body);
2980 g_free(hentry[H_X_PRIORITY].body);
2982 hentry[H_X_PRIORITY].body = NULL;
2984 if (priority < PRIORITY_HIGHEST ||
2985 priority > PRIORITY_LOWEST)
2986 priority = PRIORITY_NORMAL;
2988 compose->priority = priority;
2991 if (compose->mode == COMPOSE_REEDIT) {
2992 if (msginfo->inreplyto && *msginfo->inreplyto)
2993 compose->inreplyto = g_strdup(msginfo->inreplyto);
2995 if (msginfo->msgid && *msginfo->msgid &&
2996 compose->folder != NULL &&
2997 compose->folder->stype == F_DRAFT)
2998 compose->msgid = g_strdup(msginfo->msgid);
3000 if (msginfo->msgid && *msginfo->msgid)
3001 compose->inreplyto = g_strdup(msginfo->msgid);
3003 if (!compose->references) {
3004 if (msginfo->msgid && *msginfo->msgid) {
3005 if (msginfo->inreplyto && *msginfo->inreplyto)
3006 compose->references =
3007 g_strdup_printf("<%s>\n\t<%s>",
3011 compose->references =
3012 g_strconcat("<", msginfo->msgid, ">",
3014 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3015 compose->references =
3016 g_strconcat("<", msginfo->inreplyto, ">",
3025 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3030 cm_return_val_if_fail(msginfo != NULL, -1);
3032 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3033 procheader_get_header_fields(fp, entries);
3037 while (he != NULL && he->name != NULL) {
3039 GtkListStore *model = NULL;
3041 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3042 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3043 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3044 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3045 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3052 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3054 GSList *ref_id_list, *cur;
3058 ref_id_list = references_list_append(NULL, ref);
3059 if (!ref_id_list) return NULL;
3060 if (msgid && *msgid)
3061 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3066 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3067 /* "<" + Message-ID + ">" + CR+LF+TAB */
3068 len += strlen((gchar *)cur->data) + 5;
3070 if (len > MAX_REFERENCES_LEN) {
3071 /* remove second message-ID */
3072 if (ref_id_list && ref_id_list->next &&
3073 ref_id_list->next->next) {
3074 g_free(ref_id_list->next->data);
3075 ref_id_list = g_slist_remove
3076 (ref_id_list, ref_id_list->next->data);
3078 slist_free_strings_full(ref_id_list);
3085 new_ref = g_string_new("");
3086 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3087 if (new_ref->len > 0)
3088 g_string_append(new_ref, "\n\t");
3089 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3092 slist_free_strings_full(ref_id_list);
3094 new_ref_str = new_ref->str;
3095 g_string_free(new_ref, FALSE);
3100 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3101 const gchar *fmt, const gchar *qmark,
3102 const gchar *body, gboolean rewrap,
3103 gboolean need_unescape,
3104 const gchar *err_msg)
3106 MsgInfo* dummyinfo = NULL;
3107 gchar *quote_str = NULL;
3109 gboolean prev_autowrap;
3110 const gchar *trimmed_body = body;
3111 gint cursor_pos = -1;
3112 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3113 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3118 SIGNAL_BLOCK(buffer);
3121 dummyinfo = compose_msginfo_new_from_compose(compose);
3122 msginfo = dummyinfo;
3125 if (qmark != NULL) {
3127 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3128 compose->gtkaspell);
3130 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3132 quote_fmt_scan_string(qmark);
3135 buf = quote_fmt_get_buffer();
3138 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3140 Xstrdup_a(quote_str, buf, goto error)
3143 if (fmt && *fmt != '\0') {
3146 while (*trimmed_body == '\n')
3150 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3151 compose->gtkaspell);
3153 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3155 if (need_unescape) {
3158 /* decode \-escape sequences in the internal representation of the quote format */
3159 tmp = g_malloc(strlen(fmt)+1);
3160 pref_get_unescaped_pref(tmp, fmt);
3161 quote_fmt_scan_string(tmp);
3165 quote_fmt_scan_string(fmt);
3169 buf = quote_fmt_get_buffer();
3172 gint line = quote_fmt_get_line();
3173 alertpanel_error(err_msg, line);
3181 prev_autowrap = compose->autowrap;
3182 compose->autowrap = FALSE;
3184 mark = gtk_text_buffer_get_insert(buffer);
3185 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3186 if (g_utf8_validate(buf, -1, NULL)) {
3187 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3189 gchar *tmpout = NULL;
3190 tmpout = conv_codeset_strdup
3191 (buf, conv_get_locale_charset_str_no_utf8(),
3193 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3195 tmpout = g_malloc(strlen(buf)*2+1);
3196 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3198 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3202 cursor_pos = quote_fmt_get_cursor_pos();
3203 if (cursor_pos == -1)
3204 cursor_pos = gtk_text_iter_get_offset(&iter);
3205 compose->set_cursor_pos = cursor_pos;
3207 gtk_text_buffer_get_start_iter(buffer, &iter);
3208 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3209 gtk_text_buffer_place_cursor(buffer, &iter);
3211 compose->autowrap = prev_autowrap;
3212 if (compose->autowrap && rewrap)
3213 compose_wrap_all(compose);
3220 SIGNAL_UNBLOCK(buffer);
3222 procmsg_msginfo_free( &dummyinfo );
3227 /* if ml_post is of type addr@host and from is of type
3228 * addr-anything@host, return TRUE
3230 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3232 gchar *left_ml = NULL;
3233 gchar *right_ml = NULL;
3234 gchar *left_from = NULL;
3235 gchar *right_from = NULL;
3236 gboolean result = FALSE;
3238 if (!ml_post || !from)
3241 left_ml = g_strdup(ml_post);
3242 if (strstr(left_ml, "@")) {
3243 right_ml = strstr(left_ml, "@")+1;
3244 *(strstr(left_ml, "@")) = '\0';
3247 left_from = g_strdup(from);
3248 if (strstr(left_from, "@")) {
3249 right_from = strstr(left_from, "@")+1;
3250 *(strstr(left_from, "@")) = '\0';
3253 if (right_ml && right_from
3254 && !strncmp(left_from, left_ml, strlen(left_ml))
3255 && !strcmp(right_from, right_ml)) {
3264 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3265 gboolean respect_default_to)
3269 if (!folder || !folder->prefs)
3272 if (respect_default_to && folder->prefs->enable_default_to) {
3273 compose_entry_append(compose, folder->prefs->default_to,
3274 COMPOSE_TO, PREF_FOLDER);
3275 compose_entry_indicate(compose, folder->prefs->default_to);
3277 if (folder->prefs->enable_default_cc) {
3278 compose_entry_append(compose, folder->prefs->default_cc,
3279 COMPOSE_CC, PREF_FOLDER);
3280 compose_entry_indicate(compose, folder->prefs->default_cc);
3282 if (folder->prefs->enable_default_bcc) {
3283 compose_entry_append(compose, folder->prefs->default_bcc,
3284 COMPOSE_BCC, PREF_FOLDER);
3285 compose_entry_indicate(compose, folder->prefs->default_bcc);
3287 if (folder->prefs->enable_default_replyto) {
3288 compose_entry_append(compose, folder->prefs->default_replyto,
3289 COMPOSE_REPLYTO, PREF_FOLDER);
3290 compose_entry_indicate(compose, folder->prefs->default_replyto);
3294 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3299 if (!compose || !msginfo)
3302 if (msginfo->subject && *msginfo->subject) {
3303 buf = p = g_strdup(msginfo->subject);
3304 p += subject_get_prefix_length(p);
3305 memmove(buf, p, strlen(p) + 1);
3307 buf2 = g_strdup_printf("Re: %s", buf);
3308 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3313 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3316 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3317 gboolean to_all, gboolean to_ml,
3319 gboolean followup_and_reply_to)
3321 GSList *cc_list = NULL;
3324 gchar *replyto = NULL;
3325 gchar *ac_email = NULL;
3327 gboolean reply_to_ml = FALSE;
3328 gboolean default_reply_to = FALSE;
3330 cm_return_if_fail(compose->account != NULL);
3331 cm_return_if_fail(msginfo != NULL);
3333 reply_to_ml = to_ml && compose->ml_post;
3335 default_reply_to = msginfo->folder &&
3336 msginfo->folder->prefs->enable_default_reply_to;
3338 if (compose->account->protocol != A_NNTP) {
3339 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3341 if (reply_to_ml && !default_reply_to) {
3343 gboolean is_subscr = is_subscription(compose->ml_post,
3346 /* normal answer to ml post with a reply-to */
3347 compose_entry_append(compose,
3349 COMPOSE_TO, PREF_ML);
3350 if (compose->replyto)
3351 compose_entry_append(compose,
3353 COMPOSE_CC, PREF_ML);
3355 /* answer to subscription confirmation */
3356 if (compose->replyto)
3357 compose_entry_append(compose,
3359 COMPOSE_TO, PREF_ML);
3360 else if (msginfo->from)
3361 compose_entry_append(compose,
3363 COMPOSE_TO, PREF_ML);
3366 else if (!(to_all || to_sender) && default_reply_to) {
3367 compose_entry_append(compose,
3368 msginfo->folder->prefs->default_reply_to,
3369 COMPOSE_TO, PREF_FOLDER);
3370 compose_entry_indicate(compose,
3371 msginfo->folder->prefs->default_reply_to);
3377 compose_entry_append(compose, msginfo->from,
3378 COMPOSE_TO, PREF_NONE);
3380 Xstrdup_a(tmp1, msginfo->from, return);
3381 extract_address(tmp1);
3382 compose_entry_append(compose,
3383 (!account_find_from_address(tmp1, FALSE))
3386 COMPOSE_TO, PREF_NONE);
3387 if (compose->replyto)
3388 compose_entry_append(compose,
3390 COMPOSE_CC, PREF_NONE);
3392 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3393 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3394 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3395 if (compose->replyto) {
3396 compose_entry_append(compose,
3398 COMPOSE_TO, PREF_NONE);
3400 compose_entry_append(compose,
3401 msginfo->from ? msginfo->from : "",
3402 COMPOSE_TO, PREF_NONE);
3405 /* replying to own mail, use original recp */
3406 compose_entry_append(compose,
3407 msginfo->to ? msginfo->to : "",
3408 COMPOSE_TO, PREF_NONE);
3409 compose_entry_append(compose,
3410 msginfo->cc ? msginfo->cc : "",
3411 COMPOSE_CC, PREF_NONE);
3416 if (to_sender || (compose->followup_to &&
3417 !strncmp(compose->followup_to, "poster", 6)))
3418 compose_entry_append
3420 (compose->replyto ? compose->replyto :
3421 msginfo->from ? msginfo->from : ""),
3422 COMPOSE_TO, PREF_NONE);
3424 else if (followup_and_reply_to || to_all) {
3425 compose_entry_append
3427 (compose->replyto ? compose->replyto :
3428 msginfo->from ? msginfo->from : ""),
3429 COMPOSE_TO, 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_entry_append
3439 msginfo->cc ? msginfo->cc : "",
3440 COMPOSE_CC, PREF_NONE);
3443 compose_entry_append
3445 compose->followup_to ? compose->followup_to :
3446 compose->newsgroups ? compose->newsgroups : "",
3447 COMPOSE_NEWSGROUPS, PREF_NONE);
3449 compose_reply_set_subject(compose, msginfo);
3451 if (to_ml && compose->ml_post) return;
3452 if (!to_all || compose->account->protocol == A_NNTP) return;
3454 if (compose->replyto) {
3455 Xstrdup_a(replyto, compose->replyto, return);
3456 extract_address(replyto);
3458 if (msginfo->from) {
3459 Xstrdup_a(from, msginfo->from, return);
3460 extract_address(from);
3463 if (replyto && from)
3464 cc_list = address_list_append_with_comments(cc_list, from);
3465 if (to_all && msginfo->folder &&
3466 msginfo->folder->prefs->enable_default_reply_to)
3467 cc_list = address_list_append_with_comments(cc_list,
3468 msginfo->folder->prefs->default_reply_to);
3469 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3470 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3472 ac_email = g_utf8_strdown(compose->account->address, -1);
3475 for (cur = cc_list; cur != NULL; cur = cur->next) {
3476 gchar *addr = g_utf8_strdown(cur->data, -1);
3477 extract_address(addr);
3479 if (strcmp(ac_email, addr))
3480 compose_entry_append(compose, (gchar *)cur->data,
3481 COMPOSE_CC, PREF_NONE);
3483 debug_print("Cc address same as compose account's, ignoring\n");
3488 slist_free_strings_full(cc_list);
3494 #define SET_ENTRY(entry, str) \
3497 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3500 #define SET_ADDRESS(type, str) \
3503 compose_entry_append(compose, str, type, PREF_NONE); \
3506 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3508 cm_return_if_fail(msginfo != NULL);
3510 SET_ENTRY(subject_entry, msginfo->subject);
3511 SET_ENTRY(from_name, msginfo->from);
3512 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3513 SET_ADDRESS(COMPOSE_CC, compose->cc);
3514 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3515 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3516 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3517 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3519 compose_update_priority_menu_item(compose);
3520 compose_update_privacy_system_menu_item(compose, FALSE);
3521 compose_show_first_last_header(compose, TRUE);
3527 static void compose_insert_sig(Compose *compose, gboolean replace)
3529 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3530 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3532 GtkTextIter iter, iter_end;
3533 gint cur_pos, ins_pos;
3534 gboolean prev_autowrap;
3535 gboolean found = FALSE;
3536 gboolean exists = FALSE;
3538 cm_return_if_fail(compose->account != NULL);
3542 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3543 G_CALLBACK(compose_changed_cb),
3546 mark = gtk_text_buffer_get_insert(buffer);
3547 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3548 cur_pos = gtk_text_iter_get_offset (&iter);
3551 gtk_text_buffer_get_end_iter(buffer, &iter);
3553 exists = (compose->sig_str != NULL);
3556 GtkTextIter first_iter, start_iter, end_iter;
3558 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3560 if (!exists || compose->sig_str[0] == '\0')
3563 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3564 compose->signature_tag);
3567 /* include previous \n\n */
3568 gtk_text_iter_backward_chars(&first_iter, 1);
3569 start_iter = first_iter;
3570 end_iter = first_iter;
3572 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3573 compose->signature_tag);
3574 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3575 compose->signature_tag);
3577 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3583 g_free(compose->sig_str);
3584 compose->sig_str = account_get_signature_str(compose->account);
3586 cur_pos = gtk_text_iter_get_offset(&iter);
3588 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3589 g_free(compose->sig_str);
3590 compose->sig_str = NULL;
3592 if (compose->sig_inserted == FALSE)
3593 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3594 compose->sig_inserted = TRUE;
3596 cur_pos = gtk_text_iter_get_offset(&iter);
3597 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3599 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3600 gtk_text_iter_forward_chars(&iter, 1);
3601 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3602 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3604 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3605 cur_pos = gtk_text_buffer_get_char_count (buffer);
3608 /* put the cursor where it should be
3609 * either where the quote_fmt says, either where it was */
3610 if (compose->set_cursor_pos < 0)
3611 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3613 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3614 compose->set_cursor_pos);
3616 compose->set_cursor_pos = -1;
3617 gtk_text_buffer_place_cursor(buffer, &iter);
3618 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3619 G_CALLBACK(compose_changed_cb),
3625 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3628 GtkTextBuffer *buffer;
3631 const gchar *cur_encoding;
3632 gchar buf[BUFFSIZE];
3635 gboolean prev_autowrap;
3639 GError *error = NULL;
3645 GString *file_contents = NULL;
3646 ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;