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_use_signing(Compose *compose, gboolean use_signing);
494 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
495 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
497 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
499 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
500 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
501 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
503 static void compose_attach_drag_received_cb (GtkWidget *widget,
504 GdkDragContext *drag_context,
507 GtkSelectionData *data,
511 static void compose_insert_drag_received_cb (GtkWidget *widget,
512 GdkDragContext *drag_context,
515 GtkSelectionData *data,
519 static void compose_header_drag_received_cb (GtkWidget *widget,
520 GdkDragContext *drag_context,
523 GtkSelectionData *data,
528 static gboolean compose_drag_drop (GtkWidget *widget,
529 GdkDragContext *drag_context,
531 guint time, gpointer user_data);
532 static gboolean completion_set_focus_to_subject
537 static void text_inserted (GtkTextBuffer *buffer,
542 static Compose *compose_generic_reply(MsgInfo *msginfo,
543 ComposeQuoteMode quote_mode,
547 gboolean followup_and_reply_to,
550 static void compose_headerentry_changed_cb (GtkWidget *entry,
551 ComposeHeaderEntry *headerentry);
552 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
554 ComposeHeaderEntry *headerentry);
555 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
556 ComposeHeaderEntry *headerentry);
558 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
560 static void compose_allow_user_actions (Compose *compose, gboolean allow);
562 static void compose_nothing_cb (GtkAction *action, gpointer data)
568 static void compose_check_all (GtkAction *action, gpointer data);
569 static void compose_highlight_all (GtkAction *action, gpointer data);
570 static void compose_check_backwards (GtkAction *action, gpointer data);
571 static void compose_check_forwards_go (GtkAction *action, gpointer data);
574 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
576 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
579 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
580 FolderItem *folder_item);
582 static void compose_attach_update_label(Compose *compose);
583 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
584 gboolean respect_default_to);
585 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
586 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
588 static GtkActionEntry compose_popup_entries[] =
590 {"Compose", NULL, "Compose", NULL, NULL, NULL },
591 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
592 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
593 {"Compose/---", NULL, "---", NULL, NULL, NULL },
594 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
597 static GtkActionEntry compose_entries[] =
599 {"Menu", NULL, "Menu", NULL, NULL, NULL },
601 {"Message", NULL, N_("_Message"), NULL, NULL, NULL },
602 {"Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
604 {"Spelling", NULL, N_("_Spelling"), NULL, NULL, NULL },
606 {"Options", NULL, N_("_Options"), NULL, NULL, NULL },
607 {"Tools", NULL, N_("_Tools"), NULL, NULL, NULL },
608 {"Help", NULL, N_("_Help"), NULL, NULL, NULL },
610 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
611 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
612 {"Message/---", NULL, "---", NULL, NULL, NULL },
614 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
615 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
616 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
617 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
618 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
619 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
620 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
621 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
622 /* {"Message/---", NULL, "---", NULL, NULL, NULL }, */
623 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
626 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
627 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
628 {"Edit/---", NULL, "---", NULL, NULL, NULL },
630 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
631 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
632 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
634 {"Edit/SpecialPaste", NULL, N_("_Special paste"), NULL, NULL, NULL },
635 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
636 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
637 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
639 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
641 {"Edit/Advanced", NULL, N_("A_dvanced"), NULL, NULL, NULL },
642 {"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*/
643 {"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*/
644 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
645 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
646 {"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*/
647 {"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*/
648 {"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*/
649 {"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*/
650 {"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*/
651 {"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*/
652 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
653 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
654 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
655 {"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*/
657 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
658 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
660 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
661 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
662 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
663 /* {"Edit/---", NULL, "---", NULL, NULL, NULL }, */
664 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
667 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
668 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
669 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
670 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
672 {"Spelling/---", NULL, "---", NULL, NULL, NULL },
673 {"Spelling/Options", NULL, N_("_Options"), NULL, NULL, NULL },
677 {"Options/ReplyMode", NULL, N_("Reply _mode"), NULL, NULL, NULL },
678 {"Options/---", NULL, "---", NULL, NULL, NULL },
679 {"Options/PrivacySystem", NULL, N_("Privacy _System"), NULL, NULL, NULL },
680 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
682 /* {"Options/---", NULL, "---", NULL, NULL, NULL }, */
683 {"Options/Priority", NULL, N_("_Priority"), NULL, NULL, NULL },
685 {"Options/Encoding", NULL, N_("Character _encoding"), NULL, NULL, NULL },
686 {"Options/Encoding/---", NULL, "---", NULL, NULL, NULL },
687 #define ENC_ACTION(cs_char,c_char,string) \
688 {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
690 {"Options/Encoding/Western", NULL, N_("Western European"), NULL, NULL, NULL },
691 {"Options/Encoding/Baltic", NULL, N_("Baltic"), NULL, NULL, NULL },
692 {"Options/Encoding/Hebrew", NULL, N_("Hebrew"), NULL, NULL, NULL },
693 {"Options/Encoding/Arabic", NULL, N_("Arabic"), NULL, NULL, NULL },
694 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic"), NULL, NULL, NULL },
695 {"Options/Encoding/Japanese", NULL, N_("Japanese"), NULL, NULL, NULL },
696 {"Options/Encoding/Chinese", NULL, N_("Chinese"), NULL, NULL, NULL },
697 {"Options/Encoding/Korean", NULL, N_("Korean"), NULL, NULL, NULL },
698 {"Options/Encoding/Thai", NULL, N_("Thai"), NULL, NULL, NULL },
701 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
703 {"Tools/Template", NULL, N_("_Template"), NULL, NULL, NULL },
704 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
705 {"Tools/Actions", NULL, N_("Actio_ns"), NULL, NULL, NULL },
706 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
709 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
712 static GtkToggleActionEntry compose_toggle_entries[] =
714 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
715 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
716 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
717 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
718 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
719 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
720 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
723 static GtkRadioActionEntry compose_radio_rm_entries[] =
725 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
726 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
727 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
728 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
731 static GtkRadioActionEntry compose_radio_prio_entries[] =
733 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
734 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
735 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
736 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
737 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
740 static GtkRadioActionEntry compose_radio_enc_entries[] =
742 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
777 static GtkTargetEntry compose_mime_types[] =
779 {"text/uri-list", 0, 0},
780 {"UTF8_STRING", 0, 0},
784 static gboolean compose_put_existing_to_front(MsgInfo *info)
786 const GList *compose_list = compose_get_compose_list();
787 const GList *elem = NULL;
790 for (elem = compose_list; elem != NULL && elem->data != NULL;
792 Compose *c = (Compose*)elem->data;
794 if (!c->targetinfo || !c->targetinfo->msgid ||
798 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
799 gtkut_window_popup(c->window);
807 static GdkColor quote_color1 =
808 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 static GdkColor quote_color2 =
810 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
811 static GdkColor quote_color3 =
812 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
814 static GdkColor quote_bgcolor1 =
815 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor2 =
817 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
818 static GdkColor quote_bgcolor3 =
819 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
821 static GdkColor signature_color = {
828 static GdkColor uri_color = {
835 static void compose_create_tags(GtkTextView *text, Compose *compose)
837 GtkTextBuffer *buffer;
838 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
839 #if !GTK_CHECK_VERSION(2, 24, 0)
846 buffer = gtk_text_view_get_buffer(text);
848 if (prefs_common.enable_color) {
849 /* grab the quote colors, converting from an int to a GdkColor */
850 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
852 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
854 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
856 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
858 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
860 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
862 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
864 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
867 signature_color = quote_color1 = quote_color2 = quote_color3 =
868 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
871 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
872 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
873 "foreground-gdk", "e_color1,
874 "paragraph-background-gdk", "e_bgcolor1,
876 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
877 "foreground-gdk", "e_color2,
878 "paragraph-background-gdk", "e_bgcolor2,
880 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
881 "foreground-gdk", "e_color3,
882 "paragraph-background-gdk", "e_bgcolor3,
885 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
886 "foreground-gdk", "e_color1,
888 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
889 "foreground-gdk", "e_color2,
891 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
892 "foreground-gdk", "e_color3,
896 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
897 "foreground-gdk", &signature_color,
900 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
901 "foreground-gdk", &uri_color,
903 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
904 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
906 #if !GTK_CHECK_VERSION(2, 24, 0)
907 color[0] = quote_color1;
908 color[1] = quote_color2;
909 color[2] = quote_color3;
910 color[3] = quote_bgcolor1;
911 color[4] = quote_bgcolor2;
912 color[5] = quote_bgcolor3;
913 color[6] = signature_color;
914 color[7] = uri_color;
916 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
917 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
919 for (i = 0; i < 8; i++) {
920 if (success[i] == FALSE) {
921 g_warning("Compose: color allocation failed.");
922 quote_color1 = quote_color2 = quote_color3 =
923 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
924 signature_color = uri_color = black;
930 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
933 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
936 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
938 return compose_generic_new(account, mailto, item, NULL, NULL);
941 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
943 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
946 #define SCROLL_TO_CURSOR(compose) { \
947 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
948 gtk_text_view_get_buffer( \
949 GTK_TEXT_VIEW(compose->text))); \
950 gtk_text_view_scroll_mark_onscreen( \
951 GTK_TEXT_VIEW(compose->text), \
955 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
958 if (folderidentifier) {
959 #if !GTK_CHECK_VERSION(2, 24, 0)
960 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
962 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
964 prefs_common.compose_save_to_history = add_history(
965 prefs_common.compose_save_to_history, folderidentifier);
966 #if !GTK_CHECK_VERSION(2, 24, 0)
967 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
968 prefs_common.compose_save_to_history);
970 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
971 prefs_common.compose_save_to_history);
975 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
976 if (folderidentifier)
977 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
979 gtk_entry_set_text(GTK_ENTRY(entry), "");
982 static gchar *compose_get_save_to(Compose *compose)
985 gchar *result = NULL;
986 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
987 result = gtk_editable_get_chars(entry, 0, -1);
990 #if !GTK_CHECK_VERSION(2, 24, 0)
991 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
993 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
995 prefs_common.compose_save_to_history = add_history(
996 prefs_common.compose_save_to_history, result);
997 #if !GTK_CHECK_VERSION(2, 24, 0)
998 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
999 prefs_common.compose_save_to_history);
1001 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1002 prefs_common.compose_save_to_history);
1008 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1009 GList *attach_files, GList *listAddress )
1012 GtkTextView *textview;
1013 GtkTextBuffer *textbuf;
1015 const gchar *subject_format = NULL;
1016 const gchar *body_format = NULL;
1017 gchar *mailto_from = NULL;
1018 PrefsAccount *mailto_account = NULL;
1019 MsgInfo* dummyinfo = NULL;
1020 gint cursor_pos = -1;
1021 MailField mfield = NO_FIELD_PRESENT;
1025 /* check if mailto defines a from */
1026 if (mailto && *mailto != '\0') {
1027 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1028 /* mailto defines a from, check if we can get account prefs from it,
1029 if not, the account prefs will be guessed using other ways, but we'll keep
1032 mailto_account = account_find_from_address(mailto_from, TRUE);
1033 if (mailto_account == NULL) {
1035 Xstrdup_a(tmp_from, mailto_from, return NULL);
1036 extract_address(tmp_from);
1037 mailto_account = account_find_from_address(tmp_from, TRUE);
1041 account = mailto_account;
1044 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1045 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1046 account = account_find_from_id(item->prefs->default_account);
1048 /* if no account prefs set, fallback to the current one */
1049 if (!account) account = cur_account;
1050 cm_return_val_if_fail(account != NULL, NULL);
1052 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1054 /* override from name if mailto asked for it */
1056 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1057 g_free(mailto_from);
1059 /* override from name according to folder properties */
1060 if (item && item->prefs &&
1061 item->prefs->compose_with_format &&
1062 item->prefs->compose_override_from_format &&
1063 *item->prefs->compose_override_from_format != '\0') {
1068 dummyinfo = compose_msginfo_new_from_compose(compose);
1070 /* decode \-escape sequences in the internal representation of the quote format */
1071 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1072 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1075 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1076 compose->gtkaspell);
1078 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1080 quote_fmt_scan_string(tmp);
1083 buf = quote_fmt_get_buffer();
1085 alertpanel_error(_("New message From format error."));
1087 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1088 quote_fmt_reset_vartable();
1093 compose->replyinfo = NULL;
1094 compose->fwdinfo = NULL;
1096 textview = GTK_TEXT_VIEW(compose->text);
1097 textbuf = gtk_text_view_get_buffer(textview);
1098 compose_create_tags(textview, compose);
1100 undo_block(compose->undostruct);
1102 compose_set_dictionaries_from_folder_prefs(compose, item);
1105 if (account->auto_sig)
1106 compose_insert_sig(compose, FALSE);
1107 gtk_text_buffer_get_start_iter(textbuf, &iter);
1108 gtk_text_buffer_place_cursor(textbuf, &iter);
1110 if (account->protocol != A_NNTP) {
1111 if (mailto && *mailto != '\0') {
1112 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1115 compose_set_folder_prefs(compose, item, TRUE);
1117 if (item && item->ret_rcpt) {
1118 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1121 if (mailto && *mailto != '\0') {
1122 if (!strchr(mailto, '@'))
1123 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1125 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1126 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1127 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1128 mfield = TO_FIELD_PRESENT;
1131 * CLAWS: just don't allow return receipt request, even if the user
1132 * may want to send an email. simple but foolproof.
1134 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1136 compose_add_field_list( compose, listAddress );
1138 if (item && item->prefs && item->prefs->compose_with_format) {
1139 subject_format = item->prefs->compose_subject_format;
1140 body_format = item->prefs->compose_body_format;
1141 } else if (account->compose_with_format) {
1142 subject_format = account->compose_subject_format;
1143 body_format = account->compose_body_format;
1144 } else if (prefs_common.compose_with_format) {
1145 subject_format = prefs_common.compose_subject_format;
1146 body_format = prefs_common.compose_body_format;
1149 if (subject_format || body_format) {
1152 && *subject_format != '\0' )
1154 gchar *subject = NULL;
1159 dummyinfo = compose_msginfo_new_from_compose(compose);
1161 /* decode \-escape sequences in the internal representation of the quote format */
1162 tmp = g_malloc(strlen(subject_format)+1);
1163 pref_get_unescaped_pref(tmp, subject_format);
1165 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1167 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1168 compose->gtkaspell);
1170 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1172 quote_fmt_scan_string(tmp);
1175 buf = quote_fmt_get_buffer();
1177 alertpanel_error(_("New message subject format error."));
1179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1181 quote_fmt_reset_vartable();
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;
1229 compose_attach_append(compose, ainfo->file, ainfo->file,
1230 ainfo->content_type, ainfo->charset);
1234 compose_show_first_last_header(compose, TRUE);
1236 /* Set save folder */
1237 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1238 gchar *folderidentifier;
1240 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1241 folderidentifier = folder_item_get_identifier(item);
1242 compose_set_save_to(compose, folderidentifier);
1243 g_free(folderidentifier);
1246 /* Place cursor according to provided input (mfield) */
1248 case NO_FIELD_PRESENT:
1249 if (compose->header_last)
1250 gtk_widget_grab_focus(compose->header_last->entry);
1252 case TO_FIELD_PRESENT:
1253 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1255 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1258 gtk_widget_grab_focus(compose->subject_entry);
1260 case SUBJECT_FIELD_PRESENT:
1261 textview = GTK_TEXT_VIEW(compose->text);
1264 textbuf = gtk_text_view_get_buffer(textview);
1267 mark = gtk_text_buffer_get_insert(textbuf);
1268 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1269 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1271 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272 * only defers where it comes to the variable body
1273 * is not null. If no body is present compose->text
1274 * will be null in which case you cannot place the
1275 * cursor inside the component so. An empty component
1276 * is therefore created before placing the cursor
1278 case BODY_FIELD_PRESENT:
1279 cursor_pos = quote_fmt_get_cursor_pos();
1280 if (cursor_pos == -1)
1281 gtk_widget_grab_focus(compose->header_last->entry);
1283 gtk_widget_grab_focus(compose->text);
1287 undo_unblock(compose->undostruct);
1289 if (prefs_common.auto_exteditor)
1290 compose_exec_ext_editor(compose);
1292 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1294 SCROLL_TO_CURSOR(compose);
1296 compose->modified = FALSE;
1297 compose_set_title(compose);
1299 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1304 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1305 gboolean override_pref, const gchar *system)
1307 const gchar *privacy = NULL;
1309 cm_return_if_fail(compose != NULL);
1310 cm_return_if_fail(account != NULL);
1312 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1315 if (account->default_privacy_system && strlen(account->default_privacy_system))
1316 privacy = account->default_privacy_system;
1320 GSList *privacy_avail = privacy_get_system_ids();
1321 if (privacy_avail && g_slist_length(privacy_avail)) {
1322 privacy = (gchar *)(privacy_avail->data);
1324 g_slist_free_full(privacy_avail, g_free);
1326 if (privacy != NULL) {
1328 g_free(compose->privacy_system);
1329 compose->privacy_system = NULL;
1330 g_free(compose->encdata);
1331 compose->encdata = NULL;
1333 if (compose->privacy_system == NULL)
1334 compose->privacy_system = g_strdup(privacy);
1335 else if (*(compose->privacy_system) == '\0') {
1336 g_free(compose->privacy_system);
1337 g_free(compose->encdata);
1338 compose->encdata = NULL;
1339 compose->privacy_system = g_strdup(privacy);
1341 compose_update_privacy_system_menu_item(compose, FALSE);
1342 compose_use_encryption(compose, TRUE);
1346 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1348 const gchar *privacy = NULL;
1350 if (account->default_privacy_system && strlen(account->default_privacy_system))
1351 privacy = account->default_privacy_system;
1355 GSList *privacy_avail = privacy_get_system_ids();
1356 if (privacy_avail && g_slist_length(privacy_avail)) {
1357 privacy = (gchar *)(privacy_avail->data);
1361 if (privacy != NULL) {
1363 g_free(compose->privacy_system);
1364 compose->privacy_system = NULL;
1365 g_free(compose->encdata);
1366 compose->encdata = NULL;
1368 if (compose->privacy_system == NULL)
1369 compose->privacy_system = g_strdup(privacy);
1370 compose_update_privacy_system_menu_item(compose, FALSE);
1371 compose_use_signing(compose, TRUE);
1375 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1379 Compose *compose = NULL;
1381 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1383 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1384 cm_return_val_if_fail(msginfo != NULL, NULL);
1386 list_len = g_slist_length(msginfo_list);
1390 case COMPOSE_REPLY_TO_ADDRESS:
1391 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1392 FALSE, prefs_common.default_reply_list, FALSE, body);
1394 case COMPOSE_REPLY_WITH_QUOTE:
1395 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1396 FALSE, prefs_common.default_reply_list, FALSE, body);
1398 case COMPOSE_REPLY_WITHOUT_QUOTE:
1399 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1400 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1402 case COMPOSE_REPLY_TO_SENDER:
1403 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1404 FALSE, FALSE, TRUE, body);
1406 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1407 compose = compose_followup_and_reply_to(msginfo,
1408 COMPOSE_QUOTE_CHECK,
1409 FALSE, FALSE, body);
1411 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1412 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1413 FALSE, FALSE, TRUE, body);
1415 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1416 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1417 FALSE, FALSE, TRUE, NULL);
1419 case COMPOSE_REPLY_TO_ALL:
1420 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1421 TRUE, FALSE, FALSE, body);
1423 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1424 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1425 TRUE, FALSE, FALSE, body);
1427 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1428 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1429 TRUE, FALSE, FALSE, NULL);
1431 case COMPOSE_REPLY_TO_LIST:
1432 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1433 FALSE, TRUE, FALSE, body);
1435 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1436 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1437 FALSE, TRUE, FALSE, body);
1439 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1440 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1441 FALSE, TRUE, FALSE, NULL);
1443 case COMPOSE_FORWARD:
1444 if (prefs_common.forward_as_attachment) {
1445 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1448 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1452 case COMPOSE_FORWARD_INLINE:
1453 /* check if we reply to more than one Message */
1454 if (list_len == 1) {
1455 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1458 /* more messages FALL THROUGH */
1459 case COMPOSE_FORWARD_AS_ATTACH:
1460 compose = compose_forward_multiple(NULL, msginfo_list);
1462 case COMPOSE_REDIRECT:
1463 compose = compose_redirect(NULL, msginfo, FALSE);
1466 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1469 if (compose == NULL) {
1470 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1474 compose->rmode = mode;
1475 switch (compose->rmode) {
1477 case COMPOSE_REPLY_WITH_QUOTE:
1478 case COMPOSE_REPLY_WITHOUT_QUOTE:
1479 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1480 debug_print("reply mode Normal\n");
1481 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1482 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1484 case COMPOSE_REPLY_TO_SENDER:
1485 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1486 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1487 debug_print("reply mode Sender\n");
1488 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1490 case COMPOSE_REPLY_TO_ALL:
1491 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1492 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1493 debug_print("reply mode All\n");
1494 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1496 case COMPOSE_REPLY_TO_LIST:
1497 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1498 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1499 debug_print("reply mode List\n");
1500 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1502 case COMPOSE_REPLY_TO_ADDRESS:
1503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1511 static Compose *compose_reply(MsgInfo *msginfo,
1512 ComposeQuoteMode quote_mode,
1518 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1519 to_sender, FALSE, body);
1522 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1523 ComposeQuoteMode quote_mode,
1528 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1529 to_sender, TRUE, body);
1532 static void compose_extract_original_charset(Compose *compose)
1534 MsgInfo *info = NULL;
1535 if (compose->replyinfo) {
1536 info = compose->replyinfo;
1537 } else if (compose->fwdinfo) {
1538 info = compose->fwdinfo;
1539 } else if (compose->targetinfo) {
1540 info = compose->targetinfo;
1543 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1544 MimeInfo *partinfo = mimeinfo;
1545 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1546 partinfo = procmime_mimeinfo_next(partinfo);
1548 compose->orig_charset =
1549 g_strdup(procmime_mimeinfo_get_parameter(
1550 partinfo, "charset"));
1552 procmime_mimeinfo_free_all(&mimeinfo);
1556 #define SIGNAL_BLOCK(buffer) { \
1557 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1558 G_CALLBACK(compose_changed_cb), \
1560 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1561 G_CALLBACK(text_inserted), \
1565 #define SIGNAL_UNBLOCK(buffer) { \
1566 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1567 G_CALLBACK(compose_changed_cb), \
1569 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1570 G_CALLBACK(text_inserted), \
1574 static Compose *compose_generic_reply(MsgInfo *msginfo,
1575 ComposeQuoteMode quote_mode,
1576 gboolean to_all, gboolean to_ml,
1578 gboolean followup_and_reply_to,
1582 PrefsAccount *account = NULL;
1583 GtkTextView *textview;
1584 GtkTextBuffer *textbuf;
1585 gboolean quote = FALSE;
1586 const gchar *qmark = NULL;
1587 const gchar *body_fmt = NULL;
1588 gchar *s_system = NULL;
1590 cm_return_val_if_fail(msginfo != NULL, NULL);
1591 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1593 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1595 cm_return_val_if_fail(account != NULL, NULL);
1597 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1599 compose->updating = TRUE;
1601 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1602 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1604 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1605 if (!compose->replyinfo)
1606 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1608 compose_extract_original_charset(compose);
1610 if (msginfo->folder && msginfo->folder->ret_rcpt)
1611 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1613 /* Set save folder */
1614 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1615 gchar *folderidentifier;
1617 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1618 folderidentifier = folder_item_get_identifier(msginfo->folder);
1619 compose_set_save_to(compose, folderidentifier);
1620 g_free(folderidentifier);
1623 if (compose_parse_header(compose, msginfo) < 0) {
1624 compose->updating = FALSE;
1625 compose_destroy(compose);
1629 /* override from name according to folder properties */
1630 if (msginfo->folder && msginfo->folder->prefs &&
1631 msginfo->folder->prefs->reply_with_format &&
1632 msginfo->folder->prefs->reply_override_from_format &&
1633 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1638 /* decode \-escape sequences in the internal representation of the quote format */
1639 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1640 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1643 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1644 compose->gtkaspell);
1646 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1648 quote_fmt_scan_string(tmp);
1651 buf = quote_fmt_get_buffer();
1653 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1655 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1656 quote_fmt_reset_vartable();
1661 textview = (GTK_TEXT_VIEW(compose->text));
1662 textbuf = gtk_text_view_get_buffer(textview);
1663 compose_create_tags(textview, compose);
1665 undo_block(compose->undostruct);
1667 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1668 gtkaspell_block_check(compose->gtkaspell);
1671 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1672 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1673 /* use the reply format of folder (if enabled), or the account's one
1674 (if enabled) or fallback to the global reply format, which is always
1675 enabled (even if empty), and use the relevant quotemark */
1677 if (msginfo->folder && msginfo->folder->prefs &&
1678 msginfo->folder->prefs->reply_with_format) {
1679 qmark = msginfo->folder->prefs->reply_quotemark;
1680 body_fmt = msginfo->folder->prefs->reply_body_format;
1682 } else if (account->reply_with_format) {
1683 qmark = account->reply_quotemark;
1684 body_fmt = account->reply_body_format;
1687 qmark = prefs_common.quotemark;
1688 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1689 body_fmt = gettext(prefs_common.quotefmt);
1696 /* empty quotemark is not allowed */
1697 if (qmark == NULL || *qmark == '\0')
1699 compose_quote_fmt(compose, compose->replyinfo,
1700 body_fmt, qmark, body, FALSE, TRUE,
1701 _("The body of the \"Reply\" template has an error at line %d."));
1702 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1703 quote_fmt_reset_vartable();
1706 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1707 compose_force_encryption(compose, account, FALSE, s_system);
1710 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1711 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1712 compose_force_signing(compose, account, s_system);
1716 SIGNAL_BLOCK(textbuf);
1718 if (account->auto_sig)
1719 compose_insert_sig(compose, FALSE);
1721 compose_wrap_all(compose);
1724 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1725 gtkaspell_highlight_all(compose->gtkaspell);
1726 gtkaspell_unblock_check(compose->gtkaspell);
1728 SIGNAL_UNBLOCK(textbuf);
1730 gtk_widget_grab_focus(compose->text);
1732 undo_unblock(compose->undostruct);
1734 if (prefs_common.auto_exteditor)
1735 compose_exec_ext_editor(compose);
1737 compose->modified = FALSE;
1738 compose_set_title(compose);
1740 compose->updating = FALSE;
1741 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1742 SCROLL_TO_CURSOR(compose);
1744 if (compose->deferred_destroy) {
1745 compose_destroy(compose);
1753 #define INSERT_FW_HEADER(var, hdr) \
1754 if (msginfo->var && *msginfo->var) { \
1755 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1756 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1757 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1760 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1761 gboolean as_attach, const gchar *body,
1762 gboolean no_extedit,
1766 GtkTextView *textview;
1767 GtkTextBuffer *textbuf;
1768 gint cursor_pos = -1;
1771 cm_return_val_if_fail(msginfo != NULL, NULL);
1772 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1774 if (!account && !(account = compose_find_account(msginfo)))
1775 account = cur_account;
1777 if (!prefs_common.forward_as_attachment)
1778 mode = COMPOSE_FORWARD_INLINE;
1780 mode = COMPOSE_FORWARD;
1781 compose = compose_create(account, msginfo->folder, mode, batch);
1783 compose->updating = TRUE;
1784 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1785 if (!compose->fwdinfo)
1786 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1788 compose_extract_original_charset(compose);
1790 if (msginfo->subject && *msginfo->subject) {
1791 gchar *buf, *buf2, *p;
1793 buf = p = g_strdup(msginfo->subject);
1794 p += subject_get_prefix_length(p);
1795 memmove(buf, p, strlen(p) + 1);
1797 buf2 = g_strdup_printf("Fw: %s", buf);
1798 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1804 /* override from name according to folder properties */
1805 if (msginfo->folder && msginfo->folder->prefs &&
1806 msginfo->folder->prefs->forward_with_format &&
1807 msginfo->folder->prefs->forward_override_from_format &&
1808 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1812 MsgInfo *full_msginfo = NULL;
1815 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1817 full_msginfo = procmsg_msginfo_copy(msginfo);
1819 /* decode \-escape sequences in the internal representation of the quote format */
1820 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1821 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1824 gtkaspell_block_check(compose->gtkaspell);
1825 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1826 compose->gtkaspell);
1828 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1830 quote_fmt_scan_string(tmp);
1833 buf = quote_fmt_get_buffer();
1835 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1837 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1838 quote_fmt_reset_vartable();
1841 procmsg_msginfo_free(&full_msginfo);
1844 textview = GTK_TEXT_VIEW(compose->text);
1845 textbuf = gtk_text_view_get_buffer(textview);
1846 compose_create_tags(textview, compose);
1848 undo_block(compose->undostruct);
1852 msgfile = procmsg_get_message_file(msginfo);
1853 if (!is_file_exist(msgfile))
1854 g_warning("%s: file does not exist", msgfile);
1856 compose_attach_append(compose, msgfile, msgfile,
1857 "message/rfc822", NULL);
1861 const gchar *qmark = NULL;
1862 const gchar *body_fmt = NULL;
1863 MsgInfo *full_msginfo;
1865 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1867 full_msginfo = procmsg_msginfo_copy(msginfo);
1869 /* use the forward format of folder (if enabled), or the account's one
1870 (if enabled) or fallback to the global forward format, which is always
1871 enabled (even if empty), and use the relevant quotemark */
1872 if (msginfo->folder && msginfo->folder->prefs &&
1873 msginfo->folder->prefs->forward_with_format) {
1874 qmark = msginfo->folder->prefs->forward_quotemark;
1875 body_fmt = msginfo->folder->prefs->forward_body_format;
1877 } else if (account->forward_with_format) {
1878 qmark = account->forward_quotemark;
1879 body_fmt = account->forward_body_format;
1882 qmark = prefs_common.fw_quotemark;
1883 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1884 body_fmt = gettext(prefs_common.fw_quotefmt);
1889 /* empty quotemark is not allowed */
1890 if (qmark == NULL || *qmark == '\0')
1893 compose_quote_fmt(compose, full_msginfo,
1894 body_fmt, qmark, body, FALSE, TRUE,
1895 _("The body of the \"Forward\" template has an error at line %d."));
1896 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1897 quote_fmt_reset_vartable();
1898 compose_attach_parts(compose, msginfo);
1900 procmsg_msginfo_free(&full_msginfo);
1903 SIGNAL_BLOCK(textbuf);
1905 if (account->auto_sig)
1906 compose_insert_sig(compose, FALSE);
1908 compose_wrap_all(compose);
1911 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1912 gtkaspell_highlight_all(compose->gtkaspell);
1913 gtkaspell_unblock_check(compose->gtkaspell);
1915 SIGNAL_UNBLOCK(textbuf);
1917 cursor_pos = quote_fmt_get_cursor_pos();
1918 if (cursor_pos == -1)
1919 gtk_widget_grab_focus(compose->header_last->entry);
1921 gtk_widget_grab_focus(compose->text);
1923 if (!no_extedit && prefs_common.auto_exteditor)
1924 compose_exec_ext_editor(compose);
1927 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1928 gchar *folderidentifier;
1930 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1931 folderidentifier = folder_item_get_identifier(msginfo->folder);
1932 compose_set_save_to(compose, folderidentifier);
1933 g_free(folderidentifier);
1936 undo_unblock(compose->undostruct);
1938 compose->modified = FALSE;
1939 compose_set_title(compose);
1941 compose->updating = FALSE;
1942 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1943 SCROLL_TO_CURSOR(compose);
1945 if (compose->deferred_destroy) {
1946 compose_destroy(compose);
1950 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1955 #undef INSERT_FW_HEADER
1957 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1960 GtkTextView *textview;
1961 GtkTextBuffer *textbuf;
1965 gboolean single_mail = TRUE;
1967 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1969 if (g_slist_length(msginfo_list) > 1)
1970 single_mail = FALSE;
1972 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1973 if (((MsgInfo *)msginfo->data)->folder == NULL)
1976 /* guess account from first selected message */
1978 !(account = compose_find_account(msginfo_list->data)))
1979 account = cur_account;
1981 cm_return_val_if_fail(account != NULL, NULL);
1983 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1984 if (msginfo->data) {
1985 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1986 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1990 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1991 g_warning("no msginfo_list");
1995 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1997 compose->updating = TRUE;
1999 /* override from name according to folder properties */
2000 if (msginfo_list->data) {
2001 MsgInfo *msginfo = msginfo_list->data;
2003 if (msginfo->folder && msginfo->folder->prefs &&
2004 msginfo->folder->prefs->forward_with_format &&
2005 msginfo->folder->prefs->forward_override_from_format &&
2006 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2011 /* decode \-escape sequences in the internal representation of the quote format */
2012 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2013 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2016 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2017 compose->gtkaspell);
2019 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2021 quote_fmt_scan_string(tmp);
2024 buf = quote_fmt_get_buffer();
2026 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2028 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2029 quote_fmt_reset_vartable();
2035 textview = GTK_TEXT_VIEW(compose->text);
2036 textbuf = gtk_text_view_get_buffer(textview);
2037 compose_create_tags(textview, compose);
2039 undo_block(compose->undostruct);
2040 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2041 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2043 if (!is_file_exist(msgfile))
2044 g_warning("%s: file does not exist", msgfile);
2046 compose_attach_append(compose, msgfile, msgfile,
2047 "message/rfc822", NULL);
2052 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2053 if (info->subject && *info->subject) {
2054 gchar *buf, *buf2, *p;
2056 buf = p = g_strdup(info->subject);
2057 p += subject_get_prefix_length(p);
2058 memmove(buf, p, strlen(p) + 1);
2060 buf2 = g_strdup_printf("Fw: %s", buf);
2061 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2067 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2068 _("Fw: multiple emails"));
2071 SIGNAL_BLOCK(textbuf);
2073 if (account->auto_sig)
2074 compose_insert_sig(compose, FALSE);
2076 compose_wrap_all(compose);
2078 SIGNAL_UNBLOCK(textbuf);
2080 gtk_text_buffer_get_start_iter(textbuf, &iter);
2081 gtk_text_buffer_place_cursor(textbuf, &iter);
2083 if (prefs_common.auto_exteditor)
2084 compose_exec_ext_editor(compose);
2086 gtk_widget_grab_focus(compose->header_last->entry);
2087 undo_unblock(compose->undostruct);
2088 compose->modified = FALSE;
2089 compose_set_title(compose);
2091 compose->updating = FALSE;
2092 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2093 SCROLL_TO_CURSOR(compose);
2095 if (compose->deferred_destroy) {
2096 compose_destroy(compose);
2100 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2105 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2107 GtkTextIter start = *iter;
2108 GtkTextIter end_iter;
2109 int start_pos = gtk_text_iter_get_offset(&start);
2111 if (!compose->account->sig_sep)
2114 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2115 start_pos+strlen(compose->account->sig_sep));
2117 /* check sig separator */
2118 str = gtk_text_iter_get_text(&start, &end_iter);
2119 if (!strcmp(str, compose->account->sig_sep)) {
2121 /* check end of line (\n) */
2122 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2123 start_pos+strlen(compose->account->sig_sep));
2124 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2125 start_pos+strlen(compose->account->sig_sep)+1);
2126 tmp = gtk_text_iter_get_text(&start, &end_iter);
2127 if (!strcmp(tmp,"\n")) {
2139 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2141 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2142 Compose *compose = (Compose *)data;
2143 FolderItem *old_item = NULL;
2144 FolderItem *new_item = NULL;
2145 gchar *old_id, *new_id;
2147 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2148 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2151 old_item = hookdata->item;
2152 new_item = hookdata->item2;
2154 old_id = folder_item_get_identifier(old_item);
2155 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2157 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2158 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2159 compose->targetinfo->folder = new_item;
2162 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2163 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2164 compose->replyinfo->folder = new_item;
2167 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2168 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2169 compose->fwdinfo->folder = new_item;
2177 static void compose_colorize_signature(Compose *compose)
2179 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2181 GtkTextIter end_iter;
2182 gtk_text_buffer_get_start_iter(buffer, &iter);
2183 while (gtk_text_iter_forward_line(&iter))
2184 if (compose_is_sig_separator(compose, buffer, &iter)) {
2185 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2186 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2190 #define BLOCK_WRAP() { \
2191 prev_autowrap = compose->autowrap; \
2192 buffer = gtk_text_view_get_buffer( \
2193 GTK_TEXT_VIEW(compose->text)); \
2194 compose->autowrap = FALSE; \
2196 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2197 G_CALLBACK(compose_changed_cb), \
2199 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2200 G_CALLBACK(text_inserted), \
2203 #define UNBLOCK_WRAP() { \
2204 compose->autowrap = prev_autowrap; \
2205 if (compose->autowrap) { \
2206 gint old = compose->draft_timeout_tag; \
2207 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2208 compose_wrap_all(compose); \
2209 compose->draft_timeout_tag = old; \
2212 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2213 G_CALLBACK(compose_changed_cb), \
2215 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2216 G_CALLBACK(text_inserted), \
2220 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2222 Compose *compose = NULL;
2223 PrefsAccount *account = NULL;
2224 GtkTextView *textview;
2225 GtkTextBuffer *textbuf;
2229 gboolean use_signing = FALSE;
2230 gboolean use_encryption = FALSE;
2231 gchar *privacy_system = NULL;
2232 int priority = PRIORITY_NORMAL;
2233 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2234 gboolean autowrap = prefs_common.autowrap;
2235 gboolean autoindent = prefs_common.auto_indent;
2236 HeaderEntry *manual_headers = NULL;
2238 cm_return_val_if_fail(msginfo != NULL, NULL);
2239 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2241 if (compose_put_existing_to_front(msginfo)) {
2245 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2246 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2247 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2248 gchar *queueheader_buf = NULL;
2251 /* Select Account from queue headers */
2252 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2253 "X-Claws-Account-Id:")) {
2254 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2255 account = account_find_from_id(id);
2256 g_free(queueheader_buf);
2258 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2259 "X-Sylpheed-Account-Id:")) {
2260 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2261 account = account_find_from_id(id);
2262 g_free(queueheader_buf);
2264 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2266 id = atoi(&queueheader_buf[strlen("NAID:")]);
2267 account = account_find_from_id(id);
2268 g_free(queueheader_buf);
2270 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2272 id = atoi(&queueheader_buf[strlen("MAID:")]);
2273 account = account_find_from_id(id);
2274 g_free(queueheader_buf);
2276 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2278 account = account_find_from_address(queueheader_buf, FALSE);
2279 g_free(queueheader_buf);
2281 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2283 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2284 use_signing = param;
2285 g_free(queueheader_buf);
2287 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2288 "X-Sylpheed-Sign:")) {
2289 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2290 use_signing = param;
2291 g_free(queueheader_buf);
2293 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2294 "X-Claws-Encrypt:")) {
2295 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2296 use_encryption = param;
2297 g_free(queueheader_buf);
2299 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2300 "X-Sylpheed-Encrypt:")) {
2301 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2302 use_encryption = param;
2303 g_free(queueheader_buf);
2305 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2306 "X-Claws-Auto-Wrapping:")) {
2307 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2309 g_free(queueheader_buf);
2311 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2312 "X-Claws-Auto-Indent:")) {
2313 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2315 g_free(queueheader_buf);
2317 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2318 "X-Claws-Privacy-System:")) {
2319 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2320 g_free(queueheader_buf);
2322 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2323 "X-Sylpheed-Privacy-System:")) {
2324 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2325 g_free(queueheader_buf);
2327 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2329 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2331 g_free(queueheader_buf);
2333 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2335 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2336 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2337 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2338 if (orig_item != NULL) {
2339 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2343 g_free(queueheader_buf);
2345 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2347 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2348 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2349 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2350 if (orig_item != NULL) {
2351 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2355 g_free(queueheader_buf);
2357 /* Get manual headers */
2358 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2359 "X-Claws-Manual-Headers:")) {
2360 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2361 if (listmh && *listmh != '\0') {
2362 debug_print("Got manual headers: %s\n", listmh);
2363 manual_headers = procheader_entries_from_str(listmh);
2366 g_free(queueheader_buf);
2369 account = msginfo->folder->folder->account;
2372 if (!account && prefs_common.reedit_account_autosel) {
2374 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2375 extract_address(from);
2376 account = account_find_from_address(from, FALSE);
2381 account = cur_account;
2383 cm_return_val_if_fail(account != NULL, NULL);
2385 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2387 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2388 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2389 compose->autowrap = autowrap;
2390 compose->replyinfo = replyinfo;
2391 compose->fwdinfo = fwdinfo;
2393 compose->updating = TRUE;
2394 compose->priority = priority;
2396 if (privacy_system != NULL) {
2397 compose->privacy_system = privacy_system;
2398 compose_use_signing(compose, use_signing);
2399 compose_use_encryption(compose, use_encryption);
2400 compose_update_privacy_system_menu_item(compose, FALSE);
2402 activate_privacy_system(compose, account, FALSE);
2405 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2407 compose_extract_original_charset(compose);
2409 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2410 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2411 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2412 gchar *queueheader_buf = NULL;
2414 /* Set message save folder */
2415 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2416 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2417 compose_set_save_to(compose, &queueheader_buf[4]);
2418 g_free(queueheader_buf);
2420 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2421 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2423 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2425 g_free(queueheader_buf);
2429 if (compose_parse_header(compose, msginfo) < 0) {
2430 compose->updating = FALSE;
2431 compose_destroy(compose);
2434 compose_reedit_set_entry(compose, msginfo);
2436 textview = GTK_TEXT_VIEW(compose->text);
2437 textbuf = gtk_text_view_get_buffer(textview);
2438 compose_create_tags(textview, compose);
2440 mark = gtk_text_buffer_get_insert(textbuf);
2441 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2443 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2444 G_CALLBACK(compose_changed_cb),
2447 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2448 fp = procmime_get_first_encrypted_text_content(msginfo);
2450 compose_force_encryption(compose, account, TRUE, NULL);
2453 fp = procmime_get_first_text_content(msginfo);
2456 g_warning("Can't get text part");
2460 gchar buf[BUFFSIZE];
2461 gboolean prev_autowrap;
2462 GtkTextBuffer *buffer;
2464 while (fgets(buf, sizeof(buf), fp) != NULL) {
2466 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2472 compose_attach_parts(compose, msginfo);
2474 compose_colorize_signature(compose);
2476 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2477 G_CALLBACK(compose_changed_cb),
2480 if (manual_headers != NULL) {
2481 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2482 procheader_entries_free(manual_headers);
2483 compose->updating = FALSE;
2484 compose_destroy(compose);
2487 procheader_entries_free(manual_headers);
2490 gtk_widget_grab_focus(compose->text);
2492 if (prefs_common.auto_exteditor) {
2493 compose_exec_ext_editor(compose);
2495 compose->modified = FALSE;
2496 compose_set_title(compose);
2498 compose->updating = FALSE;
2499 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2500 SCROLL_TO_CURSOR(compose);
2502 if (compose->deferred_destroy) {
2503 compose_destroy(compose);
2507 compose->sig_str = account_get_signature_str(compose->account);
2509 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2514 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2521 cm_return_val_if_fail(msginfo != NULL, NULL);
2524 account = account_get_reply_account(msginfo,
2525 prefs_common.reply_account_autosel);
2526 cm_return_val_if_fail(account != NULL, NULL);
2528 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2530 compose->updating = TRUE;
2532 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2533 compose->replyinfo = NULL;
2534 compose->fwdinfo = NULL;
2536 compose_show_first_last_header(compose, TRUE);
2538 gtk_widget_grab_focus(compose->header_last->entry);
2540 filename = procmsg_get_message_file(msginfo);
2542 if (filename == NULL) {
2543 compose->updating = FALSE;
2544 compose_destroy(compose);
2549 compose->redirect_filename = filename;
2551 /* Set save folder */
2552 item = msginfo->folder;
2553 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2554 gchar *folderidentifier;
2556 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2557 folderidentifier = folder_item_get_identifier(item);
2558 compose_set_save_to(compose, folderidentifier);
2559 g_free(folderidentifier);
2562 compose_attach_parts(compose, msginfo);
2564 if (msginfo->subject)
2565 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2567 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2569 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2570 _("The body of the \"Redirect\" template has an error at line %d."));
2571 quote_fmt_reset_vartable();
2572 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2574 compose_colorize_signature(compose);
2577 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2578 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2579 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2581 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2586 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2589 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2591 if (compose->toolbar->draft_btn)
2592 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2593 if (compose->toolbar->insert_btn)
2594 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2595 if (compose->toolbar->attach_btn)
2596 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2597 if (compose->toolbar->sig_btn)
2598 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2599 if (compose->toolbar->exteditor_btn)
2600 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2601 if (compose->toolbar->linewrap_current_btn)
2602 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2603 if (compose->toolbar->linewrap_all_btn)
2604 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2606 compose->modified = FALSE;
2607 compose_set_title(compose);
2608 compose->updating = FALSE;
2609 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2610 SCROLL_TO_CURSOR(compose);
2612 if (compose->deferred_destroy) {
2613 compose_destroy(compose);
2617 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2622 const GList *compose_get_compose_list(void)
2624 return compose_list;
2627 void compose_entry_append(Compose *compose, const gchar *address,
2628 ComposeEntryType type, ComposePrefType pref_type)
2630 const gchar *header;
2632 gboolean in_quote = FALSE;
2633 if (!address || *address == '\0') return;
2640 header = N_("Bcc:");
2642 case COMPOSE_REPLYTO:
2643 header = N_("Reply-To:");
2645 case COMPOSE_NEWSGROUPS:
2646 header = N_("Newsgroups:");
2648 case COMPOSE_FOLLOWUPTO:
2649 header = N_( "Followup-To:");
2651 case COMPOSE_INREPLYTO:
2652 header = N_( "In-Reply-To:");
2659 header = prefs_common_translated_header_name(header);
2661 cur = begin = (gchar *)address;
2663 /* we separate the line by commas, but not if we're inside a quoted
2665 while (*cur != '\0') {
2667 in_quote = !in_quote;
2668 if (*cur == ',' && !in_quote) {
2669 gchar *tmp = g_strdup(begin);
2671 tmp[cur-begin]='\0';
2674 while (*tmp == ' ' || *tmp == '\t')
2676 compose_add_header_entry(compose, header, tmp, pref_type);
2677 compose_entry_indicate(compose, tmp);
2684 gchar *tmp = g_strdup(begin);
2686 tmp[cur-begin]='\0';
2687 while (*tmp == ' ' || *tmp == '\t')
2689 compose_add_header_entry(compose, header, tmp, pref_type);
2690 compose_entry_indicate(compose, tmp);
2695 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2700 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2701 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2702 if (gtk_entry_get_text(entry) &&
2703 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2704 gtk_widget_modify_base(
2705 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2706 GTK_STATE_NORMAL, &default_header_bgcolor);
2707 gtk_widget_modify_text(
2708 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2709 GTK_STATE_NORMAL, &default_header_color);
2714 void compose_toolbar_cb(gint action, gpointer data)
2716 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2717 Compose *compose = (Compose*)toolbar_item->parent;
2719 cm_return_if_fail(compose != NULL);
2723 compose_send_cb(NULL, compose);
2726 compose_send_later_cb(NULL, compose);
2729 compose_draft(compose, COMPOSE_QUIT_EDITING);
2732 compose_insert_file_cb(NULL, compose);
2735 compose_attach_cb(NULL, compose);
2738 compose_insert_sig(compose, FALSE);
2741 compose_insert_sig(compose, TRUE);
2744 compose_ext_editor_cb(NULL, compose);
2746 case A_LINEWRAP_CURRENT:
2747 compose_beautify_paragraph(compose, NULL, TRUE);
2749 case A_LINEWRAP_ALL:
2750 compose_wrap_all_full(compose, TRUE);
2753 compose_address_cb(NULL, compose);
2756 case A_CHECK_SPELLING:
2757 compose_check_all(NULL, compose);
2765 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2770 gchar *subject = NULL;
2774 gchar **attach = NULL;
2775 gchar *inreplyto = NULL;
2776 MailField mfield = NO_FIELD_PRESENT;
2778 /* get mailto parts but skip from */
2779 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2782 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2783 mfield = TO_FIELD_PRESENT;
2786 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2788 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2790 if (!g_utf8_validate (subject, -1, NULL)) {
2791 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2792 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2795 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2797 mfield = SUBJECT_FIELD_PRESENT;
2800 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2801 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2804 gboolean prev_autowrap = compose->autowrap;
2806 compose->autowrap = FALSE;
2808 mark = gtk_text_buffer_get_insert(buffer);
2809 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2811 if (!g_utf8_validate (body, -1, NULL)) {
2812 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2813 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2816 gtk_text_buffer_insert(buffer, &iter, body, -1);
2818 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2820 compose->autowrap = prev_autowrap;
2821 if (compose->autowrap)
2822 compose_wrap_all(compose);
2823 mfield = BODY_FIELD_PRESENT;
2827 gint i = 0, att = 0;
2828 gchar *warn_files = NULL;
2829 while (attach[i] != NULL) {
2830 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2831 if (utf8_filename) {
2832 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2833 gchar *tmp = g_strdup_printf("%s%s\n",
2834 warn_files?warn_files:"",
2840 g_free(utf8_filename);
2842 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2847 alertpanel_notice(ngettext(
2848 "The following file has been attached: \n%s",
2849 "The following files have been attached: \n%s", att), warn_files);
2854 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2867 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2869 static HeaderEntry hentry[] = {
2870 {"Reply-To:", NULL, TRUE },
2871 {"Cc:", NULL, TRUE },
2872 {"References:", NULL, FALSE },
2873 {"Bcc:", NULL, TRUE },
2874 {"Newsgroups:", NULL, TRUE },
2875 {"Followup-To:", NULL, TRUE },
2876 {"List-Post:", NULL, FALSE },
2877 {"X-Priority:", NULL, FALSE },
2878 {NULL, NULL, FALSE }
2895 cm_return_val_if_fail(msginfo != NULL, -1);
2897 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2898 procheader_get_header_fields(fp, hentry);
2901 if (hentry[H_REPLY_TO].body != NULL) {
2902 if (hentry[H_REPLY_TO].body[0] != '\0') {
2904 conv_unmime_header(hentry[H_REPLY_TO].body,
2907 g_free(hentry[H_REPLY_TO].body);
2908 hentry[H_REPLY_TO].body = NULL;
2910 if (hentry[H_CC].body != NULL) {
2911 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2912 g_free(hentry[H_CC].body);
2913 hentry[H_CC].body = NULL;
2915 if (hentry[H_REFERENCES].body != NULL) {
2916 if (compose->mode == COMPOSE_REEDIT)
2917 compose->references = hentry[H_REFERENCES].body;
2919 compose->references = compose_parse_references
2920 (hentry[H_REFERENCES].body, msginfo->msgid);
2921 g_free(hentry[H_REFERENCES].body);
2923 hentry[H_REFERENCES].body = NULL;
2925 if (hentry[H_BCC].body != NULL) {
2926 if (compose->mode == COMPOSE_REEDIT)
2928 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2929 g_free(hentry[H_BCC].body);
2930 hentry[H_BCC].body = NULL;
2932 if (hentry[H_NEWSGROUPS].body != NULL) {
2933 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2934 hentry[H_NEWSGROUPS].body = NULL;
2936 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2937 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2938 compose->followup_to =
2939 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2942 g_free(hentry[H_FOLLOWUP_TO].body);
2943 hentry[H_FOLLOWUP_TO].body = NULL;
2945 if (hentry[H_LIST_POST].body != NULL) {
2946 gchar *to = NULL, *start = NULL;
2948 extract_address(hentry[H_LIST_POST].body);
2949 if (hentry[H_LIST_POST].body[0] != '\0') {
2950 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2952 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2953 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2956 g_free(compose->ml_post);
2957 compose->ml_post = to;
2960 g_free(hentry[H_LIST_POST].body);
2961 hentry[H_LIST_POST].body = NULL;
2964 /* CLAWS - X-Priority */
2965 if (compose->mode == COMPOSE_REEDIT)
2966 if (hentry[H_X_PRIORITY].body != NULL) {
2969 priority = atoi(hentry[H_X_PRIORITY].body);
2970 g_free(hentry[H_X_PRIORITY].body);
2972 hentry[H_X_PRIORITY].body = NULL;
2974 if (priority < PRIORITY_HIGHEST ||
2975 priority > PRIORITY_LOWEST)
2976 priority = PRIORITY_NORMAL;
2978 compose->priority = priority;
2981 if (compose->mode == COMPOSE_REEDIT) {
2982 if (msginfo->inreplyto && *msginfo->inreplyto)
2983 compose->inreplyto = g_strdup(msginfo->inreplyto);
2985 if (msginfo->msgid && *msginfo->msgid &&
2986 compose->folder != NULL &&
2987 compose->folder->stype == F_DRAFT)
2988 compose->msgid = g_strdup(msginfo->msgid);
2990 if (msginfo->msgid && *msginfo->msgid)
2991 compose->inreplyto = g_strdup(msginfo->msgid);
2993 if (!compose->references) {
2994 if (msginfo->msgid && *msginfo->msgid) {
2995 if (msginfo->inreplyto && *msginfo->inreplyto)
2996 compose->references =
2997 g_strdup_printf("<%s>\n\t<%s>",
3001 compose->references =
3002 g_strconcat("<", msginfo->msgid, ">",
3004 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3005 compose->references =
3006 g_strconcat("<", msginfo->inreplyto, ">",
3015 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3020 cm_return_val_if_fail(msginfo != NULL, -1);
3022 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3023 procheader_get_header_fields(fp, entries);
3027 while (he != NULL && he->name != NULL) {
3029 GtkListStore *model = NULL;
3031 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3032 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3033 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3034 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3035 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3042 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3044 GSList *ref_id_list, *cur;
3048 ref_id_list = references_list_append(NULL, ref);
3049 if (!ref_id_list) return NULL;
3050 if (msgid && *msgid)
3051 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3056 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3057 /* "<" + Message-ID + ">" + CR+LF+TAB */
3058 len += strlen((gchar *)cur->data) + 5;
3060 if (len > MAX_REFERENCES_LEN) {
3061 /* remove second message-ID */
3062 if (ref_id_list && ref_id_list->next &&
3063 ref_id_list->next->next) {
3064 g_free(ref_id_list->next->data);
3065 ref_id_list = g_slist_remove
3066 (ref_id_list, ref_id_list->next->data);
3068 slist_free_strings_full(ref_id_list);
3075 new_ref = g_string_new("");
3076 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3077 if (new_ref->len > 0)
3078 g_string_append(new_ref, "\n\t");
3079 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3082 slist_free_strings_full(ref_id_list);
3084 new_ref_str = new_ref->str;
3085 g_string_free(new_ref, FALSE);
3090 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3091 const gchar *fmt, const gchar *qmark,
3092 const gchar *body, gboolean rewrap,
3093 gboolean need_unescape,
3094 const gchar *err_msg)
3096 MsgInfo* dummyinfo = NULL;
3097 gchar *quote_str = NULL;
3099 gboolean prev_autowrap;
3100 const gchar *trimmed_body = body;
3101 gint cursor_pos = -1;
3102 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3103 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3108 SIGNAL_BLOCK(buffer);
3111 dummyinfo = compose_msginfo_new_from_compose(compose);
3112 msginfo = dummyinfo;
3115 if (qmark != NULL) {
3117 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3118 compose->gtkaspell);
3120 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3122 quote_fmt_scan_string(qmark);
3125 buf = quote_fmt_get_buffer();
3127 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3129 Xstrdup_a(quote_str, buf, goto error)
3132 if (fmt && *fmt != '\0') {
3135 while (*trimmed_body == '\n')
3139 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3140 compose->gtkaspell);
3142 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3144 if (need_unescape) {
3147 /* decode \-escape sequences in the internal representation of the quote format */
3148 tmp = g_malloc(strlen(fmt)+1);
3149 pref_get_unescaped_pref(tmp, fmt);
3150 quote_fmt_scan_string(tmp);