2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
188 static Compose *compose_generic_new (PrefsAccount *account,
192 GList *listAddress );
194 static Compose *compose_create (PrefsAccount *account,
199 static void compose_entry_mark_default_to (Compose *compose,
200 const gchar *address);
201 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
202 ComposeQuoteMode quote_mode,
206 static Compose *compose_forward_multiple (PrefsAccount *account,
207 GSList *msginfo_list);
208 static Compose *compose_reply (MsgInfo *msginfo,
209 ComposeQuoteMode quote_mode,
214 static Compose *compose_reply_mode (ComposeMode mode,
215 GSList *msginfo_list,
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
220 static GtkWidget *compose_account_option_menu_create
222 static void compose_set_out_encoding (Compose *compose);
223 static void compose_set_template_menu (Compose *compose);
224 static void compose_destroy (Compose *compose);
226 static MailField compose_entries_set (Compose *compose,
228 ComposeEntryType to_type);
229 static gint compose_parse_header (Compose *compose,
231 static gint compose_parse_manual_headers (Compose *compose,
233 HeaderEntry *entries);
234 static gchar *compose_parse_references (const gchar *ref,
237 static gchar *compose_quote_fmt (Compose *compose,
243 gboolean need_unescape,
244 const gchar *err_msg);
246 static void compose_reply_set_entry (Compose *compose,
252 followup_and_reply_to);
253 static void compose_reedit_set_entry (Compose *compose,
256 static void compose_insert_sig (Compose *compose,
258 static ComposeInsertResult compose_insert_file (Compose *compose,
261 static gboolean compose_attach_append (Compose *compose,
264 const gchar *content_type,
265 const gchar *charset);
266 static void compose_attach_parts (Compose *compose,
269 static gboolean compose_beautify_paragraph (Compose *compose,
270 GtkTextIter *par_iter,
272 static void compose_wrap_all (Compose *compose);
273 static void compose_wrap_all_full (Compose *compose,
276 static void compose_set_title (Compose *compose);
277 static void compose_select_account (Compose *compose,
278 PrefsAccount *account,
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send (Compose *compose); */
283 static gboolean compose_check_for_valid_recipient
285 static gboolean compose_check_entries (Compose *compose,
286 gboolean check_everything);
287 static gint compose_write_to_file (Compose *compose,
290 gboolean attach_parts);
291 static gint compose_write_body_to_file (Compose *compose,
293 static gint compose_remove_reedit_target (Compose *compose,
295 static void compose_remove_draft (Compose *compose);
296 static gint compose_queue_sub (Compose *compose,
300 gboolean check_subject,
301 gboolean remove_reedit_target);
302 static int compose_add_attachments (Compose *compose,
304 static gchar *compose_get_header (Compose *compose);
305 static gchar *compose_get_manual_headers_info (Compose *compose);
307 static void compose_convert_header (Compose *compose,
312 gboolean addr_field);
314 static void compose_attach_info_free (AttachInfo *ainfo);
315 static void compose_attach_remove_selected (GtkAction *action,
318 static void compose_template_apply (Compose *compose,
321 static void compose_attach_property (GtkAction *action,
323 static void compose_attach_property_create (gboolean *cancelled);
324 static void attach_property_ok (GtkWidget *widget,
325 gboolean *cancelled);
326 static void attach_property_cancel (GtkWidget *widget,
327 gboolean *cancelled);
328 static gint attach_property_delete_event (GtkWidget *widget,
330 gboolean *cancelled);
331 static gboolean attach_property_key_pressed (GtkWidget *widget,
333 gboolean *cancelled);
335 static void compose_exec_ext_editor (Compose *compose);
337 static gint compose_exec_ext_editor_real (const gchar *file);
338 static gboolean compose_ext_editor_kill (Compose *compose);
339 static gboolean compose_input_cb (GIOChannel *source,
340 GIOCondition condition,
342 static void compose_set_ext_editor_sensitive (Compose *compose,
344 #endif /* G_OS_UNIX */
346 static void compose_undo_state_changed (UndoMain *undostruct,
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry (Compose *compose, const gchar *header,
353 gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
359 static void compose_dict_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367 GtkAllocation *allocation,
369 static gboolean compose_edit_size_alloc (GtkEditable *widget,
370 GtkAllocation *allocation,
371 GtkSHRuler *shruler);
372 static void account_activated (GtkComboBox *optmenu,
374 static void attach_selected (GtkTreeView *tree_view,
375 GtkTreePath *tree_path,
376 GtkTreeViewColumn *column,
378 static gboolean attach_button_pressed (GtkWidget *widget,
379 GdkEventButton *event,
381 static gboolean attach_key_pressed (GtkWidget *widget,
384 static void compose_send_cb (GtkAction *action, gpointer data);
385 static void compose_send_later_cb (GtkAction *action, gpointer data);
387 static void compose_save_cb (GtkAction *action,
390 static void compose_attach_cb (GtkAction *action,
392 static void compose_insert_file_cb (GtkAction *action,
394 static void compose_insert_sig_cb (GtkAction *action,
397 static void compose_close_cb (GtkAction *action,
399 static void compose_print_cb (GtkAction *action,
402 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
404 static void compose_address_cb (GtkAction *action,
406 static void about_show_cb (GtkAction *action,
408 static void compose_template_activate_cb(GtkWidget *widget,
411 static void compose_ext_editor_cb (GtkAction *action,
414 static gint compose_delete_cb (GtkWidget *widget,
418 static void compose_undo_cb (GtkAction *action,
420 static void compose_redo_cb (GtkAction *action,
422 static void compose_cut_cb (GtkAction *action,
424 static void compose_copy_cb (GtkAction *action,
426 static void compose_paste_cb (GtkAction *action,
428 static void compose_paste_as_quote_cb (GtkAction *action,
430 static void compose_paste_no_wrap_cb (GtkAction *action,
432 static void compose_paste_wrap_cb (GtkAction *action,
434 static void compose_allsel_cb (GtkAction *action,
437 static void compose_advanced_action_cb (GtkAction *action,
440 static void compose_grab_focus_cb (GtkWidget *widget,
443 static void compose_changed_cb (GtkTextBuffer *textbuf,
446 static void compose_wrap_cb (GtkAction *action,
448 static void compose_wrap_all_cb (GtkAction *action,
450 static void compose_find_cb (GtkAction *action,
452 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
454 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
457 static void compose_toggle_ruler_cb (GtkToggleAction *action,
459 static void compose_toggle_sign_cb (GtkToggleAction *action,
461 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
463 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system (Compose *compose,
466 PrefsAccount *account,
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
472 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
474 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
476 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
478 static void compose_attach_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_insert_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
494 static void compose_header_drag_received_cb (GtkWidget *widget,
495 GdkDragContext *drag_context,
498 GtkSelectionData *data,
503 static gboolean compose_drag_drop (GtkWidget *widget,
504 GdkDragContext *drag_context,
506 guint time, gpointer user_data);
507 static gboolean completion_set_focus_to_subject
512 static void text_inserted (GtkTextBuffer *buffer,
517 static Compose *compose_generic_reply(MsgInfo *msginfo,
518 ComposeQuoteMode quote_mode,
522 gboolean followup_and_reply_to,
525 static void compose_headerentry_changed_cb (GtkWidget *entry,
526 ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
529 ComposeHeaderEntry *headerentry);
530 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
531 ComposeHeaderEntry *headerentry);
533 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
535 static void compose_allow_user_actions (Compose *compose, gboolean allow);
537 static void compose_nothing_cb (GtkAction *action, gpointer data)
543 static void compose_check_all (GtkAction *action, gpointer data);
544 static void compose_highlight_all (GtkAction *action, gpointer data);
545 static void compose_check_backwards (GtkAction *action, gpointer data);
546 static void compose_check_forwards_go (GtkAction *action, gpointer data);
549 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
551 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
554 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
555 FolderItem *folder_item);
557 static void compose_attach_update_label(Compose *compose);
558 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
559 gboolean respect_default_to);
560 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
562 static GtkActionEntry compose_popup_entries[] =
564 {"Compose", NULL, "Compose" },
565 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
566 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
567 {"Compose/---", NULL, "---", NULL, NULL, NULL },
568 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
571 static GtkActionEntry compose_entries[] =
573 {"Menu", NULL, "Menu" },
575 {"Message", NULL, N_("_Message") },
576 {"Edit", NULL, N_("_Edit") },
578 {"Spelling", NULL, N_("_Spelling") },
580 {"Options", NULL, N_("_Options") },
581 {"Tools", NULL, N_("_Tools") },
582 {"Help", NULL, N_("_Help") },
584 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
585 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
586 {"Message/---", NULL, "---" },
588 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
589 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
590 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
591 /* {"Message/---", NULL, "---" }, */
592 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
593 /* {"Message/---", NULL, "---" }, */
594 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
595 /* {"Message/---", NULL, "---" }, */
596 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
599 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
600 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
601 {"Edit/---", NULL, "---" },
603 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
604 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
605 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
607 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
608 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
609 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
610 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
612 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
614 {"Edit/Advanced", NULL, N_("A_dvanced") },
615 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
616 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
617 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
618 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
619 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
620 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
621 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
622 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
623 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
624 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
625 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
626 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
627 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
628 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
630 /* {"Edit/---", NULL, "---" }, */
631 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
635 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
640 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
641 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
642 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
643 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
645 {"Spelling/---", NULL, "---" },
646 {"Spelling/Options", NULL, N_("_Options") },
651 {"Options/ReplyMode", NULL, N_("Reply _mode") },
652 {"Options/---", NULL, "---" },
653 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
654 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 /* {"Options/---", NULL, "---" }, */
658 {"Options/Priority", NULL, N_("_Priority") },
660 {"Options/Encoding", NULL, N_("Character _encoding") },
661 {"Options/Encoding/---", NULL, "---" },
662 #define ENC_ACTION(cs_char,c_char,string) \
663 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
665 {"Options/Encoding/Western", NULL, N_("Western European") },
666 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
667 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
668 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
669 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
670 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
671 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
672 {"Options/Encoding/Korean", NULL, N_("Korean") },
673 {"Options/Encoding/Thai", NULL, N_("Thai") },
676 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
678 {"Tools/Template", NULL, N_("_Template") },
679 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 {"Tools/Actions", NULL, N_("Actio_ns") },
681 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
684 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
687 static GtkToggleActionEntry compose_toggle_entries[] =
689 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
690 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
691 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
692 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
693 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
694 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
695 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
698 static GtkRadioActionEntry compose_radio_rm_entries[] =
700 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
701 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
702 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
703 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
706 static GtkRadioActionEntry compose_radio_prio_entries[] =
708 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
710 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
711 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
712 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
715 static GtkRadioActionEntry compose_radio_enc_entries[] =
717 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
751 static GtkTargetEntry compose_mime_types[] =
753 {"text/uri-list", 0, 0},
754 {"UTF8_STRING", 0, 0},
758 static gboolean compose_put_existing_to_front(MsgInfo *info)
760 GList *compose_list = compose_get_compose_list();
764 for (elem = compose_list; elem != NULL && elem->data != NULL;
766 Compose *c = (Compose*)elem->data;
768 if (!c->targetinfo || !c->targetinfo->msgid ||
772 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
773 gtkut_window_popup(c->window);
781 static GdkColor quote_color1 =
782 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color2 =
784 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_color3 =
786 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_bgcolor1 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor2 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_bgcolor3 =
793 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor signature_color = {
802 static GdkColor uri_color = {
809 static void compose_create_tags(GtkTextView *text, Compose *compose)
811 GtkTextBuffer *buffer;
812 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 #if !GTK_CHECK_VERSION(2, 24, 0)
820 buffer = gtk_text_view_get_buffer(text);
822 if (prefs_common.enable_color) {
823 /* grab the quote colors, converting from an int to a GdkColor */
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
828 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
830 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
832 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
834 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
836 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
838 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
841 signature_color = quote_color1 = quote_color2 = quote_color3 =
842 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
845 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
848 "paragraph-background-gdk", "e_bgcolor1,
850 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
851 "foreground-gdk", "e_color2,
852 "paragraph-background-gdk", "e_bgcolor2,
854 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
855 "foreground-gdk", "e_color3,
856 "paragraph-background-gdk", "e_bgcolor3,
859 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860 "foreground-gdk", "e_color1,
862 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
863 "foreground-gdk", "e_color2,
865 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
866 "foreground-gdk", "e_color3,
870 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
871 "foreground-gdk", &signature_color,
874 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
875 "foreground-gdk", &uri_color,
877 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
878 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881 color[0] = quote_color1;
882 color[1] = quote_color2;
883 color[2] = quote_color3;
884 color[3] = quote_bgcolor1;
885 color[4] = quote_bgcolor2;
886 color[5] = quote_bgcolor3;
887 color[6] = signature_color;
888 color[7] = uri_color;
890 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
891 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
893 for (i = 0; i < 8; i++) {
894 if (success[i] == FALSE) {
895 g_warning("Compose: color allocation failed.\n");
896 quote_color1 = quote_color2 = quote_color3 =
897 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
898 signature_color = uri_color = black;
904 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
907 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
910 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
912 return compose_generic_new(account, mailto, item, NULL, NULL);
915 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
917 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
920 #define SCROLL_TO_CURSOR(compose) { \
921 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
922 gtk_text_view_get_buffer( \
923 GTK_TEXT_VIEW(compose->text))); \
924 gtk_text_view_scroll_mark_onscreen( \
925 GTK_TEXT_VIEW(compose->text), \
929 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
932 if (folderidentifier) {
933 #if !GTK_CHECK_VERSION(2, 24, 0)
934 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
936 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
938 prefs_common.compose_save_to_history = add_history(
939 prefs_common.compose_save_to_history, folderidentifier);
940 #if !GTK_CHECK_VERSION(2, 24, 0)
941 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
942 prefs_common.compose_save_to_history);
944 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
945 prefs_common.compose_save_to_history);
949 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
950 if (folderidentifier)
951 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
953 gtk_entry_set_text(GTK_ENTRY(entry), "");
956 static gchar *compose_get_save_to(Compose *compose)
959 gchar *result = NULL;
960 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
961 result = gtk_editable_get_chars(entry, 0, -1);
964 #if !GTK_CHECK_VERSION(2, 24, 0)
965 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
967 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
969 prefs_common.compose_save_to_history = add_history(
970 prefs_common.compose_save_to_history, result);
971 #if !GTK_CHECK_VERSION(2, 24, 0)
972 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
973 prefs_common.compose_save_to_history);
975 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
976 prefs_common.compose_save_to_history);
982 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
983 GList *attach_files, GList *listAddress )
986 GtkTextView *textview;
987 GtkTextBuffer *textbuf;
989 const gchar *subject_format = NULL;
990 const gchar *body_format = NULL;
991 gchar *mailto_from = NULL;
992 PrefsAccount *mailto_account = NULL;
993 MsgInfo* dummyinfo = NULL;
994 gint cursor_pos = -1;
995 MailField mfield = NO_FIELD_PRESENT;
999 /* check if mailto defines a from */
1000 if (mailto && *mailto != '\0') {
1001 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1002 /* mailto defines a from, check if we can get account prefs from it,
1003 if not, the account prefs will be guessed using other ways, but we'll keep
1006 mailto_account = account_find_from_address(mailto_from, TRUE);
1007 if (mailto_account == NULL) {
1009 Xstrdup_a(tmp_from, mailto_from, return NULL);
1010 extract_address(tmp_from);
1011 mailto_account = account_find_from_address(tmp_from, TRUE);
1015 account = mailto_account;
1018 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1019 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1020 account = account_find_from_id(item->prefs->default_account);
1022 /* if no account prefs set, fallback to the current one */
1023 if (!account) account = cur_account;
1024 cm_return_val_if_fail(account != NULL, NULL);
1026 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1028 /* override from name if mailto asked for it */
1030 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1031 g_free(mailto_from);
1033 /* override from name according to folder properties */
1034 if (item && item->prefs &&
1035 item->prefs->compose_with_format &&
1036 item->prefs->compose_override_from_format &&
1037 *item->prefs->compose_override_from_format != '\0') {
1042 dummyinfo = compose_msginfo_new_from_compose(compose);
1044 /* decode \-escape sequences in the internal representation of the quote format */
1045 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1046 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1049 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1050 compose->gtkaspell);
1052 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1054 quote_fmt_scan_string(tmp);
1057 buf = quote_fmt_get_buffer();
1059 alertpanel_error(_("New message From format error."));
1061 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1062 quote_fmt_reset_vartable();
1067 compose->replyinfo = NULL;
1068 compose->fwdinfo = NULL;
1070 textview = GTK_TEXT_VIEW(compose->text);
1071 textbuf = gtk_text_view_get_buffer(textview);
1072 compose_create_tags(textview, compose);
1074 undo_block(compose->undostruct);
1076 compose_set_dictionaries_from_folder_prefs(compose, item);
1079 if (account->auto_sig)
1080 compose_insert_sig(compose, FALSE);
1081 gtk_text_buffer_get_start_iter(textbuf, &iter);
1082 gtk_text_buffer_place_cursor(textbuf, &iter);
1084 if (account->protocol != A_NNTP) {
1085 if (mailto && *mailto != '\0') {
1086 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1089 compose_set_folder_prefs(compose, item, TRUE);
1091 if (item && item->ret_rcpt) {
1092 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1095 if (mailto && *mailto != '\0') {
1096 if (!strchr(mailto, '@'))
1097 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1099 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1100 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1101 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1102 mfield = TO_FIELD_PRESENT;
1105 * CLAWS: just don't allow return receipt request, even if the user
1106 * may want to send an email. simple but foolproof.
1108 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1110 compose_add_field_list( compose, listAddress );
1112 if (item && item->prefs && item->prefs->compose_with_format) {
1113 subject_format = item->prefs->compose_subject_format;
1114 body_format = item->prefs->compose_body_format;
1115 } else if (account->compose_with_format) {
1116 subject_format = account->compose_subject_format;
1117 body_format = account->compose_body_format;
1118 } else if (prefs_common.compose_with_format) {
1119 subject_format = prefs_common.compose_subject_format;
1120 body_format = prefs_common.compose_body_format;
1123 if (subject_format || body_format) {
1126 && *subject_format != '\0' )
1128 gchar *subject = NULL;
1133 dummyinfo = compose_msginfo_new_from_compose(compose);
1135 /* decode \-escape sequences in the internal representation of the quote format */
1136 tmp = g_malloc(strlen(subject_format)+1);
1137 pref_get_unescaped_pref(tmp, subject_format);
1139 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1141 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1142 compose->gtkaspell);
1144 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1146 quote_fmt_scan_string(tmp);
1149 buf = quote_fmt_get_buffer();
1151 alertpanel_error(_("New message subject format error."));
1153 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1154 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1155 quote_fmt_reset_vartable();
1159 mfield = SUBJECT_FIELD_PRESENT;
1163 && *body_format != '\0' )
1166 GtkTextBuffer *buffer;
1167 GtkTextIter start, end;
1171 dummyinfo = compose_msginfo_new_from_compose(compose);
1173 text = GTK_TEXT_VIEW(compose->text);
1174 buffer = gtk_text_view_get_buffer(text);
1175 gtk_text_buffer_get_start_iter(buffer, &start);
1176 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1177 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1179 compose_quote_fmt(compose, dummyinfo,
1181 NULL, tmp, FALSE, TRUE,
1182 _("The body of the \"New message\" template has an error at line %d."));
1183 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1184 quote_fmt_reset_vartable();
1188 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1189 gtkaspell_highlight_all(compose->gtkaspell);
1191 mfield = BODY_FIELD_PRESENT;
1195 procmsg_msginfo_free( dummyinfo );
1201 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1202 ainfo = (AttachInfo *) curr->data;
1203 compose_attach_append(compose, ainfo->file, ainfo->name,
1204 ainfo->content_type, ainfo->charset);
1208 compose_show_first_last_header(compose, TRUE);
1210 /* Set save folder */
1211 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1212 gchar *folderidentifier;
1214 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1215 folderidentifier = folder_item_get_identifier(item);
1216 compose_set_save_to(compose, folderidentifier);
1217 g_free(folderidentifier);
1220 /* Place cursor according to provided input (mfield) */
1222 case NO_FIELD_PRESENT:
1223 if (compose->header_last)
1224 gtk_widget_grab_focus(compose->header_last->entry);
1226 case TO_FIELD_PRESENT:
1227 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1229 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1232 gtk_widget_grab_focus(compose->subject_entry);
1234 case SUBJECT_FIELD_PRESENT:
1235 textview = GTK_TEXT_VIEW(compose->text);
1238 textbuf = gtk_text_view_get_buffer(textview);
1241 mark = gtk_text_buffer_get_insert(textbuf);
1242 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1243 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1245 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1246 * only defers where it comes to the variable body
1247 * is not null. If no body is present compose->text
1248 * will be null in which case you cannot place the
1249 * cursor inside the component so. An empty component
1250 * is therefore created before placing the cursor
1252 case BODY_FIELD_PRESENT:
1253 cursor_pos = quote_fmt_get_cursor_pos();
1254 if (cursor_pos == -1)
1255 gtk_widget_grab_focus(compose->header_last->entry);
1257 gtk_widget_grab_focus(compose->text);
1261 undo_unblock(compose->undostruct);
1263 if (prefs_common.auto_exteditor)
1264 compose_exec_ext_editor(compose);
1266 compose->draft_timeout_tag = -1;
1267 SCROLL_TO_CURSOR(compose);
1269 compose->modified = FALSE;
1270 compose_set_title(compose);
1272 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1277 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1278 gboolean override_pref, const gchar *system)
1280 const gchar *privacy = NULL;
1282 cm_return_if_fail(compose != NULL);
1283 cm_return_if_fail(account != NULL);
1285 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1288 if (account->default_privacy_system && strlen(account->default_privacy_system))
1289 privacy = account->default_privacy_system;
1293 GSList *privacy_avail = privacy_get_system_ids();
1294 if (privacy_avail && g_slist_length(privacy_avail)) {
1295 privacy = (gchar *)(privacy_avail->data);
1298 if (privacy != NULL) {
1300 g_free(compose->privacy_system);
1301 compose->privacy_system = NULL;
1303 if (compose->privacy_system == NULL)
1304 compose->privacy_system = g_strdup(privacy);
1305 else if (*(compose->privacy_system) == '\0') {
1306 g_free(compose->privacy_system);
1307 compose->privacy_system = g_strdup(privacy);
1309 compose_update_privacy_system_menu_item(compose, FALSE);
1310 compose_use_encryption(compose, TRUE);
1314 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1316 const gchar *privacy = NULL;
1318 if (account->default_privacy_system && strlen(account->default_privacy_system))
1319 privacy = account->default_privacy_system;
1323 GSList *privacy_avail = privacy_get_system_ids();
1324 if (privacy_avail && g_slist_length(privacy_avail)) {
1325 privacy = (gchar *)(privacy_avail->data);
1329 if (privacy != NULL) {
1331 g_free(compose->privacy_system);
1332 compose->privacy_system = NULL;
1334 if (compose->privacy_system == NULL)
1335 compose->privacy_system = g_strdup(privacy);
1336 compose_update_privacy_system_menu_item(compose, FALSE);
1337 compose_use_signing(compose, TRUE);
1341 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1345 Compose *compose = NULL;
1347 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1349 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1350 cm_return_val_if_fail(msginfo != NULL, NULL);
1352 list_len = g_slist_length(msginfo_list);
1356 case COMPOSE_REPLY_TO_ADDRESS:
1357 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1358 FALSE, prefs_common.default_reply_list, FALSE, body);
1360 case COMPOSE_REPLY_WITH_QUOTE:
1361 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1362 FALSE, prefs_common.default_reply_list, FALSE, body);
1364 case COMPOSE_REPLY_WITHOUT_QUOTE:
1365 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1366 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1368 case COMPOSE_REPLY_TO_SENDER:
1369 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1370 FALSE, FALSE, TRUE, body);
1372 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1373 compose = compose_followup_and_reply_to(msginfo,
1374 COMPOSE_QUOTE_CHECK,
1375 FALSE, FALSE, body);
1377 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1379 FALSE, FALSE, TRUE, body);
1381 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1383 FALSE, FALSE, TRUE, NULL);
1385 case COMPOSE_REPLY_TO_ALL:
1386 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1387 TRUE, FALSE, FALSE, body);
1389 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1390 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1391 TRUE, FALSE, FALSE, body);
1393 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1394 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1395 TRUE, FALSE, FALSE, NULL);
1397 case COMPOSE_REPLY_TO_LIST:
1398 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1399 FALSE, TRUE, FALSE, body);
1401 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1402 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1403 FALSE, TRUE, FALSE, body);
1405 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1406 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1407 FALSE, TRUE, FALSE, NULL);
1409 case COMPOSE_FORWARD:
1410 if (prefs_common.forward_as_attachment) {
1411 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1414 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1418 case COMPOSE_FORWARD_INLINE:
1419 /* check if we reply to more than one Message */
1420 if (list_len == 1) {
1421 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1424 /* more messages FALL THROUGH */
1425 case COMPOSE_FORWARD_AS_ATTACH:
1426 compose = compose_forward_multiple(NULL, msginfo_list);
1428 case COMPOSE_REDIRECT:
1429 compose = compose_redirect(NULL, msginfo, FALSE);
1432 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1435 if (compose == NULL) {
1436 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1440 compose->rmode = mode;
1441 switch (compose->rmode) {
1443 case COMPOSE_REPLY_WITH_QUOTE:
1444 case COMPOSE_REPLY_WITHOUT_QUOTE:
1445 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1446 debug_print("reply mode Normal\n");
1447 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1448 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1450 case COMPOSE_REPLY_TO_SENDER:
1451 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1452 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1453 debug_print("reply mode Sender\n");
1454 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1456 case COMPOSE_REPLY_TO_ALL:
1457 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1458 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1459 debug_print("reply mode All\n");
1460 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1462 case COMPOSE_REPLY_TO_LIST:
1463 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1464 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1465 debug_print("reply mode List\n");
1466 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1468 case COMPOSE_REPLY_TO_ADDRESS:
1469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1477 static Compose *compose_reply(MsgInfo *msginfo,
1478 ComposeQuoteMode quote_mode,
1484 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1485 to_sender, FALSE, body);
1488 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1489 ComposeQuoteMode quote_mode,
1494 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1495 to_sender, TRUE, body);
1498 static void compose_extract_original_charset(Compose *compose)
1500 MsgInfo *info = NULL;
1501 if (compose->replyinfo) {
1502 info = compose->replyinfo;
1503 } else if (compose->fwdinfo) {
1504 info = compose->fwdinfo;
1505 } else if (compose->targetinfo) {
1506 info = compose->targetinfo;
1509 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1510 MimeInfo *partinfo = mimeinfo;
1511 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1512 partinfo = procmime_mimeinfo_next(partinfo);
1514 compose->orig_charset =
1515 g_strdup(procmime_mimeinfo_get_parameter(
1516 partinfo, "charset"));
1518 procmime_mimeinfo_free_all(mimeinfo);
1522 #define SIGNAL_BLOCK(buffer) { \
1523 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1524 G_CALLBACK(compose_changed_cb), \
1526 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1527 G_CALLBACK(text_inserted), \
1531 #define SIGNAL_UNBLOCK(buffer) { \
1532 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1533 G_CALLBACK(compose_changed_cb), \
1535 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1536 G_CALLBACK(text_inserted), \
1540 static Compose *compose_generic_reply(MsgInfo *msginfo,
1541 ComposeQuoteMode quote_mode,
1542 gboolean to_all, gboolean to_ml,
1544 gboolean followup_and_reply_to,
1548 PrefsAccount *account = NULL;
1549 GtkTextView *textview;
1550 GtkTextBuffer *textbuf;
1551 gboolean quote = FALSE;
1552 const gchar *qmark = NULL;
1553 const gchar *body_fmt = NULL;
1554 gchar *s_system = NULL;
1556 cm_return_val_if_fail(msginfo != NULL, NULL);
1557 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1559 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1561 cm_return_val_if_fail(account != NULL, NULL);
1563 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1565 compose->updating = TRUE;
1567 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1570 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1571 if (!compose->replyinfo)
1572 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1574 compose_extract_original_charset(compose);
1576 if (msginfo->folder && msginfo->folder->ret_rcpt)
1577 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1579 /* Set save folder */
1580 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1581 gchar *folderidentifier;
1583 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1584 folderidentifier = folder_item_get_identifier(msginfo->folder);
1585 compose_set_save_to(compose, folderidentifier);
1586 g_free(folderidentifier);
1589 if (compose_parse_header(compose, msginfo) < 0) {
1590 compose->updating = FALSE;
1591 compose_destroy(compose);
1595 /* override from name according to folder properties */
1596 if (msginfo->folder && msginfo->folder->prefs &&
1597 msginfo->folder->prefs->reply_with_format &&
1598 msginfo->folder->prefs->reply_override_from_format &&
1599 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1604 /* decode \-escape sequences in the internal representation of the quote format */
1605 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1606 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1609 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1610 compose->gtkaspell);
1612 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1614 quote_fmt_scan_string(tmp);
1617 buf = quote_fmt_get_buffer();
1619 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1621 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1622 quote_fmt_reset_vartable();
1627 textview = (GTK_TEXT_VIEW(compose->text));
1628 textbuf = gtk_text_view_get_buffer(textview);
1629 compose_create_tags(textview, compose);
1631 undo_block(compose->undostruct);
1633 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1634 gtkaspell_block_check(compose->gtkaspell);
1637 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1638 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1639 /* use the reply format of folder (if enabled), or the account's one
1640 (if enabled) or fallback to the global reply format, which is always
1641 enabled (even if empty), and use the relevant quotemark */
1643 if (msginfo->folder && msginfo->folder->prefs &&
1644 msginfo->folder->prefs->reply_with_format) {
1645 qmark = msginfo->folder->prefs->reply_quotemark;
1646 body_fmt = msginfo->folder->prefs->reply_body_format;
1648 } else if (account->reply_with_format) {
1649 qmark = account->reply_quotemark;
1650 body_fmt = account->reply_body_format;
1653 qmark = prefs_common.quotemark;
1654 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1655 body_fmt = gettext(prefs_common.quotefmt);
1662 /* empty quotemark is not allowed */
1663 if (qmark == NULL || *qmark == '\0')
1665 compose_quote_fmt(compose, compose->replyinfo,
1666 body_fmt, qmark, body, FALSE, TRUE,
1667 _("The body of the \"Reply\" template has an error at line %d."));
1668 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1669 quote_fmt_reset_vartable();
1672 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1673 compose_force_encryption(compose, account, FALSE, s_system);
1676 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1677 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1678 compose_force_signing(compose, account, s_system);
1682 SIGNAL_BLOCK(textbuf);
1684 if (account->auto_sig)
1685 compose_insert_sig(compose, FALSE);
1687 compose_wrap_all(compose);
1690 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1691 gtkaspell_highlight_all(compose->gtkaspell);
1692 gtkaspell_unblock_check(compose->gtkaspell);
1694 SIGNAL_UNBLOCK(textbuf);
1696 gtk_widget_grab_focus(compose->text);
1698 undo_unblock(compose->undostruct);
1700 if (prefs_common.auto_exteditor)
1701 compose_exec_ext_editor(compose);
1703 compose->modified = FALSE;
1704 compose_set_title(compose);
1706 compose->updating = FALSE;
1707 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1708 SCROLL_TO_CURSOR(compose);
1710 if (compose->deferred_destroy) {
1711 compose_destroy(compose);
1719 #define INSERT_FW_HEADER(var, hdr) \
1720 if (msginfo->var && *msginfo->var) { \
1721 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1722 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1723 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1726 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1727 gboolean as_attach, const gchar *body,
1728 gboolean no_extedit,
1732 GtkTextView *textview;
1733 GtkTextBuffer *textbuf;
1734 gint cursor_pos = -1;
1737 cm_return_val_if_fail(msginfo != NULL, NULL);
1738 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1741 !(account = compose_guess_forward_account_from_msginfo
1743 account = cur_account;
1745 if (!prefs_common.forward_as_attachment)
1746 mode = COMPOSE_FORWARD_INLINE;
1748 mode = COMPOSE_FORWARD;
1749 compose = compose_create(account, msginfo->folder, mode, batch);
1751 compose->updating = TRUE;
1752 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1753 if (!compose->fwdinfo)
1754 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1756 compose_extract_original_charset(compose);
1758 if (msginfo->subject && *msginfo->subject) {
1759 gchar *buf, *buf2, *p;
1761 buf = p = g_strdup(msginfo->subject);
1762 p += subject_get_prefix_length(p);
1763 memmove(buf, p, strlen(p) + 1);
1765 buf2 = g_strdup_printf("Fw: %s", buf);
1766 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1772 /* override from name according to folder properties */
1773 if (msginfo->folder && msginfo->folder->prefs &&
1774 msginfo->folder->prefs->forward_with_format &&
1775 msginfo->folder->prefs->forward_override_from_format &&
1776 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1780 MsgInfo *full_msginfo = NULL;
1783 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1785 full_msginfo = procmsg_msginfo_copy(msginfo);
1787 /* decode \-escape sequences in the internal representation of the quote format */
1788 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1789 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1792 gtkaspell_block_check(compose->gtkaspell);
1793 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1794 compose->gtkaspell);
1796 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1798 quote_fmt_scan_string(tmp);
1801 buf = quote_fmt_get_buffer();
1803 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1805 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1806 quote_fmt_reset_vartable();
1809 procmsg_msginfo_free(full_msginfo);
1812 textview = GTK_TEXT_VIEW(compose->text);
1813 textbuf = gtk_text_view_get_buffer(textview);
1814 compose_create_tags(textview, compose);
1816 undo_block(compose->undostruct);
1820 msgfile = procmsg_get_message_file(msginfo);
1821 if (!is_file_exist(msgfile))
1822 g_warning("%s: file not exist\n", msgfile);
1824 compose_attach_append(compose, msgfile, msgfile,
1825 "message/rfc822", NULL);
1829 const gchar *qmark = NULL;
1830 const gchar *body_fmt = NULL;
1831 MsgInfo *full_msginfo;
1833 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1835 full_msginfo = procmsg_msginfo_copy(msginfo);
1837 /* use the forward format of folder (if enabled), or the account's one
1838 (if enabled) or fallback to the global forward format, which is always
1839 enabled (even if empty), and use the relevant quotemark */
1840 if (msginfo->folder && msginfo->folder->prefs &&
1841 msginfo->folder->prefs->forward_with_format) {
1842 qmark = msginfo->folder->prefs->forward_quotemark;
1843 body_fmt = msginfo->folder->prefs->forward_body_format;
1845 } else if (account->forward_with_format) {
1846 qmark = account->forward_quotemark;
1847 body_fmt = account->forward_body_format;
1850 qmark = prefs_common.fw_quotemark;
1851 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1852 body_fmt = gettext(prefs_common.fw_quotefmt);
1857 /* empty quotemark is not allowed */
1858 if (qmark == NULL || *qmark == '\0')
1861 compose_quote_fmt(compose, full_msginfo,
1862 body_fmt, qmark, body, FALSE, TRUE,
1863 _("The body of the \"Forward\" template has an error at line %d."));
1864 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1865 quote_fmt_reset_vartable();
1866 compose_attach_parts(compose, msginfo);
1868 procmsg_msginfo_free(full_msginfo);
1871 SIGNAL_BLOCK(textbuf);
1873 if (account->auto_sig)
1874 compose_insert_sig(compose, FALSE);
1876 compose_wrap_all(compose);
1879 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1880 gtkaspell_highlight_all(compose->gtkaspell);
1881 gtkaspell_unblock_check(compose->gtkaspell);
1883 SIGNAL_UNBLOCK(textbuf);
1885 cursor_pos = quote_fmt_get_cursor_pos();
1886 if (cursor_pos == -1)
1887 gtk_widget_grab_focus(compose->header_last->entry);
1889 gtk_widget_grab_focus(compose->text);
1891 if (!no_extedit && prefs_common.auto_exteditor)
1892 compose_exec_ext_editor(compose);
1895 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1896 gchar *folderidentifier;
1898 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1899 folderidentifier = folder_item_get_identifier(msginfo->folder);
1900 compose_set_save_to(compose, folderidentifier);
1901 g_free(folderidentifier);
1904 undo_unblock(compose->undostruct);
1906 compose->modified = FALSE;
1907 compose_set_title(compose);
1909 compose->updating = FALSE;
1910 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1911 SCROLL_TO_CURSOR(compose);
1913 if (compose->deferred_destroy) {
1914 compose_destroy(compose);
1918 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1923 #undef INSERT_FW_HEADER
1925 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1928 GtkTextView *textview;
1929 GtkTextBuffer *textbuf;
1933 gboolean single_mail = TRUE;
1935 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1937 if (g_slist_length(msginfo_list) > 1)
1938 single_mail = FALSE;
1940 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1941 if (((MsgInfo *)msginfo->data)->folder == NULL)
1944 /* guess account from first selected message */
1946 !(account = compose_guess_forward_account_from_msginfo
1947 (msginfo_list->data)))
1948 account = cur_account;
1950 cm_return_val_if_fail(account != NULL, NULL);
1952 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1953 if (msginfo->data) {
1954 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1955 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1959 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1960 g_warning("no msginfo_list");
1964 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1966 compose->updating = TRUE;
1968 /* override from name according to folder properties */
1969 if (msginfo_list->data) {
1970 MsgInfo *msginfo = msginfo_list->data;
1972 if (msginfo->folder && msginfo->folder->prefs &&
1973 msginfo->folder->prefs->forward_with_format &&
1974 msginfo->folder->prefs->forward_override_from_format &&
1975 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1980 /* decode \-escape sequences in the internal representation of the quote format */
1981 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1982 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1985 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1986 compose->gtkaspell);
1988 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1990 quote_fmt_scan_string(tmp);
1993 buf = quote_fmt_get_buffer();
1995 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1997 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1998 quote_fmt_reset_vartable();
2004 textview = GTK_TEXT_VIEW(compose->text);
2005 textbuf = gtk_text_view_get_buffer(textview);
2006 compose_create_tags(textview, compose);
2008 undo_block(compose->undostruct);
2009 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2010 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2012 if (!is_file_exist(msgfile))
2013 g_warning("%s: file not exist\n", msgfile);
2015 compose_attach_append(compose, msgfile, msgfile,
2016 "message/rfc822", NULL);
2021 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2022 if (info->subject && *info->subject) {
2023 gchar *buf, *buf2, *p;
2025 buf = p = g_strdup(info->subject);
2026 p += subject_get_prefix_length(p);
2027 memmove(buf, p, strlen(p) + 1);
2029 buf2 = g_strdup_printf("Fw: %s", buf);
2030 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2036 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2037 _("Fw: multiple emails"));
2040 SIGNAL_BLOCK(textbuf);
2042 if (account->auto_sig)
2043 compose_insert_sig(compose, FALSE);
2045 compose_wrap_all(compose);
2047 SIGNAL_UNBLOCK(textbuf);
2049 gtk_text_buffer_get_start_iter(textbuf, &iter);
2050 gtk_text_buffer_place_cursor(textbuf, &iter);
2052 gtk_widget_grab_focus(compose->header_last->entry);
2053 undo_unblock(compose->undostruct);
2054 compose->modified = FALSE;
2055 compose_set_title(compose);
2057 compose->updating = FALSE;
2058 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2059 SCROLL_TO_CURSOR(compose);
2061 if (compose->deferred_destroy) {
2062 compose_destroy(compose);
2066 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2071 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2073 GtkTextIter start = *iter;
2074 GtkTextIter end_iter;
2075 int start_pos = gtk_text_iter_get_offset(&start);
2077 if (!compose->account->sig_sep)
2080 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2081 start_pos+strlen(compose->account->sig_sep));
2083 /* check sig separator */
2084 str = gtk_text_iter_get_text(&start, &end_iter);
2085 if (!strcmp(str, compose->account->sig_sep)) {
2087 /* check end of line (\n) */
2088 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2089 start_pos+strlen(compose->account->sig_sep));
2090 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2091 start_pos+strlen(compose->account->sig_sep)+1);
2092 tmp = gtk_text_iter_get_text(&start, &end_iter);
2093 if (!strcmp(tmp,"\n")) {
2105 static void compose_colorize_signature(Compose *compose)
2107 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2109 GtkTextIter end_iter;
2110 gtk_text_buffer_get_start_iter(buffer, &iter);
2111 while (gtk_text_iter_forward_line(&iter))
2112 if (compose_is_sig_separator(compose, buffer, &iter)) {
2113 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2114 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2118 #define BLOCK_WRAP() { \
2119 prev_autowrap = compose->autowrap; \
2120 buffer = gtk_text_view_get_buffer( \
2121 GTK_TEXT_VIEW(compose->text)); \
2122 compose->autowrap = FALSE; \
2124 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2125 G_CALLBACK(compose_changed_cb), \
2127 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2128 G_CALLBACK(text_inserted), \
2131 #define UNBLOCK_WRAP() { \
2132 compose->autowrap = prev_autowrap; \
2133 if (compose->autowrap) { \
2134 gint old = compose->draft_timeout_tag; \
2135 compose->draft_timeout_tag = -2; \
2136 compose_wrap_all(compose); \
2137 compose->draft_timeout_tag = old; \
2140 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2141 G_CALLBACK(compose_changed_cb), \
2143 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2144 G_CALLBACK(text_inserted), \
2148 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2150 Compose *compose = NULL;
2151 PrefsAccount *account = NULL;
2152 GtkTextView *textview;
2153 GtkTextBuffer *textbuf;
2157 gchar buf[BUFFSIZE];
2158 gboolean use_signing = FALSE;
2159 gboolean use_encryption = FALSE;
2160 gchar *privacy_system = NULL;
2161 int priority = PRIORITY_NORMAL;
2162 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2163 gboolean autowrap = prefs_common.autowrap;
2164 gboolean autoindent = prefs_common.auto_indent;
2165 HeaderEntry *manual_headers = NULL;
2167 cm_return_val_if_fail(msginfo != NULL, NULL);
2168 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2170 if (compose_put_existing_to_front(msginfo)) {
2174 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2175 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2176 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2177 gchar queueheader_buf[BUFFSIZE];
2180 /* Select Account from queue headers */
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2183 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2184 account = account_find_from_id(id);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2188 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2189 account = account_find_from_id(id);
2191 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2192 sizeof(queueheader_buf), "NAID:")) {
2193 id = atoi(&queueheader_buf[strlen("NAID:")]);
2194 account = account_find_from_id(id);
2196 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2197 sizeof(queueheader_buf), "MAID:")) {
2198 id = atoi(&queueheader_buf[strlen("MAID:")]);
2199 account = account_find_from_id(id);
2201 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2202 sizeof(queueheader_buf), "S:")) {
2203 account = account_find_from_address(queueheader_buf, FALSE);
2205 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2206 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2207 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2208 use_signing = param;
2211 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2212 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2213 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2214 use_signing = param;
2217 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2218 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2219 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2220 use_encryption = param;
2222 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2223 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2224 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2225 use_encryption = param;
2227 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2228 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2229 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2232 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2233 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2234 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2237 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2238 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2239 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2241 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2242 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2243 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2245 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2246 sizeof(queueheader_buf), "X-Priority: ")) {
2247 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2250 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2251 sizeof(queueheader_buf), "RMID:")) {
2252 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2253 if (tokens[0] && tokens[1] && tokens[2]) {
2254 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2255 if (orig_item != NULL) {
2256 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2261 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2262 sizeof(queueheader_buf), "FMID:")) {
2263 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2264 if (tokens[0] && tokens[1] && tokens[2]) {
2265 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2266 if (orig_item != NULL) {
2267 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2272 /* Get manual headers */
2273 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2274 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2275 if (*listmh != '\0') {
2276 debug_print("Got manual headers: %s\n", listmh);
2277 manual_headers = procheader_entries_from_str(listmh);
2282 account = msginfo->folder->folder->account;
2285 if (!account && prefs_common.reedit_account_autosel) {
2286 gchar from[BUFFSIZE];
2287 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2288 extract_address(from);
2289 account = account_find_from_address(from, FALSE);
2293 account = cur_account;
2295 cm_return_val_if_fail(account != NULL, NULL);
2297 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2299 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2300 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2301 compose->autowrap = autowrap;
2302 compose->replyinfo = replyinfo;
2303 compose->fwdinfo = fwdinfo;
2305 compose->updating = TRUE;
2306 compose->priority = priority;
2308 if (privacy_system != NULL) {
2309 compose->privacy_system = privacy_system;
2310 compose_use_signing(compose, use_signing);
2311 compose_use_encryption(compose, use_encryption);
2312 compose_update_privacy_system_menu_item(compose, FALSE);
2314 activate_privacy_system(compose, account, FALSE);
2317 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2319 compose_extract_original_charset(compose);
2321 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2322 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2323 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2324 gchar queueheader_buf[BUFFSIZE];
2326 /* Set message save folder */
2327 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2328 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2329 compose_set_save_to(compose, &queueheader_buf[4]);
2331 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2332 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2334 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2339 if (compose_parse_header(compose, msginfo) < 0) {
2340 compose->updating = FALSE;
2341 compose_destroy(compose);
2344 compose_reedit_set_entry(compose, msginfo);
2346 textview = GTK_TEXT_VIEW(compose->text);
2347 textbuf = gtk_text_view_get_buffer(textview);
2348 compose_create_tags(textview, compose);
2350 mark = gtk_text_buffer_get_insert(textbuf);
2351 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2353 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2354 G_CALLBACK(compose_changed_cb),
2357 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2358 fp = procmime_get_first_encrypted_text_content(msginfo);
2360 compose_force_encryption(compose, account, TRUE, NULL);
2363 fp = procmime_get_first_text_content(msginfo);
2366 g_warning("Can't get text part\n");
2370 gboolean prev_autowrap;
2371 GtkTextBuffer *buffer;
2373 while (fgets(buf, sizeof(buf), fp) != NULL) {
2375 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2381 compose_attach_parts(compose, msginfo);
2383 compose_colorize_signature(compose);
2385 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2386 G_CALLBACK(compose_changed_cb),
2389 if (manual_headers != NULL) {
2390 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2391 procheader_entries_free(manual_headers);
2392 compose->updating = FALSE;
2393 compose_destroy(compose);
2396 procheader_entries_free(manual_headers);
2399 gtk_widget_grab_focus(compose->text);
2401 if (prefs_common.auto_exteditor) {
2402 compose_exec_ext_editor(compose);
2404 compose->modified = FALSE;
2405 compose_set_title(compose);
2407 compose->updating = FALSE;
2408 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2409 SCROLL_TO_CURSOR(compose);
2411 if (compose->deferred_destroy) {
2412 compose_destroy(compose);
2416 compose->sig_str = account_get_signature_str(compose->account);
2418 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2423 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2430 cm_return_val_if_fail(msginfo != NULL, NULL);
2433 account = account_get_reply_account(msginfo,
2434 prefs_common.reply_account_autosel);
2435 cm_return_val_if_fail(account != NULL, NULL);
2437 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2439 compose->updating = TRUE;
2441 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2442 compose->replyinfo = NULL;
2443 compose->fwdinfo = NULL;
2445 compose_show_first_last_header(compose, TRUE);
2447 gtk_widget_grab_focus(compose->header_last->entry);
2449 filename = procmsg_get_message_file(msginfo);
2451 if (filename == NULL) {
2452 compose->updating = FALSE;
2453 compose_destroy(compose);
2458 compose->redirect_filename = filename;
2460 /* Set save folder */
2461 item = msginfo->folder;
2462 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2463 gchar *folderidentifier;
2465 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2466 folderidentifier = folder_item_get_identifier(item);
2467 compose_set_save_to(compose, folderidentifier);
2468 g_free(folderidentifier);
2471 compose_attach_parts(compose, msginfo);
2473 if (msginfo->subject)
2474 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2476 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2478 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2479 _("The body of the \"Redirect\" template has an error at line %d."));
2480 quote_fmt_reset_vartable();
2481 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2483 compose_colorize_signature(compose);
2486 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2487 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2488 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2490 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2491 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2492 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2493 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2494 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2495 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2496 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2497 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2499 if (compose->toolbar->draft_btn)
2500 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2501 if (compose->toolbar->insert_btn)
2502 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2503 if (compose->toolbar->attach_btn)
2504 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2505 if (compose->toolbar->sig_btn)
2506 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2507 if (compose->toolbar->exteditor_btn)
2508 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2509 if (compose->toolbar->linewrap_current_btn)
2510 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2511 if (compose->toolbar->linewrap_all_btn)
2512 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2514 compose->modified = FALSE;
2515 compose_set_title(compose);
2516 compose->updating = FALSE;
2517 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2518 SCROLL_TO_CURSOR(compose);
2520 if (compose->deferred_destroy) {
2521 compose_destroy(compose);
2525 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2530 GList *compose_get_compose_list(void)
2532 return compose_list;
2535 void compose_entry_append(Compose *compose, const gchar *address,
2536 ComposeEntryType type, ComposePrefType pref_type)
2538 const gchar *header;
2540 gboolean in_quote = FALSE;
2541 if (!address || *address == '\0') return;
2548 header = N_("Bcc:");
2550 case COMPOSE_REPLYTO:
2551 header = N_("Reply-To:");
2553 case COMPOSE_NEWSGROUPS:
2554 header = N_("Newsgroups:");
2556 case COMPOSE_FOLLOWUPTO:
2557 header = N_( "Followup-To:");
2559 case COMPOSE_INREPLYTO:
2560 header = N_( "In-Reply-To:");
2567 header = prefs_common_translated_header_name(header);
2569 cur = begin = (gchar *)address;
2571 /* we separate the line by commas, but not if we're inside a quoted
2573 while (*cur != '\0') {
2575 in_quote = !in_quote;
2576 if (*cur == ',' && !in_quote) {
2577 gchar *tmp = g_strdup(begin);
2579 tmp[cur-begin]='\0';
2582 while (*tmp == ' ' || *tmp == '\t')
2584 compose_add_header_entry(compose, header, tmp, pref_type);
2591 gchar *tmp = g_strdup(begin);
2593 tmp[cur-begin]='\0';
2594 while (*tmp == ' ' || *tmp == '\t')
2596 compose_add_header_entry(compose, header, tmp, pref_type);
2601 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2603 #if !GTK_CHECK_VERSION(3, 0, 0)
2604 static GdkColor yellow;
2605 static GdkColor black;
2606 static gboolean yellow_initialised = FALSE;
2608 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2609 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2614 #if !GTK_CHECK_VERSION(3, 0, 0)
2615 if (!yellow_initialised) {
2616 gdk_color_parse("#f5f6be", &yellow);
2617 gdk_color_parse("#000000", &black);
2618 yellow_initialised = gdk_colormap_alloc_color(
2619 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2620 yellow_initialised &= gdk_colormap_alloc_color(
2621 gdk_colormap_get_system(), &black, FALSE, TRUE);
2625 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2626 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2627 if (gtk_entry_get_text(entry) &&
2628 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2629 #if !GTK_CHECK_VERSION(3, 0, 0)
2630 if (yellow_initialised) {
2632 gtk_widget_modify_base(
2633 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2634 GTK_STATE_NORMAL, &yellow);
2635 gtk_widget_modify_text(
2636 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2637 GTK_STATE_NORMAL, &black);
2638 #if !GTK_CHECK_VERSION(3, 0, 0)
2645 void compose_toolbar_cb(gint action, gpointer data)
2647 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2648 Compose *compose = (Compose*)toolbar_item->parent;
2650 cm_return_if_fail(compose != NULL);
2654 compose_send_cb(NULL, compose);
2657 compose_send_later_cb(NULL, compose);
2660 compose_draft(compose, COMPOSE_QUIT_EDITING);
2663 compose_insert_file_cb(NULL, compose);
2666 compose_attach_cb(NULL, compose);
2669 compose_insert_sig(compose, FALSE);
2672 compose_ext_editor_cb(NULL, compose);
2674 case A_LINEWRAP_CURRENT:
2675 compose_beautify_paragraph(compose, NULL, TRUE);
2677 case A_LINEWRAP_ALL:
2678 compose_wrap_all_full(compose, TRUE);
2681 compose_address_cb(NULL, compose);
2684 case A_CHECK_SPELLING:
2685 compose_check_all(NULL, compose);
2693 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2698 gchar *subject = NULL;
2702 gchar **attach = NULL;
2703 gchar *inreplyto = NULL;
2704 MailField mfield = NO_FIELD_PRESENT;
2706 /* get mailto parts but skip from */
2707 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2710 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2711 mfield = TO_FIELD_PRESENT;
2714 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2716 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2718 if (!g_utf8_validate (subject, -1, NULL)) {
2719 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2720 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2723 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2725 mfield = SUBJECT_FIELD_PRESENT;
2728 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2729 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2732 gboolean prev_autowrap = compose->autowrap;
2734 compose->autowrap = FALSE;
2736 mark = gtk_text_buffer_get_insert(buffer);
2737 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2739 if (!g_utf8_validate (body, -1, NULL)) {
2740 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2741 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2744 gtk_text_buffer_insert(buffer, &iter, body, -1);
2746 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2748 compose->autowrap = prev_autowrap;
2749 if (compose->autowrap)
2750 compose_wrap_all(compose);
2751 mfield = BODY_FIELD_PRESENT;
2755 gint i = 0, att = 0;
2756 gchar *warn_files = NULL;
2757 while (attach[i] != NULL) {
2758 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2759 if (utf8_filename) {
2760 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2761 gchar *tmp = g_strdup_printf("%s%s\n",
2762 warn_files?warn_files:"",
2768 g_free(utf8_filename);
2770 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2775 alertpanel_notice(ngettext(
2776 "The following file has been attached: \n%s",
2777 "The following files have been attached: \n%s", att), warn_files);
2782 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2795 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2797 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2798 {"Cc:", NULL, TRUE},
2799 {"References:", NULL, FALSE},
2800 {"Bcc:", NULL, TRUE},
2801 {"Newsgroups:", NULL, TRUE},
2802 {"Followup-To:", NULL, TRUE},
2803 {"List-Post:", NULL, FALSE},
2804 {"X-Priority:", NULL, FALSE},
2805 {NULL, NULL, FALSE}};
2821 cm_return_val_if_fail(msginfo != NULL, -1);
2823 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2824 procheader_get_header_fields(fp, hentry);
2827 if (hentry[H_REPLY_TO].body != NULL) {
2828 if (hentry[H_REPLY_TO].body[0] != '\0') {
2830 conv_unmime_header(hentry[H_REPLY_TO].body,
2833 g_free(hentry[H_REPLY_TO].body);
2834 hentry[H_REPLY_TO].body = NULL;
2836 if (hentry[H_CC].body != NULL) {
2837 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2838 g_free(hentry[H_CC].body);
2839 hentry[H_CC].body = NULL;
2841 if (hentry[H_REFERENCES].body != NULL) {
2842 if (compose->mode == COMPOSE_REEDIT)
2843 compose->references = hentry[H_REFERENCES].body;
2845 compose->references = compose_parse_references
2846 (hentry[H_REFERENCES].body, msginfo->msgid);
2847 g_free(hentry[H_REFERENCES].body);
2849 hentry[H_REFERENCES].body = NULL;
2851 if (hentry[H_BCC].body != NULL) {
2852 if (compose->mode == COMPOSE_REEDIT)
2854 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2855 g_free(hentry[H_BCC].body);
2856 hentry[H_BCC].body = NULL;
2858 if (hentry[H_NEWSGROUPS].body != NULL) {
2859 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2860 hentry[H_NEWSGROUPS].body = NULL;
2862 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2863 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2864 compose->followup_to =
2865 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2868 g_free(hentry[H_FOLLOWUP_TO].body);
2869 hentry[H_FOLLOWUP_TO].body = NULL;
2871 if (hentry[H_LIST_POST].body != NULL) {
2872 gchar *to = NULL, *start = NULL;
2874 extract_address(hentry[H_LIST_POST].body);
2875 if (hentry[H_LIST_POST].body[0] != '\0') {
2876 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2878 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2879 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2882 g_free(compose->ml_post);
2883 compose->ml_post = to;
2886 g_free(hentry[H_LIST_POST].body);
2887 hentry[H_LIST_POST].body = NULL;
2890 /* CLAWS - X-Priority */
2891 if (compose->mode == COMPOSE_REEDIT)
2892 if (hentry[H_X_PRIORITY].body != NULL) {
2895 priority = atoi(hentry[H_X_PRIORITY].body);
2896 g_free(hentry[H_X_PRIORITY].body);
2898 hentry[H_X_PRIORITY].body = NULL;
2900 if (priority < PRIORITY_HIGHEST ||
2901 priority > PRIORITY_LOWEST)
2902 priority = PRIORITY_NORMAL;
2904 compose->priority = priority;
2907 if (compose->mode == COMPOSE_REEDIT) {
2908 if (msginfo->inreplyto && *msginfo->inreplyto)
2909 compose->inreplyto = g_strdup(msginfo->inreplyto);
2913 if (msginfo->msgid && *msginfo->msgid)
2914 compose->inreplyto = g_strdup(msginfo->msgid);
2916 if (!compose->references) {
2917 if (msginfo->msgid && *msginfo->msgid) {
2918 if (msginfo->inreplyto && *msginfo->inreplyto)
2919 compose->references =
2920 g_strdup_printf("<%s>\n\t<%s>",
2924 compose->references =
2925 g_strconcat("<", msginfo->msgid, ">",
2927 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2928 compose->references =
2929 g_strconcat("<", msginfo->inreplyto, ">",
2937 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2942 cm_return_val_if_fail(msginfo != NULL, -1);
2944 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2945 procheader_get_header_fields(fp, entries);
2949 while (he != NULL && he->name != NULL) {
2951 GtkListStore *model = NULL;
2953 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2954 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2955 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2956 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2957 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2964 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2966 GSList *ref_id_list, *cur;
2970 ref_id_list = references_list_append(NULL, ref);
2971 if (!ref_id_list) return NULL;
2972 if (msgid && *msgid)
2973 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2978 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2979 /* "<" + Message-ID + ">" + CR+LF+TAB */
2980 len += strlen((gchar *)cur->data) + 5;
2982 if (len > MAX_REFERENCES_LEN) {
2983 /* remove second message-ID */
2984 if (ref_id_list && ref_id_list->next &&
2985 ref_id_list->next->next) {
2986 g_free(ref_id_list->next->data);
2987 ref_id_list = g_slist_remove
2988 (ref_id_list, ref_id_list->next->data);
2990 slist_free_strings_full(ref_id_list);
2997 new_ref = g_string_new("");
2998 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2999 if (new_ref->len > 0)
3000 g_string_append(new_ref, "\n\t");
3001 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3004 slist_free_strings_full(ref_id_list);
3006 new_ref_str = new_ref->str;
3007 g_string_free(new_ref, FALSE);
3012 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3013 const gchar *fmt, const gchar *qmark,
3014 const gchar *body, gboolean rewrap,
3015 gboolean need_unescape,
3016 const gchar *err_msg)
3018 MsgInfo* dummyinfo = NULL;
3019 gchar *quote_str = NULL;
3021 gboolean prev_autowrap;
3022 const gchar *trimmed_body = body;
3023 gint cursor_pos = -1;
3024 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3025 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3030 SIGNAL_BLOCK(buffer);
3033 dummyinfo = compose_msginfo_new_from_compose(compose);
3034 msginfo = dummyinfo;
3037 if (qmark != NULL) {
3039 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3040 compose->gtkaspell);
3042 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3044 quote_fmt_scan_string(qmark);
3047 buf = quote_fmt_get_buffer();
3049 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3051 Xstrdup_a(quote_str, buf, goto error)
3054 if (fmt && *fmt != '\0') {
3057 while (*trimmed_body == '\n')
3061 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3062 compose->gtkaspell);
3064 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3066 if (need_unescape) {
3069 /* decode \-escape sequences in the internal representation of the quote format */
3070 tmp = g_malloc(strlen(fmt)+1);
3071 pref_get_unescaped_pref(tmp, fmt);
3072 quote_fmt_scan_string(tmp);
3076 quote_fmt_scan_string(fmt);
3080 buf = quote_fmt_get_buffer();
3082 gint line = quote_fmt_get_line();
3083 alertpanel_error(err_msg, line);
3089 prev_autowrap = compose->autowrap;
3090 compose->autowrap = FALSE;
3092 mark = gtk_text_buffer_get_insert(buffer);
3093 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3094 if (g_utf8_validate(buf, -1, NULL)) {
3095 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3097 gchar *tmpout = NULL;
3098 tmpout = conv_codeset_strdup
3099 (buf, conv_get_locale_charset_str_no_utf8(),
3101 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3103 tmpout = g_malloc(strlen(buf)*2+1);
3104 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3106 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3110 cursor_pos = quote_fmt_get_cursor_pos();
3111 if (cursor_pos == -1)
3112 cursor_pos = gtk_text_iter_get_offset(&iter);
3113 compose->set_cursor_pos = cursor_pos;
3115 gtk_text_buffer_get_start_iter(buffer, &iter);
3116 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3117 gtk_text_buffer_place_cursor(buffer, &iter);
3119 compose->autowrap = prev_autowrap;
3120 if (compose->autowrap && rewrap)
3121 compose_wrap_all(compose);
3128 SIGNAL_UNBLOCK(buffer);
3130 procmsg_msginfo_free( dummyinfo );
3135 /* if ml_post is of type addr@host and from is of type
3136 * addr-anything@host, return TRUE
3138 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3140 gchar *left_ml = NULL;
3141 gchar *right_ml = NULL;
3142 gchar *left_from = NULL;
3143 gchar *right_from = NULL;
3144 gboolean result = FALSE;
3146 if (!ml_post || !from)
3149 left_ml = g_strdup(ml_post);
3150 if (strstr(left_ml, "@")) {
3151 right_ml = strstr(left_ml, "@")+1;
3152 *(strstr(left_ml, "@")) = '\0';
3155 left_from = g_strdup(from);
3156 if (strstr(left_from, "@")) {
3157 right_from = strstr(left_from, "@")+1;
3158 *(strstr(left_from, "@")) = '\0';
3161 if (left_ml && left_from && right_ml && right_from
3162 && !strncmp(left_from, left_ml, strlen(left_ml))
3163 && !strcmp(right_from, right_ml)) {
3172 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3173 gboolean respect_default_to)
3177 if (!folder || !folder->prefs)
3180 if (respect_default_to && folder->prefs->enable_default_to) {
3181 compose_entry_append(compose, folder->prefs->default_to,
3182 COMPOSE_TO, PREF_FOLDER);
3183 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3185 if (folder->prefs->enable_default_cc)
3186 compose_entry_append(compose, folder->prefs->default_cc,