2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
188 static Compose *compose_generic_new (PrefsAccount *account,
192 GList *listAddress );
194 static Compose *compose_create (PrefsAccount *account,
199 static void compose_entry_mark_default_to (Compose *compose,
200 const gchar *address);
201 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
202 ComposeQuoteMode quote_mode,
206 static Compose *compose_forward_multiple (PrefsAccount *account,
207 GSList *msginfo_list);
208 static Compose *compose_reply (MsgInfo *msginfo,
209 ComposeQuoteMode quote_mode,
214 static Compose *compose_reply_mode (ComposeMode mode,
215 GSList *msginfo_list,
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
220 static GtkWidget *compose_account_option_menu_create
222 static void compose_set_out_encoding (Compose *compose);
223 static void compose_set_template_menu (Compose *compose);
224 static void compose_destroy (Compose *compose);
226 static MailField compose_entries_set (Compose *compose,
228 ComposeEntryType to_type);
229 static gint compose_parse_header (Compose *compose,
231 static gint compose_parse_manual_headers (Compose *compose,
233 HeaderEntry *entries);
234 static gchar *compose_parse_references (const gchar *ref,
237 static gchar *compose_quote_fmt (Compose *compose,
243 gboolean need_unescape,
244 const gchar *err_msg);
246 static void compose_reply_set_entry (Compose *compose,
252 followup_and_reply_to);
253 static void compose_reedit_set_entry (Compose *compose,
256 static void compose_insert_sig (Compose *compose,
258 static ComposeInsertResult compose_insert_file (Compose *compose,
261 static gboolean compose_attach_append (Compose *compose,
264 const gchar *content_type,
265 const gchar *charset);
266 static void compose_attach_parts (Compose *compose,
269 static gboolean compose_beautify_paragraph (Compose *compose,
270 GtkTextIter *par_iter,
272 static void compose_wrap_all (Compose *compose);
273 static void compose_wrap_all_full (Compose *compose,
276 static void compose_set_title (Compose *compose);
277 static void compose_select_account (Compose *compose,
278 PrefsAccount *account,
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send (Compose *compose); */
283 static gboolean compose_check_for_valid_recipient
285 static gboolean compose_check_entries (Compose *compose,
286 gboolean check_everything);
287 static gint compose_write_to_file (Compose *compose,
290 gboolean attach_parts);
291 static gint compose_write_body_to_file (Compose *compose,
293 static gint compose_remove_reedit_target (Compose *compose,
295 static void compose_remove_draft (Compose *compose);
296 static gint compose_queue_sub (Compose *compose,
300 gboolean check_subject,
301 gboolean remove_reedit_target);
302 static int compose_add_attachments (Compose *compose,
304 static gchar *compose_get_header (Compose *compose);
305 static gchar *compose_get_manual_headers_info (Compose *compose);
307 static void compose_convert_header (Compose *compose,
312 gboolean addr_field);
314 static void compose_attach_info_free (AttachInfo *ainfo);
315 static void compose_attach_remove_selected (GtkAction *action,
318 static void compose_template_apply (Compose *compose,
321 static void compose_attach_property (GtkAction *action,
323 static void compose_attach_property_create (gboolean *cancelled);
324 static void attach_property_ok (GtkWidget *widget,
325 gboolean *cancelled);
326 static void attach_property_cancel (GtkWidget *widget,
327 gboolean *cancelled);
328 static gint attach_property_delete_event (GtkWidget *widget,
330 gboolean *cancelled);
331 static gboolean attach_property_key_pressed (GtkWidget *widget,
333 gboolean *cancelled);
335 static void compose_exec_ext_editor (Compose *compose);
337 static gint compose_exec_ext_editor_real (const gchar *file);
338 static gboolean compose_ext_editor_kill (Compose *compose);
339 static gboolean compose_input_cb (GIOChannel *source,
340 GIOCondition condition,
342 static void compose_set_ext_editor_sensitive (Compose *compose,
344 #endif /* G_OS_UNIX */
346 static void compose_undo_state_changed (UndoMain *undostruct,
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry (Compose *compose, const gchar *header,
353 gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
359 static void compose_dict_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367 GtkAllocation *allocation,
369 static gboolean compose_edit_size_alloc (GtkEditable *widget,
370 GtkAllocation *allocation,
371 GtkSHRuler *shruler);
372 static void account_activated (GtkComboBox *optmenu,
374 static void attach_selected (GtkTreeView *tree_view,
375 GtkTreePath *tree_path,
376 GtkTreeViewColumn *column,
378 static gboolean attach_button_pressed (GtkWidget *widget,
379 GdkEventButton *event,
381 static gboolean attach_key_pressed (GtkWidget *widget,
384 static void compose_send_cb (GtkAction *action, gpointer data);
385 static void compose_send_later_cb (GtkAction *action, gpointer data);
387 static void compose_save_cb (GtkAction *action,
390 static void compose_attach_cb (GtkAction *action,
392 static void compose_insert_file_cb (GtkAction *action,
394 static void compose_insert_sig_cb (GtkAction *action,
397 static void compose_close_cb (GtkAction *action,
399 static void compose_print_cb (GtkAction *action,
402 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
404 static void compose_address_cb (GtkAction *action,
406 static void about_show_cb (GtkAction *action,
408 static void compose_template_activate_cb(GtkWidget *widget,
411 static void compose_ext_editor_cb (GtkAction *action,
414 static gint compose_delete_cb (GtkWidget *widget,
418 static void compose_undo_cb (GtkAction *action,
420 static void compose_redo_cb (GtkAction *action,
422 static void compose_cut_cb (GtkAction *action,
424 static void compose_copy_cb (GtkAction *action,
426 static void compose_paste_cb (GtkAction *action,
428 static void compose_paste_as_quote_cb (GtkAction *action,
430 static void compose_paste_no_wrap_cb (GtkAction *action,
432 static void compose_paste_wrap_cb (GtkAction *action,
434 static void compose_allsel_cb (GtkAction *action,
437 static void compose_advanced_action_cb (GtkAction *action,
440 static void compose_grab_focus_cb (GtkWidget *widget,
443 static void compose_changed_cb (GtkTextBuffer *textbuf,
446 static void compose_wrap_cb (GtkAction *action,
448 static void compose_wrap_all_cb (GtkAction *action,
450 static void compose_find_cb (GtkAction *action,
452 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
454 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
457 static void compose_toggle_ruler_cb (GtkToggleAction *action,
459 static void compose_toggle_sign_cb (GtkToggleAction *action,
461 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
463 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system (Compose *compose,
466 PrefsAccount *account,
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
472 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
474 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
476 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
478 static void compose_attach_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_insert_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
494 static void compose_header_drag_received_cb (GtkWidget *widget,
495 GdkDragContext *drag_context,
498 GtkSelectionData *data,
503 static gboolean compose_drag_drop (GtkWidget *widget,
504 GdkDragContext *drag_context,
506 guint time, gpointer user_data);
507 static gboolean completion_set_focus_to_subject
512 static void text_inserted (GtkTextBuffer *buffer,
517 static Compose *compose_generic_reply(MsgInfo *msginfo,
518 ComposeQuoteMode quote_mode,
522 gboolean followup_and_reply_to,
525 static void compose_headerentry_changed_cb (GtkWidget *entry,
526 ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
529 ComposeHeaderEntry *headerentry);
530 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
531 ComposeHeaderEntry *headerentry);
533 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
535 static void compose_allow_user_actions (Compose *compose, gboolean allow);
537 static void compose_nothing_cb (GtkAction *action, gpointer data)
543 static void compose_check_all (GtkAction *action, gpointer data);
544 static void compose_highlight_all (GtkAction *action, gpointer data);
545 static void compose_check_backwards (GtkAction *action, gpointer data);
546 static void compose_check_forwards_go (GtkAction *action, gpointer data);
549 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
551 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
554 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
555 FolderItem *folder_item);
557 static void compose_attach_update_label(Compose *compose);
558 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
559 gboolean respect_default_to);
560 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
562 static GtkActionEntry compose_popup_entries[] =
564 {"Compose", NULL, "Compose" },
565 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
566 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
567 {"Compose/---", NULL, "---", NULL, NULL, NULL },
568 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
571 static GtkActionEntry compose_entries[] =
573 {"Menu", NULL, "Menu" },
575 {"Message", NULL, N_("_Message") },
576 {"Edit", NULL, N_("_Edit") },
578 {"Spelling", NULL, N_("_Spelling") },
580 {"Options", NULL, N_("_Options") },
581 {"Tools", NULL, N_("_Tools") },
582 {"Help", NULL, N_("_Help") },
584 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
585 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
586 {"Message/---", NULL, "---" },
588 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
589 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
590 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
591 /* {"Message/---", NULL, "---" }, */
592 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
593 /* {"Message/---", NULL, "---" }, */
594 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
595 /* {"Message/---", NULL, "---" }, */
596 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
599 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
600 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
601 {"Edit/---", NULL, "---" },
603 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
604 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
605 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
607 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
608 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
609 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
610 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
612 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
614 {"Edit/Advanced", NULL, N_("A_dvanced") },
615 {"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*/
616 {"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*/
617 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
618 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"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*/
623 {"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*/
624 {"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*/
625 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
626 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
627 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
628 {"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*/
630 /* {"Edit/---", NULL, "---" }, */
631 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
635 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
640 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
641 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
642 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
643 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
645 {"Spelling/---", NULL, "---" },
646 {"Spelling/Options", NULL, N_("_Options") },
651 {"Options/ReplyMode", NULL, N_("Reply _mode") },
652 {"Options/---", NULL, "---" },
653 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
654 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 /* {"Options/---", NULL, "---" }, */
658 {"Options/Priority", NULL, N_("_Priority") },
660 {"Options/Encoding", NULL, N_("Character _encoding") },
661 {"Options/Encoding/---", NULL, "---" },
662 #define ENC_ACTION(cs_char,c_char,string) \
663 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
665 {"Options/Encoding/Western", NULL, N_("Western European") },
666 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
667 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
668 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
669 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
670 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
671 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
672 {"Options/Encoding/Korean", NULL, N_("Korean") },
673 {"Options/Encoding/Thai", NULL, N_("Thai") },
676 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
678 {"Tools/Template", NULL, N_("_Template") },
679 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 {"Tools/Actions", NULL, N_("Actio_ns") },
681 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
684 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
687 static GtkToggleActionEntry compose_toggle_entries[] =
689 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
690 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
691 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
692 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
693 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
694 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
695 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
698 static GtkRadioActionEntry compose_radio_rm_entries[] =
700 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
701 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
702 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
703 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
706 static GtkRadioActionEntry compose_radio_prio_entries[] =
708 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
710 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
711 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
712 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
715 static GtkRadioActionEntry compose_radio_enc_entries[] =
717 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
751 static GtkTargetEntry compose_mime_types[] =
753 {"text/uri-list", 0, 0},
754 {"UTF8_STRING", 0, 0},
758 static gboolean compose_put_existing_to_front(MsgInfo *info)
760 GList *compose_list = compose_get_compose_list();
764 for (elem = compose_list; elem != NULL && elem->data != NULL;
766 Compose *c = (Compose*)elem->data;
768 if (!c->targetinfo || !c->targetinfo->msgid ||
772 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
773 gtkut_window_popup(c->window);
781 static GdkColor quote_color1 =
782 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color2 =
784 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_color3 =
786 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_bgcolor1 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor2 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_bgcolor3 =
793 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor signature_color = {
802 static GdkColor uri_color = {
809 static void compose_create_tags(GtkTextView *text, Compose *compose)
811 GtkTextBuffer *buffer;
812 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 #if !GTK_CHECK_VERSION(2, 24, 0)
820 buffer = gtk_text_view_get_buffer(text);
822 if (prefs_common.enable_color) {
823 /* grab the quote colors, converting from an int to a GdkColor */
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
828 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
830 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
832 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
834 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
836 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
838 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
841 signature_color = quote_color1 = quote_color2 = quote_color3 =
842 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
845 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
848 "paragraph-background-gdk", "e_bgcolor1,
850 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
851 "foreground-gdk", "e_color2,
852 "paragraph-background-gdk", "e_bgcolor2,
854 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
855 "foreground-gdk", "e_color3,
856 "paragraph-background-gdk", "e_bgcolor3,
859 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860 "foreground-gdk", "e_color1,
862 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
863 "foreground-gdk", "e_color2,
865 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
866 "foreground-gdk", "e_color3,
870 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
871 "foreground-gdk", &signature_color,
874 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
875 "foreground-gdk", &uri_color,
877 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
878 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881 color[0] = quote_color1;
882 color[1] = quote_color2;
883 color[2] = quote_color3;
884 color[3] = quote_bgcolor1;
885 color[4] = quote_bgcolor2;
886 color[5] = quote_bgcolor3;
887 color[6] = signature_color;
888 color[7] = uri_color;
890 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
891 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
893 for (i = 0; i < 8; i++) {
894 if (success[i] == FALSE) {
895 g_warning("Compose: color allocation failed.\n");
896 quote_color1 = quote_color2 = quote_color3 =
897 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
898 signature_color = uri_color = black;
904 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
907 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
910 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
912 return compose_generic_new(account, mailto, item, NULL, NULL);
915 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
917 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
920 #define SCROLL_TO_CURSOR(compose) { \
921 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
922 gtk_text_view_get_buffer( \
923 GTK_TEXT_VIEW(compose->text))); \
924 gtk_text_view_scroll_mark_onscreen( \
925 GTK_TEXT_VIEW(compose->text), \
929 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
932 if (folderidentifier) {
933 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
934 prefs_common.compose_save_to_history = add_history(
935 prefs_common.compose_save_to_history, folderidentifier);
936 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
937 prefs_common.compose_save_to_history);
940 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
941 if (folderidentifier)
942 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
944 gtk_entry_set_text(GTK_ENTRY(entry), "");
947 static gchar *compose_get_save_to(Compose *compose)
950 gchar *result = NULL;
951 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
952 result = gtk_editable_get_chars(entry, 0, -1);
955 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
956 prefs_common.compose_save_to_history = add_history(
957 prefs_common.compose_save_to_history, result);
958 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
959 prefs_common.compose_save_to_history);
964 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
965 GList *attach_files, GList *listAddress )
968 GtkTextView *textview;
969 GtkTextBuffer *textbuf;
971 const gchar *subject_format = NULL;
972 const gchar *body_format = NULL;
973 gchar *mailto_from = NULL;
974 PrefsAccount *mailto_account = NULL;
975 MsgInfo* dummyinfo = NULL;
976 gint cursor_pos = -1;
977 MailField mfield = NO_FIELD_PRESENT;
981 /* check if mailto defines a from */
982 if (mailto && *mailto != '\0') {
983 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
984 /* mailto defines a from, check if we can get account prefs from it,
985 if not, the account prefs will be guessed using other ways, but we'll keep
988 mailto_account = account_find_from_address(mailto_from, TRUE);
990 account = mailto_account;
993 /* if no account prefs set from mailto, set if from folder prefs (if any) */
994 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
995 account = account_find_from_id(item->prefs->default_account);
997 /* if no account prefs set, fallback to the current one */
998 if (!account) account = cur_account;
999 cm_return_val_if_fail(account != NULL, NULL);
1001 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1003 /* override from name if mailto asked for it */
1005 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1006 g_free(mailto_from);
1008 /* override from name according to folder properties */
1009 if (item && item->prefs &&
1010 item->prefs->compose_with_format &&
1011 item->prefs->compose_override_from_format &&
1012 *item->prefs->compose_override_from_format != '\0') {
1017 dummyinfo = compose_msginfo_new_from_compose(compose);
1019 /* decode \-escape sequences in the internal representation of the quote format */
1020 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1021 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1024 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1025 compose->gtkaspell);
1027 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1029 quote_fmt_scan_string(tmp);
1032 buf = quote_fmt_get_buffer();
1034 alertpanel_error(_("New message From format error."));
1036 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1037 quote_fmt_reset_vartable();
1042 compose->replyinfo = NULL;
1043 compose->fwdinfo = NULL;
1045 textview = GTK_TEXT_VIEW(compose->text);
1046 textbuf = gtk_text_view_get_buffer(textview);
1047 compose_create_tags(textview, compose);
1049 undo_block(compose->undostruct);
1051 compose_set_dictionaries_from_folder_prefs(compose, item);
1054 if (account->auto_sig)
1055 compose_insert_sig(compose, FALSE);
1056 gtk_text_buffer_get_start_iter(textbuf, &iter);
1057 gtk_text_buffer_place_cursor(textbuf, &iter);
1059 if (account->protocol != A_NNTP) {
1060 if (mailto && *mailto != '\0') {
1061 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1064 compose_set_folder_prefs(compose, item, TRUE);
1066 if (item && item->ret_rcpt) {
1067 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1070 if (mailto && *mailto != '\0') {
1071 if (!strchr(mailto, '@'))
1072 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1074 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1075 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1076 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1077 mfield = TO_FIELD_PRESENT;
1080 * CLAWS: just don't allow return receipt request, even if the user
1081 * may want to send an email. simple but foolproof.
1083 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1085 compose_add_field_list( compose, listAddress );
1087 if (item && item->prefs && item->prefs->compose_with_format) {
1088 subject_format = item->prefs->compose_subject_format;
1089 body_format = item->prefs->compose_body_format;
1090 } else if (account->compose_with_format) {
1091 subject_format = account->compose_subject_format;
1092 body_format = account->compose_body_format;
1093 } else if (prefs_common.compose_with_format) {
1094 subject_format = prefs_common.compose_subject_format;
1095 body_format = prefs_common.compose_body_format;
1098 if (subject_format || body_format) {
1101 && *subject_format != '\0' )
1103 gchar *subject = NULL;
1108 dummyinfo = compose_msginfo_new_from_compose(compose);
1110 /* decode \-escape sequences in the internal representation of the quote format */
1111 tmp = g_malloc(strlen(subject_format)+1);
1112 pref_get_unescaped_pref(tmp, subject_format);
1114 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1116 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1117 compose->gtkaspell);
1119 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1121 quote_fmt_scan_string(tmp);
1124 buf = quote_fmt_get_buffer();
1126 alertpanel_error(_("New message subject format error."));
1128 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1129 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1130 quote_fmt_reset_vartable();
1134 mfield = SUBJECT_FIELD_PRESENT;
1138 && *body_format != '\0' )
1141 GtkTextBuffer *buffer;
1142 GtkTextIter start, end;
1146 dummyinfo = compose_msginfo_new_from_compose(compose);
1148 text = GTK_TEXT_VIEW(compose->text);
1149 buffer = gtk_text_view_get_buffer(text);
1150 gtk_text_buffer_get_start_iter(buffer, &start);
1151 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1152 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1154 compose_quote_fmt(compose, dummyinfo,
1156 NULL, tmp, FALSE, TRUE,
1157 _("The body of the \"New message\" template has an error at line %d."));
1158 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1159 quote_fmt_reset_vartable();
1163 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1164 gtkaspell_highlight_all(compose->gtkaspell);
1166 mfield = BODY_FIELD_PRESENT;
1170 procmsg_msginfo_free( dummyinfo );
1176 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1177 ainfo = (AttachInfo *) curr->data;
1178 compose_attach_append(compose, ainfo->file, ainfo->name,
1179 ainfo->content_type, ainfo->charset);
1183 compose_show_first_last_header(compose, TRUE);
1185 /* Set save folder */
1186 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1187 gchar *folderidentifier;
1189 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1190 folderidentifier = folder_item_get_identifier(item);
1191 compose_set_save_to(compose, folderidentifier);
1192 g_free(folderidentifier);
1195 /* Place cursor according to provided input (mfield) */
1197 case NO_FIELD_PRESENT:
1198 if (compose->header_last)
1199 gtk_widget_grab_focus(compose->header_last->entry);
1201 case TO_FIELD_PRESENT:
1202 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1204 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1207 gtk_widget_grab_focus(compose->subject_entry);
1209 case SUBJECT_FIELD_PRESENT:
1210 textview = GTK_TEXT_VIEW(compose->text);
1213 textbuf = gtk_text_view_get_buffer(textview);
1216 mark = gtk_text_buffer_get_insert(textbuf);
1217 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1218 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1220 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1221 * only defers where it comes to the variable body
1222 * is not null. If no body is present compose->text
1223 * will be null in which case you cannot place the
1224 * cursor inside the component so. An empty component
1225 * is therefore created before placing the cursor
1227 case BODY_FIELD_PRESENT:
1228 cursor_pos = quote_fmt_get_cursor_pos();
1229 if (cursor_pos == -1)
1230 gtk_widget_grab_focus(compose->header_last->entry);
1232 gtk_widget_grab_focus(compose->text);
1236 undo_unblock(compose->undostruct);
1238 if (prefs_common.auto_exteditor)
1239 compose_exec_ext_editor(compose);
1241 compose->draft_timeout_tag = -1;
1242 SCROLL_TO_CURSOR(compose);
1244 compose->modified = FALSE;
1245 compose_set_title(compose);
1247 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1252 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1253 gboolean override_pref, const gchar *system)
1255 const gchar *privacy = NULL;
1257 cm_return_if_fail(compose != NULL);
1258 cm_return_if_fail(account != NULL);
1260 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1265 else if (account->default_privacy_system
1266 && strlen(account->default_privacy_system)) {
1267 privacy = account->default_privacy_system;
1269 GSList *privacy_avail = privacy_get_system_ids();
1270 if (privacy_avail && g_slist_length(privacy_avail)) {
1271 privacy = (gchar *)(privacy_avail->data);
1274 if (privacy != NULL) {
1276 g_free(compose->privacy_system);
1277 compose->privacy_system = NULL;
1279 if (compose->privacy_system == NULL)
1280 compose->privacy_system = g_strdup(privacy);
1281 else if (*(compose->privacy_system) == '\0') {
1282 g_free(compose->privacy_system);
1283 compose->privacy_system = g_strdup(privacy);
1285 compose_update_privacy_system_menu_item(compose, FALSE);
1286 compose_use_encryption(compose, TRUE);
1290 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1292 const gchar *privacy = NULL;
1296 else if (account->default_privacy_system
1297 && strlen(account->default_privacy_system)) {
1298 privacy = account->default_privacy_system;
1300 GSList *privacy_avail = privacy_get_system_ids();
1301 if (privacy_avail && g_slist_length(privacy_avail)) {
1302 privacy = (gchar *)(privacy_avail->data);
1306 if (privacy != NULL) {
1308 g_free(compose->privacy_system);
1309 compose->privacy_system = NULL;
1311 if (compose->privacy_system == NULL)
1312 compose->privacy_system = g_strdup(privacy);
1313 compose_update_privacy_system_menu_item(compose, FALSE);
1314 compose_use_signing(compose, TRUE);
1318 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1322 Compose *compose = NULL;
1324 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1326 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1327 cm_return_val_if_fail(msginfo != NULL, NULL);
1329 list_len = g_slist_length(msginfo_list);
1333 case COMPOSE_REPLY_TO_ADDRESS:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1335 FALSE, prefs_common.default_reply_list, FALSE, body);
1337 case COMPOSE_REPLY_WITH_QUOTE:
1338 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1339 FALSE, prefs_common.default_reply_list, FALSE, body);
1341 case COMPOSE_REPLY_WITHOUT_QUOTE:
1342 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1343 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1345 case COMPOSE_REPLY_TO_SENDER:
1346 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1347 FALSE, FALSE, TRUE, body);
1349 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1350 compose = compose_followup_and_reply_to(msginfo,
1351 COMPOSE_QUOTE_CHECK,
1352 FALSE, FALSE, body);
1354 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1355 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1356 FALSE, FALSE, TRUE, body);
1358 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1359 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1360 FALSE, FALSE, TRUE, NULL);
1362 case COMPOSE_REPLY_TO_ALL:
1363 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1364 TRUE, FALSE, FALSE, body);
1366 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1367 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1368 TRUE, FALSE, FALSE, body);
1370 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1371 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1372 TRUE, FALSE, FALSE, NULL);
1374 case COMPOSE_REPLY_TO_LIST:
1375 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1376 FALSE, TRUE, FALSE, body);
1378 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1379 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1380 FALSE, TRUE, FALSE, body);
1382 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1383 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1384 FALSE, TRUE, FALSE, NULL);
1386 case COMPOSE_FORWARD:
1387 if (prefs_common.forward_as_attachment) {
1388 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1391 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1395 case COMPOSE_FORWARD_INLINE:
1396 /* check if we reply to more than one Message */
1397 if (list_len == 1) {
1398 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1401 /* more messages FALL THROUGH */
1402 case COMPOSE_FORWARD_AS_ATTACH:
1403 compose = compose_forward_multiple(NULL, msginfo_list);
1405 case COMPOSE_REDIRECT:
1406 compose = compose_redirect(NULL, msginfo, FALSE);
1409 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1412 if (compose == NULL) {
1413 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1417 compose->rmode = mode;
1418 switch (compose->rmode) {
1420 case COMPOSE_REPLY_WITH_QUOTE:
1421 case COMPOSE_REPLY_WITHOUT_QUOTE:
1422 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1423 debug_print("reply mode Normal\n");
1424 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1425 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1427 case COMPOSE_REPLY_TO_SENDER:
1428 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1429 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1430 debug_print("reply mode Sender\n");
1431 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1433 case COMPOSE_REPLY_TO_ALL:
1434 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1435 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1436 debug_print("reply mode All\n");
1437 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1439 case COMPOSE_REPLY_TO_LIST:
1440 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1441 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1442 debug_print("reply mode List\n");
1443 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1445 case COMPOSE_REPLY_TO_ADDRESS:
1446 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1454 static Compose *compose_reply(MsgInfo *msginfo,
1455 ComposeQuoteMode quote_mode,
1461 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1462 to_sender, FALSE, body);
1465 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1466 ComposeQuoteMode quote_mode,
1471 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1472 to_sender, TRUE, body);
1475 static void compose_extract_original_charset(Compose *compose)
1477 MsgInfo *info = NULL;
1478 if (compose->replyinfo) {
1479 info = compose->replyinfo;
1480 } else if (compose->fwdinfo) {
1481 info = compose->fwdinfo;
1482 } else if (compose->targetinfo) {
1483 info = compose->targetinfo;
1486 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1487 MimeInfo *partinfo = mimeinfo;
1488 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1489 partinfo = procmime_mimeinfo_next(partinfo);
1491 compose->orig_charset =
1492 g_strdup(procmime_mimeinfo_get_parameter(
1493 partinfo, "charset"));
1495 procmime_mimeinfo_free_all(mimeinfo);
1499 #define SIGNAL_BLOCK(buffer) { \
1500 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1501 G_CALLBACK(compose_changed_cb), \
1503 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1504 G_CALLBACK(text_inserted), \
1508 #define SIGNAL_UNBLOCK(buffer) { \
1509 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1510 G_CALLBACK(compose_changed_cb), \
1512 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1513 G_CALLBACK(text_inserted), \
1517 static Compose *compose_generic_reply(MsgInfo *msginfo,
1518 ComposeQuoteMode quote_mode,
1519 gboolean to_all, gboolean to_ml,
1521 gboolean followup_and_reply_to,
1525 PrefsAccount *account = NULL;
1526 GtkTextView *textview;
1527 GtkTextBuffer *textbuf;
1528 gboolean quote = FALSE;
1529 const gchar *qmark = NULL;
1530 const gchar *body_fmt = NULL;
1531 gchar *s_system = NULL;
1533 cm_return_val_if_fail(msginfo != NULL, NULL);
1534 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1536 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1538 cm_return_val_if_fail(account != NULL, NULL);
1540 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1542 compose->updating = TRUE;
1544 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1545 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1547 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1548 if (!compose->replyinfo)
1549 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1551 compose_extract_original_charset(compose);
1553 if (msginfo->folder && msginfo->folder->ret_rcpt)
1554 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1556 /* Set save folder */
1557 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1558 gchar *folderidentifier;
1560 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1561 folderidentifier = folder_item_get_identifier(msginfo->folder);
1562 compose_set_save_to(compose, folderidentifier);
1563 g_free(folderidentifier);
1566 if (compose_parse_header(compose, msginfo) < 0) {
1567 compose->updating = FALSE;
1568 compose_destroy(compose);
1572 /* override from name according to folder properties */
1573 if (msginfo->folder && msginfo->folder->prefs &&
1574 msginfo->folder->prefs->reply_with_format &&
1575 msginfo->folder->prefs->reply_override_from_format &&
1576 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1581 /* decode \-escape sequences in the internal representation of the quote format */
1582 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1583 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1586 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1587 compose->gtkaspell);
1589 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1591 quote_fmt_scan_string(tmp);
1594 buf = quote_fmt_get_buffer();
1596 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1598 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1599 quote_fmt_reset_vartable();
1604 textview = (GTK_TEXT_VIEW(compose->text));
1605 textbuf = gtk_text_view_get_buffer(textview);
1606 compose_create_tags(textview, compose);
1608 undo_block(compose->undostruct);
1610 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1611 gtkaspell_block_check(compose->gtkaspell);
1614 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1615 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1616 /* use the reply format of folder (if enabled), or the account's one
1617 (if enabled) or fallback to the global reply format, which is always
1618 enabled (even if empty), and use the relevant quotemark */
1620 if (msginfo->folder && msginfo->folder->prefs &&
1621 msginfo->folder->prefs->reply_with_format) {
1622 qmark = msginfo->folder->prefs->reply_quotemark;
1623 body_fmt = msginfo->folder->prefs->reply_body_format;
1625 } else if (account->reply_with_format) {
1626 qmark = account->reply_quotemark;
1627 body_fmt = account->reply_body_format;
1630 qmark = prefs_common.quotemark;
1631 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1632 body_fmt = gettext(prefs_common.quotefmt);
1639 /* empty quotemark is not allowed */
1640 if (qmark == NULL || *qmark == '\0')
1642 compose_quote_fmt(compose, compose->replyinfo,
1643 body_fmt, qmark, body, FALSE, TRUE,
1644 _("The body of the \"Reply\" template has an error at line %d."));
1645 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1646 quote_fmt_reset_vartable();
1649 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1650 compose_force_encryption(compose, account, FALSE, s_system);
1653 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1654 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1655 compose_force_signing(compose, account, s_system);
1659 SIGNAL_BLOCK(textbuf);
1661 if (account->auto_sig)
1662 compose_insert_sig(compose, FALSE);
1664 compose_wrap_all(compose);
1667 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1668 gtkaspell_highlight_all(compose->gtkaspell);
1669 gtkaspell_unblock_check(compose->gtkaspell);
1671 SIGNAL_UNBLOCK(textbuf);
1673 gtk_widget_grab_focus(compose->text);
1675 undo_unblock(compose->undostruct);
1677 if (prefs_common.auto_exteditor)
1678 compose_exec_ext_editor(compose);
1680 compose->modified = FALSE;
1681 compose_set_title(compose);
1683 compose->updating = FALSE;
1684 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1685 SCROLL_TO_CURSOR(compose);
1687 if (compose->deferred_destroy) {
1688 compose_destroy(compose);
1696 #define INSERT_FW_HEADER(var, hdr) \
1697 if (msginfo->var && *msginfo->var) { \
1698 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1699 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1700 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1703 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1704 gboolean as_attach, const gchar *body,
1705 gboolean no_extedit,
1709 GtkTextView *textview;
1710 GtkTextBuffer *textbuf;
1711 gint cursor_pos = -1;
1714 cm_return_val_if_fail(msginfo != NULL, NULL);
1715 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1718 !(account = compose_guess_forward_account_from_msginfo
1720 account = cur_account;
1722 if (!prefs_common.forward_as_attachment)
1723 mode = COMPOSE_FORWARD_INLINE;
1725 mode = COMPOSE_FORWARD;
1726 compose = compose_create(account, msginfo->folder, mode, batch);
1728 compose->updating = TRUE;
1729 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1730 if (!compose->fwdinfo)
1731 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1733 compose_extract_original_charset(compose);
1735 if (msginfo->subject && *msginfo->subject) {
1736 gchar *buf, *buf2, *p;
1738 buf = p = g_strdup(msginfo->subject);
1739 p += subject_get_prefix_length(p);
1740 memmove(buf, p, strlen(p) + 1);
1742 buf2 = g_strdup_printf("Fw: %s", buf);
1743 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1749 /* override from name according to folder properties */
1750 if (msginfo->folder && msginfo->folder->prefs &&
1751 msginfo->folder->prefs->forward_with_format &&
1752 msginfo->folder->prefs->forward_override_from_format &&
1753 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1757 MsgInfo *full_msginfo = NULL;
1760 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1762 full_msginfo = procmsg_msginfo_copy(msginfo);
1764 /* decode \-escape sequences in the internal representation of the quote format */
1765 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1766 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1769 gtkaspell_block_check(compose->gtkaspell);
1770 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1771 compose->gtkaspell);
1773 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1775 quote_fmt_scan_string(tmp);
1778 buf = quote_fmt_get_buffer();
1780 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1782 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1783 quote_fmt_reset_vartable();
1786 procmsg_msginfo_free(full_msginfo);
1789 textview = GTK_TEXT_VIEW(compose->text);
1790 textbuf = gtk_text_view_get_buffer(textview);
1791 compose_create_tags(textview, compose);
1793 undo_block(compose->undostruct);
1797 msgfile = procmsg_get_message_file(msginfo);
1798 if (!is_file_exist(msgfile))
1799 g_warning("%s: file not exist\n", msgfile);
1801 compose_attach_append(compose, msgfile, msgfile,
1802 "message/rfc822", NULL);
1806 const gchar *qmark = NULL;
1807 const gchar *body_fmt = NULL;
1808 MsgInfo *full_msginfo;
1810 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1812 full_msginfo = procmsg_msginfo_copy(msginfo);
1814 /* use the forward format of folder (if enabled), or the account's one
1815 (if enabled) or fallback to the global forward format, which is always
1816 enabled (even if empty), and use the relevant quotemark */
1817 if (msginfo->folder && msginfo->folder->prefs &&
1818 msginfo->folder->prefs->forward_with_format) {
1819 qmark = msginfo->folder->prefs->forward_quotemark;
1820 body_fmt = msginfo->folder->prefs->forward_body_format;
1822 } else if (account->forward_with_format) {
1823 qmark = account->forward_quotemark;
1824 body_fmt = account->forward_body_format;
1827 qmark = prefs_common.fw_quotemark;
1828 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1829 body_fmt = gettext(prefs_common.fw_quotefmt);
1834 /* empty quotemark is not allowed */
1835 if (qmark == NULL || *qmark == '\0')
1838 compose_quote_fmt(compose, full_msginfo,
1839 body_fmt, qmark, body, FALSE, TRUE,
1840 _("The body of the \"Forward\" template has an error at line %d."));
1841 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1842 quote_fmt_reset_vartable();
1843 compose_attach_parts(compose, msginfo);
1845 procmsg_msginfo_free(full_msginfo);
1848 SIGNAL_BLOCK(textbuf);
1850 if (account->auto_sig)
1851 compose_insert_sig(compose, FALSE);
1853 compose_wrap_all(compose);
1856 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1857 gtkaspell_highlight_all(compose->gtkaspell);
1858 gtkaspell_unblock_check(compose->gtkaspell);
1860 SIGNAL_UNBLOCK(textbuf);
1862 cursor_pos = quote_fmt_get_cursor_pos();
1863 if (cursor_pos == -1)
1864 gtk_widget_grab_focus(compose->header_last->entry);
1866 gtk_widget_grab_focus(compose->text);
1868 if (!no_extedit && prefs_common.auto_exteditor)
1869 compose_exec_ext_editor(compose);
1872 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1873 gchar *folderidentifier;
1875 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1876 folderidentifier = folder_item_get_identifier(msginfo->folder);
1877 compose_set_save_to(compose, folderidentifier);
1878 g_free(folderidentifier);
1881 undo_unblock(compose->undostruct);
1883 compose->modified = FALSE;
1884 compose_set_title(compose);
1886 compose->updating = FALSE;
1887 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1888 SCROLL_TO_CURSOR(compose);
1890 if (compose->deferred_destroy) {
1891 compose_destroy(compose);
1895 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1900 #undef INSERT_FW_HEADER
1902 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1905 GtkTextView *textview;
1906 GtkTextBuffer *textbuf;
1910 gboolean single_mail = TRUE;
1912 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1914 if (g_slist_length(msginfo_list) > 1)
1915 single_mail = FALSE;
1917 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1918 if (((MsgInfo *)msginfo->data)->folder == NULL)
1921 /* guess account from first selected message */
1923 !(account = compose_guess_forward_account_from_msginfo
1924 (msginfo_list->data)))
1925 account = cur_account;
1927 cm_return_val_if_fail(account != NULL, NULL);
1929 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1930 if (msginfo->data) {
1931 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1932 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1936 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1937 g_warning("no msginfo_list");
1941 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1943 compose->updating = TRUE;
1945 /* override from name according to folder properties */
1946 if (msginfo_list->data) {
1947 MsgInfo *msginfo = msginfo_list->data;
1949 if (msginfo->folder && msginfo->folder->prefs &&
1950 msginfo->folder->prefs->forward_with_format &&
1951 msginfo->folder->prefs->forward_override_from_format &&
1952 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1957 /* decode \-escape sequences in the internal representation of the quote format */
1958 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1959 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1962 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1963 compose->gtkaspell);
1965 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1967 quote_fmt_scan_string(tmp);
1970 buf = quote_fmt_get_buffer();
1972 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1974 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1975 quote_fmt_reset_vartable();
1981 textview = GTK_TEXT_VIEW(compose->text);
1982 textbuf = gtk_text_view_get_buffer(textview);
1983 compose_create_tags(textview, compose);
1985 undo_block(compose->undostruct);
1986 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1987 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1989 if (!is_file_exist(msgfile))
1990 g_warning("%s: file not exist\n", msgfile);
1992 compose_attach_append(compose, msgfile, msgfile,
1993 "message/rfc822", NULL);
1998 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1999 if (info->subject && *info->subject) {
2000 gchar *buf, *buf2, *p;
2002 buf = p = g_strdup(info->subject);
2003 p += subject_get_prefix_length(p);
2004 memmove(buf, p, strlen(p) + 1);
2006 buf2 = g_strdup_printf("Fw: %s", buf);
2007 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2013 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2014 _("Fw: multiple emails"));
2017 SIGNAL_BLOCK(textbuf);
2019 if (account->auto_sig)
2020 compose_insert_sig(compose, FALSE);
2022 compose_wrap_all(compose);
2024 SIGNAL_UNBLOCK(textbuf);
2026 gtk_text_buffer_get_start_iter(textbuf, &iter);
2027 gtk_text_buffer_place_cursor(textbuf, &iter);
2029 gtk_widget_grab_focus(compose->header_last->entry);
2030 undo_unblock(compose->undostruct);
2031 compose->modified = FALSE;
2032 compose_set_title(compose);
2034 compose->updating = FALSE;
2035 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2036 SCROLL_TO_CURSOR(compose);
2038 if (compose->deferred_destroy) {
2039 compose_destroy(compose);
2043 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2048 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2050 GtkTextIter start = *iter;
2051 GtkTextIter end_iter;
2052 int start_pos = gtk_text_iter_get_offset(&start);
2054 if (!compose->account->sig_sep)
2057 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2058 start_pos+strlen(compose->account->sig_sep));
2060 /* check sig separator */
2061 str = gtk_text_iter_get_text(&start, &end_iter);
2062 if (!strcmp(str, compose->account->sig_sep)) {
2064 /* check end of line (\n) */
2065 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2066 start_pos+strlen(compose->account->sig_sep));
2067 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2068 start_pos+strlen(compose->account->sig_sep)+1);
2069 tmp = gtk_text_iter_get_text(&start, &end_iter);
2070 if (!strcmp(tmp,"\n")) {
2082 static void compose_colorize_signature(Compose *compose)
2084 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2086 GtkTextIter end_iter;
2087 gtk_text_buffer_get_start_iter(buffer, &iter);
2088 while (gtk_text_iter_forward_line(&iter))
2089 if (compose_is_sig_separator(compose, buffer, &iter)) {
2090 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2091 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2095 #define BLOCK_WRAP() { \
2096 prev_autowrap = compose->autowrap; \
2097 buffer = gtk_text_view_get_buffer( \
2098 GTK_TEXT_VIEW(compose->text)); \
2099 compose->autowrap = FALSE; \
2101 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2102 G_CALLBACK(compose_changed_cb), \
2104 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2105 G_CALLBACK(text_inserted), \
2108 #define UNBLOCK_WRAP() { \
2109 compose->autowrap = prev_autowrap; \
2110 if (compose->autowrap) { \
2111 gint old = compose->draft_timeout_tag; \
2112 compose->draft_timeout_tag = -2; \
2113 compose_wrap_all(compose); \
2114 compose->draft_timeout_tag = old; \
2117 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2118 G_CALLBACK(compose_changed_cb), \
2120 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2121 G_CALLBACK(text_inserted), \
2125 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2127 Compose *compose = NULL;
2128 PrefsAccount *account = NULL;
2129 GtkTextView *textview;
2130 GtkTextBuffer *textbuf;
2134 gchar buf[BUFFSIZE];
2135 gboolean use_signing = FALSE;
2136 gboolean use_encryption = FALSE;
2137 gchar *privacy_system = NULL;
2138 int priority = PRIORITY_NORMAL;
2139 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2140 gboolean autowrap = prefs_common.autowrap;
2141 gboolean autoindent = prefs_common.auto_indent;
2142 HeaderEntry *manual_headers = NULL;
2144 cm_return_val_if_fail(msginfo != NULL, NULL);
2145 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2147 if (compose_put_existing_to_front(msginfo)) {
2151 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2152 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2153 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2154 gchar queueheader_buf[BUFFSIZE];
2157 /* Select Account from queue headers */
2158 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2159 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2160 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2161 account = account_find_from_id(id);
2163 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2164 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2165 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2166 account = account_find_from_id(id);
2168 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2169 sizeof(queueheader_buf), "NAID:")) {
2170 id = atoi(&queueheader_buf[strlen("NAID:")]);
2171 account = account_find_from_id(id);
2173 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2174 sizeof(queueheader_buf), "MAID:")) {
2175 id = atoi(&queueheader_buf[strlen("MAID:")]);
2176 account = account_find_from_id(id);
2178 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2179 sizeof(queueheader_buf), "S:")) {
2180 account = account_find_from_address(queueheader_buf, FALSE);
2182 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2184 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2185 use_signing = param;
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2190 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2191 use_signing = param;
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2196 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2197 use_encryption = param;
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2201 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2202 use_encryption = param;
2204 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2205 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2206 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2209 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2210 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2211 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2214 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2215 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2216 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2218 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2219 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2220 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2222 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2223 sizeof(queueheader_buf), "X-Priority: ")) {
2224 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2227 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2228 sizeof(queueheader_buf), "RMID:")) {
2229 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2230 if (tokens[0] && tokens[1] && tokens[2]) {
2231 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2232 if (orig_item != NULL) {
2233 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2238 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2239 sizeof(queueheader_buf), "FMID:")) {
2240 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2241 if (tokens[0] && tokens[1] && tokens[2]) {
2242 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2243 if (orig_item != NULL) {
2244 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2249 /* Get manual headers */
2250 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2251 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2252 if (*listmh != '\0') {
2253 debug_print("Got manual headers: %s\n", listmh);
2254 manual_headers = procheader_entries_from_str(listmh);
2259 account = msginfo->folder->folder->account;
2262 if (!account && prefs_common.reedit_account_autosel) {
2263 gchar from[BUFFSIZE];
2264 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2265 extract_address(from);
2266 account = account_find_from_address(from, FALSE);
2270 account = cur_account;
2272 cm_return_val_if_fail(account != NULL, NULL);
2274 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2276 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2277 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2278 compose->autowrap = autowrap;
2279 compose->replyinfo = replyinfo;
2280 compose->fwdinfo = fwdinfo;
2282 compose->updating = TRUE;
2283 compose->priority = priority;
2285 if (privacy_system != NULL) {
2286 compose->privacy_system = privacy_system;
2287 compose_use_signing(compose, use_signing);
2288 compose_use_encryption(compose, use_encryption);
2289 compose_update_privacy_system_menu_item(compose, FALSE);
2291 activate_privacy_system(compose, account, FALSE);
2294 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2296 compose_extract_original_charset(compose);
2298 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2299 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2300 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2301 gchar queueheader_buf[BUFFSIZE];
2303 /* Set message save folder */
2304 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2305 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2306 compose_set_save_to(compose, &queueheader_buf[4]);
2308 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2309 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2311 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2316 if (compose_parse_header(compose, msginfo) < 0) {
2317 compose->updating = FALSE;
2318 compose_destroy(compose);
2321 compose_reedit_set_entry(compose, msginfo);
2323 textview = GTK_TEXT_VIEW(compose->text);
2324 textbuf = gtk_text_view_get_buffer(textview);
2325 compose_create_tags(textview, compose);
2327 mark = gtk_text_buffer_get_insert(textbuf);
2328 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2330 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2331 G_CALLBACK(compose_changed_cb),
2334 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2335 fp = procmime_get_first_encrypted_text_content(msginfo);
2337 compose_force_encryption(compose, account, TRUE, NULL);
2340 fp = procmime_get_first_text_content(msginfo);
2343 g_warning("Can't get text part\n");
2347 gboolean prev_autowrap;
2348 GtkTextBuffer *buffer;
2350 while (fgets(buf, sizeof(buf), fp) != NULL) {
2352 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2358 compose_attach_parts(compose, msginfo);
2360 compose_colorize_signature(compose);
2362 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2363 G_CALLBACK(compose_changed_cb),
2366 if (manual_headers != NULL) {
2367 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2368 procheader_entries_free(manual_headers);
2369 compose->updating = FALSE;
2370 compose_destroy(compose);
2373 procheader_entries_free(manual_headers);
2376 gtk_widget_grab_focus(compose->text);
2378 if (prefs_common.auto_exteditor) {
2379 compose_exec_ext_editor(compose);
2381 compose->modified = FALSE;
2382 compose_set_title(compose);
2384 compose->updating = FALSE;
2385 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2386 SCROLL_TO_CURSOR(compose);
2388 if (compose->deferred_destroy) {
2389 compose_destroy(compose);
2393 compose->sig_str = account_get_signature_str(compose->account);
2395 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2400 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2407 cm_return_val_if_fail(msginfo != NULL, NULL);
2410 account = account_get_reply_account(msginfo,
2411 prefs_common.reply_account_autosel);
2412 cm_return_val_if_fail(account != NULL, NULL);
2414 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2416 compose->updating = TRUE;
2418 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2419 compose->replyinfo = NULL;
2420 compose->fwdinfo = NULL;
2422 compose_show_first_last_header(compose, TRUE);
2424 gtk_widget_grab_focus(compose->header_last->entry);
2426 filename = procmsg_get_message_file(msginfo);
2428 if (filename == NULL) {
2429 compose->updating = FALSE;
2430 compose_destroy(compose);
2435 compose->redirect_filename = filename;
2437 /* Set save folder */
2438 item = msginfo->folder;
2439 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2440 gchar *folderidentifier;
2442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2443 folderidentifier = folder_item_get_identifier(item);
2444 compose_set_save_to(compose, folderidentifier);
2445 g_free(folderidentifier);
2448 compose_attach_parts(compose, msginfo);
2450 if (msginfo->subject)
2451 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2453 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2455 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2456 _("The body of the \"Redirect\" template has an error at line %d."));
2457 quote_fmt_reset_vartable();
2458 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2460 compose_colorize_signature(compose);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2465 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2474 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2476 if (compose->toolbar->draft_btn)
2477 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2478 if (compose->toolbar->insert_btn)
2479 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2480 if (compose->toolbar->attach_btn)
2481 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2482 if (compose->toolbar->sig_btn)
2483 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2484 if (compose->toolbar->exteditor_btn)
2485 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2486 if (compose->toolbar->linewrap_current_btn)
2487 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2488 if (compose->toolbar->linewrap_all_btn)
2489 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2491 compose->modified = FALSE;
2492 compose_set_title(compose);
2493 compose->updating = FALSE;
2494 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2495 SCROLL_TO_CURSOR(compose);
2497 if (compose->deferred_destroy) {
2498 compose_destroy(compose);
2502 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2507 GList *compose_get_compose_list(void)
2509 return compose_list;
2512 void compose_entry_append(Compose *compose, const gchar *address,
2513 ComposeEntryType type, ComposePrefType pref_type)
2515 const gchar *header;
2517 gboolean in_quote = FALSE;
2518 if (!address || *address == '\0') return;
2525 header = N_("Bcc:");
2527 case COMPOSE_REPLYTO:
2528 header = N_("Reply-To:");
2530 case COMPOSE_NEWSGROUPS:
2531 header = N_("Newsgroups:");
2533 case COMPOSE_FOLLOWUPTO:
2534 header = N_( "Followup-To:");
2536 case COMPOSE_INREPLYTO:
2537 header = N_( "In-Reply-To:");
2544 header = prefs_common_translated_header_name(header);
2546 cur = begin = (gchar *)address;
2548 /* we separate the line by commas, but not if we're inside a quoted
2550 while (*cur != '\0') {
2552 in_quote = !in_quote;
2553 if (*cur == ',' && !in_quote) {
2554 gchar *tmp = g_strdup(begin);
2556 tmp[cur-begin]='\0';
2559 while (*tmp == ' ' || *tmp == '\t')
2561 compose_add_header_entry(compose, header, tmp, pref_type);
2568 gchar *tmp = g_strdup(begin);
2570 tmp[cur-begin]='\0';
2571 while (*tmp == ' ' || *tmp == '\t')
2573 compose_add_header_entry(compose, header, tmp, pref_type);
2578 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2580 #if !GTK_CHECK_VERSION(3, 0, 0)
2581 static GdkColor yellow;
2582 static GdkColor black;
2583 static gboolean yellow_initialised = FALSE;
2585 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2586 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2591 #if !GTK_CHECK_VERSION(3, 0, 0)
2592 if (!yellow_initialised) {
2593 gdk_color_parse("#f5f6be", &yellow);
2594 gdk_color_parse("#000000", &black);
2595 yellow_initialised = gdk_colormap_alloc_color(
2596 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2597 yellow_initialised &= gdk_colormap_alloc_color(
2598 gdk_colormap_get_system(), &black, FALSE, TRUE);
2602 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2603 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2604 if (gtk_entry_get_text(entry) &&
2605 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2606 #if !GTK_CHECK_VERSION(3, 0, 0)
2607 if (yellow_initialised) {
2609 gtk_widget_modify_base(
2610 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2611 GTK_STATE_NORMAL, &yellow);
2612 gtk_widget_modify_text(
2613 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2614 GTK_STATE_NORMAL, &black);
2615 #if !GTK_CHECK_VERSION(3, 0, 0)
2622 void compose_toolbar_cb(gint action, gpointer data)
2624 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2625 Compose *compose = (Compose*)toolbar_item->parent;
2627 cm_return_if_fail(compose != NULL);
2631 compose_send_cb(NULL, compose);
2634 compose_send_later_cb(NULL, compose);
2637 compose_draft(compose, COMPOSE_QUIT_EDITING);
2640 compose_insert_file_cb(NULL, compose);
2643 compose_attach_cb(NULL, compose);
2646 compose_insert_sig(compose, FALSE);
2649 compose_ext_editor_cb(NULL, compose);
2651 case A_LINEWRAP_CURRENT:
2652 compose_beautify_paragraph(compose, NULL, TRUE);
2654 case A_LINEWRAP_ALL:
2655 compose_wrap_all_full(compose, TRUE);
2658 compose_address_cb(NULL, compose);
2661 case A_CHECK_SPELLING:
2662 compose_check_all(NULL, compose);
2670 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2675 gchar *subject = NULL;
2679 gchar **attach = NULL;
2680 gchar *inreplyto = NULL;
2681 MailField mfield = NO_FIELD_PRESENT;
2683 /* get mailto parts but skip from */
2684 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2687 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2688 mfield = TO_FIELD_PRESENT;
2691 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2693 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2695 if (!g_utf8_validate (subject, -1, NULL)) {
2696 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2697 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2700 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2702 mfield = SUBJECT_FIELD_PRESENT;
2705 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2706 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2709 gboolean prev_autowrap = compose->autowrap;
2711 compose->autowrap = FALSE;
2713 mark = gtk_text_buffer_get_insert(buffer);
2714 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2716 if (!g_utf8_validate (body, -1, NULL)) {
2717 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2718 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2721 gtk_text_buffer_insert(buffer, &iter, body, -1);
2723 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2725 compose->autowrap = prev_autowrap;
2726 if (compose->autowrap)
2727 compose_wrap_all(compose);
2728 mfield = BODY_FIELD_PRESENT;
2732 gint i = 0, att = 0;
2733 gchar *warn_files = NULL;
2734 while (attach[i] != NULL) {
2735 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2736 if (utf8_filename) {
2737 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2738 gchar *tmp = g_strdup_printf("%s%s\n",
2739 warn_files?warn_files:"",
2745 g_free(utf8_filename);
2747 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2752 alertpanel_notice(ngettext(
2753 "The following file has been attached: \n%s",
2754 "The following files have been attached: \n%s", att), warn_files);
2759 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2772 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2774 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2775 {"Cc:", NULL, TRUE},
2776 {"References:", NULL, FALSE},
2777 {"Bcc:", NULL, TRUE},
2778 {"Newsgroups:", NULL, TRUE},
2779 {"Followup-To:", NULL, TRUE},
2780 {"List-Post:", NULL, FALSE},
2781 {"X-Priority:", NULL, FALSE},
2782 {NULL, NULL, FALSE}};
2798 cm_return_val_if_fail(msginfo != NULL, -1);
2800 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2801 procheader_get_header_fields(fp, hentry);
2804 if (hentry[H_REPLY_TO].body != NULL) {
2805 if (hentry[H_REPLY_TO].body[0] != '\0') {
2807 conv_unmime_header(hentry[H_REPLY_TO].body,
2810 g_free(hentry[H_REPLY_TO].body);
2811 hentry[H_REPLY_TO].body = NULL;
2813 if (hentry[H_CC].body != NULL) {
2814 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2815 g_free(hentry[H_CC].body);
2816 hentry[H_CC].body = NULL;
2818 if (hentry[H_REFERENCES].body != NULL) {
2819 if (compose->mode == COMPOSE_REEDIT)
2820 compose->references = hentry[H_REFERENCES].body;
2822 compose->references = compose_parse_references
2823 (hentry[H_REFERENCES].body, msginfo->msgid);
2824 g_free(hentry[H_REFERENCES].body);
2826 hentry[H_REFERENCES].body = NULL;
2828 if (hentry[H_BCC].body != NULL) {
2829 if (compose->mode == COMPOSE_REEDIT)
2831 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2832 g_free(hentry[H_BCC].body);
2833 hentry[H_BCC].body = NULL;
2835 if (hentry[H_NEWSGROUPS].body != NULL) {
2836 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2837 hentry[H_NEWSGROUPS].body = NULL;
2839 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2840 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2841 compose->followup_to =
2842 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2845 g_free(hentry[H_FOLLOWUP_TO].body);
2846 hentry[H_FOLLOWUP_TO].body = NULL;
2848 if (hentry[H_LIST_POST].body != NULL) {
2849 gchar *to = NULL, *start = NULL;
2851 extract_address(hentry[H_LIST_POST].body);
2852 if (hentry[H_LIST_POST].body[0] != '\0') {
2853 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2855 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2856 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2859 g_free(compose->ml_post);
2860 compose->ml_post = to;
2863 g_free(hentry[H_LIST_POST].body);
2864 hentry[H_LIST_POST].body = NULL;
2867 /* CLAWS - X-Priority */
2868 if (compose->mode == COMPOSE_REEDIT)
2869 if (hentry[H_X_PRIORITY].body != NULL) {
2872 priority = atoi(hentry[H_X_PRIORITY].body);
2873 g_free(hentry[H_X_PRIORITY].body);
2875 hentry[H_X_PRIORITY].body = NULL;
2877 if (priority < PRIORITY_HIGHEST ||
2878 priority > PRIORITY_LOWEST)
2879 priority = PRIORITY_NORMAL;
2881 compose->priority = priority;
2884 if (compose->mode == COMPOSE_REEDIT) {
2885 if (msginfo->inreplyto && *msginfo->inreplyto)
2886 compose->inreplyto = g_strdup(msginfo->inreplyto);
2890 if (msginfo->msgid && *msginfo->msgid)
2891 compose->inreplyto = g_strdup(msginfo->msgid);
2893 if (!compose->references) {
2894 if (msginfo->msgid && *msginfo->msgid) {
2895 if (msginfo->inreplyto && *msginfo->inreplyto)
2896 compose->references =
2897 g_strdup_printf("<%s>\n\t<%s>",
2901 compose->references =
2902 g_strconcat("<", msginfo->msgid, ">",
2904 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2905 compose->references =
2906 g_strconcat("<", msginfo->inreplyto, ">",
2914 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2919 cm_return_val_if_fail(msginfo != NULL, -1);
2921 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2922 procheader_get_header_fields(fp, entries);
2926 while (he != NULL && he->name != NULL) {
2928 GtkListStore *model = NULL;
2930 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2931 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2932 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2933 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2934 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2941 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2943 GSList *ref_id_list, *cur;
2947 ref_id_list = references_list_append(NULL, ref);
2948 if (!ref_id_list) return NULL;
2949 if (msgid && *msgid)
2950 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2955 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2956 /* "<" + Message-ID + ">" + CR+LF+TAB */
2957 len += strlen((gchar *)cur->data) + 5;
2959 if (len > MAX_REFERENCES_LEN) {
2960 /* remove second message-ID */
2961 if (ref_id_list && ref_id_list->next &&
2962 ref_id_list->next->next) {
2963 g_free(ref_id_list->next->data);
2964 ref_id_list = g_slist_remove
2965 (ref_id_list, ref_id_list->next->data);
2967 slist_free_strings_full(ref_id_list);
2974 new_ref = g_string_new("");
2975 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2976 if (new_ref->len > 0)
2977 g_string_append(new_ref, "\n\t");
2978 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2981 slist_free_strings_full(ref_id_list);
2983 new_ref_str = new_ref->str;
2984 g_string_free(new_ref, FALSE);
2989 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2990 const gchar *fmt, const gchar *qmark,
2991 const gchar *body, gboolean rewrap,
2992 gboolean need_unescape,
2993 const gchar *err_msg)
2995 MsgInfo* dummyinfo = NULL;
2996 gchar *quote_str = NULL;
2998 gboolean prev_autowrap;
2999 const gchar *trimmed_body = body;
3000 gint cursor_pos = -1;
3001 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3002 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3007 SIGNAL_BLOCK(buffer);
3010 dummyinfo = compose_msginfo_new_from_compose(compose);
3011 msginfo = dummyinfo;
3014 if (qmark != NULL) {
3016 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3017 compose->gtkaspell);
3019 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3021 quote_fmt_scan_string(qmark);
3024 buf = quote_fmt_get_buffer();
3026 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3028 Xstrdup_a(quote_str, buf, goto error)
3031 if (fmt && *fmt != '\0') {
3034 while (*trimmed_body == '\n')
3038 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3039 compose->gtkaspell);
3041 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3043 if (need_unescape) {
3046 /* decode \-escape sequences in the internal representation of the quote format */
3047 tmp = g_malloc(strlen(fmt)+1);
3048 pref_get_unescaped_pref(tmp, fmt);
3049 quote_fmt_scan_string(tmp);
3053 quote_fmt_scan_string(fmt);
3057 buf = quote_fmt_get_buffer();
3059 gint line = quote_fmt_get_line();
3060 alertpanel_error(err_msg, line);
3066 prev_autowrap = compose->autowrap;
3067 compose->autowrap = FALSE;
3069 mark = gtk_text_buffer_get_insert(buffer);
3070 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3071 if (g_utf8_validate(buf, -1, NULL)) {
3072 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3074 gchar *tmpout = NULL;
3075 tmpout = conv_codeset_strdup
3076 (buf, conv_get_locale_charset_str_no_utf8(),
3078 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3080 tmpout = g_malloc(strlen(buf)*2+1);
3081 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3083 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3087 cursor_pos = quote_fmt_get_cursor_pos();
3088 if (cursor_pos == -1)
3089 cursor_pos = gtk_text_iter_get_offset(&iter);
3090 compose->set_cursor_pos = cursor_pos;
3092 gtk_text_buffer_get_start_iter(buffer, &iter);
3093 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3094 gtk_text_buffer_place_cursor(buffer, &iter);
3096 compose->autowrap = prev_autowrap;
3097 if (compose->autowrap && rewrap)
3098 compose_wrap_all(compose);
3105 SIGNAL_UNBLOCK(buffer);
3107 procmsg_msginfo_free( dummyinfo );
3112 /* if ml_post is of type addr@host and from is of type
3113 * addr-anything@host, return TRUE
3115 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3117 gchar *left_ml = NULL;
3118 gchar *right_ml = NULL;
3119 gchar *left_from = NULL;
3120 gchar *right_from = NULL;
3121 gboolean result = FALSE;
3123 if (!ml_post || !from)
3126 left_ml = g_strdup(ml_post);
3127 if (strstr(left_ml, "@")) {
3128 right_ml = strstr(left_ml, "@")+1;
3129 *(strstr(left_ml, "@")) = '\0';
3132 left_from = g_strdup(from);
3133 if (strstr(left_from, "@")) {
3134 right_from = strstr(left_from, "@")+1;
3135 *(strstr(left_from, "@")) = '\0';
3138 if (left_ml && left_from && right_ml && right_from
3139 && !strncmp(left_from, left_ml, strlen(left_ml))
3140 && !strcmp(right_from, right_ml)) {
3149 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3150 gboolean respect_default_to)
3154 if (!folder || !folder->prefs)
3157 if (respect_default_to && folder->prefs->enable_default_to) {
3158 compose_entry_append(compose, folder->prefs->default_to,
3159 COMPOSE_TO, PREF_FOLDER);
3160 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3162 if (folder->prefs->enable_default_cc)
3163 compose_entry_append(compose, folder->prefs->default_cc,
3164 COMPOSE_CC, PREF_FOLDER);
3165 if (folder->prefs->enable_default_bcc)
3166 compose_entry_append(compose, folder->prefs->default_bcc,
3167 COMPOSE_BCC, PREF_FOLDER);
3168 if (folder->prefs->enable_default_replyto)
3169 compose_entry_append(compose, folder->prefs->default_replyto,
3170 COMPOSE_REPLYTO, PREF_FOLDER);
3173 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3178 if (!compose || !msginfo)
3181 if (msginfo->subject && *msginfo->subject) {
3182 buf = p = g_strdup(msginfo->subject);
3183 p += subject_get_prefix_length(p);
3184 memmove(buf, p, strlen(p) + 1);
3186 buf2 = g_strdup_printf("Re: %s", buf);