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"
121 #define N_ATTACH_COLS (N_COL_COLUMNS)
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
139 } ComposeCallAdvancedAction;
143 PRIORITY_HIGHEST = 1,
152 COMPOSE_INSERT_SUCCESS,
153 COMPOSE_INSERT_READ_ERROR,
154 COMPOSE_INSERT_INVALID_CHARACTER,
155 COMPOSE_INSERT_NO_FILE
156 } ComposeInsertResult;
160 COMPOSE_WRITE_FOR_SEND,
161 COMPOSE_WRITE_FOR_STORE
166 COMPOSE_QUOTE_FORCED,
173 SUBJECT_FIELD_PRESENT,
178 #define B64_LINE_SIZE 57
179 #define B64_BUFFSIZE 77
181 #define MAX_REFERENCES_LEN 999
183 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
184 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
186 static GdkColor default_header_bgcolor = {
193 static GdkColor default_header_color = {
200 static GList *compose_list = NULL;
201 static GSList *extra_headers = NULL;
203 static Compose *compose_generic_new (PrefsAccount *account,
207 GList *listAddress );
209 static Compose *compose_create (PrefsAccount *account,
214 static void compose_entry_indicate (Compose *compose,
215 const gchar *address);
216 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
217 ComposeQuoteMode quote_mode,
221 static Compose *compose_forward_multiple (PrefsAccount *account,
222 GSList *msginfo_list);
223 static Compose *compose_reply (MsgInfo *msginfo,
224 ComposeQuoteMode quote_mode,
229 static Compose *compose_reply_mode (ComposeMode mode,
230 GSList *msginfo_list,
232 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
233 static void compose_update_privacy_systems_menu(Compose *compose);
235 static GtkWidget *compose_account_option_menu_create
237 static void compose_set_out_encoding (Compose *compose);
238 static void compose_set_template_menu (Compose *compose);
239 static void compose_destroy (Compose *compose);
241 static MailField compose_entries_set (Compose *compose,
243 ComposeEntryType to_type);
244 static gint compose_parse_header (Compose *compose,
246 static gint compose_parse_manual_headers (Compose *compose,
248 HeaderEntry *entries);
249 static gchar *compose_parse_references (const gchar *ref,
252 static gchar *compose_quote_fmt (Compose *compose,
258 gboolean need_unescape,
259 const gchar *err_msg);
261 static void compose_reply_set_entry (Compose *compose,
267 followup_and_reply_to);
268 static void compose_reedit_set_entry (Compose *compose,
271 static void compose_insert_sig (Compose *compose,
273 static ComposeInsertResult compose_insert_file (Compose *compose,
276 static gboolean compose_attach_append (Compose *compose,
279 const gchar *content_type,
280 const gchar *charset);
281 static void compose_attach_parts (Compose *compose,
284 static gboolean compose_beautify_paragraph (Compose *compose,
285 GtkTextIter *par_iter,
287 static void compose_wrap_all (Compose *compose);
288 static void compose_wrap_all_full (Compose *compose,
291 static void compose_set_title (Compose *compose);
292 static void compose_select_account (Compose *compose,
293 PrefsAccount *account,
296 static PrefsAccount *compose_current_mail_account(void);
297 /* static gint compose_send (Compose *compose); */
298 static gboolean compose_check_for_valid_recipient
300 static gboolean compose_check_entries (Compose *compose,
301 gboolean check_everything);
302 static gint compose_write_to_file (Compose *compose,
305 gboolean attach_parts);
306 static gint compose_write_body_to_file (Compose *compose,
308 static gint compose_remove_reedit_target (Compose *compose,
310 static void compose_remove_draft (Compose *compose);
311 static gint compose_queue_sub (Compose *compose,
315 gboolean check_subject,
316 gboolean remove_reedit_target);
317 static int compose_add_attachments (Compose *compose,
319 static gchar *compose_get_header (Compose *compose);
320 static gchar *compose_get_manual_headers_info (Compose *compose);
322 static void compose_convert_header (Compose *compose,
327 gboolean addr_field);
329 static void compose_attach_info_free (AttachInfo *ainfo);
330 static void compose_attach_remove_selected (GtkAction *action,
333 static void compose_template_apply (Compose *compose,
336 static void compose_attach_property (GtkAction *action,
338 static void compose_attach_property_create (gboolean *cancelled);
339 static void attach_property_ok (GtkWidget *widget,
340 gboolean *cancelled);
341 static void attach_property_cancel (GtkWidget *widget,
342 gboolean *cancelled);
343 static gint attach_property_delete_event (GtkWidget *widget,
345 gboolean *cancelled);
346 static gboolean attach_property_key_pressed (GtkWidget *widget,
348 gboolean *cancelled);
350 static void compose_exec_ext_editor (Compose *compose);
352 static gint compose_exec_ext_editor_real (const gchar *file,
353 GdkNativeWindow socket_wid);
354 static gboolean compose_ext_editor_kill (Compose *compose);
355 static gboolean compose_input_cb (GIOChannel *source,
356 GIOCondition condition,
358 static void compose_set_ext_editor_sensitive (Compose *compose,
360 static gboolean compose_get_ext_editor_cmd_valid();
361 static gboolean compose_get_ext_editor_uses_socket();
362 static gboolean compose_ext_editor_plug_removed_cb
365 #endif /* G_OS_UNIX */
367 static void compose_undo_state_changed (UndoMain *undostruct,
372 static void compose_create_header_entry (Compose *compose);
373 static void compose_add_header_entry (Compose *compose, const gchar *header,
374 gchar *text, ComposePrefType pref_type);
375 static void compose_remove_header_entries(Compose *compose);
377 static void compose_update_priority_menu_item(Compose * compose);
379 static void compose_spell_menu_changed (void *data);
380 static void compose_dict_changed (void *data);
382 static void compose_add_field_list ( Compose *compose,
383 GList *listAddress );
385 /* callback functions */
387 static void compose_notebook_size_alloc (GtkNotebook *notebook,
388 GtkAllocation *allocation,
390 static gboolean compose_edit_size_alloc (GtkEditable *widget,
391 GtkAllocation *allocation,
392 GtkSHRuler *shruler);
393 static void account_activated (GtkComboBox *optmenu,
395 static void attach_selected (GtkTreeView *tree_view,
396 GtkTreePath *tree_path,
397 GtkTreeViewColumn *column,
399 static gboolean attach_button_pressed (GtkWidget *widget,
400 GdkEventButton *event,
402 static gboolean attach_key_pressed (GtkWidget *widget,
405 static void compose_send_cb (GtkAction *action, gpointer data);
406 static void compose_send_later_cb (GtkAction *action, gpointer data);
408 static void compose_save_cb (GtkAction *action,
411 static void compose_attach_cb (GtkAction *action,
413 static void compose_insert_file_cb (GtkAction *action,
415 static void compose_insert_sig_cb (GtkAction *action,
417 static void compose_replace_sig_cb (GtkAction *action,
420 static void compose_close_cb (GtkAction *action,
422 static void compose_print_cb (GtkAction *action,
425 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
427 static void compose_address_cb (GtkAction *action,
429 static void about_show_cb (GtkAction *action,
431 static void compose_template_activate_cb(GtkWidget *widget,
434 static void compose_ext_editor_cb (GtkAction *action,
437 static gint compose_delete_cb (GtkWidget *widget,
441 static void compose_undo_cb (GtkAction *action,
443 static void compose_redo_cb (GtkAction *action,
445 static void compose_cut_cb (GtkAction *action,
447 static void compose_copy_cb (GtkAction *action,
449 static void compose_paste_cb (GtkAction *action,
451 static void compose_paste_as_quote_cb (GtkAction *action,
453 static void compose_paste_no_wrap_cb (GtkAction *action,
455 static void compose_paste_wrap_cb (GtkAction *action,
457 static void compose_allsel_cb (GtkAction *action,
460 static void compose_advanced_action_cb (GtkAction *action,
463 static void compose_grab_focus_cb (GtkWidget *widget,
466 static void compose_changed_cb (GtkTextBuffer *textbuf,
469 static void compose_wrap_cb (GtkAction *action,
471 static void compose_wrap_all_cb (GtkAction *action,
473 static void compose_find_cb (GtkAction *action,
475 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
477 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
480 static void compose_toggle_ruler_cb (GtkToggleAction *action,
482 static void compose_toggle_sign_cb (GtkToggleAction *action,
484 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
486 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
487 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
488 static void activate_privacy_system (Compose *compose,
489 PrefsAccount *account,
491 static void compose_use_signing(Compose *compose, gboolean use_signing);
492 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
493 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
495 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
497 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
498 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
499 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
501 static void compose_attach_drag_received_cb (GtkWidget *widget,
502 GdkDragContext *drag_context,
505 GtkSelectionData *data,
509 static void compose_insert_drag_received_cb (GtkWidget *widget,
510 GdkDragContext *drag_context,
513 GtkSelectionData *data,
517 static void compose_header_drag_received_cb (GtkWidget *widget,
518 GdkDragContext *drag_context,
521 GtkSelectionData *data,
526 static gboolean compose_drag_drop (GtkWidget *widget,
527 GdkDragContext *drag_context,
529 guint time, gpointer user_data);
530 static gboolean completion_set_focus_to_subject
535 static void text_inserted (GtkTextBuffer *buffer,
540 static Compose *compose_generic_reply(MsgInfo *msginfo,
541 ComposeQuoteMode quote_mode,
545 gboolean followup_and_reply_to,
548 static void compose_headerentry_changed_cb (GtkWidget *entry,
549 ComposeHeaderEntry *headerentry);
550 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
552 ComposeHeaderEntry *headerentry);
553 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
554 ComposeHeaderEntry *headerentry);
556 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
558 static void compose_allow_user_actions (Compose *compose, gboolean allow);
560 static void compose_nothing_cb (GtkAction *action, gpointer data)
566 static void compose_check_all (GtkAction *action, gpointer data);
567 static void compose_highlight_all (GtkAction *action, gpointer data);
568 static void compose_check_backwards (GtkAction *action, gpointer data);
569 static void compose_check_forwards_go (GtkAction *action, gpointer data);
572 static PrefsAccount *compose_find_account (MsgInfo *msginfo);
574 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
577 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
578 FolderItem *folder_item);
580 static void compose_attach_update_label(Compose *compose);
581 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
582 gboolean respect_default_to);
583 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
584 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
586 static GtkActionEntry compose_popup_entries[] =
588 {"Compose", NULL, "Compose" },
589 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
590 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
591 {"Compose/---", NULL, "---", NULL, NULL, NULL },
592 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
595 static GtkActionEntry compose_entries[] =
597 {"Menu", NULL, "Menu" },
599 {"Message", NULL, N_("_Message") },
600 {"Edit", NULL, N_("_Edit") },
602 {"Spelling", NULL, N_("_Spelling") },
604 {"Options", NULL, N_("_Options") },
605 {"Tools", NULL, N_("_Tools") },
606 {"Help", NULL, N_("_Help") },
608 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
609 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
610 {"Message/---", NULL, "---" },
612 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
613 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
614 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
615 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
616 /* {"Message/---", NULL, "---" }, */
617 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
618 /* {"Message/---", NULL, "---" }, */
619 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
620 /* {"Message/---", NULL, "---" }, */
621 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
624 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
625 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
626 {"Edit/---", NULL, "---" },
628 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
629 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
630 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
632 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
633 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
634 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
635 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
637 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
639 {"Edit/Advanced", NULL, N_("A_dvanced") },
640 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
641 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
642 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
643 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
644 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
645 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
646 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
647 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
648 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
649 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
650 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
651 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
652 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
653 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
655 /* {"Edit/---", NULL, "---" }, */
656 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
658 /* {"Edit/---", NULL, "---" }, */
659 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
660 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
661 /* {"Edit/---", NULL, "---" }, */
662 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
665 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
666 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
667 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
668 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
670 {"Spelling/---", NULL, "---" },
671 {"Spelling/Options", NULL, N_("_Options") },
676 {"Options/ReplyMode", NULL, N_("Reply _mode") },
677 {"Options/---", NULL, "---" },
678 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
679 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
681 /* {"Options/---", NULL, "---" }, */
683 {"Options/Priority", NULL, N_("_Priority") },
685 {"Options/Encoding", NULL, N_("Character _encoding") },
686 {"Options/Encoding/---", 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") },
691 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
692 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
693 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
694 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
695 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
696 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
697 {"Options/Encoding/Korean", NULL, N_("Korean") },
698 {"Options/Encoding/Thai", NULL, N_("Thai") },
701 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
703 {"Tools/Template", NULL, N_("_Template") },
704 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
705 {"Tools/Actions", NULL, N_("Actio_ns") },
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) }, /* TOGGLE */
715 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
716 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
717 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
718 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
719 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
720 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* 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 gchar buf[BUFFSIZE];
2229 gboolean use_signing = FALSE;
2230 gboolean use_encryption = FALSE;
2231 gchar *privacy_system = NULL;
2232 int priority = PRIORITY_NORMAL;
2233 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2234 gboolean autowrap = prefs_common.autowrap;
2235 gboolean autoindent = prefs_common.auto_indent;
2236 HeaderEntry *manual_headers = NULL;
2238 cm_return_val_if_fail(msginfo != NULL, NULL);
2239 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2241 if (compose_put_existing_to_front(msginfo)) {
2245 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2246 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2247 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2248 gchar queueheader_buf[BUFFSIZE];
2251 /* Select Account from queue headers */
2252 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2253 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2254 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2255 account = account_find_from_id(id);
2257 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2258 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2259 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2260 account = account_find_from_id(id);
2262 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2263 sizeof(queueheader_buf), "NAID:")) {
2264 id = atoi(&queueheader_buf[strlen("NAID:")]);
2265 account = account_find_from_id(id);
2267 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2268 sizeof(queueheader_buf), "MAID:")) {
2269 id = atoi(&queueheader_buf[strlen("MAID:")]);
2270 account = account_find_from_id(id);
2272 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2273 sizeof(queueheader_buf), "S:")) {
2274 account = account_find_from_address(queueheader_buf, FALSE);
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2277 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2278 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2279 use_signing = param;
2282 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2283 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2284 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2285 use_signing = param;
2288 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2289 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2290 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2291 use_encryption = param;
2293 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2294 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2295 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2296 use_encryption = param;
2298 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2299 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2300 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2303 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2304 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2305 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2308 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2309 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2310 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2312 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2313 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2314 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2316 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2317 sizeof(queueheader_buf), "X-Priority: ")) {
2318 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2321 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2322 sizeof(queueheader_buf), "RMID:")) {
2323 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2324 if (tokens[0] && tokens[1] && tokens[2]) {
2325 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2326 if (orig_item != NULL) {
2327 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2332 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2333 sizeof(queueheader_buf), "FMID:")) {
2334 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2335 if (tokens[0] && tokens[1] && tokens[2]) {
2336 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2337 if (orig_item != NULL) {
2338 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2343 /* Get manual headers */
2344 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2345 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2346 if (*listmh != '\0') {
2347 debug_print("Got manual headers: %s\n", listmh);
2348 manual_headers = procheader_entries_from_str(listmh);
2353 account = msginfo->folder->folder->account;
2356 if (!account && prefs_common.reedit_account_autosel) {
2357 gchar from[BUFFSIZE];
2358 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2359 extract_address(from);
2360 account = account_find_from_address(from, FALSE);
2364 account = cur_account;
2366 cm_return_val_if_fail(account != NULL, NULL);
2368 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2370 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2371 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2372 compose->autowrap = autowrap;
2373 compose->replyinfo = replyinfo;
2374 compose->fwdinfo = fwdinfo;
2376 compose->updating = TRUE;
2377 compose->priority = priority;
2379 if (privacy_system != NULL) {
2380 compose->privacy_system = privacy_system;
2381 compose_use_signing(compose, use_signing);
2382 compose_use_encryption(compose, use_encryption);
2383 compose_update_privacy_system_menu_item(compose, FALSE);
2385 activate_privacy_system(compose, account, FALSE);
2388 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2390 compose_extract_original_charset(compose);
2392 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2393 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2394 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2395 gchar queueheader_buf[BUFFSIZE];
2397 /* Set message save folder */
2398 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2399 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2400 compose_set_save_to(compose, &queueheader_buf[4]);
2402 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2403 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2405 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2410 if (compose_parse_header(compose, msginfo) < 0) {
2411 compose->updating = FALSE;
2412 compose_destroy(compose);
2415 compose_reedit_set_entry(compose, msginfo);
2417 textview = GTK_TEXT_VIEW(compose->text);
2418 textbuf = gtk_text_view_get_buffer(textview);
2419 compose_create_tags(textview, compose);
2421 mark = gtk_text_buffer_get_insert(textbuf);
2422 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2424 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2425 G_CALLBACK(compose_changed_cb),
2428 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2429 fp = procmime_get_first_encrypted_text_content(msginfo);
2431 compose_force_encryption(compose, account, TRUE, NULL);
2434 fp = procmime_get_first_text_content(msginfo);
2437 g_warning("Can't get text part");
2441 gboolean prev_autowrap;
2442 GtkTextBuffer *buffer;
2444 while (fgets(buf, sizeof(buf), fp) != NULL) {
2446 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2452 compose_attach_parts(compose, msginfo);
2454 compose_colorize_signature(compose);
2456 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2457 G_CALLBACK(compose_changed_cb),
2460 if (manual_headers != NULL) {
2461 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2462 procheader_entries_free(manual_headers);
2463 compose->updating = FALSE;
2464 compose_destroy(compose);
2467 procheader_entries_free(manual_headers);
2470 gtk_widget_grab_focus(compose->text);
2472 if (prefs_common.auto_exteditor) {
2473 compose_exec_ext_editor(compose);
2475 compose->modified = FALSE;
2476 compose_set_title(compose);
2478 compose->updating = FALSE;
2479 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2480 SCROLL_TO_CURSOR(compose);
2482 if (compose->deferred_destroy) {
2483 compose_destroy(compose);
2487 compose->sig_str = account_get_signature_str(compose->account);
2489 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2494 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2501 cm_return_val_if_fail(msginfo != NULL, NULL);
2504 account = account_get_reply_account(msginfo,
2505 prefs_common.reply_account_autosel);
2506 cm_return_val_if_fail(account != NULL, NULL);
2508 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2510 compose->updating = TRUE;
2512 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2513 compose->replyinfo = NULL;
2514 compose->fwdinfo = NULL;
2516 compose_show_first_last_header(compose, TRUE);
2518 gtk_widget_grab_focus(compose->header_last->entry);
2520 filename = procmsg_get_message_file(msginfo);
2522 if (filename == NULL) {
2523 compose->updating = FALSE;
2524 compose_destroy(compose);
2529 compose->redirect_filename = filename;
2531 /* Set save folder */
2532 item = msginfo->folder;
2533 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2534 gchar *folderidentifier;
2536 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2537 folderidentifier = folder_item_get_identifier(item);
2538 compose_set_save_to(compose, folderidentifier);
2539 g_free(folderidentifier);
2542 compose_attach_parts(compose, msginfo);
2544 if (msginfo->subject)
2545 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2547 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2549 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2550 _("The body of the \"Redirect\" template has an error at line %d."));
2551 quote_fmt_reset_vartable();
2552 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2554 compose_colorize_signature(compose);
2557 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2558 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2559 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2561 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2562 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2563 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2564 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2565 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2566 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2567 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2569 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2571 if (compose->toolbar->draft_btn)
2572 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2573 if (compose->toolbar->insert_btn)
2574 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2575 if (compose->toolbar->attach_btn)
2576 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2577 if (compose->toolbar->sig_btn)
2578 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2579 if (compose->toolbar->exteditor_btn)
2580 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2581 if (compose->toolbar->linewrap_current_btn)
2582 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2583 if (compose->toolbar->linewrap_all_btn)
2584 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2586 compose->modified = FALSE;
2587 compose_set_title(compose);
2588 compose->updating = FALSE;
2589 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2590 SCROLL_TO_CURSOR(compose);
2592 if (compose->deferred_destroy) {
2593 compose_destroy(compose);
2597 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2602 const GList *compose_get_compose_list(void)
2604 return compose_list;
2607 void compose_entry_append(Compose *compose, const gchar *address,
2608 ComposeEntryType type, ComposePrefType pref_type)
2610 const gchar *header;
2612 gboolean in_quote = FALSE;
2613 if (!address || *address == '\0') return;
2620 header = N_("Bcc:");
2622 case COMPOSE_REPLYTO:
2623 header = N_("Reply-To:");
2625 case COMPOSE_NEWSGROUPS:
2626 header = N_("Newsgroups:");
2628 case COMPOSE_FOLLOWUPTO:
2629 header = N_( "Followup-To:");
2631 case COMPOSE_INREPLYTO:
2632 header = N_( "In-Reply-To:");
2639 header = prefs_common_translated_header_name(header);
2641 cur = begin = (gchar *)address;
2643 /* we separate the line by commas, but not if we're inside a quoted
2645 while (*cur != '\0') {
2647 in_quote = !in_quote;
2648 if (*cur == ',' && !in_quote) {
2649 gchar *tmp = g_strdup(begin);
2651 tmp[cur-begin]='\0';
2654 while (*tmp == ' ' || *tmp == '\t')
2656 compose_add_header_entry(compose, header, tmp, pref_type);
2657 compose_entry_indicate(compose, tmp);
2664 gchar *tmp = g_strdup(begin);
2666 tmp[cur-begin]='\0';
2667 while (*tmp == ' ' || *tmp == '\t')
2669 compose_add_header_entry(compose, header, tmp, pref_type);
2670 compose_entry_indicate(compose, tmp);
2675 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2680 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2681 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2682 if (gtk_entry_get_text(entry) &&
2683 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2684 gtk_widget_modify_base(
2685 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2686 GTK_STATE_NORMAL, &default_header_bgcolor);
2687 gtk_widget_modify_text(
2688 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2689 GTK_STATE_NORMAL, &default_header_color);
2694 void compose_toolbar_cb(gint action, gpointer data)
2696 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2697 Compose *compose = (Compose*)toolbar_item->parent;
2699 cm_return_if_fail(compose != NULL);
2703 compose_send_cb(NULL, compose);
2706 compose_send_later_cb(NULL, compose);
2709 compose_draft(compose, COMPOSE_QUIT_EDITING);
2712 compose_insert_file_cb(NULL, compose);
2715 compose_attach_cb(NULL, compose);
2718 compose_insert_sig(compose, FALSE);
2721 compose_insert_sig(compose, TRUE);
2724 compose_ext_editor_cb(NULL, compose);
2726 case A_LINEWRAP_CURRENT:
2727 compose_beautify_paragraph(compose, NULL, TRUE);
2729 case A_LINEWRAP_ALL:
2730 compose_wrap_all_full(compose, TRUE);
2733 compose_address_cb(NULL, compose);
2736 case A_CHECK_SPELLING:
2737 compose_check_all(NULL, compose);
2745 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2750 gchar *subject = NULL;
2754 gchar **attach = NULL;
2755 gchar *inreplyto = NULL;
2756 MailField mfield = NO_FIELD_PRESENT;
2758 /* get mailto parts but skip from */
2759 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2762 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2763 mfield = TO_FIELD_PRESENT;
2766 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2768 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2770 if (!g_utf8_validate (subject, -1, NULL)) {
2771 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2772 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2775 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2777 mfield = SUBJECT_FIELD_PRESENT;
2780 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2781 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2784 gboolean prev_autowrap = compose->autowrap;
2786 compose->autowrap = FALSE;
2788 mark = gtk_text_buffer_get_insert(buffer);
2789 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2791 if (!g_utf8_validate (body, -1, NULL)) {
2792 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2793 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2796 gtk_text_buffer_insert(buffer, &iter, body, -1);
2798 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2800 compose->autowrap = prev_autowrap;
2801 if (compose->autowrap)
2802 compose_wrap_all(compose);
2803 mfield = BODY_FIELD_PRESENT;
2807 gint i = 0, att = 0;
2808 gchar *warn_files = NULL;
2809 while (attach[i] != NULL) {
2810 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2811 if (utf8_filename) {
2812 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2813 gchar *tmp = g_strdup_printf("%s%s\n",
2814 warn_files?warn_files:"",
2820 g_free(utf8_filename);
2822 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2827 alertpanel_notice(ngettext(
2828 "The following file has been attached: \n%s",
2829 "The following files have been attached: \n%s", att), warn_files);
2834 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2847 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2849 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2850 {"Cc:", NULL, TRUE},
2851 {"References:", NULL, FALSE},
2852 {"Bcc:", NULL, TRUE},
2853 {"Newsgroups:", NULL, TRUE},
2854 {"Followup-To:", NULL, TRUE},
2855 {"List-Post:", NULL, FALSE},
2856 {"X-Priority:", NULL, FALSE},
2857 {NULL, NULL, FALSE}};
2873 cm_return_val_if_fail(msginfo != NULL, -1);
2875 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2876 procheader_get_header_fields(fp, hentry);
2879 if (hentry[H_REPLY_TO].body != NULL) {
2880 if (hentry[H_REPLY_TO].body[0] != '\0') {
2882 conv_unmime_header(hentry[H_REPLY_TO].body,
2885 g_free(hentry[H_REPLY_TO].body);
2886 hentry[H_REPLY_TO].body = NULL;
2888 if (hentry[H_CC].body != NULL) {
2889 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2890 g_free(hentry[H_CC].body);
2891 hentry[H_CC].body = NULL;
2893 if (hentry[H_REFERENCES].body != NULL) {
2894 if (compose->mode == COMPOSE_REEDIT)
2895 compose->references = hentry[H_REFERENCES].body;
2897 compose->references = compose_parse_references
2898 (hentry[H_REFERENCES].body, msginfo->msgid);
2899 g_free(hentry[H_REFERENCES].body);
2901 hentry[H_REFERENCES].body = NULL;
2903 if (hentry[H_BCC].body != NULL) {
2904 if (compose->mode == COMPOSE_REEDIT)
2906 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2907 g_free(hentry[H_BCC].body);
2908 hentry[H_BCC].body = NULL;
2910 if (hentry[H_NEWSGROUPS].body != NULL) {
2911 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2912 hentry[H_NEWSGROUPS].body = NULL;
2914 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2915 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2916 compose->followup_to =
2917 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2920 g_free(hentry[H_FOLLOWUP_TO].body);
2921 hentry[H_FOLLOWUP_TO].body = NULL;
2923 if (hentry[H_LIST_POST].body != NULL) {
2924 gchar *to = NULL, *start = NULL;
2926 extract_address(hentry[H_LIST_POST].body);
2927 if (hentry[H_LIST_POST].body[0] != '\0') {
2928 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2930 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2931 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2934 g_free(compose->ml_post);
2935 compose->ml_post = to;
2938 g_free(hentry[H_LIST_POST].body);
2939 hentry[H_LIST_POST].body = NULL;
2942 /* CLAWS - X-Priority */
2943 if (compose->mode == COMPOSE_REEDIT)
2944 if (hentry[H_X_PRIORITY].body != NULL) {
2947 priority = atoi(hentry[H_X_PRIORITY].body);
2948 g_free(hentry[H_X_PRIORITY].body);
2950 hentry[H_X_PRIORITY].body = NULL;
2952 if (priority < PRIORITY_HIGHEST ||
2953 priority > PRIORITY_LOWEST)
2954 priority = PRIORITY_NORMAL;
2956 compose->priority = priority;
2959 if (compose->mode == COMPOSE_REEDIT) {
2960 if (msginfo->inreplyto && *msginfo->inreplyto)
2961 compose->inreplyto = g_strdup(msginfo->inreplyto);
2963 if (msginfo->msgid && *msginfo->msgid)
2964 compose->msgid = g_strdup(msginfo->msgid);
2966 if (msginfo->msgid && *msginfo->msgid)
2967 compose->inreplyto = g_strdup(msginfo->msgid);
2969 if (!compose->references) {
2970 if (msginfo->msgid && *msginfo->msgid) {
2971 if (msginfo->inreplyto && *msginfo->inreplyto)
2972 compose->references =
2973 g_strdup_printf("<%s>\n\t<%s>",
2977 compose->references =
2978 g_strconcat("<", msginfo->msgid, ">",
2980 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2981 compose->references =
2982 g_strconcat("<", msginfo->inreplyto, ">",
2991 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2996 cm_return_val_if_fail(msginfo != NULL, -1);
2998 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2999 procheader_get_header_fields(fp, entries);
3003 while (he != NULL && he->name != NULL) {
3005 GtkListStore *model = NULL;
3007 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3008 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3009 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3010 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3011 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3018 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3020 GSList *ref_id_list, *cur;
3024 ref_id_list = references_list_append(NULL, ref);
3025 if (!ref_id_list) return NULL;
3026 if (msgid && *msgid)
3027 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3032 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3033 /* "<" + Message-ID + ">" + CR+LF+TAB */
3034 len += strlen((gchar *)cur->data) + 5;
3036 if (len > MAX_REFERENCES_LEN) {
3037 /* remove second message-ID */
3038 if (ref_id_list && ref_id_list->next &&
3039 ref_id_list->next->next) {
3040 g_free(ref_id_list->next->data);
3041 ref_id_list = g_slist_remove
3042 (ref_id_list, ref_id_list->next->data);
3044 slist_free_strings_full(ref_id_list);
3051 new_ref = g_string_new("");
3052 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3053 if (new_ref->len > 0)
3054 g_string_append(new_ref, "\n\t");
3055 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3058 slist_free_strings_full(ref_id_list);
3060 new_ref_str = new_ref->str;
3061 g_string_free(new_ref, FALSE);
3066 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3067 const gchar *fmt, const gchar *qmark,
3068 const gchar *body, gboolean rewrap,
3069 gboolean need_unescape,
3070 const gchar *err_msg)
3072 MsgInfo* dummyinfo = NULL;
3073 gchar *quote_str = NULL;
3075 gboolean prev_autowrap;
3076 const gchar *trimmed_body = body;
3077 gint cursor_pos = -1;
3078 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3079 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3084 SIGNAL_BLOCK(buffer);
3087 dummyinfo = compose_msginfo_new_from_compose(compose);
3088 msginfo = dummyinfo;
3091 if (qmark != NULL) {
3093 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3094 compose->gtkaspell);
3096 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3098 quote_fmt_scan_string(qmark);
3101 buf = quote_fmt_get_buffer();
3103 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3105 Xstrdup_a(quote_str, buf, goto error)
3108 if (fmt && *fmt != '\0') {
3111 while (*trimmed_body == '\n')
3115 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3116 compose->gtkaspell);
3118 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3120 if (need_unescape) {
3123 /* decode \-escape sequences in the internal representation of the quote format */
3124 tmp = g_malloc(strlen(fmt)+1);
3125 pref_get_unescaped_pref(tmp, fmt);
3126 quote_fmt_scan_string(tmp);
3130 quote_fmt_scan_string(fmt);
3134 buf = quote_fmt_get_buffer();
3136 gint line = quote_fmt_get_line();
3137 alertpanel_error(err_msg, line);
3143 prev_autowrap = compose->autowrap;
3144 compose->autowrap = FALSE;
3146 mark = gtk_text_buffer_get_insert(buffer);
3147 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3148 if (g_utf8_validate(buf, -1, NULL)) {
3149 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3151 gchar *tmpout = NULL;
3152 tmpout = conv_codeset_strdup
3153 (buf, conv_get_locale_charset_str_no_utf8(),
3155 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3157 tmpout = g_malloc(strlen(buf)*2+1);
3158 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3160 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3164 cursor_pos = quote_fmt_get_cursor_pos();
3165 if (cursor_pos == -1)
3166 cursor_pos = gtk_text_iter_get_offset(&iter);
3167 compose->set_cursor_pos = cursor_pos;
3169 gtk_text_buffer_get_start_iter(buffer, &iter);
3170 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3171 gtk_text_buffer_place_cursor(buffer, &iter);
3173 compose->autowrap = prev_autowrap;
3174 if (compose->autowrap && rewrap)
3175 compose_wrap_all(compose);