2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void account_activated (GtkComboBox *optmenu,
359 static void attach_selected (GtkTreeView *tree_view,
360 GtkTreePath *tree_path,
361 GtkTreeViewColumn *column,
363 static gboolean attach_button_pressed (GtkWidget *widget,
364 GdkEventButton *event,
366 static gboolean attach_key_pressed (GtkWidget *widget,
369 static void compose_send_cb (GtkAction *action, gpointer data);
370 static void compose_send_later_cb (GtkAction *action, gpointer data);
372 static void compose_save_cb (GtkAction *action,
375 static void compose_attach_cb (GtkAction *action,
377 static void compose_insert_file_cb (GtkAction *action,
379 static void compose_insert_sig_cb (GtkAction *action,
382 static void compose_close_cb (GtkAction *action,
385 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
387 static void compose_address_cb (GtkAction *action,
389 static void about_show_cb (GtkAction *action,
391 static void compose_template_activate_cb(GtkWidget *widget,
394 static void compose_ext_editor_cb (GtkAction *action,
397 static gint compose_delete_cb (GtkWidget *widget,
401 static void compose_undo_cb (GtkAction *action,
403 static void compose_redo_cb (GtkAction *action,
405 static void compose_cut_cb (GtkAction *action,
407 static void compose_copy_cb (GtkAction *action,
409 static void compose_paste_cb (GtkAction *action,
411 static void compose_paste_as_quote_cb (GtkAction *action,
413 static void compose_paste_no_wrap_cb (GtkAction *action,
415 static void compose_paste_wrap_cb (GtkAction *action,
417 static void compose_allsel_cb (GtkAction *action,
420 static void compose_advanced_action_cb (GtkAction *action,
423 static void compose_grab_focus_cb (GtkWidget *widget,
426 static void compose_changed_cb (GtkTextBuffer *textbuf,
429 static void compose_wrap_cb (GtkAction *action,
431 static void compose_wrap_all_cb (GtkAction *action,
433 static void compose_find_cb (GtkAction *action,
435 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
437 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
440 static void compose_toggle_ruler_cb (GtkToggleAction *action,
442 static void compose_toggle_sign_cb (GtkToggleAction *action,
444 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
446 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
447 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
448 static void activate_privacy_system (Compose *compose,
449 PrefsAccount *account,
451 static void compose_use_signing(Compose *compose, gboolean use_signing);
452 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
453 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
455 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
457 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
458 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
459 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
461 static void compose_attach_drag_received_cb (GtkWidget *widget,
462 GdkDragContext *drag_context,
465 GtkSelectionData *data,
469 static void compose_insert_drag_received_cb (GtkWidget *widget,
470 GdkDragContext *drag_context,
473 GtkSelectionData *data,
477 static void compose_header_drag_received_cb (GtkWidget *widget,
478 GdkDragContext *drag_context,
481 GtkSelectionData *data,
486 static gboolean compose_drag_drop (GtkWidget *widget,
487 GdkDragContext *drag_context,
489 guint time, gpointer user_data);
491 static void text_inserted (GtkTextBuffer *buffer,
496 static Compose *compose_generic_reply(MsgInfo *msginfo,
497 ComposeQuoteMode quote_mode,
501 gboolean followup_and_reply_to,
504 static void compose_headerentry_changed_cb (GtkWidget *entry,
505 ComposeHeaderEntry *headerentry);
506 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
508 ComposeHeaderEntry *headerentry);
509 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
510 ComposeHeaderEntry *headerentry);
512 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
514 static void compose_allow_user_actions (Compose *compose, gboolean allow);
516 static void compose_nothing_cb (GtkAction *action, gpointer data)
522 static void compose_check_all (GtkAction *action, gpointer data);
523 static void compose_highlight_all (GtkAction *action, gpointer data);
524 static void compose_check_backwards (GtkAction *action, gpointer data);
525 static void compose_check_forwards_go (GtkAction *action, gpointer data);
528 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
530 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
533 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
534 FolderItem *folder_item);
536 static void compose_attach_update_label(Compose *compose);
537 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
538 gboolean respect_default_to);
540 static GtkActionEntry compose_popup_entries[] =
542 {"Compose", NULL, "Compose" },
543 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
544 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
545 {"Compose/---", NULL, "---", NULL, NULL, NULL },
546 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
549 static GtkActionEntry compose_entries[] =
551 {"Menu", NULL, "Menu" },
553 {"Message", NULL, N_("_Message") },
554 {"Edit", NULL, N_("_Edit") },
556 {"Spelling", NULL, N_("_Spelling") },
558 {"Options", NULL, N_("_Options") },
559 {"Tools", NULL, N_("_Tools") },
560 {"Help", NULL, N_("_Help") },
562 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
563 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
564 {"Message/---", NULL, "---" },
566 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
567 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
568 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
569 /* {"Message/---", NULL, "---" }, */
570 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
571 /* {"Message/---", NULL, "---" }, */
572 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
575 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
576 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
577 {"Edit/---", NULL, "---" },
579 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
580 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
581 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
583 {"Edit/SpecialPaste", NULL, N_("Special paste") },
584 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
585 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
586 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
588 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
590 {"Edit/Advanced", NULL, N_("A_dvanced") },
591 {"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*/
592 {"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*/
593 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
594 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
595 {"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*/
596 {"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*/
597 {"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*/
598 {"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*/
599 {"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*/
600 {"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*/
601 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
602 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
603 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
604 {"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*/
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
611 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
612 /* {"Edit/---", NULL, "---" }, */
613 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
616 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
617 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
618 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
619 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
621 {"Spelling/---", NULL, "---" },
622 {"Spelling/Options", NULL, N_("_Options") },
627 {"Options/ReplyMode", NULL, N_("Reply _mode") },
628 {"Options/---", NULL, "---" },
629 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
630 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
632 /* {"Options/---", NULL, "---" }, */
634 {"Options/Priority", NULL, N_("_Priority") },
636 {"Options/Encoding", NULL, N_("Character _encoding") },
637 {"Options/Encoding/---", NULL, "---" },
638 #define ENC_ACTION(cs_char,c_char,string) \
639 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
641 {"Options/Encoding/Western", NULL, N_("Western European") },
642 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
643 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
644 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
645 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
646 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
647 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
648 {"Options/Encoding/Korean", NULL, N_("Korean") },
649 {"Options/Encoding/Thai", NULL, N_("Thai") },
652 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
654 {"Tools/Template", NULL, N_("_Template") },
655 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 {"Tools/Actions", NULL, N_("Actio_ns") },
657 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
660 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
663 static GtkToggleActionEntry compose_toggle_entries[] =
665 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
666 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
667 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
668 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
669 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
670 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
671 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
674 static GtkRadioActionEntry compose_radio_rm_entries[] =
676 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
677 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
682 static GtkRadioActionEntry compose_radio_prio_entries[] =
684 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
691 static GtkRadioActionEntry compose_radio_enc_entries[] =
693 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
727 static GtkTargetEntry compose_mime_types[] =
729 {"text/uri-list", 0, 0},
730 {"UTF8_STRING", 0, 0},
734 static gboolean compose_put_existing_to_front(MsgInfo *info)
736 GList *compose_list = compose_get_compose_list();
740 for (elem = compose_list; elem != NULL && elem->data != NULL;
742 Compose *c = (Compose*)elem->data;
744 if (!c->targetinfo || !c->targetinfo->msgid ||
748 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
749 gtkut_window_popup(c->window);
757 static GdkColor quote_color1 =
758 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor quote_color2 =
760 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor quote_color3 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_bgcolor1 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
766 static GdkColor quote_bgcolor2 =
767 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor quote_bgcolor3 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
771 static GdkColor signature_color = {
778 static GdkColor uri_color = {
785 static void compose_create_tags(GtkTextView *text, Compose *compose)
787 GtkTextBuffer *buffer;
788 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
789 #if !GTK_CHECK_VERSION(2, 24, 0)
796 buffer = gtk_text_view_get_buffer(text);
798 if (prefs_common.enable_color) {
799 /* grab the quote colors, converting from an int to a GdkColor */
800 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
806 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
810 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
812 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
814 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
817 signature_color = quote_color1 = quote_color2 = quote_color3 =
818 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
821 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
822 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
823 "foreground-gdk", "e_color1,
824 "paragraph-background-gdk", "e_bgcolor1,
826 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
827 "foreground-gdk", "e_color2,
828 "paragraph-background-gdk", "e_bgcolor2,
830 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
831 "foreground-gdk", "e_color3,
832 "paragraph-background-gdk", "e_bgcolor3,
835 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
836 "foreground-gdk", "e_color1,
838 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
839 "foreground-gdk", "e_color2,
841 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
842 "foreground-gdk", "e_color3,
846 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
847 "foreground-gdk", &signature_color,
850 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
851 "foreground-gdk", &uri_color,
853 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
854 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
856 color[0] = quote_color1;
857 color[1] = quote_color2;
858 color[2] = quote_color3;
859 color[3] = quote_bgcolor1;
860 color[4] = quote_bgcolor2;
861 color[5] = quote_bgcolor3;
862 color[6] = signature_color;
863 color[7] = uri_color;
864 #if !GTK_CHECK_VERSION(2, 24, 0)
865 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
866 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
868 for (i = 0; i < 8; i++) {
869 if (success[i] == FALSE) {
872 g_warning("Compose: color allocation failed.\n");
873 style = gtk_widget_get_style(GTK_WIDGET(text));
874 quote_color1 = quote_color2 = quote_color3 =
875 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
876 signature_color = uri_color = black;
882 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
883 GPtrArray *attach_files)
885 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
888 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
890 return compose_generic_new(account, mailto, item, NULL, NULL);
893 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
895 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
898 #define SCROLL_TO_CURSOR(compose) { \
899 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
900 gtk_text_view_get_buffer( \
901 GTK_TEXT_VIEW(compose->text))); \
902 gtk_text_view_scroll_mark_onscreen( \
903 GTK_TEXT_VIEW(compose->text), \
907 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
910 if (folderidentifier) {
911 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
912 prefs_common.compose_save_to_history = add_history(
913 prefs_common.compose_save_to_history, folderidentifier);
914 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
915 prefs_common.compose_save_to_history);
918 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
919 if (folderidentifier)
920 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
922 gtk_entry_set_text(GTK_ENTRY(entry), "");
925 static gchar *compose_get_save_to(Compose *compose)
928 gchar *result = NULL;
929 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
930 result = gtk_editable_get_chars(entry, 0, -1);
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, result);
936 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
937 prefs_common.compose_save_to_history);
942 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
943 GPtrArray *attach_files, GList *listAddress )
946 GtkTextView *textview;
947 GtkTextBuffer *textbuf;
949 const gchar *subject_format = NULL;
950 const gchar *body_format = NULL;
951 gchar *mailto_from = NULL;
952 PrefsAccount *mailto_account = NULL;
953 MsgInfo* dummyinfo = NULL;
954 gint cursor_pos = -1;
955 MailField mfield = NO_FIELD_PRESENT;
959 /* check if mailto defines a from */
960 if (mailto && *mailto != '\0') {
961 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
962 /* mailto defines a from, check if we can get account prefs from it,
963 if not, the account prefs will be guessed using other ways, but we'll keep
966 mailto_account = account_find_from_address(mailto_from, TRUE);
968 account = mailto_account;
971 /* if no account prefs set from mailto, set if from folder prefs (if any) */
972 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
973 account = account_find_from_id(item->prefs->default_account);
975 /* if no account prefs set, fallback to the current one */
976 if (!account) account = cur_account;
977 cm_return_val_if_fail(account != NULL, NULL);
979 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
981 /* override from name if mailto asked for it */
983 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
986 /* override from name according to folder properties */
987 if (item && item->prefs &&
988 item->prefs->compose_with_format &&
989 item->prefs->compose_override_from_format &&
990 *item->prefs->compose_override_from_format != '\0') {
995 dummyinfo = compose_msginfo_new_from_compose(compose);
997 /* decode \-escape sequences in the internal representation of the quote format */
998 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
999 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1002 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1003 compose->gtkaspell);
1005 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1007 quote_fmt_scan_string(tmp);
1010 buf = quote_fmt_get_buffer();
1012 alertpanel_error(_("New message From format error."));
1014 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1015 quote_fmt_reset_vartable();
1020 compose->replyinfo = NULL;
1021 compose->fwdinfo = NULL;
1023 textview = GTK_TEXT_VIEW(compose->text);
1024 textbuf = gtk_text_view_get_buffer(textview);
1025 compose_create_tags(textview, compose);
1027 undo_block(compose->undostruct);
1029 compose_set_dictionaries_from_folder_prefs(compose, item);
1032 if (account->auto_sig)
1033 compose_insert_sig(compose, FALSE);
1034 gtk_text_buffer_get_start_iter(textbuf, &iter);
1035 gtk_text_buffer_place_cursor(textbuf, &iter);
1037 if (account->protocol != A_NNTP) {
1038 if (mailto && *mailto != '\0') {
1039 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1042 compose_set_folder_prefs(compose, item, TRUE);
1044 if (item && item->ret_rcpt) {
1045 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1048 if (mailto && *mailto != '\0') {
1049 if (!strchr(mailto, '@'))
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1052 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1053 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1054 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1055 mfield = TO_FIELD_PRESENT;
1058 * CLAWS: just don't allow return receipt request, even if the user
1059 * may want to send an email. simple but foolproof.
1061 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1063 compose_add_field_list( compose, listAddress );
1065 if (item && item->prefs && item->prefs->compose_with_format) {
1066 subject_format = item->prefs->compose_subject_format;
1067 body_format = item->prefs->compose_body_format;
1068 } else if (account->compose_with_format) {
1069 subject_format = account->compose_subject_format;
1070 body_format = account->compose_body_format;
1071 } else if (prefs_common.compose_with_format) {
1072 subject_format = prefs_common.compose_subject_format;
1073 body_format = prefs_common.compose_body_format;
1076 if (subject_format || body_format) {
1079 && *subject_format != '\0' )
1081 gchar *subject = NULL;
1086 dummyinfo = compose_msginfo_new_from_compose(compose);
1088 /* decode \-escape sequences in the internal representation of the quote format */
1089 tmp = g_malloc(strlen(subject_format)+1);
1090 pref_get_unescaped_pref(tmp, subject_format);
1092 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1094 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1095 compose->gtkaspell);
1097 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1099 quote_fmt_scan_string(tmp);
1102 buf = quote_fmt_get_buffer();
1104 alertpanel_error(_("New message subject format error."));
1106 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1107 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1108 quote_fmt_reset_vartable();
1112 mfield = SUBJECT_FIELD_PRESENT;
1116 && *body_format != '\0' )
1119 GtkTextBuffer *buffer;
1120 GtkTextIter start, end;
1124 dummyinfo = compose_msginfo_new_from_compose(compose);
1126 text = GTK_TEXT_VIEW(compose->text);
1127 buffer = gtk_text_view_get_buffer(text);
1128 gtk_text_buffer_get_start_iter(buffer, &start);
1129 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1130 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1132 compose_quote_fmt(compose, dummyinfo,
1134 NULL, tmp, FALSE, TRUE,
1135 _("The body of the \"New message\" template has an error at line %d."));
1136 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1137 quote_fmt_reset_vartable();
1141 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1142 gtkaspell_highlight_all(compose->gtkaspell);
1144 mfield = BODY_FIELD_PRESENT;
1148 procmsg_msginfo_free( dummyinfo );
1154 for (i = 0; i < attach_files->len; i++) {
1155 file = g_ptr_array_index(attach_files, i);
1156 compose_attach_append(compose, file, file, NULL, NULL);
1160 compose_show_first_last_header(compose, TRUE);
1162 /* Set save folder */
1163 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1164 gchar *folderidentifier;
1166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1167 folderidentifier = folder_item_get_identifier(item);
1168 compose_set_save_to(compose, folderidentifier);
1169 g_free(folderidentifier);
1172 /* Place cursor according to provided input (mfield) */
1174 case NO_FIELD_PRESENT:
1175 if (compose->header_last)
1176 gtk_widget_grab_focus(compose->header_last->entry);
1178 case TO_FIELD_PRESENT:
1179 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1181 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1184 gtk_widget_grab_focus(compose->subject_entry);
1186 case SUBJECT_FIELD_PRESENT:
1187 textview = GTK_TEXT_VIEW(compose->text);
1190 textbuf = gtk_text_view_get_buffer(textview);
1193 mark = gtk_text_buffer_get_insert(textbuf);
1194 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1195 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1197 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1198 * only defers where it comes to the variable body
1199 * is not null. If no body is present compose->text
1200 * will be null in which case you cannot place the
1201 * cursor inside the component so. An empty component
1202 * is therefore created before placing the cursor
1204 case BODY_FIELD_PRESENT:
1205 cursor_pos = quote_fmt_get_cursor_pos();
1206 if (cursor_pos == -1)
1207 gtk_widget_grab_focus(compose->header_last->entry);
1209 gtk_widget_grab_focus(compose->text);
1213 undo_unblock(compose->undostruct);
1215 if (prefs_common.auto_exteditor)
1216 compose_exec_ext_editor(compose);
1218 compose->draft_timeout_tag = -1;
1219 SCROLL_TO_CURSOR(compose);
1221 compose->modified = FALSE;
1222 compose_set_title(compose);
1224 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1229 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1230 gboolean override_pref, const gchar *system)
1232 const gchar *privacy = NULL;
1234 cm_return_if_fail(compose != NULL);
1235 cm_return_if_fail(account != NULL);
1237 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1242 else if (account->default_privacy_system
1243 && strlen(account->default_privacy_system)) {
1244 privacy = account->default_privacy_system;
1246 GSList *privacy_avail = privacy_get_system_ids();
1247 if (privacy_avail && g_slist_length(privacy_avail)) {
1248 privacy = (gchar *)(privacy_avail->data);
1251 if (privacy != NULL) {
1253 g_free(compose->privacy_system);
1254 compose->privacy_system = NULL;
1256 if (compose->privacy_system == NULL)
1257 compose->privacy_system = g_strdup(privacy);
1258 else if (*(compose->privacy_system) == '\0') {
1259 g_free(compose->privacy_system);
1260 compose->privacy_system = g_strdup(privacy);
1262 compose_update_privacy_system_menu_item(compose, FALSE);
1263 compose_use_encryption(compose, TRUE);
1267 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1269 const gchar *privacy = NULL;
1273 else if (account->default_privacy_system
1274 && strlen(account->default_privacy_system)) {
1275 privacy = account->default_privacy_system;
1277 GSList *privacy_avail = privacy_get_system_ids();
1278 if (privacy_avail && g_slist_length(privacy_avail)) {
1279 privacy = (gchar *)(privacy_avail->data);
1283 if (privacy != NULL) {
1285 g_free(compose->privacy_system);
1286 compose->privacy_system = NULL;
1288 if (compose->privacy_system == NULL)
1289 compose->privacy_system = g_strdup(privacy);
1290 compose_update_privacy_system_menu_item(compose, FALSE);
1291 compose_use_signing(compose, TRUE);
1295 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1299 Compose *compose = NULL;
1301 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1303 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1304 cm_return_val_if_fail(msginfo != NULL, NULL);
1306 list_len = g_slist_length(msginfo_list);
1310 case COMPOSE_REPLY_TO_ADDRESS:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1312 FALSE, prefs_common.default_reply_list, FALSE, body);
1314 case COMPOSE_REPLY_WITH_QUOTE:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1316 FALSE, prefs_common.default_reply_list, FALSE, body);
1318 case COMPOSE_REPLY_WITHOUT_QUOTE:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1320 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1322 case COMPOSE_REPLY_TO_SENDER:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1324 FALSE, FALSE, TRUE, body);
1326 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1327 compose = compose_followup_and_reply_to(msginfo,
1328 COMPOSE_QUOTE_CHECK,
1329 FALSE, FALSE, body);
1331 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1333 FALSE, FALSE, TRUE, body);
1335 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1337 FALSE, FALSE, TRUE, NULL);
1339 case COMPOSE_REPLY_TO_ALL:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1341 TRUE, FALSE, FALSE, body);
1343 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1345 TRUE, FALSE, FALSE, body);
1347 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1349 TRUE, FALSE, FALSE, NULL);
1351 case COMPOSE_REPLY_TO_LIST:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1353 FALSE, TRUE, FALSE, body);
1355 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1357 FALSE, TRUE, FALSE, body);
1359 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1361 FALSE, TRUE, FALSE, NULL);
1363 case COMPOSE_FORWARD:
1364 if (prefs_common.forward_as_attachment) {
1365 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1368 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1372 case COMPOSE_FORWARD_INLINE:
1373 /* check if we reply to more than one Message */
1374 if (list_len == 1) {
1375 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1378 /* more messages FALL THROUGH */
1379 case COMPOSE_FORWARD_AS_ATTACH:
1380 compose = compose_forward_multiple(NULL, msginfo_list);
1382 case COMPOSE_REDIRECT:
1383 compose = compose_redirect(NULL, msginfo, FALSE);
1386 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1389 if (compose == NULL) {
1390 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1394 compose->rmode = mode;
1395 switch (compose->rmode) {
1397 case COMPOSE_REPLY_WITH_QUOTE:
1398 case COMPOSE_REPLY_WITHOUT_QUOTE:
1399 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1400 debug_print("reply mode Normal\n");
1401 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1402 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1404 case COMPOSE_REPLY_TO_SENDER:
1405 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1406 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1407 debug_print("reply mode Sender\n");
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1410 case COMPOSE_REPLY_TO_ALL:
1411 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1412 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1413 debug_print("reply mode All\n");
1414 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1416 case COMPOSE_REPLY_TO_LIST:
1417 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1418 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1419 debug_print("reply mode List\n");
1420 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1422 case COMPOSE_REPLY_TO_ADDRESS:
1423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1431 static Compose *compose_reply(MsgInfo *msginfo,
1432 ComposeQuoteMode quote_mode,
1438 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1439 to_sender, FALSE, body);
1442 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1443 ComposeQuoteMode quote_mode,
1448 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1449 to_sender, TRUE, body);
1452 static void compose_extract_original_charset(Compose *compose)
1454 MsgInfo *info = NULL;
1455 if (compose->replyinfo) {
1456 info = compose->replyinfo;
1457 } else if (compose->fwdinfo) {
1458 info = compose->fwdinfo;
1459 } else if (compose->targetinfo) {
1460 info = compose->targetinfo;
1463 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1464 MimeInfo *partinfo = mimeinfo;
1465 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1466 partinfo = procmime_mimeinfo_next(partinfo);
1468 compose->orig_charset =
1469 g_strdup(procmime_mimeinfo_get_parameter(
1470 partinfo, "charset"));
1472 procmime_mimeinfo_free_all(mimeinfo);
1476 #define SIGNAL_BLOCK(buffer) { \
1477 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1478 G_CALLBACK(compose_changed_cb), \
1480 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(text_inserted), \
1485 #define SIGNAL_UNBLOCK(buffer) { \
1486 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1487 G_CALLBACK(compose_changed_cb), \
1489 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1490 G_CALLBACK(text_inserted), \
1494 static Compose *compose_generic_reply(MsgInfo *msginfo,
1495 ComposeQuoteMode quote_mode,
1496 gboolean to_all, gboolean to_ml,
1498 gboolean followup_and_reply_to,
1502 PrefsAccount *account = NULL;
1503 GtkTextView *textview;
1504 GtkTextBuffer *textbuf;
1505 gboolean quote = FALSE;
1506 const gchar *qmark = NULL;
1507 const gchar *body_fmt = NULL;
1508 gchar *s_system = NULL;
1510 cm_return_val_if_fail(msginfo != NULL, NULL);
1511 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1513 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1515 cm_return_val_if_fail(account != NULL, NULL);
1517 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1519 compose->updating = TRUE;
1521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1522 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1524 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1525 if (!compose->replyinfo)
1526 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1528 compose_extract_original_charset(compose);
1530 if (msginfo->folder && msginfo->folder->ret_rcpt)
1531 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1533 /* Set save folder */
1534 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1535 gchar *folderidentifier;
1537 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1538 folderidentifier = folder_item_get_identifier(msginfo->folder);
1539 compose_set_save_to(compose, folderidentifier);
1540 g_free(folderidentifier);
1543 if (compose_parse_header(compose, msginfo) < 0) {
1544 compose->updating = FALSE;
1545 compose_destroy(compose);
1549 /* override from name according to folder properties */
1550 if (msginfo->folder && msginfo->folder->prefs &&
1551 msginfo->folder->prefs->reply_with_format &&
1552 msginfo->folder->prefs->reply_override_from_format &&
1553 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1558 /* decode \-escape sequences in the internal representation of the quote format */
1559 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1560 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1563 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1564 compose->gtkaspell);
1566 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1568 quote_fmt_scan_string(tmp);
1571 buf = quote_fmt_get_buffer();
1573 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1575 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1576 quote_fmt_reset_vartable();
1581 textview = (GTK_TEXT_VIEW(compose->text));
1582 textbuf = gtk_text_view_get_buffer(textview);
1583 compose_create_tags(textview, compose);
1585 undo_block(compose->undostruct);
1587 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1590 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1591 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1592 /* use the reply format of folder (if enabled), or the account's one
1593 (if enabled) or fallback to the global reply format, which is always
1594 enabled (even if empty), and use the relevant quotemark */
1596 if (msginfo->folder && msginfo->folder->prefs &&
1597 msginfo->folder->prefs->reply_with_format) {
1598 qmark = msginfo->folder->prefs->reply_quotemark;
1599 body_fmt = msginfo->folder->prefs->reply_body_format;
1601 } else if (account->reply_with_format) {
1602 qmark = account->reply_quotemark;
1603 body_fmt = account->reply_body_format;
1606 qmark = prefs_common.quotemark;
1607 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1608 body_fmt = gettext(prefs_common.quotefmt);
1615 /* empty quotemark is not allowed */
1616 if (qmark == NULL || *qmark == '\0')
1618 compose_quote_fmt(compose, compose->replyinfo,
1619 body_fmt, qmark, body, FALSE, TRUE,
1620 _("The body of the \"Reply\" template has an error at line %d."));
1621 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1622 quote_fmt_reset_vartable();
1624 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1625 gtkaspell_highlight_all(compose->gtkaspell);
1629 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1630 compose_force_encryption(compose, account, FALSE, s_system);
1633 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1634 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1635 compose_force_signing(compose, account, s_system);
1639 SIGNAL_BLOCK(textbuf);
1641 if (account->auto_sig)
1642 compose_insert_sig(compose, FALSE);
1644 compose_wrap_all(compose);
1646 SIGNAL_UNBLOCK(textbuf);
1648 gtk_widget_grab_focus(compose->text);
1650 undo_unblock(compose->undostruct);
1652 if (prefs_common.auto_exteditor)
1653 compose_exec_ext_editor(compose);
1655 compose->modified = FALSE;
1656 compose_set_title(compose);
1658 compose->updating = FALSE;
1659 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1660 SCROLL_TO_CURSOR(compose);
1662 if (compose->deferred_destroy) {
1663 compose_destroy(compose);
1671 #define INSERT_FW_HEADER(var, hdr) \
1672 if (msginfo->var && *msginfo->var) { \
1673 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1674 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1675 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1678 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1679 gboolean as_attach, const gchar *body,
1680 gboolean no_extedit,
1684 GtkTextView *textview;
1685 GtkTextBuffer *textbuf;
1686 gint cursor_pos = -1;
1689 cm_return_val_if_fail(msginfo != NULL, NULL);
1690 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1693 !(account = compose_guess_forward_account_from_msginfo
1695 account = cur_account;
1697 if (!prefs_common.forward_as_attachment)
1698 mode = COMPOSE_FORWARD_INLINE;
1700 mode = COMPOSE_FORWARD;
1701 compose = compose_create(account, msginfo->folder, mode, batch);
1703 compose->updating = TRUE;
1704 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1705 if (!compose->fwdinfo)
1706 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1708 compose_extract_original_charset(compose);
1710 if (msginfo->subject && *msginfo->subject) {
1711 gchar *buf, *buf2, *p;
1713 buf = p = g_strdup(msginfo->subject);
1714 p += subject_get_prefix_length(p);
1715 memmove(buf, p, strlen(p) + 1);
1717 buf2 = g_strdup_printf("Fw: %s", buf);
1718 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1724 /* override from name according to folder properties */
1725 if (msginfo->folder && msginfo->folder->prefs &&
1726 msginfo->folder->prefs->forward_with_format &&
1727 msginfo->folder->prefs->forward_override_from_format &&
1728 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1732 MsgInfo *full_msginfo = NULL;
1735 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1737 full_msginfo = procmsg_msginfo_copy(msginfo);
1739 /* decode \-escape sequences in the internal representation of the quote format */
1740 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1741 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1744 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1745 compose->gtkaspell);
1747 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1749 quote_fmt_scan_string(tmp);
1752 buf = quote_fmt_get_buffer();
1754 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1756 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1757 quote_fmt_reset_vartable();
1760 procmsg_msginfo_free(full_msginfo);
1763 textview = GTK_TEXT_VIEW(compose->text);
1764 textbuf = gtk_text_view_get_buffer(textview);
1765 compose_create_tags(textview, compose);
1767 undo_block(compose->undostruct);
1771 msgfile = procmsg_get_message_file(msginfo);
1772 if (!is_file_exist(msgfile))
1773 g_warning("%s: file not exist\n", msgfile);
1775 compose_attach_append(compose, msgfile, msgfile,
1776 "message/rfc822", NULL);
1780 const gchar *qmark = NULL;
1781 const gchar *body_fmt = NULL;
1782 MsgInfo *full_msginfo;
1784 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1785 body_fmt = gettext(prefs_common.fw_quotefmt);
1789 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1791 full_msginfo = procmsg_msginfo_copy(msginfo);
1793 /* use the forward format of folder (if enabled), or the account's one
1794 (if enabled) or fallback to the global forward format, which is always
1795 enabled (even if empty), and use the relevant quotemark */
1796 if (msginfo->folder && msginfo->folder->prefs &&
1797 msginfo->folder->prefs->forward_with_format) {
1798 qmark = msginfo->folder->prefs->forward_quotemark;
1799 body_fmt = msginfo->folder->prefs->forward_body_format;
1801 } else if (account->forward_with_format) {
1802 qmark = account->forward_quotemark;
1803 body_fmt = account->forward_body_format;
1806 qmark = prefs_common.fw_quotemark;
1807 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1808 body_fmt = gettext(prefs_common.fw_quotefmt);
1813 /* empty quotemark is not allowed */
1814 if (qmark == NULL || *qmark == '\0')
1817 compose_quote_fmt(compose, full_msginfo,
1818 body_fmt, qmark, body, FALSE, TRUE,
1819 _("The body of the \"Forward\" template has an error at line %d."));
1820 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1821 quote_fmt_reset_vartable();
1822 compose_attach_parts(compose, msginfo);
1824 procmsg_msginfo_free(full_msginfo);
1826 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1827 gtkaspell_highlight_all(compose->gtkaspell);
1831 SIGNAL_BLOCK(textbuf);
1833 if (account->auto_sig)
1834 compose_insert_sig(compose, FALSE);
1836 compose_wrap_all(compose);
1838 SIGNAL_UNBLOCK(textbuf);
1840 cursor_pos = quote_fmt_get_cursor_pos();
1841 if (cursor_pos == -1)
1842 gtk_widget_grab_focus(compose->header_last->entry);
1844 gtk_widget_grab_focus(compose->text);
1846 if (!no_extedit && prefs_common.auto_exteditor)
1847 compose_exec_ext_editor(compose);
1850 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1851 gchar *folderidentifier;
1853 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1854 folderidentifier = folder_item_get_identifier(msginfo->folder);
1855 compose_set_save_to(compose, folderidentifier);
1856 g_free(folderidentifier);
1859 undo_unblock(compose->undostruct);
1861 compose->modified = FALSE;
1862 compose_set_title(compose);
1864 compose->updating = FALSE;
1865 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1866 SCROLL_TO_CURSOR(compose);
1868 if (compose->deferred_destroy) {
1869 compose_destroy(compose);
1873 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1878 #undef INSERT_FW_HEADER
1880 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1883 GtkTextView *textview;
1884 GtkTextBuffer *textbuf;
1888 gboolean single_mail = TRUE;
1890 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1892 if (g_slist_length(msginfo_list) > 1)
1893 single_mail = FALSE;
1895 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1896 if (((MsgInfo *)msginfo->data)->folder == NULL)
1899 /* guess account from first selected message */
1901 !(account = compose_guess_forward_account_from_msginfo
1902 (msginfo_list->data)))
1903 account = cur_account;
1905 cm_return_val_if_fail(account != NULL, NULL);
1907 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1908 if (msginfo->data) {
1909 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1910 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1914 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1915 g_warning("no msginfo_list");
1919 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1921 compose->updating = TRUE;
1923 /* override from name according to folder properties */
1924 if (msginfo_list->data) {
1925 MsgInfo *msginfo = msginfo_list->data;
1927 if (msginfo->folder && msginfo->folder->prefs &&
1928 msginfo->folder->prefs->forward_with_format &&
1929 msginfo->folder->prefs->forward_override_from_format &&
1930 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1935 /* decode \-escape sequences in the internal representation of the quote format */
1936 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1937 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1940 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1941 compose->gtkaspell);
1943 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1945 quote_fmt_scan_string(tmp);
1948 buf = quote_fmt_get_buffer();
1950 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1952 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1953 quote_fmt_reset_vartable();
1959 textview = GTK_TEXT_VIEW(compose->text);
1960 textbuf = gtk_text_view_get_buffer(textview);
1961 compose_create_tags(textview, compose);
1963 undo_block(compose->undostruct);
1964 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1965 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1967 if (!is_file_exist(msgfile))
1968 g_warning("%s: file not exist\n", msgfile);
1970 compose_attach_append(compose, msgfile, msgfile,
1971 "message/rfc822", NULL);
1976 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1977 if (info->subject && *info->subject) {
1978 gchar *buf, *buf2, *p;
1980 buf = p = g_strdup(info->subject);
1981 p += subject_get_prefix_length(p);
1982 memmove(buf, p, strlen(p) + 1);
1984 buf2 = g_strdup_printf("Fw: %s", buf);
1985 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1991 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1992 _("Fw: multiple emails"));
1995 SIGNAL_BLOCK(textbuf);
1997 if (account->auto_sig)
1998 compose_insert_sig(compose, FALSE);
2000 compose_wrap_all(compose);
2002 SIGNAL_UNBLOCK(textbuf);
2004 gtk_text_buffer_get_start_iter(textbuf, &iter);
2005 gtk_text_buffer_place_cursor(textbuf, &iter);
2007 gtk_widget_grab_focus(compose->header_last->entry);
2008 undo_unblock(compose->undostruct);
2009 compose->modified = FALSE;
2010 compose_set_title(compose);
2012 compose->updating = FALSE;
2013 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2014 SCROLL_TO_CURSOR(compose);
2016 if (compose->deferred_destroy) {
2017 compose_destroy(compose);
2021 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2026 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2028 GtkTextIter start = *iter;
2029 GtkTextIter end_iter;
2030 int start_pos = gtk_text_iter_get_offset(&start);
2032 if (!compose->account->sig_sep)
2035 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2036 start_pos+strlen(compose->account->sig_sep));
2038 /* check sig separator */
2039 str = gtk_text_iter_get_text(&start, &end_iter);
2040 if (!strcmp(str, compose->account->sig_sep)) {
2042 /* check end of line (\n) */
2043 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2044 start_pos+strlen(compose->account->sig_sep));
2045 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2046 start_pos+strlen(compose->account->sig_sep)+1);
2047 tmp = gtk_text_iter_get_text(&start, &end_iter);
2048 if (!strcmp(tmp,"\n")) {
2060 static void compose_colorize_signature(Compose *compose)
2062 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2064 GtkTextIter end_iter;
2065 gtk_text_buffer_get_start_iter(buffer, &iter);
2066 while (gtk_text_iter_forward_line(&iter))
2067 if (compose_is_sig_separator(compose, buffer, &iter)) {
2068 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2069 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2073 #define BLOCK_WRAP() { \
2074 prev_autowrap = compose->autowrap; \
2075 buffer = gtk_text_view_get_buffer( \
2076 GTK_TEXT_VIEW(compose->text)); \
2077 compose->autowrap = FALSE; \
2079 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2080 G_CALLBACK(compose_changed_cb), \
2082 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2083 G_CALLBACK(text_inserted), \
2086 #define UNBLOCK_WRAP() { \
2087 compose->autowrap = prev_autowrap; \
2088 if (compose->autowrap) { \
2089 gint old = compose->draft_timeout_tag; \
2090 compose->draft_timeout_tag = -2; \
2091 compose_wrap_all(compose); \
2092 compose->draft_timeout_tag = old; \
2095 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2096 G_CALLBACK(compose_changed_cb), \
2098 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2099 G_CALLBACK(text_inserted), \
2103 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2105 Compose *compose = NULL;
2106 PrefsAccount *account = NULL;
2107 GtkTextView *textview;
2108 GtkTextBuffer *textbuf;
2112 gchar buf[BUFFSIZE];
2113 gboolean use_signing = FALSE;
2114 gboolean use_encryption = FALSE;
2115 gchar *privacy_system = NULL;
2116 int priority = PRIORITY_NORMAL;
2117 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2118 gboolean autowrap = prefs_common.autowrap;
2119 gboolean autoindent = prefs_common.auto_indent;
2121 cm_return_val_if_fail(msginfo != NULL, NULL);
2122 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2124 if (compose_put_existing_to_front(msginfo)) {
2128 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2129 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2130 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2131 gchar queueheader_buf[BUFFSIZE];
2134 /* Select Account from queue headers */
2135 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2136 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2137 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2138 account = account_find_from_id(id);
2140 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2141 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2142 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2143 account = account_find_from_id(id);
2145 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2146 sizeof(queueheader_buf), "NAID:")) {
2147 id = atoi(&queueheader_buf[strlen("NAID:")]);
2148 account = account_find_from_id(id);
2150 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2151 sizeof(queueheader_buf), "MAID:")) {
2152 id = atoi(&queueheader_buf[strlen("MAID:")]);
2153 account = account_find_from_id(id);
2155 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "S:")) {
2157 account = account_find_from_address(queueheader_buf, FALSE);
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2160 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2161 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2162 use_signing = param;
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2167 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2168 use_signing = param;
2171 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2173 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2174 use_encryption = param;
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2178 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2179 use_encryption = param;
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2188 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2191 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2192 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2193 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2197 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Priority: ")) {
2201 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2204 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2205 sizeof(queueheader_buf), "RMID:")) {
2206 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2207 if (tokens[0] && tokens[1] && tokens[2]) {
2208 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2209 if (orig_item != NULL) {
2210 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2215 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2216 sizeof(queueheader_buf), "FMID:")) {
2217 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2218 if (tokens[0] && tokens[1] && tokens[2]) {
2219 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2220 if (orig_item != NULL) {
2221 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2227 account = msginfo->folder->folder->account;
2230 if (!account && prefs_common.reedit_account_autosel) {
2231 gchar from[BUFFSIZE];
2232 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2233 extract_address(from);
2234 account = account_find_from_address(from, FALSE);
2238 account = cur_account;
2240 cm_return_val_if_fail(account != NULL, NULL);
2242 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2244 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2245 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2246 compose->autowrap = autowrap;
2247 compose->replyinfo = replyinfo;
2248 compose->fwdinfo = fwdinfo;
2250 compose->updating = TRUE;
2251 compose->priority = priority;
2253 if (privacy_system != NULL) {
2254 compose->privacy_system = privacy_system;
2255 compose_use_signing(compose, use_signing);
2256 compose_use_encryption(compose, use_encryption);
2257 compose_update_privacy_system_menu_item(compose, FALSE);
2259 activate_privacy_system(compose, account, FALSE);
2262 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2264 compose_extract_original_charset(compose);
2266 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2267 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2268 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2269 gchar queueheader_buf[BUFFSIZE];
2271 /* Set message save folder */
2272 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2273 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2274 compose_set_save_to(compose, &queueheader_buf[4]);
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2277 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2279 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2284 if (compose_parse_header(compose, msginfo) < 0) {
2285 compose->updating = FALSE;
2286 compose_destroy(compose);
2289 compose_reedit_set_entry(compose, msginfo);
2291 textview = GTK_TEXT_VIEW(compose->text);
2292 textbuf = gtk_text_view_get_buffer(textview);
2293 compose_create_tags(textview, compose);
2295 mark = gtk_text_buffer_get_insert(textbuf);
2296 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2298 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2299 G_CALLBACK(compose_changed_cb),
2302 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2303 fp = procmime_get_first_encrypted_text_content(msginfo);
2305 compose_force_encryption(compose, account, TRUE, NULL);
2308 fp = procmime_get_first_text_content(msginfo);
2311 g_warning("Can't get text part\n");
2315 gboolean prev_autowrap = compose->autowrap;
2316 GtkTextBuffer *buffer = textbuf;
2318 while (fgets(buf, sizeof(buf), fp) != NULL) {
2320 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2326 compose_attach_parts(compose, msginfo);
2328 compose_colorize_signature(compose);
2330 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2331 G_CALLBACK(compose_changed_cb),
2334 gtk_widget_grab_focus(compose->text);
2336 if (prefs_common.auto_exteditor) {
2337 compose_exec_ext_editor(compose);
2339 compose->modified = FALSE;
2340 compose_set_title(compose);
2342 compose->updating = FALSE;
2343 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2344 SCROLL_TO_CURSOR(compose);
2346 if (compose->deferred_destroy) {
2347 compose_destroy(compose);
2351 compose->sig_str = account_get_signature_str(compose->account);
2353 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2358 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2365 cm_return_val_if_fail(msginfo != NULL, NULL);
2368 account = account_get_reply_account(msginfo,
2369 prefs_common.reply_account_autosel);
2370 cm_return_val_if_fail(account != NULL, NULL);
2372 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2374 compose->updating = TRUE;
2376 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2377 compose->replyinfo = NULL;
2378 compose->fwdinfo = NULL;
2380 compose_show_first_last_header(compose, TRUE);
2382 gtk_widget_grab_focus(compose->header_last->entry);
2384 filename = procmsg_get_message_file(msginfo);
2386 if (filename == NULL) {
2387 compose->updating = FALSE;
2388 compose_destroy(compose);
2393 compose->redirect_filename = filename;
2395 /* Set save folder */
2396 item = msginfo->folder;
2397 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2398 gchar *folderidentifier;
2400 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2401 folderidentifier = folder_item_get_identifier(item);
2402 compose_set_save_to(compose, folderidentifier);
2403 g_free(folderidentifier);
2406 compose_attach_parts(compose, msginfo);
2408 if (msginfo->subject)
2409 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2411 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2413 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2414 _("The body of the \"Redirect\" template has an error at line %d."));
2415 quote_fmt_reset_vartable();
2416 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2418 compose_colorize_signature(compose);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2426 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2427 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2428 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2429 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2430 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2431 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2432 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2434 if (compose->toolbar->draft_btn)
2435 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2436 if (compose->toolbar->insert_btn)
2437 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2438 if (compose->toolbar->attach_btn)
2439 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2440 if (compose->toolbar->sig_btn)
2441 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2442 if (compose->toolbar->exteditor_btn)
2443 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2444 if (compose->toolbar->linewrap_current_btn)
2445 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2446 if (compose->toolbar->linewrap_all_btn)
2447 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2449 compose->modified = FALSE;
2450 compose_set_title(compose);
2451 compose->updating = FALSE;
2452 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2453 SCROLL_TO_CURSOR(compose);
2455 if (compose->deferred_destroy) {
2456 compose_destroy(compose);
2460 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2465 GList *compose_get_compose_list(void)
2467 return compose_list;
2470 void compose_entry_append(Compose *compose, const gchar *address,
2471 ComposeEntryType type, ComposePrefType pref_type)
2473 const gchar *header;
2475 gboolean in_quote = FALSE;
2476 if (!address || *address == '\0') return;
2483 header = N_("Bcc:");
2485 case COMPOSE_REPLYTO:
2486 header = N_("Reply-To:");
2488 case COMPOSE_NEWSGROUPS:
2489 header = N_("Newsgroups:");
2491 case COMPOSE_FOLLOWUPTO:
2492 header = N_( "Followup-To:");
2494 case COMPOSE_INREPLYTO:
2495 header = N_( "In-Reply-To:");
2502 header = prefs_common_translated_header_name(header);
2504 cur = begin = (gchar *)address;
2506 /* we separate the line by commas, but not if we're inside a quoted
2508 while (*cur != '\0') {
2510 in_quote = !in_quote;
2511 if (*cur == ',' && !in_quote) {
2512 gchar *tmp = g_strdup(begin);
2514 tmp[cur-begin]='\0';
2517 while (*tmp == ' ' || *tmp == '\t')
2519 compose_add_header_entry(compose, header, tmp, pref_type);
2526 gchar *tmp = g_strdup(begin);
2528 tmp[cur-begin]='\0';
2531 while (*tmp == ' ' || *tmp == '\t')
2533 compose_add_header_entry(compose, header, tmp, pref_type);
2538 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2540 #if !GTK_CHECK_VERSION(3, 0, 0)
2541 static GdkColor yellow;
2542 static GdkColor black;
2543 static gboolean yellow_initialised = FALSE;
2545 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2546 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2551 #if !GTK_CHECK_VERSION(3, 0, 0)
2552 if (!yellow_initialised) {
2553 gdk_color_parse("#f5f6be", &yellow);
2554 gdk_color_parse("#000000", &black);
2555 yellow_initialised = gdk_colormap_alloc_color(
2556 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2557 yellow_initialised &= gdk_colormap_alloc_color(
2558 gdk_colormap_get_system(), &black, FALSE, TRUE);
2562 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2563 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2564 if (gtk_entry_get_text(entry) &&
2565 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2566 #if !GTK_CHECK_VERSION(3, 0, 0)
2567 if (yellow_initialised) {
2569 gtk_widget_modify_base(
2570 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2571 GTK_STATE_NORMAL, &yellow);
2572 gtk_widget_modify_text(
2573 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2574 GTK_STATE_NORMAL, &black);
2575 #if !GTK_CHECK_VERSION(3, 0, 0)
2582 void compose_toolbar_cb(gint action, gpointer data)
2584 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2585 Compose *compose = (Compose*)toolbar_item->parent;
2587 cm_return_if_fail(compose != NULL);
2591 compose_send_cb(NULL, compose);
2594 compose_send_later_cb(NULL, compose);
2597 compose_draft(compose, COMPOSE_QUIT_EDITING);
2600 compose_insert_file_cb(NULL, compose);
2603 compose_attach_cb(NULL, compose);
2606 compose_insert_sig(compose, FALSE);
2609 compose_ext_editor_cb(NULL, compose);
2611 case A_LINEWRAP_CURRENT:
2612 compose_beautify_paragraph(compose, NULL, TRUE);
2614 case A_LINEWRAP_ALL:
2615 compose_wrap_all_full(compose, TRUE);
2618 compose_address_cb(NULL, compose);
2621 case A_CHECK_SPELLING:
2622 compose_check_all(NULL, compose);
2630 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2635 gchar *subject = NULL;
2639 gchar **attach = NULL;
2640 gchar *inreplyto = NULL;
2641 MailField mfield = NO_FIELD_PRESENT;
2643 /* get mailto parts but skip from */
2644 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2647 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2648 mfield = TO_FIELD_PRESENT;
2651 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2653 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2655 if (!g_utf8_validate (subject, -1, NULL)) {
2656 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2657 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2660 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2662 mfield = SUBJECT_FIELD_PRESENT;
2665 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2666 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2669 gboolean prev_autowrap = compose->autowrap;
2671 compose->autowrap = FALSE;
2673 mark = gtk_text_buffer_get_insert(buffer);
2674 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2676 if (!g_utf8_validate (body, -1, NULL)) {
2677 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2678 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2681 gtk_text_buffer_insert(buffer, &iter, body, -1);
2683 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2685 compose->autowrap = prev_autowrap;
2686 if (compose->autowrap)
2687 compose_wrap_all(compose);
2688 mfield = BODY_FIELD_PRESENT;
2692 gint i = 0, att = 0;
2693 gchar *warn_files = NULL;
2694 while (attach[i] != NULL) {
2695 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2696 if (utf8_filename) {
2697 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2698 gchar *tmp = g_strdup_printf("%s%s\n",
2699 warn_files?warn_files:"",
2705 g_free(utf8_filename);
2707 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2712 alertpanel_notice(ngettext(
2713 "The following file has been attached: \n%s",
2714 "The following files have been attached: \n%s", att), warn_files);
2719 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2732 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2734 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2735 {"Cc:", NULL, TRUE},
2736 {"References:", NULL, FALSE},
2737 {"Bcc:", NULL, TRUE},
2738 {"Newsgroups:", NULL, TRUE},
2739 {"Followup-To:", NULL, TRUE},
2740 {"List-Post:", NULL, FALSE},
2741 {"X-Priority:", NULL, FALSE},
2742 {NULL, NULL, FALSE}};
2758 cm_return_val_if_fail(msginfo != NULL, -1);
2760 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2761 procheader_get_header_fields(fp, hentry);
2764 if (hentry[H_REPLY_TO].body != NULL) {
2765 if (hentry[H_REPLY_TO].body[0] != '\0') {
2767 conv_unmime_header(hentry[H_REPLY_TO].body,
2770 g_free(hentry[H_REPLY_TO].body);
2771 hentry[H_REPLY_TO].body = NULL;
2773 if (hentry[H_CC].body != NULL) {
2774 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2775 g_free(hentry[H_CC].body);
2776 hentry[H_CC].body = NULL;
2778 if (hentry[H_REFERENCES].body != NULL) {
2779 if (compose->mode == COMPOSE_REEDIT)
2780 compose->references = hentry[H_REFERENCES].body;
2782 compose->references = compose_parse_references
2783 (hentry[H_REFERENCES].body, msginfo->msgid);
2784 g_free(hentry[H_REFERENCES].body);
2786 hentry[H_REFERENCES].body = NULL;
2788 if (hentry[H_BCC].body != NULL) {
2789 if (compose->mode == COMPOSE_REEDIT)
2791 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2792 g_free(hentry[H_BCC].body);
2793 hentry[H_BCC].body = NULL;
2795 if (hentry[H_NEWSGROUPS].body != NULL) {
2796 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2797 hentry[H_NEWSGROUPS].body = NULL;
2799 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2800 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2801 compose->followup_to =
2802 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2805 g_free(hentry[H_FOLLOWUP_TO].body);
2806 hentry[H_FOLLOWUP_TO].body = NULL;
2808 if (hentry[H_LIST_POST].body != NULL) {
2809 gchar *to = NULL, *start = NULL;
2811 extract_address(hentry[H_LIST_POST].body);
2812 if (hentry[H_LIST_POST].body[0] != '\0') {
2813 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2815 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2816 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2819 g_free(compose->ml_post);
2820 compose->ml_post = to;
2823 g_free(hentry[H_LIST_POST].body);
2824 hentry[H_LIST_POST].body = NULL;
2827 /* CLAWS - X-Priority */
2828 if (compose->mode == COMPOSE_REEDIT)
2829 if (hentry[H_X_PRIORITY].body != NULL) {
2832 priority = atoi(hentry[H_X_PRIORITY].body);
2833 g_free(hentry[H_X_PRIORITY].body);
2835 hentry[H_X_PRIORITY].body = NULL;
2837 if (priority < PRIORITY_HIGHEST ||
2838 priority > PRIORITY_LOWEST)
2839 priority = PRIORITY_NORMAL;
2841 compose->priority = priority;
2844 if (compose->mode == COMPOSE_REEDIT) {
2845 if (msginfo->inreplyto && *msginfo->inreplyto)
2846 compose->inreplyto = g_strdup(msginfo->inreplyto);
2850 if (msginfo->msgid && *msginfo->msgid)
2851 compose->inreplyto = g_strdup(msginfo->msgid);
2853 if (!compose->references) {
2854 if (msginfo->msgid && *msginfo->msgid) {
2855 if (msginfo->inreplyto && *msginfo->inreplyto)
2856 compose->references =
2857 g_strdup_printf("<%s>\n\t<%s>",
2861 compose->references =
2862 g_strconcat("<", msginfo->msgid, ">",
2864 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2865 compose->references =
2866 g_strconcat("<", msginfo->inreplyto, ">",
2874 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2876 GSList *ref_id_list, *cur;
2880 ref_id_list = references_list_append(NULL, ref);
2881 if (!ref_id_list) return NULL;
2882 if (msgid && *msgid)
2883 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2888 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2889 /* "<" + Message-ID + ">" + CR+LF+TAB */
2890 len += strlen((gchar *)cur->data) + 5;
2892 if (len > MAX_REFERENCES_LEN) {
2893 /* remove second message-ID */
2894 if (ref_id_list && ref_id_list->next &&
2895 ref_id_list->next->next) {
2896 g_free(ref_id_list->next->data);
2897 ref_id_list = g_slist_remove
2898 (ref_id_list, ref_id_list->next->data);
2900 slist_free_strings(ref_id_list);
2901 g_slist_free(ref_id_list);
2908 new_ref = g_string_new("");
2909 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2910 if (new_ref->len > 0)
2911 g_string_append(new_ref, "\n\t");
2912 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2915 slist_free_strings(ref_id_list);
2916 g_slist_free(ref_id_list);
2918 new_ref_str = new_ref->str;
2919 g_string_free(new_ref, FALSE);
2924 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2925 const gchar *fmt, const gchar *qmark,
2926 const gchar *body, gboolean rewrap,
2927 gboolean need_unescape,
2928 const gchar *err_msg)
2930 MsgInfo* dummyinfo = NULL;
2931 gchar *quote_str = NULL;
2933 gboolean prev_autowrap;
2934 const gchar *trimmed_body = body;
2935 gint cursor_pos = -1;
2936 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2937 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2942 SIGNAL_BLOCK(buffer);
2945 dummyinfo = compose_msginfo_new_from_compose(compose);
2946 msginfo = dummyinfo;
2949 if (qmark != NULL) {
2951 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2952 compose->gtkaspell);
2954 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2956 quote_fmt_scan_string(qmark);
2959 buf = quote_fmt_get_buffer();
2961 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2963 Xstrdup_a(quote_str, buf, goto error)
2966 if (fmt && *fmt != '\0') {
2969 while (*trimmed_body == '\n')
2973 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2974 compose->gtkaspell);
2976 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2978 if (need_unescape) {
2981 /* decode \-escape sequences in the internal representation of the quote format */
2982 tmp = g_malloc(strlen(fmt)+1);
2983 pref_get_unescaped_pref(tmp, fmt);
2984 quote_fmt_scan_string(tmp);
2988 quote_fmt_scan_string(fmt);
2992 buf = quote_fmt_get_buffer();
2994 gint line = quote_fmt_get_line();
2995 alertpanel_error(err_msg, line);
3001 prev_autowrap = compose->autowrap;
3002 compose->autowrap = FALSE;
3004 mark = gtk_text_buffer_get_insert(buffer);
3005 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3006 if (g_utf8_validate(buf, -1, NULL)) {
3007 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3009 gchar *tmpout = NULL;
3010 tmpout = conv_codeset_strdup
3011 (buf, conv_get_locale_charset_str_no_utf8(),
3013 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3015 tmpout = g_malloc(strlen(buf)*2+1);
3016 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3018 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3022 cursor_pos = quote_fmt_get_cursor_pos();
3023 if (cursor_pos == -1)
3024 cursor_pos = gtk_text_iter_get_offset(&iter);
3025 compose->set_cursor_pos = cursor_pos;
3027 gtk_text_buffer_get_start_iter(buffer, &iter);
3028 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3029 gtk_text_buffer_place_cursor(buffer, &iter);
3031 compose->autowrap = prev_autowrap;
3032 if (compose->autowrap && rewrap)
3033 compose_wrap_all(compose);
3040 SIGNAL_UNBLOCK(buffer);
3042 procmsg_msginfo_free( dummyinfo );
3047 /* if ml_post is of type addr@host and from is of type
3048 * addr-anything@host, return TRUE
3050 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3052 gchar *left_ml = NULL;
3053 gchar *right_ml = NULL;
3054 gchar *left_from = NULL;
3055 gchar *right_from = NULL;
3056 gboolean result = FALSE;
3058 if (!ml_post || !from)
3061 left_ml = g_strdup(ml_post);
3062 if (strstr(left_ml, "@")) {
3063 right_ml = strstr(left_ml, "@")+1;
3064 *(strstr(left_ml, "@")) = '\0';
3067 left_from = g_strdup(from);
3068 if (strstr(left_from, "@")) {
3069 right_from = strstr(left_from, "@")+1;
3070 *(strstr(left_from, "@")) = '\0';
3073 if (left_ml && left_from && right_ml && right_from
3074 && !strncmp(left_from, left_ml, strlen(left_ml))
3075 && !strcmp(right_from, right_ml)) {
3084 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3085 gboolean respect_default_to)
3089 if (!folder || !folder->prefs)
3092 if (respect_default_to && folder->prefs->enable_default_to) {
3093 compose_entry_append(compose, folder->prefs->default_to,
3094 COMPOSE_TO, PREF_FOLDER);
3095 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3097 if (folder->prefs->enable_default_cc)
3098 compose_entry_append(compose, folder->prefs->default_cc,
3099 COMPOSE_CC, PREF_FOLDER);
3100 if (folder->prefs->enable_default_bcc)
3101 compose_entry_append(compose, folder->prefs->default_bcc,
3102 COMPOSE_BCC, PREF_FOLDER);
3103 if (folder->prefs->enable_default_replyto)
3104 compose_entry_append(compose, folder->prefs->default_replyto,
3105 COMPOSE_REPLYTO, PREF_FOLDER);
3108 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3113 if (!compose || !msginfo)
3116 if (msginfo->subject && *msginfo->subject) {
3117 buf = p = g_strdup(msginfo->subject);
3118 p += subject_get_prefix_length(p);
3119 memmove(buf, p, strlen(p) + 1);
3121 buf2 = g_strdup_printf("Re: %s", buf);
3122 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3130 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3131 gboolean to_all, gboolean to_ml,
3133 gboolean followup_and_reply_to)
3135 GSList *cc_list = NULL;
3138 gchar *replyto = NULL;
3139 gchar *ac_email = NULL;
3141 gboolean reply_to_ml = FALSE;
3142 gboolean default_reply_to = FALSE;
3144 cm_return_if_fail(compose->account != NULL);
3145 cm_return_if_fail(msginfo != NULL);
3147 reply_to_ml = to_ml && compose->ml_post;
3149 default_reply_to = msginfo->folder &&
3150 msginfo->folder->prefs->enable_default_reply_to;
3152 if (compose->account->protocol != A_NNTP) {
3153 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3155 if (reply_to_ml && !default_reply_to) {
3157 gboolean is_subscr = is_subscription(compose->ml_post,
3160 /* normal answer to ml post with a reply-to */
3161 compose_entry_append(compose,
3163 COMPOSE_TO, PREF_ML);
3164 if (compose->replyto)
3165 compose_entry_append(compose,
3167 COMPOSE_CC, PREF_ML);
3169 /* answer to subscription confirmation */
3170 if (compose->replyto)
3171 compose_entry_append(compose,
3173 COMPOSE_TO, PREF_ML);
3174 else if (msginfo->from)
3175 compose_entry_append(compose,
3177 COMPOSE_TO, PREF_ML);
3180 else if (!(to_all || to_sender) && default_reply_to) {
3181 compose_entry_append(compose,