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);
1325 if (privacy != NULL) {
1327 g_free(compose->privacy_system);
1328 compose->privacy_system = NULL;
1329 g_free(compose->encdata);
1330 compose->encdata = NULL;
1332 if (compose->privacy_system == NULL)
1333 compose->privacy_system = g_strdup(privacy);
1334 else if (*(compose->privacy_system) == '\0') {
1335 g_free(compose->privacy_system);
1336 g_free(compose->encdata);
1337 compose->encdata = NULL;
1338 compose->privacy_system = g_strdup(privacy);
1340 compose_update_privacy_system_menu_item(compose, FALSE);
1341 compose_use_encryption(compose, TRUE);
1345 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1347 const gchar *privacy = NULL;
1349 if (account->default_privacy_system && strlen(account->default_privacy_system))
1350 privacy = account->default_privacy_system;
1354 GSList *privacy_avail = privacy_get_system_ids();
1355 if (privacy_avail && g_slist_length(privacy_avail)) {
1356 privacy = (gchar *)(privacy_avail->data);
1360 if (privacy != NULL) {
1362 g_free(compose->privacy_system);
1363 compose->privacy_system = NULL;
1364 g_free(compose->encdata);
1365 compose->encdata = NULL;
1367 if (compose->privacy_system == NULL)
1368 compose->privacy_system = g_strdup(privacy);
1369 compose_update_privacy_system_menu_item(compose, FALSE);
1370 compose_use_signing(compose, TRUE);
1374 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1378 Compose *compose = NULL;
1380 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1382 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1383 cm_return_val_if_fail(msginfo != NULL, NULL);
1385 list_len = g_slist_length(msginfo_list);
1389 case COMPOSE_REPLY_TO_ADDRESS:
1390 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1391 FALSE, prefs_common.default_reply_list, FALSE, body);
1393 case COMPOSE_REPLY_WITH_QUOTE:
1394 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1395 FALSE, prefs_common.default_reply_list, FALSE, body);
1397 case COMPOSE_REPLY_WITHOUT_QUOTE:
1398 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1399 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1401 case COMPOSE_REPLY_TO_SENDER:
1402 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1403 FALSE, FALSE, TRUE, body);
1405 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1406 compose = compose_followup_and_reply_to(msginfo,
1407 COMPOSE_QUOTE_CHECK,
1408 FALSE, FALSE, body);
1410 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1411 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1412 FALSE, FALSE, TRUE, body);
1414 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1415 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1416 FALSE, FALSE, TRUE, NULL);
1418 case COMPOSE_REPLY_TO_ALL:
1419 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1420 TRUE, FALSE, FALSE, body);
1422 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1423 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1424 TRUE, FALSE, FALSE, body);
1426 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1427 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1428 TRUE, FALSE, FALSE, NULL);
1430 case COMPOSE_REPLY_TO_LIST:
1431 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1432 FALSE, TRUE, FALSE, body);
1434 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1435 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1436 FALSE, TRUE, FALSE, body);
1438 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1439 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1440 FALSE, TRUE, FALSE, NULL);
1442 case COMPOSE_FORWARD:
1443 if (prefs_common.forward_as_attachment) {
1444 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1447 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1451 case COMPOSE_FORWARD_INLINE:
1452 /* check if we reply to more than one Message */
1453 if (list_len == 1) {
1454 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1457 /* more messages FALL THROUGH */
1458 case COMPOSE_FORWARD_AS_ATTACH:
1459 compose = compose_forward_multiple(NULL, msginfo_list);
1461 case COMPOSE_REDIRECT:
1462 compose = compose_redirect(NULL, msginfo, FALSE);
1465 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1468 if (compose == NULL) {
1469 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1473 compose->rmode = mode;
1474 switch (compose->rmode) {
1476 case COMPOSE_REPLY_WITH_QUOTE:
1477 case COMPOSE_REPLY_WITHOUT_QUOTE:
1478 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1479 debug_print("reply mode Normal\n");
1480 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1481 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1483 case COMPOSE_REPLY_TO_SENDER:
1484 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1485 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1486 debug_print("reply mode Sender\n");
1487 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1489 case COMPOSE_REPLY_TO_ALL:
1490 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1491 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1492 debug_print("reply mode All\n");
1493 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1495 case COMPOSE_REPLY_TO_LIST:
1496 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1497 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1498 debug_print("reply mode List\n");
1499 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1501 case COMPOSE_REPLY_TO_ADDRESS:
1502 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1510 static Compose *compose_reply(MsgInfo *msginfo,
1511 ComposeQuoteMode quote_mode,
1517 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1518 to_sender, FALSE, body);
1521 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1522 ComposeQuoteMode quote_mode,
1527 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1528 to_sender, TRUE, body);
1531 static void compose_extract_original_charset(Compose *compose)
1533 MsgInfo *info = NULL;
1534 if (compose->replyinfo) {
1535 info = compose->replyinfo;
1536 } else if (compose->fwdinfo) {
1537 info = compose->fwdinfo;
1538 } else if (compose->targetinfo) {
1539 info = compose->targetinfo;
1542 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1543 MimeInfo *partinfo = mimeinfo;
1544 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1545 partinfo = procmime_mimeinfo_next(partinfo);
1547 compose->orig_charset =
1548 g_strdup(procmime_mimeinfo_get_parameter(
1549 partinfo, "charset"));
1551 procmime_mimeinfo_free_all(&mimeinfo);
1555 #define SIGNAL_BLOCK(buffer) { \
1556 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1557 G_CALLBACK(compose_changed_cb), \
1559 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1560 G_CALLBACK(text_inserted), \
1564 #define SIGNAL_UNBLOCK(buffer) { \
1565 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1566 G_CALLBACK(compose_changed_cb), \
1568 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1569 G_CALLBACK(text_inserted), \
1573 static Compose *compose_generic_reply(MsgInfo *msginfo,
1574 ComposeQuoteMode quote_mode,
1575 gboolean to_all, gboolean to_ml,
1577 gboolean followup_and_reply_to,
1581 PrefsAccount *account = NULL;
1582 GtkTextView *textview;
1583 GtkTextBuffer *textbuf;
1584 gboolean quote = FALSE;
1585 const gchar *qmark = NULL;
1586 const gchar *body_fmt = NULL;
1587 gchar *s_system = NULL;
1589 cm_return_val_if_fail(msginfo != NULL, NULL);
1590 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1592 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1594 cm_return_val_if_fail(account != NULL, NULL);
1596 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1598 compose->updating = TRUE;
1600 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1601 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1603 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1604 if (!compose->replyinfo)
1605 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1607 compose_extract_original_charset(compose);
1609 if (msginfo->folder && msginfo->folder->ret_rcpt)
1610 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1612 /* Set save folder */
1613 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1614 gchar *folderidentifier;
1616 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1617 folderidentifier = folder_item_get_identifier(msginfo->folder);
1618 compose_set_save_to(compose, folderidentifier);
1619 g_free(folderidentifier);
1622 if (compose_parse_header(compose, msginfo) < 0) {
1623 compose->updating = FALSE;
1624 compose_destroy(compose);
1628 /* override from name according to folder properties */
1629 if (msginfo->folder && msginfo->folder->prefs &&
1630 msginfo->folder->prefs->reply_with_format &&
1631 msginfo->folder->prefs->reply_override_from_format &&
1632 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1637 /* decode \-escape sequences in the internal representation of the quote format */
1638 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1639 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1642 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1643 compose->gtkaspell);
1645 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1647 quote_fmt_scan_string(tmp);
1650 buf = quote_fmt_get_buffer();
1652 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1654 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1655 quote_fmt_reset_vartable();
1660 textview = (GTK_TEXT_VIEW(compose->text));
1661 textbuf = gtk_text_view_get_buffer(textview);
1662 compose_create_tags(textview, compose);
1664 undo_block(compose->undostruct);
1666 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1667 gtkaspell_block_check(compose->gtkaspell);
1670 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1671 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1672 /* use the reply format of folder (if enabled), or the account's one
1673 (if enabled) or fallback to the global reply format, which is always
1674 enabled (even if empty), and use the relevant quotemark */
1676 if (msginfo->folder && msginfo->folder->prefs &&
1677 msginfo->folder->prefs->reply_with_format) {
1678 qmark = msginfo->folder->prefs->reply_quotemark;
1679 body_fmt = msginfo->folder->prefs->reply_body_format;
1681 } else if (account->reply_with_format) {
1682 qmark = account->reply_quotemark;
1683 body_fmt = account->reply_body_format;
1686 qmark = prefs_common.quotemark;
1687 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1688 body_fmt = gettext(prefs_common.quotefmt);
1695 /* empty quotemark is not allowed */
1696 if (qmark == NULL || *qmark == '\0')
1698 compose_quote_fmt(compose, compose->replyinfo,
1699 body_fmt, qmark, body, FALSE, TRUE,
1700 _("The body of the \"Reply\" template has an error at line %d."));
1701 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1702 quote_fmt_reset_vartable();
1705 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1706 compose_force_encryption(compose, account, FALSE, s_system);
1709 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1710 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1711 compose_force_signing(compose, account, s_system);
1715 SIGNAL_BLOCK(textbuf);
1717 if (account->auto_sig)
1718 compose_insert_sig(compose, FALSE);
1720 compose_wrap_all(compose);
1723 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1724 gtkaspell_highlight_all(compose->gtkaspell);
1725 gtkaspell_unblock_check(compose->gtkaspell);
1727 SIGNAL_UNBLOCK(textbuf);
1729 gtk_widget_grab_focus(compose->text);
1731 undo_unblock(compose->undostruct);
1733 if (prefs_common.auto_exteditor)
1734 compose_exec_ext_editor(compose);
1736 compose->modified = FALSE;
1737 compose_set_title(compose);
1739 compose->updating = FALSE;
1740 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1741 SCROLL_TO_CURSOR(compose);
1743 if (compose->deferred_destroy) {
1744 compose_destroy(compose);
1752 #define INSERT_FW_HEADER(var, hdr) \
1753 if (msginfo->var && *msginfo->var) { \
1754 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1755 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1756 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1759 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1760 gboolean as_attach, const gchar *body,
1761 gboolean no_extedit,
1765 GtkTextView *textview;
1766 GtkTextBuffer *textbuf;
1767 gint cursor_pos = -1;
1770 cm_return_val_if_fail(msginfo != NULL, NULL);
1771 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1773 if (!account && !(account = compose_find_account(msginfo)))
1774 account = cur_account;
1776 if (!prefs_common.forward_as_attachment)
1777 mode = COMPOSE_FORWARD_INLINE;
1779 mode = COMPOSE_FORWARD;
1780 compose = compose_create(account, msginfo->folder, mode, batch);
1782 compose->updating = TRUE;
1783 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1784 if (!compose->fwdinfo)
1785 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1787 compose_extract_original_charset(compose);
1789 if (msginfo->subject && *msginfo->subject) {
1790 gchar *buf, *buf2, *p;
1792 buf = p = g_strdup(msginfo->subject);
1793 p += subject_get_prefix_length(p);
1794 memmove(buf, p, strlen(p) + 1);
1796 buf2 = g_strdup_printf("Fw: %s", buf);
1797 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1803 /* override from name according to folder properties */
1804 if (msginfo->folder && msginfo->folder->prefs &&
1805 msginfo->folder->prefs->forward_with_format &&
1806 msginfo->folder->prefs->forward_override_from_format &&
1807 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1811 MsgInfo *full_msginfo = NULL;
1814 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1816 full_msginfo = procmsg_msginfo_copy(msginfo);
1818 /* decode \-escape sequences in the internal representation of the quote format */
1819 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1820 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1823 gtkaspell_block_check(compose->gtkaspell);
1824 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1825 compose->gtkaspell);
1827 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1829 quote_fmt_scan_string(tmp);
1832 buf = quote_fmt_get_buffer();
1834 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1836 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1837 quote_fmt_reset_vartable();
1840 procmsg_msginfo_free(&full_msginfo);
1843 textview = GTK_TEXT_VIEW(compose->text);
1844 textbuf = gtk_text_view_get_buffer(textview);
1845 compose_create_tags(textview, compose);
1847 undo_block(compose->undostruct);
1851 msgfile = procmsg_get_message_file(msginfo);
1852 if (!is_file_exist(msgfile))
1853 g_warning("%s: file does not exist", msgfile);
1855 compose_attach_append(compose, msgfile, msgfile,
1856 "message/rfc822", NULL);
1860 const gchar *qmark = NULL;
1861 const gchar *body_fmt = NULL;
1862 MsgInfo *full_msginfo;
1864 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1866 full_msginfo = procmsg_msginfo_copy(msginfo);
1868 /* use the forward format of folder (if enabled), or the account's one
1869 (if enabled) or fallback to the global forward format, which is always
1870 enabled (even if empty), and use the relevant quotemark */
1871 if (msginfo->folder && msginfo->folder->prefs &&
1872 msginfo->folder->prefs->forward_with_format) {
1873 qmark = msginfo->folder->prefs->forward_quotemark;
1874 body_fmt = msginfo->folder->prefs->forward_body_format;
1876 } else if (account->forward_with_format) {
1877 qmark = account->forward_quotemark;
1878 body_fmt = account->forward_body_format;
1881 qmark = prefs_common.fw_quotemark;
1882 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1883 body_fmt = gettext(prefs_common.fw_quotefmt);
1888 /* empty quotemark is not allowed */
1889 if (qmark == NULL || *qmark == '\0')
1892 compose_quote_fmt(compose, full_msginfo,
1893 body_fmt, qmark, body, FALSE, TRUE,
1894 _("The body of the \"Forward\" template has an error at line %d."));
1895 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1896 quote_fmt_reset_vartable();
1897 compose_attach_parts(compose, msginfo);
1899 procmsg_msginfo_free(&full_msginfo);
1902 SIGNAL_BLOCK(textbuf);
1904 if (account->auto_sig)
1905 compose_insert_sig(compose, FALSE);
1907 compose_wrap_all(compose);
1910 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1911 gtkaspell_highlight_all(compose->gtkaspell);
1912 gtkaspell_unblock_check(compose->gtkaspell);
1914 SIGNAL_UNBLOCK(textbuf);
1916 cursor_pos = quote_fmt_get_cursor_pos();
1917 if (cursor_pos == -1)
1918 gtk_widget_grab_focus(compose->header_last->entry);
1920 gtk_widget_grab_focus(compose->text);
1922 if (!no_extedit && prefs_common.auto_exteditor)
1923 compose_exec_ext_editor(compose);
1926 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1927 gchar *folderidentifier;
1929 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1930 folderidentifier = folder_item_get_identifier(msginfo->folder);
1931 compose_set_save_to(compose, folderidentifier);
1932 g_free(folderidentifier);
1935 undo_unblock(compose->undostruct);
1937 compose->modified = FALSE;
1938 compose_set_title(compose);
1940 compose->updating = FALSE;
1941 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1942 SCROLL_TO_CURSOR(compose);
1944 if (compose->deferred_destroy) {
1945 compose_destroy(compose);
1949 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1954 #undef INSERT_FW_HEADER
1956 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1959 GtkTextView *textview;
1960 GtkTextBuffer *textbuf;
1964 gboolean single_mail = TRUE;
1966 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1968 if (g_slist_length(msginfo_list) > 1)
1969 single_mail = FALSE;
1971 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1972 if (((MsgInfo *)msginfo->data)->folder == NULL)
1975 /* guess account from first selected message */
1977 !(account = compose_find_account(msginfo_list->data)))
1978 account = cur_account;
1980 cm_return_val_if_fail(account != NULL, NULL);
1982 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1983 if (msginfo->data) {
1984 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1985 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1989 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1990 g_warning("no msginfo_list");
1994 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1996 compose->updating = TRUE;
1998 /* override from name according to folder properties */
1999 if (msginfo_list->data) {
2000 MsgInfo *msginfo = msginfo_list->data;
2002 if (msginfo->folder && msginfo->folder->prefs &&
2003 msginfo->folder->prefs->forward_with_format &&
2004 msginfo->folder->prefs->forward_override_from_format &&
2005 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2010 /* decode \-escape sequences in the internal representation of the quote format */
2011 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2012 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2015 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2016 compose->gtkaspell);
2018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2020 quote_fmt_scan_string(tmp);
2023 buf = quote_fmt_get_buffer();
2025 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2027 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2028 quote_fmt_reset_vartable();
2034 textview = GTK_TEXT_VIEW(compose->text);
2035 textbuf = gtk_text_view_get_buffer(textview);
2036 compose_create_tags(textview, compose);
2038 undo_block(compose->undostruct);
2039 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2040 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2042 if (!is_file_exist(msgfile))
2043 g_warning("%s: file does not exist", msgfile);
2045 compose_attach_append(compose, msgfile, msgfile,
2046 "message/rfc822", NULL);
2051 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2052 if (info->subject && *info->subject) {
2053 gchar *buf, *buf2, *p;
2055 buf = p = g_strdup(info->subject);
2056 p += subject_get_prefix_length(p);
2057 memmove(buf, p, strlen(p) + 1);
2059 buf2 = g_strdup_printf("Fw: %s", buf);
2060 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2066 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2067 _("Fw: multiple emails"));
2070 SIGNAL_BLOCK(textbuf);
2072 if (account->auto_sig)
2073 compose_insert_sig(compose, FALSE);
2075 compose_wrap_all(compose);
2077 SIGNAL_UNBLOCK(textbuf);
2079 gtk_text_buffer_get_start_iter(textbuf, &iter);
2080 gtk_text_buffer_place_cursor(textbuf, &iter);
2082 if (prefs_common.auto_exteditor)
2083 compose_exec_ext_editor(compose);
2085 gtk_widget_grab_focus(compose->header_last->entry);
2086 undo_unblock(compose->undostruct);
2087 compose->modified = FALSE;
2088 compose_set_title(compose);
2090 compose->updating = FALSE;
2091 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2092 SCROLL_TO_CURSOR(compose);
2094 if (compose->deferred_destroy) {
2095 compose_destroy(compose);
2099 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2104 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2106 GtkTextIter start = *iter;
2107 GtkTextIter end_iter;
2108 int start_pos = gtk_text_iter_get_offset(&start);
2110 if (!compose->account->sig_sep)
2113 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2114 start_pos+strlen(compose->account->sig_sep));
2116 /* check sig separator */
2117 str = gtk_text_iter_get_text(&start, &end_iter);
2118 if (!strcmp(str, compose->account->sig_sep)) {
2120 /* check end of line (\n) */
2121 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2122 start_pos+strlen(compose->account->sig_sep));
2123 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2124 start_pos+strlen(compose->account->sig_sep)+1);
2125 tmp = gtk_text_iter_get_text(&start, &end_iter);
2126 if (!strcmp(tmp,"\n")) {
2138 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2140 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2141 Compose *compose = (Compose *)data;
2142 FolderItem *old_item = NULL;
2143 FolderItem *new_item = NULL;
2144 gchar *old_id, *new_id;
2146 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2147 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2150 old_item = hookdata->item;
2151 new_item = hookdata->item2;
2153 old_id = folder_item_get_identifier(old_item);
2154 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2156 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2157 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2158 compose->targetinfo->folder = new_item;
2161 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2162 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2163 compose->replyinfo->folder = new_item;
2166 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2167 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2168 compose->fwdinfo->folder = new_item;
2176 static void compose_colorize_signature(Compose *compose)
2178 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2180 GtkTextIter end_iter;
2181 gtk_text_buffer_get_start_iter(buffer, &iter);
2182 while (gtk_text_iter_forward_line(&iter))
2183 if (compose_is_sig_separator(compose, buffer, &iter)) {
2184 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2185 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2189 #define BLOCK_WRAP() { \
2190 prev_autowrap = compose->autowrap; \
2191 buffer = gtk_text_view_get_buffer( \
2192 GTK_TEXT_VIEW(compose->text)); \
2193 compose->autowrap = FALSE; \
2195 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2196 G_CALLBACK(compose_changed_cb), \
2198 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2199 G_CALLBACK(text_inserted), \
2202 #define UNBLOCK_WRAP() { \
2203 compose->autowrap = prev_autowrap; \
2204 if (compose->autowrap) { \
2205 gint old = compose->draft_timeout_tag; \
2206 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2207 compose_wrap_all(compose); \
2208 compose->draft_timeout_tag = old; \
2211 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2212 G_CALLBACK(compose_changed_cb), \
2214 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2215 G_CALLBACK(text_inserted), \
2219 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2221 Compose *compose = NULL;
2222 PrefsAccount *account = NULL;
2223 GtkTextView *textview;
2224 GtkTextBuffer *textbuf;
2228 gboolean use_signing = FALSE;
2229 gboolean use_encryption = FALSE;
2230 gchar *privacy_system = NULL;
2231 int priority = PRIORITY_NORMAL;
2232 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2233 gboolean autowrap = prefs_common.autowrap;
2234 gboolean autoindent = prefs_common.auto_indent;
2235 HeaderEntry *manual_headers = NULL;
2237 cm_return_val_if_fail(msginfo != NULL, NULL);
2238 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2240 if (compose_put_existing_to_front(msginfo)) {
2244 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2245 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2246 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2247 gchar *queueheader_buf = NULL;
2250 /* Select Account from queue headers */
2251 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2252 "X-Claws-Account-Id:")) {
2253 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2254 account = account_find_from_id(id);
2255 g_free(queueheader_buf);
2257 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2258 "X-Sylpheed-Account-Id:")) {
2259 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2260 account = account_find_from_id(id);
2261 g_free(queueheader_buf);
2263 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2265 id = atoi(&queueheader_buf[strlen("NAID:")]);
2266 account = account_find_from_id(id);
2267 g_free(queueheader_buf);
2269 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2271 id = atoi(&queueheader_buf[strlen("MAID:")]);
2272 account = account_find_from_id(id);
2273 g_free(queueheader_buf);
2275 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2277 account = account_find_from_address(queueheader_buf, FALSE);
2278 g_free(queueheader_buf);
2280 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2282 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2283 use_signing = param;
2284 g_free(queueheader_buf);
2286 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2287 "X-Sylpheed-Sign:")) {
2288 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2289 use_signing = param;
2290 g_free(queueheader_buf);
2292 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2293 "X-Claws-Encrypt:")) {
2294 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2295 use_encryption = param;
2296 g_free(queueheader_buf);
2298 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2299 "X-Sylpheed-Encrypt:")) {
2300 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2301 use_encryption = param;
2302 g_free(queueheader_buf);
2304 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2305 "X-Claws-Auto-Wrapping:")) {
2306 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2308 g_free(queueheader_buf);
2310 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2311 "X-Claws-Auto-Indent:")) {
2312 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2314 g_free(queueheader_buf);
2316 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2317 "X-Claws-Privacy-System:")) {
2318 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2319 g_free(queueheader_buf);
2321 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2322 "X-Sylpheed-Privacy-System:")) {
2323 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2324 g_free(queueheader_buf);
2326 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2328 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2330 g_free(queueheader_buf);
2332 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2334 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2335 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2336 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2337 if (orig_item != NULL) {
2338 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2342 g_free(queueheader_buf);
2344 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2346 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2347 if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2348 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2349 if (orig_item != NULL) {
2350 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2354 g_free(queueheader_buf);
2356 /* Get manual headers */
2357 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2358 "X-Claws-Manual-Headers:")) {
2359 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2360 if (listmh && *listmh != '\0') {
2361 debug_print("Got manual headers: %s\n", listmh);
2362 manual_headers = procheader_entries_from_str(listmh);
2365 g_free(queueheader_buf);
2368 account = msginfo->folder->folder->account;
2371 if (!account && prefs_common.reedit_account_autosel) {
2373 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2374 extract_address(from);
2375 account = account_find_from_address(from, FALSE);
2380 account = cur_account;
2382 cm_return_val_if_fail(account != NULL, NULL);
2384 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2386 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2387 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2388 compose->autowrap = autowrap;
2389 compose->replyinfo = replyinfo;
2390 compose->fwdinfo = fwdinfo;
2392 compose->updating = TRUE;
2393 compose->priority = priority;
2395 if (privacy_system != NULL) {
2396 compose->privacy_system = privacy_system;
2397 compose_use_signing(compose, use_signing);
2398 compose_use_encryption(compose, use_encryption);
2399 compose_update_privacy_system_menu_item(compose, FALSE);
2401 activate_privacy_system(compose, account, FALSE);
2404 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2406 compose_extract_original_charset(compose);
2408 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2409 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2410 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2411 gchar *queueheader_buf = NULL;
2413 /* Set message save folder */
2414 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2416 compose_set_save_to(compose, &queueheader_buf[4]);
2417 g_free(queueheader_buf);
2419 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2420 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2422 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2424 g_free(queueheader_buf);
2428 if (compose_parse_header(compose, msginfo) < 0) {
2429 compose->updating = FALSE;
2430 compose_destroy(compose);
2433 compose_reedit_set_entry(compose, msginfo);
2435 textview = GTK_TEXT_VIEW(compose->text);
2436 textbuf = gtk_text_view_get_buffer(textview);
2437 compose_create_tags(textview, compose);
2439 mark = gtk_text_buffer_get_insert(textbuf);
2440 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2442 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2443 G_CALLBACK(compose_changed_cb),
2446 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2447 fp = procmime_get_first_encrypted_text_content(msginfo);
2449 compose_force_encryption(compose, account, TRUE, NULL);
2452 fp = procmime_get_first_text_content(msginfo);
2455 g_warning("Can't get text part");
2459 gchar buf[BUFFSIZE];
2460 gboolean prev_autowrap;
2461 GtkTextBuffer *buffer;
2463 while (fgets(buf, sizeof(buf), fp) != NULL) {
2465 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2471 compose_attach_parts(compose, msginfo);
2473 compose_colorize_signature(compose);
2475 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2476 G_CALLBACK(compose_changed_cb),
2479 if (manual_headers != NULL) {
2480 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2481 procheader_entries_free(manual_headers);
2482 compose->updating = FALSE;
2483 compose_destroy(compose);
2486 procheader_entries_free(manual_headers);
2489 gtk_widget_grab_focus(compose->text);
2491 if (prefs_common.auto_exteditor) {
2492 compose_exec_ext_editor(compose);
2494 compose->modified = FALSE;
2495 compose_set_title(compose);
2497 compose->updating = FALSE;
2498 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2499 SCROLL_TO_CURSOR(compose);
2501 if (compose->deferred_destroy) {
2502 compose_destroy(compose);
2506 compose->sig_str = account_get_signature_str(compose->account);
2508 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2513 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2520 cm_return_val_if_fail(msginfo != NULL, NULL);
2523 account = account_get_reply_account(msginfo,
2524 prefs_common.reply_account_autosel);
2525 cm_return_val_if_fail(account != NULL, NULL);
2527 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2529 compose->updating = TRUE;
2531 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2532 compose->replyinfo = NULL;
2533 compose->fwdinfo = NULL;
2535 compose_show_first_last_header(compose, TRUE);
2537 gtk_widget_grab_focus(compose->header_last->entry);
2539 filename = procmsg_get_message_file(msginfo);
2541 if (filename == NULL) {
2542 compose->updating = FALSE;
2543 compose_destroy(compose);
2548 compose->redirect_filename = filename;
2550 /* Set save folder */
2551 item = msginfo->folder;
2552 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2553 gchar *folderidentifier;
2555 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2556 folderidentifier = folder_item_get_identifier(item);
2557 compose_set_save_to(compose, folderidentifier);
2558 g_free(folderidentifier);
2561 compose_attach_parts(compose, msginfo);
2563 if (msginfo->subject)
2564 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2566 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2568 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2569 _("The body of the \"Redirect\" template has an error at line %d."));
2570 quote_fmt_reset_vartable();
2571 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2573 compose_colorize_signature(compose);
2576 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2577 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2578 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2581 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2585 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2586 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2590 if (compose->toolbar->draft_btn)
2591 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2592 if (compose->toolbar->insert_btn)
2593 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2594 if (compose->toolbar->attach_btn)
2595 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2596 if (compose->toolbar->sig_btn)
2597 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2598 if (compose->toolbar->exteditor_btn)
2599 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2600 if (compose->toolbar->linewrap_current_btn)
2601 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2602 if (compose->toolbar->linewrap_all_btn)
2603 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2605 compose->modified = FALSE;
2606 compose_set_title(compose);
2607 compose->updating = FALSE;
2608 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2609 SCROLL_TO_CURSOR(compose);
2611 if (compose->deferred_destroy) {
2612 compose_destroy(compose);
2616 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2621 const GList *compose_get_compose_list(void)
2623 return compose_list;
2626 void compose_entry_append(Compose *compose, const gchar *address,
2627 ComposeEntryType type, ComposePrefType pref_type)
2629 const gchar *header;
2631 gboolean in_quote = FALSE;
2632 if (!address || *address == '\0') return;
2639 header = N_("Bcc:");
2641 case COMPOSE_REPLYTO:
2642 header = N_("Reply-To:");
2644 case COMPOSE_NEWSGROUPS:
2645 header = N_("Newsgroups:");
2647 case COMPOSE_FOLLOWUPTO:
2648 header = N_( "Followup-To:");
2650 case COMPOSE_INREPLYTO:
2651 header = N_( "In-Reply-To:");
2658 header = prefs_common_translated_header_name(header);
2660 cur = begin = (gchar *)address;
2662 /* we separate the line by commas, but not if we're inside a quoted
2664 while (*cur != '\0') {
2666 in_quote = !in_quote;
2667 if (*cur == ',' && !in_quote) {
2668 gchar *tmp = g_strdup(begin);
2670 tmp[cur-begin]='\0';
2673 while (*tmp == ' ' || *tmp == '\t')
2675 compose_add_header_entry(compose, header, tmp, pref_type);
2676 compose_entry_indicate(compose, tmp);
2683 gchar *tmp = g_strdup(begin);
2685 tmp[cur-begin]='\0';
2686 while (*tmp == ' ' || *tmp == '\t')
2688 compose_add_header_entry(compose, header, tmp, pref_type);
2689 compose_entry_indicate(compose, tmp);
2694 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2699 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2700 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2701 if (gtk_entry_get_text(entry) &&
2702 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2703 gtk_widget_modify_base(
2704 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2705 GTK_STATE_NORMAL, &default_header_bgcolor);
2706 gtk_widget_modify_text(
2707 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2708 GTK_STATE_NORMAL, &default_header_color);
2713 void compose_toolbar_cb(gint action, gpointer data)
2715 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2716 Compose *compose = (Compose*)toolbar_item->parent;
2718 cm_return_if_fail(compose != NULL);
2722 compose_send_cb(NULL, compose);
2725 compose_send_later_cb(NULL, compose);
2728 compose_draft(compose, COMPOSE_QUIT_EDITING);
2731 compose_insert_file_cb(NULL, compose);
2734 compose_attach_cb(NULL, compose);
2737 compose_insert_sig(compose, FALSE);
2740 compose_insert_sig(compose, TRUE);
2743 compose_ext_editor_cb(NULL, compose);
2745 case A_LINEWRAP_CURRENT:
2746 compose_beautify_paragraph(compose, NULL, TRUE);
2748 case A_LINEWRAP_ALL:
2749 compose_wrap_all_full(compose, TRUE);
2752 compose_address_cb(NULL, compose);
2755 case A_CHECK_SPELLING:
2756 compose_check_all(NULL, compose);
2764 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2769 gchar *subject = NULL;
2773 gchar **attach = NULL;
2774 gchar *inreplyto = NULL;
2775 MailField mfield = NO_FIELD_PRESENT;
2777 /* get mailto parts but skip from */
2778 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2781 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2782 mfield = TO_FIELD_PRESENT;
2785 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2787 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2789 if (!g_utf8_validate (subject, -1, NULL)) {
2790 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2791 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2794 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2796 mfield = SUBJECT_FIELD_PRESENT;
2799 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2800 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2803 gboolean prev_autowrap = compose->autowrap;
2805 compose->autowrap = FALSE;
2807 mark = gtk_text_buffer_get_insert(buffer);
2808 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2810 if (!g_utf8_validate (body, -1, NULL)) {
2811 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2812 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2815 gtk_text_buffer_insert(buffer, &iter, body, -1);
2817 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2819 compose->autowrap = prev_autowrap;
2820 if (compose->autowrap)
2821 compose_wrap_all(compose);
2822 mfield = BODY_FIELD_PRESENT;
2826 gint i = 0, att = 0;
2827 gchar *warn_files = NULL;
2828 while (attach[i] != NULL) {
2829 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2830 if (utf8_filename) {
2831 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2832 gchar *tmp = g_strdup_printf("%s%s\n",
2833 warn_files?warn_files:"",
2839 g_free(utf8_filename);
2841 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2846 alertpanel_notice(ngettext(
2847 "The following file has been attached: \n%s",
2848 "The following files have been attached: \n%s", att), warn_files);
2853 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2866 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2868 static HeaderEntry hentry[] = {
2869 {"Reply-To:", NULL, TRUE },
2870 {"Cc:", NULL, TRUE },
2871 {"References:", NULL, FALSE },
2872 {"Bcc:", NULL, TRUE },
2873 {"Newsgroups:", NULL, TRUE },
2874 {"Followup-To:", NULL, TRUE },
2875 {"List-Post:", NULL, FALSE },
2876 {"X-Priority:", NULL, FALSE },
2877 {NULL, NULL, FALSE }
2894 cm_return_val_if_fail(msginfo != NULL, -1);
2896 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2897 procheader_get_header_fields(fp, hentry);
2900 if (hentry[H_REPLY_TO].body != NULL) {
2901 if (hentry[H_REPLY_TO].body[0] != '\0') {
2903 conv_unmime_header(hentry[H_REPLY_TO].body,
2906 g_free(hentry[H_REPLY_TO].body);
2907 hentry[H_REPLY_TO].body = NULL;
2909 if (hentry[H_CC].body != NULL) {
2910 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2911 g_free(hentry[H_CC].body);
2912 hentry[H_CC].body = NULL;
2914 if (hentry[H_REFERENCES].body != NULL) {
2915 if (compose->mode == COMPOSE_REEDIT)
2916 compose->references = hentry[H_REFERENCES].body;
2918 compose->references = compose_parse_references
2919 (hentry[H_REFERENCES].body, msginfo->msgid);
2920 g_free(hentry[H_REFERENCES].body);
2922 hentry[H_REFERENCES].body = NULL;
2924 if (hentry[H_BCC].body != NULL) {
2925 if (compose->mode == COMPOSE_REEDIT)
2927 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2928 g_free(hentry[H_BCC].body);
2929 hentry[H_BCC].body = NULL;
2931 if (hentry[H_NEWSGROUPS].body != NULL) {
2932 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2933 hentry[H_NEWSGROUPS].body = NULL;
2935 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2936 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2937 compose->followup_to =
2938 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2941 g_free(hentry[H_FOLLOWUP_TO].body);
2942 hentry[H_FOLLOWUP_TO].body = NULL;
2944 if (hentry[H_LIST_POST].body != NULL) {
2945 gchar *to = NULL, *start = NULL;
2947 extract_address(hentry[H_LIST_POST].body);
2948 if (hentry[H_LIST_POST].body[0] != '\0') {
2949 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2951 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2952 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2955 g_free(compose->ml_post);
2956 compose->ml_post = to;
2959 g_free(hentry[H_LIST_POST].body);
2960 hentry[H_LIST_POST].body = NULL;
2963 /* CLAWS - X-Priority */
2964 if (compose->mode == COMPOSE_REEDIT)
2965 if (hentry[H_X_PRIORITY].body != NULL) {
2968 priority = atoi(hentry[H_X_PRIORITY].body);
2969 g_free(hentry[H_X_PRIORITY].body);
2971 hentry[H_X_PRIORITY].body = NULL;
2973 if (priority < PRIORITY_HIGHEST ||
2974 priority > PRIORITY_LOWEST)
2975 priority = PRIORITY_NORMAL;
2977 compose->priority = priority;
2980 if (compose->mode == COMPOSE_REEDIT) {
2981 if (msginfo->inreplyto && *msginfo->inreplyto)
2982 compose->inreplyto = g_strdup(msginfo->inreplyto);
2984 if (msginfo->msgid && *msginfo->msgid)
2985 compose->msgid = g_strdup(msginfo->msgid);
2987 if (msginfo->msgid && *msginfo->msgid)
2988 compose->inreplyto = g_strdup(msginfo->msgid);
2990 if (!compose->references) {
2991 if (msginfo->msgid && *msginfo->msgid) {
2992 if (msginfo->inreplyto && *msginfo->inreplyto)
2993 compose->references =
2994 g_strdup_printf("<%s>\n\t<%s>",
2998 compose->references =
2999 g_strconcat("<", msginfo->msgid, ">",
3001 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3002 compose->references =
3003 g_strconcat("<", msginfo->inreplyto, ">",
3012 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3017 cm_return_val_if_fail(msginfo != NULL, -1);
3019 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3020 procheader_get_header_fields(fp, entries);
3024 while (he != NULL && he->name != NULL) {
3026 GtkListStore *model = NULL;
3028 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3029 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3030 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3031 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3032 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3039 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3041 GSList *ref_id_list, *cur;
3045 ref_id_list = references_list_append(NULL, ref);
3046 if (!ref_id_list) return NULL;
3047 if (msgid && *msgid)
3048 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3053 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3054 /* "<" + Message-ID + ">" + CR+LF+TAB */
3055 len += strlen((gchar *)cur->data) + 5;
3057 if (len > MAX_REFERENCES_LEN) {
3058 /* remove second message-ID */
3059 if (ref_id_list && ref_id_list->next &&
3060 ref_id_list->next->next) {
3061 g_free(ref_id_list->next->data);
3062 ref_id_list = g_slist_remove
3063 (ref_id_list, ref_id_list->next->data);
3065 slist_free_strings_full(ref_id_list);
3072 new_ref = g_string_new("");
3073 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3074 if (new_ref->len > 0)
3075 g_string_append(new_ref, "\n\t");
3076 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3079 slist_free_strings_full(ref_id_list);
3081 new_ref_str = new_ref->str;
3082 g_string_free(new_ref, FALSE);
3087 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3088 const gchar *fmt, const gchar *qmark,
3089 const gchar *body, gboolean rewrap,
3090 gboolean need_unescape,
3091 const gchar *err_msg)
3093 MsgInfo* dummyinfo = NULL;
3094 gchar *quote_str = NULL;
3096 gboolean prev_autowrap;
3097 const gchar *trimmed_body = body;
3098 gint cursor_pos = -1;
3099 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3100 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3105 SIGNAL_BLOCK(buffer);
3108 dummyinfo = compose_msginfo_new_from_compose(compose);
3109 msginfo = dummyinfo;
3112 if (qmark != NULL) {
3114 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3115 compose->gtkaspell);
3117 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3119 quote_fmt_scan_string(qmark);
3122 buf = quote_fmt_get_buffer();
3124 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3126 Xstrdup_a(quote_str, buf, goto error)
3129 if (fmt && *fmt != '\0') {
3132 while (*trimmed_body == '\n')
3136 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3137 compose->gtkaspell);
3139 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3141 if (need_unescape) {
3144 /* decode \-escape sequences in the internal representation of the quote format */
3145 tmp = g_malloc(strlen(fmt)+1);
3146 pref_get_unescaped_pref(tmp, fmt);
3147 quote_fmt_scan_string(tmp);
3151 quote_fmt_scan_string(fmt);