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,
384 static void compose_print_cb (GtkAction *action,
387 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
389 static void compose_address_cb (GtkAction *action,
391 static void about_show_cb (GtkAction *action,
393 static void compose_template_activate_cb(GtkWidget *widget,
396 static void compose_ext_editor_cb (GtkAction *action,
399 static gint compose_delete_cb (GtkWidget *widget,
403 static void compose_undo_cb (GtkAction *action,
405 static void compose_redo_cb (GtkAction *action,
407 static void compose_cut_cb (GtkAction *action,
409 static void compose_copy_cb (GtkAction *action,
411 static void compose_paste_cb (GtkAction *action,
413 static void compose_paste_as_quote_cb (GtkAction *action,
415 static void compose_paste_no_wrap_cb (GtkAction *action,
417 static void compose_paste_wrap_cb (GtkAction *action,
419 static void compose_allsel_cb (GtkAction *action,
422 static void compose_advanced_action_cb (GtkAction *action,
425 static void compose_grab_focus_cb (GtkWidget *widget,
428 static void compose_changed_cb (GtkTextBuffer *textbuf,
431 static void compose_wrap_cb (GtkAction *action,
433 static void compose_wrap_all_cb (GtkAction *action,
435 static void compose_find_cb (GtkAction *action,
437 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
439 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
442 static void compose_toggle_ruler_cb (GtkToggleAction *action,
444 static void compose_toggle_sign_cb (GtkToggleAction *action,
446 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
448 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
449 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
450 static void activate_privacy_system (Compose *compose,
451 PrefsAccount *account,
453 static void compose_use_signing(Compose *compose, gboolean use_signing);
454 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
455 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
457 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
459 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
460 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
461 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
463 static void compose_attach_drag_received_cb (GtkWidget *widget,
464 GdkDragContext *drag_context,
467 GtkSelectionData *data,
471 static void compose_insert_drag_received_cb (GtkWidget *widget,
472 GdkDragContext *drag_context,
475 GtkSelectionData *data,
479 static void compose_header_drag_received_cb (GtkWidget *widget,
480 GdkDragContext *drag_context,
483 GtkSelectionData *data,
488 static gboolean compose_drag_drop (GtkWidget *widget,
489 GdkDragContext *drag_context,
491 guint time, gpointer user_data);
493 static void text_inserted (GtkTextBuffer *buffer,
498 static Compose *compose_generic_reply(MsgInfo *msginfo,
499 ComposeQuoteMode quote_mode,
503 gboolean followup_and_reply_to,
506 static void compose_headerentry_changed_cb (GtkWidget *entry,
507 ComposeHeaderEntry *headerentry);
508 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
510 ComposeHeaderEntry *headerentry);
511 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
512 ComposeHeaderEntry *headerentry);
514 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
516 static void compose_allow_user_actions (Compose *compose, gboolean allow);
518 static void compose_nothing_cb (GtkAction *action, gpointer data)
524 static void compose_check_all (GtkAction *action, gpointer data);
525 static void compose_highlight_all (GtkAction *action, gpointer data);
526 static void compose_check_backwards (GtkAction *action, gpointer data);
527 static void compose_check_forwards_go (GtkAction *action, gpointer data);
530 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
532 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
535 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
536 FolderItem *folder_item);
538 static void compose_attach_update_label(Compose *compose);
539 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
540 gboolean respect_default_to);
542 static GtkActionEntry compose_popup_entries[] =
544 {"Compose", NULL, "Compose" },
545 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
546 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
547 {"Compose/---", NULL, "---", NULL, NULL, NULL },
548 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
551 static GtkActionEntry compose_entries[] =
553 {"Menu", NULL, "Menu" },
555 {"Message", NULL, N_("_Message") },
556 {"Edit", NULL, N_("_Edit") },
558 {"Spelling", NULL, N_("_Spelling") },
560 {"Options", NULL, N_("_Options") },
561 {"Tools", NULL, N_("_Tools") },
562 {"Help", NULL, N_("_Help") },
564 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
565 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
566 {"Message/---", NULL, "---" },
568 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
569 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
570 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
571 /* {"Message/---", NULL, "---" }, */
572 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
573 /* {"Message/---", NULL, "---" }, */
574 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
575 /* {"Message/---", NULL, "---" }, */
576 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
579 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
580 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
581 {"Edit/---", NULL, "---" },
583 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
584 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
585 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
587 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
588 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
589 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
590 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
592 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
594 {"Edit/Advanced", NULL, N_("A_dvanced") },
595 {"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*/
596 {"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*/
597 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
598 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
599 {"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*/
600 {"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*/
601 {"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*/
602 {"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*/
603 {"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*/
604 {"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*/
605 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
606 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
607 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
608 {"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*/
610 /* {"Edit/---", NULL, "---" }, */
611 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
613 /* {"Edit/---", NULL, "---" }, */
614 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
615 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
616 /* {"Edit/---", NULL, "---" }, */
617 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
620 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
621 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
622 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
623 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
625 {"Spelling/---", NULL, "---" },
626 {"Spelling/Options", NULL, N_("_Options") },
631 {"Options/ReplyMode", NULL, N_("Reply _mode") },
632 {"Options/---", NULL, "---" },
633 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
634 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
636 /* {"Options/---", NULL, "---" }, */
638 {"Options/Priority", NULL, N_("_Priority") },
640 {"Options/Encoding", NULL, N_("Character _encoding") },
641 {"Options/Encoding/---", NULL, "---" },
642 #define ENC_ACTION(cs_char,c_char,string) \
643 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
645 {"Options/Encoding/Western", NULL, N_("Western European") },
646 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
647 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
648 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
649 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
650 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
651 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
652 {"Options/Encoding/Korean", NULL, N_("Korean") },
653 {"Options/Encoding/Thai", NULL, N_("Thai") },
656 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
658 {"Tools/Template", NULL, N_("_Template") },
659 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
660 {"Tools/Actions", NULL, N_("Actio_ns") },
661 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
664 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
667 static GtkToggleActionEntry compose_toggle_entries[] =
669 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
670 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
671 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
672 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
673 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
674 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
675 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
678 static GtkRadioActionEntry compose_radio_rm_entries[] =
680 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
681 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
682 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
683 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
686 static GtkRadioActionEntry compose_radio_prio_entries[] =
688 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
689 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
690 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
691 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
692 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
695 static GtkRadioActionEntry compose_radio_enc_entries[] =
697 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
731 static GtkTargetEntry compose_mime_types[] =
733 {"text/uri-list", 0, 0},
734 {"UTF8_STRING", 0, 0},
738 static gboolean compose_put_existing_to_front(MsgInfo *info)
740 GList *compose_list = compose_get_compose_list();
744 for (elem = compose_list; elem != NULL && elem->data != NULL;
746 Compose *c = (Compose*)elem->data;
748 if (!c->targetinfo || !c->targetinfo->msgid ||
752 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
753 gtkut_window_popup(c->window);
761 static GdkColor quote_color1 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
763 static GdkColor quote_color2 =
764 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_color3 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor quote_bgcolor1 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor quote_bgcolor2 =
771 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor quote_bgcolor3 =
773 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
775 static GdkColor signature_color = {
782 static GdkColor uri_color = {
789 static void compose_create_tags(GtkTextView *text, Compose *compose)
791 GtkTextBuffer *buffer;
792 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 #if !GTK_CHECK_VERSION(2, 24, 0)
800 buffer = gtk_text_view_get_buffer(text);
802 if (prefs_common.enable_color) {
803 /* grab the quote colors, converting from an int to a GdkColor */
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
806 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
810 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
812 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
814 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
816 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
818 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
821 signature_color = quote_color1 = quote_color2 = quote_color3 =
822 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
825 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
826 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
827 "foreground-gdk", "e_color1,
828 "paragraph-background-gdk", "e_bgcolor1,
830 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
831 "foreground-gdk", "e_color2,
832 "paragraph-background-gdk", "e_bgcolor2,
834 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
835 "foreground-gdk", "e_color3,
836 "paragraph-background-gdk", "e_bgcolor3,
839 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
840 "foreground-gdk", "e_color1,
842 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
843 "foreground-gdk", "e_color2,
845 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
846 "foreground-gdk", "e_color3,
850 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
851 "foreground-gdk", &signature_color,
854 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
855 "foreground-gdk", &uri_color,
857 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
858 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
860 color[0] = quote_color1;
861 color[1] = quote_color2;
862 color[2] = quote_color3;
863 color[3] = quote_bgcolor1;
864 color[4] = quote_bgcolor2;
865 color[5] = quote_bgcolor3;
866 color[6] = signature_color;
867 color[7] = uri_color;
868 #if !GTK_CHECK_VERSION(2, 24, 0)
869 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
870 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
872 for (i = 0; i < 8; i++) {
873 if (success[i] == FALSE) {
876 g_warning("Compose: color allocation failed.\n");
877 style = gtk_widget_get_style(GTK_WIDGET(text));
878 quote_color1 = quote_color2 = quote_color3 =
879 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
880 signature_color = uri_color = black;
886 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
887 GPtrArray *attach_files)
889 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
892 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
894 return compose_generic_new(account, mailto, item, NULL, NULL);
897 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
899 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
902 #define SCROLL_TO_CURSOR(compose) { \
903 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
904 gtk_text_view_get_buffer( \
905 GTK_TEXT_VIEW(compose->text))); \
906 gtk_text_view_scroll_mark_onscreen( \
907 GTK_TEXT_VIEW(compose->text), \
911 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
914 if (folderidentifier) {
915 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
916 prefs_common.compose_save_to_history = add_history(
917 prefs_common.compose_save_to_history, folderidentifier);
918 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
919 prefs_common.compose_save_to_history);
922 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
923 if (folderidentifier)
924 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
926 gtk_entry_set_text(GTK_ENTRY(entry), "");
929 static gchar *compose_get_save_to(Compose *compose)
932 gchar *result = NULL;
933 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
934 result = gtk_editable_get_chars(entry, 0, -1);
937 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
938 prefs_common.compose_save_to_history = add_history(
939 prefs_common.compose_save_to_history, result);
940 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
941 prefs_common.compose_save_to_history);
946 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
947 GPtrArray *attach_files, GList *listAddress )
950 GtkTextView *textview;
951 GtkTextBuffer *textbuf;
953 const gchar *subject_format = NULL;
954 const gchar *body_format = NULL;
955 gchar *mailto_from = NULL;
956 PrefsAccount *mailto_account = NULL;
957 MsgInfo* dummyinfo = NULL;
958 gint cursor_pos = -1;
959 MailField mfield = NO_FIELD_PRESENT;
963 /* check if mailto defines a from */
964 if (mailto && *mailto != '\0') {
965 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
966 /* mailto defines a from, check if we can get account prefs from it,
967 if not, the account prefs will be guessed using other ways, but we'll keep
970 mailto_account = account_find_from_address(mailto_from, TRUE);
972 account = mailto_account;
975 /* if no account prefs set from mailto, set if from folder prefs (if any) */
976 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
977 account = account_find_from_id(item->prefs->default_account);
979 /* if no account prefs set, fallback to the current one */
980 if (!account) account = cur_account;
981 cm_return_val_if_fail(account != NULL, NULL);
983 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
985 /* override from name if mailto asked for it */
987 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
990 /* override from name according to folder properties */
991 if (item && item->prefs &&
992 item->prefs->compose_with_format &&
993 item->prefs->compose_override_from_format &&
994 *item->prefs->compose_override_from_format != '\0') {
999 dummyinfo = compose_msginfo_new_from_compose(compose);
1001 /* decode \-escape sequences in the internal representation of the quote format */
1002 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1003 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1006 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1007 compose->gtkaspell);
1009 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1011 quote_fmt_scan_string(tmp);
1014 buf = quote_fmt_get_buffer();
1016 alertpanel_error(_("New message From format error."));
1018 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1019 quote_fmt_reset_vartable();
1024 compose->replyinfo = NULL;
1025 compose->fwdinfo = NULL;
1027 textview = GTK_TEXT_VIEW(compose->text);
1028 textbuf = gtk_text_view_get_buffer(textview);
1029 compose_create_tags(textview, compose);
1031 undo_block(compose->undostruct);
1033 compose_set_dictionaries_from_folder_prefs(compose, item);
1036 if (account->auto_sig)
1037 compose_insert_sig(compose, FALSE);
1038 gtk_text_buffer_get_start_iter(textbuf, &iter);
1039 gtk_text_buffer_place_cursor(textbuf, &iter);
1041 if (account->protocol != A_NNTP) {
1042 if (mailto && *mailto != '\0') {
1043 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1046 compose_set_folder_prefs(compose, item, TRUE);
1048 if (item && item->ret_rcpt) {
1049 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1052 if (mailto && *mailto != '\0') {
1053 if (!strchr(mailto, '@'))
1054 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1056 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1057 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1058 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1059 mfield = TO_FIELD_PRESENT;
1062 * CLAWS: just don't allow return receipt request, even if the user
1063 * may want to send an email. simple but foolproof.
1065 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1067 compose_add_field_list( compose, listAddress );
1069 if (item && item->prefs && item->prefs->compose_with_format) {
1070 subject_format = item->prefs->compose_subject_format;
1071 body_format = item->prefs->compose_body_format;
1072 } else if (account->compose_with_format) {
1073 subject_format = account->compose_subject_format;
1074 body_format = account->compose_body_format;
1075 } else if (prefs_common.compose_with_format) {
1076 subject_format = prefs_common.compose_subject_format;
1077 body_format = prefs_common.compose_body_format;
1080 if (subject_format || body_format) {
1083 && *subject_format != '\0' )
1085 gchar *subject = NULL;
1090 dummyinfo = compose_msginfo_new_from_compose(compose);
1092 /* decode \-escape sequences in the internal representation of the quote format */
1093 tmp = g_malloc(strlen(subject_format)+1);
1094 pref_get_unescaped_pref(tmp, subject_format);
1096 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1098 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1099 compose->gtkaspell);
1101 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1103 quote_fmt_scan_string(tmp);
1106 buf = quote_fmt_get_buffer();
1108 alertpanel_error(_("New message subject format error."));
1110 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1111 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1112 quote_fmt_reset_vartable();
1116 mfield = SUBJECT_FIELD_PRESENT;
1120 && *body_format != '\0' )
1123 GtkTextBuffer *buffer;
1124 GtkTextIter start, end;
1128 dummyinfo = compose_msginfo_new_from_compose(compose);
1130 text = GTK_TEXT_VIEW(compose->text);
1131 buffer = gtk_text_view_get_buffer(text);
1132 gtk_text_buffer_get_start_iter(buffer, &start);
1133 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1134 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1136 compose_quote_fmt(compose, dummyinfo,
1138 NULL, tmp, FALSE, TRUE,
1139 _("The body of the \"New message\" template has an error at line %d."));
1140 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1141 quote_fmt_reset_vartable();
1145 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1146 gtkaspell_highlight_all(compose->gtkaspell);
1148 mfield = BODY_FIELD_PRESENT;
1152 procmsg_msginfo_free( dummyinfo );
1158 for (i = 0; i < attach_files->len; i++) {
1159 file = g_ptr_array_index(attach_files, i);
1160 compose_attach_append(compose, file, file, NULL, NULL);
1164 compose_show_first_last_header(compose, TRUE);
1166 /* Set save folder */
1167 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1168 gchar *folderidentifier;
1170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1171 folderidentifier = folder_item_get_identifier(item);
1172 compose_set_save_to(compose, folderidentifier);
1173 g_free(folderidentifier);
1176 /* Place cursor according to provided input (mfield) */
1178 case NO_FIELD_PRESENT:
1179 if (compose->header_last)
1180 gtk_widget_grab_focus(compose->header_last->entry);
1182 case TO_FIELD_PRESENT:
1183 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1185 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1188 gtk_widget_grab_focus(compose->subject_entry);
1190 case SUBJECT_FIELD_PRESENT:
1191 textview = GTK_TEXT_VIEW(compose->text);
1194 textbuf = gtk_text_view_get_buffer(textview);
1197 mark = gtk_text_buffer_get_insert(textbuf);
1198 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1199 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1201 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1202 * only defers where it comes to the variable body
1203 * is not null. If no body is present compose->text
1204 * will be null in which case you cannot place the
1205 * cursor inside the component so. An empty component
1206 * is therefore created before placing the cursor
1208 case BODY_FIELD_PRESENT:
1209 cursor_pos = quote_fmt_get_cursor_pos();
1210 if (cursor_pos == -1)
1211 gtk_widget_grab_focus(compose->header_last->entry);
1213 gtk_widget_grab_focus(compose->text);
1217 undo_unblock(compose->undostruct);
1219 if (prefs_common.auto_exteditor)
1220 compose_exec_ext_editor(compose);
1222 compose->draft_timeout_tag = -1;
1223 SCROLL_TO_CURSOR(compose);
1225 compose->modified = FALSE;
1226 compose_set_title(compose);
1228 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1233 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1234 gboolean override_pref, const gchar *system)
1236 const gchar *privacy = NULL;
1238 cm_return_if_fail(compose != NULL);
1239 cm_return_if_fail(account != NULL);
1241 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1246 else if (account->default_privacy_system
1247 && strlen(account->default_privacy_system)) {
1248 privacy = account->default_privacy_system;
1250 GSList *privacy_avail = privacy_get_system_ids();
1251 if (privacy_avail && g_slist_length(privacy_avail)) {
1252 privacy = (gchar *)(privacy_avail->data);
1255 if (privacy != NULL) {
1257 g_free(compose->privacy_system);
1258 compose->privacy_system = NULL;
1260 if (compose->privacy_system == NULL)
1261 compose->privacy_system = g_strdup(privacy);
1262 else if (*(compose->privacy_system) == '\0') {
1263 g_free(compose->privacy_system);
1264 compose->privacy_system = g_strdup(privacy);
1266 compose_update_privacy_system_menu_item(compose, FALSE);
1267 compose_use_encryption(compose, TRUE);
1271 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1273 const gchar *privacy = NULL;
1277 else if (account->default_privacy_system
1278 && strlen(account->default_privacy_system)) {
1279 privacy = account->default_privacy_system;
1281 GSList *privacy_avail = privacy_get_system_ids();
1282 if (privacy_avail && g_slist_length(privacy_avail)) {
1283 privacy = (gchar *)(privacy_avail->data);
1287 if (privacy != NULL) {
1289 g_free(compose->privacy_system);
1290 compose->privacy_system = NULL;
1292 if (compose->privacy_system == NULL)
1293 compose->privacy_system = g_strdup(privacy);
1294 compose_update_privacy_system_menu_item(compose, FALSE);
1295 compose_use_signing(compose, TRUE);
1299 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1303 Compose *compose = NULL;
1305 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1307 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1308 cm_return_val_if_fail(msginfo != NULL, NULL);
1310 list_len = g_slist_length(msginfo_list);
1314 case COMPOSE_REPLY_TO_ADDRESS:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1316 FALSE, prefs_common.default_reply_list, FALSE, body);
1318 case COMPOSE_REPLY_WITH_QUOTE:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1320 FALSE, prefs_common.default_reply_list, FALSE, body);
1322 case COMPOSE_REPLY_WITHOUT_QUOTE:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1324 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1326 case COMPOSE_REPLY_TO_SENDER:
1327 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1328 FALSE, FALSE, TRUE, body);
1330 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1331 compose = compose_followup_and_reply_to(msginfo,
1332 COMPOSE_QUOTE_CHECK,
1333 FALSE, FALSE, body);
1335 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1337 FALSE, FALSE, TRUE, body);
1339 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1341 FALSE, FALSE, TRUE, NULL);
1343 case COMPOSE_REPLY_TO_ALL:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345 TRUE, FALSE, FALSE, body);
1347 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1349 TRUE, FALSE, FALSE, body);
1351 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1353 TRUE, FALSE, FALSE, NULL);
1355 case COMPOSE_REPLY_TO_LIST:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1357 FALSE, TRUE, FALSE, body);
1359 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1361 FALSE, TRUE, FALSE, body);
1363 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1364 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1365 FALSE, TRUE, FALSE, NULL);
1367 case COMPOSE_FORWARD:
1368 if (prefs_common.forward_as_attachment) {
1369 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1372 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1376 case COMPOSE_FORWARD_INLINE:
1377 /* check if we reply to more than one Message */
1378 if (list_len == 1) {
1379 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1382 /* more messages FALL THROUGH */
1383 case COMPOSE_FORWARD_AS_ATTACH:
1384 compose = compose_forward_multiple(NULL, msginfo_list);
1386 case COMPOSE_REDIRECT:
1387 compose = compose_redirect(NULL, msginfo, FALSE);
1390 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1393 if (compose == NULL) {
1394 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1398 compose->rmode = mode;
1399 switch (compose->rmode) {
1401 case COMPOSE_REPLY_WITH_QUOTE:
1402 case COMPOSE_REPLY_WITHOUT_QUOTE:
1403 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1404 debug_print("reply mode Normal\n");
1405 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1406 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1408 case COMPOSE_REPLY_TO_SENDER:
1409 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1410 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1411 debug_print("reply mode Sender\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1414 case COMPOSE_REPLY_TO_ALL:
1415 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1416 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1417 debug_print("reply mode All\n");
1418 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1420 case COMPOSE_REPLY_TO_LIST:
1421 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1422 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1423 debug_print("reply mode List\n");
1424 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1426 case COMPOSE_REPLY_TO_ADDRESS:
1427 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1435 static Compose *compose_reply(MsgInfo *msginfo,
1436 ComposeQuoteMode quote_mode,
1442 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1443 to_sender, FALSE, body);
1446 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1447 ComposeQuoteMode quote_mode,
1452 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1453 to_sender, TRUE, body);
1456 static void compose_extract_original_charset(Compose *compose)
1458 MsgInfo *info = NULL;
1459 if (compose->replyinfo) {
1460 info = compose->replyinfo;
1461 } else if (compose->fwdinfo) {
1462 info = compose->fwdinfo;
1463 } else if (compose->targetinfo) {
1464 info = compose->targetinfo;
1467 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1468 MimeInfo *partinfo = mimeinfo;
1469 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1470 partinfo = procmime_mimeinfo_next(partinfo);
1472 compose->orig_charset =
1473 g_strdup(procmime_mimeinfo_get_parameter(
1474 partinfo, "charset"));
1476 procmime_mimeinfo_free_all(mimeinfo);
1480 #define SIGNAL_BLOCK(buffer) { \
1481 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1482 G_CALLBACK(compose_changed_cb), \
1484 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1485 G_CALLBACK(text_inserted), \
1489 #define SIGNAL_UNBLOCK(buffer) { \
1490 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1491 G_CALLBACK(compose_changed_cb), \
1493 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1494 G_CALLBACK(text_inserted), \
1498 static Compose *compose_generic_reply(MsgInfo *msginfo,
1499 ComposeQuoteMode quote_mode,
1500 gboolean to_all, gboolean to_ml,
1502 gboolean followup_and_reply_to,
1506 PrefsAccount *account = NULL;
1507 GtkTextView *textview;
1508 GtkTextBuffer *textbuf;
1509 gboolean quote = FALSE;
1510 const gchar *qmark = NULL;
1511 const gchar *body_fmt = NULL;
1512 gchar *s_system = NULL;
1514 cm_return_val_if_fail(msginfo != NULL, NULL);
1515 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1517 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1519 cm_return_val_if_fail(account != NULL, NULL);
1521 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1523 compose->updating = TRUE;
1525 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1526 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1528 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1529 if (!compose->replyinfo)
1530 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1532 compose_extract_original_charset(compose);
1534 if (msginfo->folder && msginfo->folder->ret_rcpt)
1535 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1537 /* Set save folder */
1538 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1539 gchar *folderidentifier;
1541 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1542 folderidentifier = folder_item_get_identifier(msginfo->folder);
1543 compose_set_save_to(compose, folderidentifier);
1544 g_free(folderidentifier);
1547 if (compose_parse_header(compose, msginfo) < 0) {
1548 compose->updating = FALSE;
1549 compose_destroy(compose);
1553 /* override from name according to folder properties */
1554 if (msginfo->folder && msginfo->folder->prefs &&
1555 msginfo->folder->prefs->reply_with_format &&
1556 msginfo->folder->prefs->reply_override_from_format &&
1557 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1562 /* decode \-escape sequences in the internal representation of the quote format */
1563 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1564 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1567 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1568 compose->gtkaspell);
1570 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1572 quote_fmt_scan_string(tmp);
1575 buf = quote_fmt_get_buffer();
1577 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1579 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1580 quote_fmt_reset_vartable();
1585 textview = (GTK_TEXT_VIEW(compose->text));
1586 textbuf = gtk_text_view_get_buffer(textview);
1587 compose_create_tags(textview, compose);
1589 undo_block(compose->undostruct);
1591 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1592 gtkaspell_block_check(compose->gtkaspell);
1595 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1596 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1597 /* use the reply format of folder (if enabled), or the account's one
1598 (if enabled) or fallback to the global reply format, which is always
1599 enabled (even if empty), and use the relevant quotemark */
1601 if (msginfo->folder && msginfo->folder->prefs &&
1602 msginfo->folder->prefs->reply_with_format) {
1603 qmark = msginfo->folder->prefs->reply_quotemark;
1604 body_fmt = msginfo->folder->prefs->reply_body_format;
1606 } else if (account->reply_with_format) {
1607 qmark = account->reply_quotemark;
1608 body_fmt = account->reply_body_format;
1611 qmark = prefs_common.quotemark;
1612 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1613 body_fmt = gettext(prefs_common.quotefmt);
1620 /* empty quotemark is not allowed */
1621 if (qmark == NULL || *qmark == '\0')
1623 compose_quote_fmt(compose, compose->replyinfo,
1624 body_fmt, qmark, body, FALSE, TRUE,
1625 _("The body of the \"Reply\" template has an error at line %d."));
1626 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1627 quote_fmt_reset_vartable();
1630 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1631 compose_force_encryption(compose, account, FALSE, s_system);
1634 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1635 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1636 compose_force_signing(compose, account, s_system);
1640 SIGNAL_BLOCK(textbuf);
1642 if (account->auto_sig)
1643 compose_insert_sig(compose, FALSE);
1645 compose_wrap_all(compose);
1648 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1649 gtkaspell_highlight_all(compose->gtkaspell);
1650 gtkaspell_unblock_check(compose->gtkaspell);
1652 SIGNAL_UNBLOCK(textbuf);
1654 gtk_widget_grab_focus(compose->text);
1656 undo_unblock(compose->undostruct);
1658 if (prefs_common.auto_exteditor)
1659 compose_exec_ext_editor(compose);
1661 compose->modified = FALSE;
1662 compose_set_title(compose);
1664 compose->updating = FALSE;
1665 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1666 SCROLL_TO_CURSOR(compose);
1668 if (compose->deferred_destroy) {
1669 compose_destroy(compose);
1677 #define INSERT_FW_HEADER(var, hdr) \
1678 if (msginfo->var && *msginfo->var) { \
1679 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1680 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1681 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1684 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1685 gboolean as_attach, const gchar *body,
1686 gboolean no_extedit,
1690 GtkTextView *textview;
1691 GtkTextBuffer *textbuf;
1692 gint cursor_pos = -1;
1695 cm_return_val_if_fail(msginfo != NULL, NULL);
1696 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1699 !(account = compose_guess_forward_account_from_msginfo
1701 account = cur_account;
1703 if (!prefs_common.forward_as_attachment)
1704 mode = COMPOSE_FORWARD_INLINE;
1706 mode = COMPOSE_FORWARD;
1707 compose = compose_create(account, msginfo->folder, mode, batch);
1709 compose->updating = TRUE;
1710 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1711 if (!compose->fwdinfo)
1712 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1714 compose_extract_original_charset(compose);
1716 if (msginfo->subject && *msginfo->subject) {
1717 gchar *buf, *buf2, *p;
1719 buf = p = g_strdup(msginfo->subject);
1720 p += subject_get_prefix_length(p);
1721 memmove(buf, p, strlen(p) + 1);
1723 buf2 = g_strdup_printf("Fw: %s", buf);
1724 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1730 /* override from name according to folder properties */
1731 if (msginfo->folder && msginfo->folder->prefs &&
1732 msginfo->folder->prefs->forward_with_format &&
1733 msginfo->folder->prefs->forward_override_from_format &&
1734 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1738 MsgInfo *full_msginfo = NULL;
1741 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1743 full_msginfo = procmsg_msginfo_copy(msginfo);
1745 /* decode \-escape sequences in the internal representation of the quote format */
1746 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1747 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1750 gtkaspell_block_check(compose->gtkaspell);
1751 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1752 compose->gtkaspell);
1754 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1756 quote_fmt_scan_string(tmp);
1759 buf = quote_fmt_get_buffer();
1761 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1763 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1764 quote_fmt_reset_vartable();
1767 procmsg_msginfo_free(full_msginfo);
1770 textview = GTK_TEXT_VIEW(compose->text);
1771 textbuf = gtk_text_view_get_buffer(textview);
1772 compose_create_tags(textview, compose);
1774 undo_block(compose->undostruct);
1778 msgfile = procmsg_get_message_file(msginfo);
1779 if (!is_file_exist(msgfile))
1780 g_warning("%s: file not exist\n", msgfile);
1782 compose_attach_append(compose, msgfile, msgfile,
1783 "message/rfc822", NULL);
1787 const gchar *qmark = NULL;
1788 const gchar *body_fmt = NULL;
1789 MsgInfo *full_msginfo;
1791 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1792 body_fmt = gettext(prefs_common.fw_quotefmt);
1796 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1798 full_msginfo = procmsg_msginfo_copy(msginfo);
1800 /* use the forward format of folder (if enabled), or the account's one
1801 (if enabled) or fallback to the global forward format, which is always
1802 enabled (even if empty), and use the relevant quotemark */
1803 if (msginfo->folder && msginfo->folder->prefs &&
1804 msginfo->folder->prefs->forward_with_format) {
1805 qmark = msginfo->folder->prefs->forward_quotemark;
1806 body_fmt = msginfo->folder->prefs->forward_body_format;
1808 } else if (account->forward_with_format) {
1809 qmark = account->forward_quotemark;
1810 body_fmt = account->forward_body_format;
1813 qmark = prefs_common.fw_quotemark;
1814 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1815 body_fmt = gettext(prefs_common.fw_quotefmt);
1820 /* empty quotemark is not allowed */
1821 if (qmark == NULL || *qmark == '\0')
1824 compose_quote_fmt(compose, full_msginfo,
1825 body_fmt, qmark, body, FALSE, TRUE,
1826 _("The body of the \"Forward\" template has an error at line %d."));
1827 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1828 quote_fmt_reset_vartable();
1829 compose_attach_parts(compose, msginfo);
1831 procmsg_msginfo_free(full_msginfo);
1834 SIGNAL_BLOCK(textbuf);
1836 if (account->auto_sig)
1837 compose_insert_sig(compose, FALSE);
1839 compose_wrap_all(compose);
1842 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1843 gtkaspell_highlight_all(compose->gtkaspell);
1844 gtkaspell_unblock_check(compose->gtkaspell);
1846 SIGNAL_UNBLOCK(textbuf);
1848 cursor_pos = quote_fmt_get_cursor_pos();
1849 if (cursor_pos == -1)
1850 gtk_widget_grab_focus(compose->header_last->entry);
1852 gtk_widget_grab_focus(compose->text);
1854 if (!no_extedit && prefs_common.auto_exteditor)
1855 compose_exec_ext_editor(compose);
1858 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1859 gchar *folderidentifier;
1861 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1862 folderidentifier = folder_item_get_identifier(msginfo->folder);
1863 compose_set_save_to(compose, folderidentifier);
1864 g_free(folderidentifier);
1867 undo_unblock(compose->undostruct);
1869 compose->modified = FALSE;
1870 compose_set_title(compose);
1872 compose->updating = FALSE;
1873 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1874 SCROLL_TO_CURSOR(compose);
1876 if (compose->deferred_destroy) {
1877 compose_destroy(compose);
1881 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1886 #undef INSERT_FW_HEADER
1888 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1891 GtkTextView *textview;
1892 GtkTextBuffer *textbuf;
1896 gboolean single_mail = TRUE;
1898 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1900 if (g_slist_length(msginfo_list) > 1)
1901 single_mail = FALSE;
1903 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1904 if (((MsgInfo *)msginfo->data)->folder == NULL)
1907 /* guess account from first selected message */
1909 !(account = compose_guess_forward_account_from_msginfo
1910 (msginfo_list->data)))
1911 account = cur_account;
1913 cm_return_val_if_fail(account != NULL, NULL);
1915 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1916 if (msginfo->data) {
1917 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1918 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1922 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1923 g_warning("no msginfo_list");
1927 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1929 compose->updating = TRUE;
1931 /* override from name according to folder properties */
1932 if (msginfo_list->data) {
1933 MsgInfo *msginfo = msginfo_list->data;
1935 if (msginfo->folder && msginfo->folder->prefs &&
1936 msginfo->folder->prefs->forward_with_format &&
1937 msginfo->folder->prefs->forward_override_from_format &&
1938 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1943 /* decode \-escape sequences in the internal representation of the quote format */
1944 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1945 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1948 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1949 compose->gtkaspell);
1951 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1953 quote_fmt_scan_string(tmp);
1956 buf = quote_fmt_get_buffer();
1958 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1960 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1961 quote_fmt_reset_vartable();
1967 textview = GTK_TEXT_VIEW(compose->text);
1968 textbuf = gtk_text_view_get_buffer(textview);
1969 compose_create_tags(textview, compose);
1971 undo_block(compose->undostruct);
1972 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1973 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1975 if (!is_file_exist(msgfile))
1976 g_warning("%s: file not exist\n", msgfile);
1978 compose_attach_append(compose, msgfile, msgfile,
1979 "message/rfc822", NULL);
1984 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1985 if (info->subject && *info->subject) {
1986 gchar *buf, *buf2, *p;
1988 buf = p = g_strdup(info->subject);
1989 p += subject_get_prefix_length(p);
1990 memmove(buf, p, strlen(p) + 1);
1992 buf2 = g_strdup_printf("Fw: %s", buf);
1993 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1999 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2000 _("Fw: multiple emails"));
2003 SIGNAL_BLOCK(textbuf);
2005 if (account->auto_sig)
2006 compose_insert_sig(compose, FALSE);
2008 compose_wrap_all(compose);
2010 SIGNAL_UNBLOCK(textbuf);
2012 gtk_text_buffer_get_start_iter(textbuf, &iter);
2013 gtk_text_buffer_place_cursor(textbuf, &iter);
2015 gtk_widget_grab_focus(compose->header_last->entry);
2016 undo_unblock(compose->undostruct);
2017 compose->modified = FALSE;
2018 compose_set_title(compose);
2020 compose->updating = FALSE;
2021 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2022 SCROLL_TO_CURSOR(compose);
2024 if (compose->deferred_destroy) {
2025 compose_destroy(compose);
2029 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2034 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2036 GtkTextIter start = *iter;
2037 GtkTextIter end_iter;
2038 int start_pos = gtk_text_iter_get_offset(&start);
2040 if (!compose->account->sig_sep)
2043 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2044 start_pos+strlen(compose->account->sig_sep));
2046 /* check sig separator */
2047 str = gtk_text_iter_get_text(&start, &end_iter);
2048 if (!strcmp(str, compose->account->sig_sep)) {
2050 /* check end of line (\n) */
2051 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2052 start_pos+strlen(compose->account->sig_sep));
2053 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2054 start_pos+strlen(compose->account->sig_sep)+1);
2055 tmp = gtk_text_iter_get_text(&start, &end_iter);
2056 if (!strcmp(tmp,"\n")) {
2068 static void compose_colorize_signature(Compose *compose)
2070 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2072 GtkTextIter end_iter;
2073 gtk_text_buffer_get_start_iter(buffer, &iter);
2074 while (gtk_text_iter_forward_line(&iter))
2075 if (compose_is_sig_separator(compose, buffer, &iter)) {
2076 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2077 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2081 #define BLOCK_WRAP() { \
2082 prev_autowrap = compose->autowrap; \
2083 buffer = gtk_text_view_get_buffer( \
2084 GTK_TEXT_VIEW(compose->text)); \
2085 compose->autowrap = FALSE; \
2087 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2088 G_CALLBACK(compose_changed_cb), \
2090 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2091 G_CALLBACK(text_inserted), \
2094 #define UNBLOCK_WRAP() { \
2095 compose->autowrap = prev_autowrap; \
2096 if (compose->autowrap) { \
2097 gint old = compose->draft_timeout_tag; \
2098 compose->draft_timeout_tag = -2; \
2099 compose_wrap_all(compose); \
2100 compose->draft_timeout_tag = old; \
2103 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2104 G_CALLBACK(compose_changed_cb), \
2106 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2107 G_CALLBACK(text_inserted), \
2111 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2113 Compose *compose = NULL;
2114 PrefsAccount *account = NULL;
2115 GtkTextView *textview;
2116 GtkTextBuffer *textbuf;
2120 gchar buf[BUFFSIZE];
2121 gboolean use_signing = FALSE;
2122 gboolean use_encryption = FALSE;
2123 gchar *privacy_system = NULL;
2124 int priority = PRIORITY_NORMAL;
2125 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2126 gboolean autowrap = prefs_common.autowrap;
2127 gboolean autoindent = prefs_common.auto_indent;
2129 cm_return_val_if_fail(msginfo != NULL, NULL);
2130 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2132 if (compose_put_existing_to_front(msginfo)) {
2136 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2137 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2138 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2139 gchar queueheader_buf[BUFFSIZE];
2142 /* Select Account from queue headers */
2143 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2144 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2145 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2146 account = account_find_from_id(id);
2148 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2149 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2150 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2151 account = account_find_from_id(id);
2153 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2154 sizeof(queueheader_buf), "NAID:")) {
2155 id = atoi(&queueheader_buf[strlen("NAID:")]);
2156 account = account_find_from_id(id);
2158 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2159 sizeof(queueheader_buf), "MAID:")) {
2160 id = atoi(&queueheader_buf[strlen("MAID:")]);
2161 account = account_find_from_id(id);
2163 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2164 sizeof(queueheader_buf), "S:")) {
2165 account = account_find_from_address(queueheader_buf, FALSE);
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2169 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2170 use_signing = param;
2173 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2174 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2175 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2176 use_signing = param;
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2181 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2182 use_encryption = param;
2184 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2185 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2186 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2187 use_encryption = param;
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2191 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2196 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2201 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2205 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Priority: ")) {
2209 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2212 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2213 sizeof(queueheader_buf), "RMID:")) {
2214 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2215 if (tokens[0] && tokens[1] && tokens[2]) {
2216 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2217 if (orig_item != NULL) {
2218 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2223 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2224 sizeof(queueheader_buf), "FMID:")) {
2225 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2226 if (tokens[0] && tokens[1] && tokens[2]) {
2227 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2228 if (orig_item != NULL) {
2229 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2235 account = msginfo->folder->folder->account;
2238 if (!account && prefs_common.reedit_account_autosel) {
2239 gchar from[BUFFSIZE];
2240 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2241 extract_address(from);
2242 account = account_find_from_address(from, FALSE);
2246 account = cur_account;
2248 cm_return_val_if_fail(account != NULL, NULL);
2250 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2252 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2253 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2254 compose->autowrap = autowrap;
2255 compose->replyinfo = replyinfo;
2256 compose->fwdinfo = fwdinfo;
2258 compose->updating = TRUE;
2259 compose->priority = priority;
2261 if (privacy_system != NULL) {
2262 compose->privacy_system = privacy_system;
2263 compose_use_signing(compose, use_signing);
2264 compose_use_encryption(compose, use_encryption);
2265 compose_update_privacy_system_menu_item(compose, FALSE);
2267 activate_privacy_system(compose, account, FALSE);
2270 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2272 compose_extract_original_charset(compose);
2274 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2275 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2276 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2277 gchar queueheader_buf[BUFFSIZE];
2279 /* Set message save folder */
2280 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2282 compose_set_save_to(compose, &queueheader_buf[4]);
2284 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2285 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2287 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2292 if (compose_parse_header(compose, msginfo) < 0) {
2293 compose->updating = FALSE;
2294 compose_destroy(compose);
2297 compose_reedit_set_entry(compose, msginfo);
2299 textview = GTK_TEXT_VIEW(compose->text);
2300 textbuf = gtk_text_view_get_buffer(textview);
2301 compose_create_tags(textview, compose);
2303 mark = gtk_text_buffer_get_insert(textbuf);
2304 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2306 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2307 G_CALLBACK(compose_changed_cb),
2310 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2311 fp = procmime_get_first_encrypted_text_content(msginfo);
2313 compose_force_encryption(compose, account, TRUE, NULL);
2316 fp = procmime_get_first_text_content(msginfo);
2319 g_warning("Can't get text part\n");
2323 gboolean prev_autowrap = compose->autowrap;
2324 GtkTextBuffer *buffer = textbuf;
2326 while (fgets(buf, sizeof(buf), fp) != NULL) {
2328 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2334 compose_attach_parts(compose, msginfo);
2336 compose_colorize_signature(compose);
2338 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2339 G_CALLBACK(compose_changed_cb),
2342 gtk_widget_grab_focus(compose->text);
2344 if (prefs_common.auto_exteditor) {
2345 compose_exec_ext_editor(compose);
2347 compose->modified = FALSE;
2348 compose_set_title(compose);
2350 compose->updating = FALSE;
2351 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2352 SCROLL_TO_CURSOR(compose);
2354 if (compose->deferred_destroy) {
2355 compose_destroy(compose);
2359 compose->sig_str = account_get_signature_str(compose->account);
2361 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2366 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2373 cm_return_val_if_fail(msginfo != NULL, NULL);
2376 account = account_get_reply_account(msginfo,
2377 prefs_common.reply_account_autosel);
2378 cm_return_val_if_fail(account != NULL, NULL);
2380 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2382 compose->updating = TRUE;
2384 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2385 compose->replyinfo = NULL;
2386 compose->fwdinfo = NULL;
2388 compose_show_first_last_header(compose, TRUE);
2390 gtk_widget_grab_focus(compose->header_last->entry);
2392 filename = procmsg_get_message_file(msginfo);
2394 if (filename == NULL) {
2395 compose->updating = FALSE;
2396 compose_destroy(compose);
2401 compose->redirect_filename = filename;
2403 /* Set save folder */
2404 item = msginfo->folder;
2405 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2406 gchar *folderidentifier;
2408 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2409 folderidentifier = folder_item_get_identifier(item);
2410 compose_set_save_to(compose, folderidentifier);
2411 g_free(folderidentifier);
2414 compose_attach_parts(compose, msginfo);
2416 if (msginfo->subject)
2417 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2419 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2421 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2422 _("The body of the \"Redirect\" template has an error at line %d."));
2423 quote_fmt_reset_vartable();
2424 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2426 compose_colorize_signature(compose);
2429 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2430 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2431 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2433 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2435 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2436 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2437 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2438 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2439 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2440 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2442 if (compose->toolbar->draft_btn)
2443 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2444 if (compose->toolbar->insert_btn)
2445 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2446 if (compose->toolbar->attach_btn)
2447 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2448 if (compose->toolbar->sig_btn)
2449 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2450 if (compose->toolbar->exteditor_btn)
2451 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2452 if (compose->toolbar->linewrap_current_btn)
2453 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2454 if (compose->toolbar->linewrap_all_btn)
2455 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2457 compose->modified = FALSE;
2458 compose_set_title(compose);
2459 compose->updating = FALSE;
2460 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2461 SCROLL_TO_CURSOR(compose);
2463 if (compose->deferred_destroy) {
2464 compose_destroy(compose);
2468 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2473 GList *compose_get_compose_list(void)
2475 return compose_list;
2478 void compose_entry_append(Compose *compose, const gchar *address,
2479 ComposeEntryType type, ComposePrefType pref_type)
2481 const gchar *header;
2483 gboolean in_quote = FALSE;
2484 if (!address || *address == '\0') return;
2491 header = N_("Bcc:");
2493 case COMPOSE_REPLYTO:
2494 header = N_("Reply-To:");
2496 case COMPOSE_NEWSGROUPS:
2497 header = N_("Newsgroups:");
2499 case COMPOSE_FOLLOWUPTO:
2500 header = N_( "Followup-To:");
2502 case COMPOSE_INREPLYTO:
2503 header = N_( "In-Reply-To:");
2510 header = prefs_common_translated_header_name(header);
2512 cur = begin = (gchar *)address;
2514 /* we separate the line by commas, but not if we're inside a quoted
2516 while (*cur != '\0') {
2518 in_quote = !in_quote;
2519 if (*cur == ',' && !in_quote) {
2520 gchar *tmp = g_strdup(begin);
2522 tmp[cur-begin]='\0';
2525 while (*tmp == ' ' || *tmp == '\t')
2527 compose_add_header_entry(compose, header, tmp, pref_type);
2534 gchar *tmp = g_strdup(begin);
2536 tmp[cur-begin]='\0';
2539 while (*tmp == ' ' || *tmp == '\t')
2541 compose_add_header_entry(compose, header, tmp, pref_type);
2546 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2548 #if !GTK_CHECK_VERSION(3, 0, 0)
2549 static GdkColor yellow;
2550 static GdkColor black;
2551 static gboolean yellow_initialised = FALSE;
2553 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2554 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2559 #if !GTK_CHECK_VERSION(3, 0, 0)
2560 if (!yellow_initialised) {
2561 gdk_color_parse("#f5f6be", &yellow);
2562 gdk_color_parse("#000000", &black);
2563 yellow_initialised = gdk_colormap_alloc_color(
2564 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2565 yellow_initialised &= gdk_colormap_alloc_color(
2566 gdk_colormap_get_system(), &black, FALSE, TRUE);
2570 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2571 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2572 if (gtk_entry_get_text(entry) &&
2573 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2574 #if !GTK_CHECK_VERSION(3, 0, 0)
2575 if (yellow_initialised) {
2577 gtk_widget_modify_base(
2578 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2579 GTK_STATE_NORMAL, &yellow);
2580 gtk_widget_modify_text(
2581 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2582 GTK_STATE_NORMAL, &black);
2583 #if !GTK_CHECK_VERSION(3, 0, 0)
2590 void compose_toolbar_cb(gint action, gpointer data)
2592 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2593 Compose *compose = (Compose*)toolbar_item->parent;
2595 cm_return_if_fail(compose != NULL);
2599 compose_send_cb(NULL, compose);
2602 compose_send_later_cb(NULL, compose);
2605 compose_draft(compose, COMPOSE_QUIT_EDITING);
2608 compose_insert_file_cb(NULL, compose);
2611 compose_attach_cb(NULL, compose);
2614 compose_insert_sig(compose, FALSE);
2617 compose_ext_editor_cb(NULL, compose);
2619 case A_LINEWRAP_CURRENT:
2620 compose_beautify_paragraph(compose, NULL, TRUE);
2622 case A_LINEWRAP_ALL:
2623 compose_wrap_all_full(compose, TRUE);
2626 compose_address_cb(NULL, compose);
2629 case A_CHECK_SPELLING:
2630 compose_check_all(NULL, compose);
2638 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2643 gchar *subject = NULL;
2647 gchar **attach = NULL;
2648 gchar *inreplyto = NULL;
2649 MailField mfield = NO_FIELD_PRESENT;
2651 /* get mailto parts but skip from */
2652 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2655 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2656 mfield = TO_FIELD_PRESENT;
2659 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2661 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2663 if (!g_utf8_validate (subject, -1, NULL)) {
2664 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2665 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2668 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2670 mfield = SUBJECT_FIELD_PRESENT;
2673 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2674 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2677 gboolean prev_autowrap = compose->autowrap;
2679 compose->autowrap = FALSE;
2681 mark = gtk_text_buffer_get_insert(buffer);
2682 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2684 if (!g_utf8_validate (body, -1, NULL)) {
2685 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2686 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2689 gtk_text_buffer_insert(buffer, &iter, body, -1);
2691 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2693 compose->autowrap = prev_autowrap;
2694 if (compose->autowrap)
2695 compose_wrap_all(compose);
2696 mfield = BODY_FIELD_PRESENT;
2700 gint i = 0, att = 0;
2701 gchar *warn_files = NULL;
2702 while (attach[i] != NULL) {
2703 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2704 if (utf8_filename) {
2705 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2706 gchar *tmp = g_strdup_printf("%s%s\n",
2707 warn_files?warn_files:"",
2713 g_free(utf8_filename);
2715 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2720 alertpanel_notice(ngettext(
2721 "The following file has been attached: \n%s",
2722 "The following files have been attached: \n%s", att), warn_files);
2727 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2740 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2742 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2743 {"Cc:", NULL, TRUE},
2744 {"References:", NULL, FALSE},
2745 {"Bcc:", NULL, TRUE},
2746 {"Newsgroups:", NULL, TRUE},
2747 {"Followup-To:", NULL, TRUE},
2748 {"List-Post:", NULL, FALSE},
2749 {"X-Priority:", NULL, FALSE},
2750 {NULL, NULL, FALSE}};
2766 cm_return_val_if_fail(msginfo != NULL, -1);
2768 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2769 procheader_get_header_fields(fp, hentry);
2772 if (hentry[H_REPLY_TO].body != NULL) {
2773 if (hentry[H_REPLY_TO].body[0] != '\0') {
2775 conv_unmime_header(hentry[H_REPLY_TO].body,
2778 g_free(hentry[H_REPLY_TO].body);
2779 hentry[H_REPLY_TO].body = NULL;
2781 if (hentry[H_CC].body != NULL) {
2782 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2783 g_free(hentry[H_CC].body);
2784 hentry[H_CC].body = NULL;
2786 if (hentry[H_REFERENCES].body != NULL) {
2787 if (compose->mode == COMPOSE_REEDIT)
2788 compose->references = hentry[H_REFERENCES].body;
2790 compose->references = compose_parse_references
2791 (hentry[H_REFERENCES].body, msginfo->msgid);
2792 g_free(hentry[H_REFERENCES].body);
2794 hentry[H_REFERENCES].body = NULL;
2796 if (hentry[H_BCC].body != NULL) {
2797 if (compose->mode == COMPOSE_REEDIT)
2799 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2800 g_free(hentry[H_BCC].body);
2801 hentry[H_BCC].body = NULL;
2803 if (hentry[H_NEWSGROUPS].body != NULL) {
2804 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2805 hentry[H_NEWSGROUPS].body = NULL;
2807 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2808 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2809 compose->followup_to =
2810 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2813 g_free(hentry[H_FOLLOWUP_TO].body);
2814 hentry[H_FOLLOWUP_TO].body = NULL;
2816 if (hentry[H_LIST_POST].body != NULL) {
2817 gchar *to = NULL, *start = NULL;
2819 extract_address(hentry[H_LIST_POST].body);
2820 if (hentry[H_LIST_POST].body[0] != '\0') {
2821 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2823 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2824 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2827 g_free(compose->ml_post);
2828 compose->ml_post = to;
2831 g_free(hentry[H_LIST_POST].body);
2832 hentry[H_LIST_POST].body = NULL;
2835 /* CLAWS - X-Priority */
2836 if (compose->mode == COMPOSE_REEDIT)
2837 if (hentry[H_X_PRIORITY].body != NULL) {
2840 priority = atoi(hentry[H_X_PRIORITY].body);
2841 g_free(hentry[H_X_PRIORITY].body);
2843 hentry[H_X_PRIORITY].body = NULL;
2845 if (priority < PRIORITY_HIGHEST ||
2846 priority > PRIORITY_LOWEST)
2847 priority = PRIORITY_NORMAL;
2849 compose->priority = priority;
2852 if (compose->mode == COMPOSE_REEDIT) {
2853 if (msginfo->inreplyto && *msginfo->inreplyto)
2854 compose->inreplyto = g_strdup(msginfo->inreplyto);
2858 if (msginfo->msgid && *msginfo->msgid)
2859 compose->inreplyto = g_strdup(msginfo->msgid);
2861 if (!compose->references) {
2862 if (msginfo->msgid && *msginfo->msgid) {
2863 if (msginfo->inreplyto && *msginfo->inreplyto)
2864 compose->references =
2865 g_strdup_printf("<%s>\n\t<%s>",
2869 compose->references =
2870 g_strconcat("<", msginfo->msgid, ">",
2872 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2873 compose->references =
2874 g_strconcat("<", msginfo->inreplyto, ">",
2882 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2884 GSList *ref_id_list, *cur;
2888 ref_id_list = references_list_append(NULL, ref);
2889 if (!ref_id_list) return NULL;
2890 if (msgid && *msgid)
2891 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2896 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2897 /* "<" + Message-ID + ">" + CR+LF+TAB */
2898 len += strlen((gchar *)cur->data) + 5;
2900 if (len > MAX_REFERENCES_LEN) {
2901 /* remove second message-ID */
2902 if (ref_id_list && ref_id_list->next &&
2903 ref_id_list->next->next) {
2904 g_free(ref_id_list->next->data);
2905 ref_id_list = g_slist_remove
2906 (ref_id_list, ref_id_list->next->data);
2908 slist_free_strings(ref_id_list);
2909 g_slist_free(ref_id_list);
2916 new_ref = g_string_new("");
2917 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2918 if (new_ref->len > 0)
2919 g_string_append(new_ref, "\n\t");
2920 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2923 slist_free_strings(ref_id_list);
2924 g_slist_free(ref_id_list);
2926 new_ref_str = new_ref->str;
2927 g_string_free(new_ref, FALSE);
2932 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2933 const gchar *fmt, const gchar *qmark,
2934 const gchar *body, gboolean rewrap,
2935 gboolean need_unescape,
2936 const gchar *err_msg)
2938 MsgInfo* dummyinfo = NULL;
2939 gchar *quote_str = NULL;
2941 gboolean prev_autowrap;
2942 const gchar *trimmed_body = body;
2943 gint cursor_pos = -1;
2944 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2945 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2950 SIGNAL_BLOCK(buffer);
2953 dummyinfo = compose_msginfo_new_from_compose(compose);
2954 msginfo = dummyinfo;
2957 if (qmark != NULL) {
2959 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2960 compose->gtkaspell);
2962 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2964 quote_fmt_scan_string(qmark);
2967 buf = quote_fmt_get_buffer();
2969 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2971 Xstrdup_a(quote_str, buf, goto error)
2974 if (fmt && *fmt != '\0') {
2977 while (*trimmed_body == '\n')
2981 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2982 compose->gtkaspell);
2984 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2986 if (need_unescape) {
2989 /* decode \-escape sequences in the internal representation of the quote format */
2990 tmp = g_malloc(strlen(fmt)+1);
2991 pref_get_unescaped_pref(tmp, fmt);
2992 quote_fmt_scan_string(tmp);
2996 quote_fmt_scan_string(fmt);
3000 buf = quote_fmt_get_buffer();
3002 gint line = quote_fmt_get_line();
3003 alertpanel_error(err_msg, line);
3009 prev_autowrap = compose->autowrap;
3010 compose->autowrap = FALSE;
3012 mark = gtk_text_buffer_get_insert(buffer);
3013 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3014 if (g_utf8_validate(buf, -1, NULL)) {
3015 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3017 gchar *tmpout = NULL;
3018 tmpout = conv_codeset_strdup
3019 (buf, conv_get_locale_charset_str_no_utf8(),
3021 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3023 tmpout = g_malloc(strlen(buf)*2+1);
3024 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3026 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3030 cursor_pos = quote_fmt_get_cursor_pos();
3031 if (cursor_pos == -1)
3032 cursor_pos = gtk_text_iter_get_offset(&iter);
3033 compose->set_cursor_pos = cursor_pos;
3035 gtk_text_buffer_get_start_iter(buffer, &iter);
3036 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3037 gtk_text_buffer_place_cursor(buffer, &iter);
3039 compose->autowrap = prev_autowrap;
3040 if (compose->autowrap && rewrap)
3041 compose_wrap_all(compose);
3048 SIGNAL_UNBLOCK(buffer);
3050 procmsg_msginfo_free( dummyinfo );
3055 /* if ml_post is of type addr@host and from is of type
3056 * addr-anything@host, return TRUE
3058 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3060 gchar *left_ml = NULL;
3061 gchar *right_ml = NULL;
3062 gchar *left_from = NULL;
3063 gchar *right_from = NULL;
3064 gboolean result = FALSE;
3066 if (!ml_post || !from)
3069 left_ml = g_strdup(ml_post);
3070 if (strstr(left_ml, "@")) {
3071 right_ml = strstr(left_ml, "@")+1;
3072 *(strstr(left_ml, "@")) = '\0';
3075 left_from = g_strdup(from);
3076 if (strstr(left_from, "@")) {
3077 right_from = strstr(left_from, "@")+1;
3078 *(strstr(left_from, "@")) = '\0';
3081 if (left_ml && left_from && right_ml && right_from
3082 && !strncmp(left_from, left_ml, strlen(left_ml))
3083 && !strcmp(right_from, right_ml)) {
3092 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3093 gboolean respect_default_to)
3097 if (!folder || !folder->prefs)
3100 if (respect_default_to && folder->prefs->enable_default_to) {
3101 compose_entry_append(compose, folder->prefs->default_to,
3102 COMPOSE_TO, PREF_FOLDER);
3103 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3105 if (folder->prefs->enable_default_cc)
3106 compose_entry_append(compose, folder->prefs->default_cc,
3107 COMPOSE_CC, PREF_FOLDER);
3108 if (folder->prefs->enable_default_bcc)
3109 compose_entry_append(compose, folder->prefs->default_bcc,
3110 COMPOSE_BCC, PREF_FOLDER);
3111 if (folder->prefs->enable_default_replyto)
3112 compose_entry_append(compose, folder->prefs->default_replyto,
3113 COMPOSE_REPLYTO, PREF_FOLDER);
3116 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3121 if (!compose || !msginfo)
3124 if (msginfo->subject && *msginfo->subject) {
3125 buf = p = g_strdup(msginfo->subject);
3126 p += subject_get_prefix_length(p);
3127 memmove(buf, p, strlen(p) + 1);
3129 buf2 = g_strdup_printf("Re: %s", buf);
3130 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3135 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3138 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3139 gboolean to_all, gboolean to_ml,
3141 gboolean followup_and_reply_to)
3143 GSList *cc_list = NULL;
3146 gchar *replyto = NULL;
3147 gchar *ac_email = NULL;
3149 gboolean reply_to_ml = FALSE;
3150 gboolean default_reply_to = FALSE;
3152 cm_return_if_fail(compose->account != NULL);
3153 cm_return_if_fail(msginfo != NULL);
3155 reply_to_ml = to_ml && compose->ml_post;
3157 default_reply_to = msginfo->folder &&
3158 msginfo->folder->prefs->enable_default_reply_to;
3160 if (compose->account->protocol != A_NNTP) {
3161 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3163 if (reply_to_ml && !default_reply_to) {
3165 gboolean is_subscr = is_subscription(compose->ml_post,
3168 /* normal answer to ml post with a reply-to */
3169 compose_entry_append(compose,
3171 COMPOSE_TO, PREF_ML);
3172 if (compose->replyto)
3173 compose_entry_append(compose,
3175 COMPOSE_CC, PREF_ML);
3177 /* answer to subscription confirmation */
3178 if (compose->replyto)
3179 compose_entry_append(compose,
3181 COMPOSE_TO, PREF_ML);
3182 else if (msginfo->from)