2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
167 SUBJECT_FIELD_PRESENT,
172 #define B64_LINE_SIZE 57
173 #define B64_BUFFSIZE 77
175 #define MAX_REFERENCES_LEN 999
177 static GList *compose_list = NULL;
179 static Compose *compose_generic_new (PrefsAccount *account,
182 GPtrArray *attach_files,
183 GList *listAddress );
185 static Compose *compose_create (PrefsAccount *account,
190 static void compose_entry_mark_default_to (Compose *compose,
191 const gchar *address);
192 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
197 static Compose *compose_forward_multiple (PrefsAccount *account,
198 GSList *msginfo_list);
199 static Compose *compose_reply (MsgInfo *msginfo,
200 ComposeQuoteMode quote_mode,
205 static Compose *compose_reply_mode (ComposeMode mode,
206 GSList *msginfo_list,
208 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
209 static void compose_update_privacy_systems_menu(Compose *compose);
211 static GtkWidget *compose_account_option_menu_create
213 static void compose_set_out_encoding (Compose *compose);
214 static void compose_set_template_menu (Compose *compose);
215 static void compose_destroy (Compose *compose);
217 static MailField compose_entries_set (Compose *compose,
219 ComposeEntryType to_type);
220 static gint compose_parse_header (Compose *compose,
222 static gchar *compose_parse_references (const gchar *ref,
225 static gchar *compose_quote_fmt (Compose *compose,
231 gboolean need_unescape,
232 const gchar *err_msg);
234 static void compose_reply_set_entry (Compose *compose,
240 followup_and_reply_to);
241 static void compose_reedit_set_entry (Compose *compose,
244 static void compose_insert_sig (Compose *compose,
246 static ComposeInsertResult compose_insert_file (Compose *compose,
249 static gboolean compose_attach_append (Compose *compose,
252 const gchar *content_type);
253 static void compose_attach_parts (Compose *compose,
256 static gboolean compose_beautify_paragraph (Compose *compose,
257 GtkTextIter *par_iter,
259 static void compose_wrap_all (Compose *compose);
260 static void compose_wrap_all_full (Compose *compose,
263 static void compose_set_title (Compose *compose);
264 static void compose_select_account (Compose *compose,
265 PrefsAccount *account,
268 static PrefsAccount *compose_current_mail_account(void);
269 /* static gint compose_send (Compose *compose); */
270 static gboolean compose_check_for_valid_recipient
272 static gboolean compose_check_entries (Compose *compose,
273 gboolean check_everything);
274 static gint compose_write_to_file (Compose *compose,
277 gboolean attach_parts);
278 static gint compose_write_body_to_file (Compose *compose,
280 static gint compose_remove_reedit_target (Compose *compose,
282 static void compose_remove_draft (Compose *compose);
283 static gint compose_queue_sub (Compose *compose,
287 gboolean check_subject,
288 gboolean remove_reedit_target);
289 static int compose_add_attachments (Compose *compose,
291 static gchar *compose_get_header (Compose *compose);
293 static void compose_convert_header (Compose *compose,
298 gboolean addr_field);
300 static void compose_attach_info_free (AttachInfo *ainfo);
301 static void compose_attach_remove_selected (GtkAction *action,
304 static void compose_template_apply (Compose *compose,
307 static void compose_attach_property (GtkAction *action,
309 static void compose_attach_property_create (gboolean *cancelled);
310 static void attach_property_ok (GtkWidget *widget,
311 gboolean *cancelled);
312 static void attach_property_cancel (GtkWidget *widget,
313 gboolean *cancelled);
314 static gint attach_property_delete_event (GtkWidget *widget,
316 gboolean *cancelled);
317 static gboolean attach_property_key_pressed (GtkWidget *widget,
319 gboolean *cancelled);
321 static void compose_exec_ext_editor (Compose *compose);
323 static gint compose_exec_ext_editor_real (const gchar *file);
324 static gboolean compose_ext_editor_kill (Compose *compose);
325 static gboolean compose_input_cb (GIOChannel *source,
326 GIOCondition condition,
328 static void compose_set_ext_editor_sensitive (Compose *compose,
330 #endif /* G_OS_UNIX */
332 static void compose_undo_state_changed (UndoMain *undostruct,
337 static void compose_create_header_entry (Compose *compose);
338 static void compose_add_header_entry (Compose *compose, const gchar *header,
339 gchar *text, ComposePrefType pref_type);
340 static void compose_remove_header_entries(Compose *compose);
342 static void compose_update_priority_menu_item(Compose * compose);
344 static void compose_spell_menu_changed (void *data);
345 static void compose_dict_changed (void *data);
347 static void compose_add_field_list ( Compose *compose,
348 GList *listAddress );
350 /* callback functions */
352 static gboolean compose_edit_size_alloc (GtkEditable *widget,
353 GtkAllocation *allocation,
354 GtkSHRuler *shruler);
355 static void account_activated (GtkComboBox *optmenu,
357 static void attach_selected (GtkTreeView *tree_view,
358 GtkTreePath *tree_path,
359 GtkTreeViewColumn *column,
361 static gboolean attach_button_pressed (GtkWidget *widget,
362 GdkEventButton *event,
364 static gboolean attach_key_pressed (GtkWidget *widget,
367 static void compose_send_cb (GtkAction *action, gpointer data);
368 static void compose_send_later_cb (GtkAction *action, gpointer data);
370 static void compose_save_cb (GtkAction *action,
373 static void compose_attach_cb (GtkAction *action,
375 static void compose_insert_file_cb (GtkAction *action,
377 static void compose_insert_sig_cb (GtkAction *action,
380 static void compose_close_cb (GtkAction *action,
383 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
385 static void compose_address_cb (GtkAction *action,
387 static void about_show_cb (GtkAction *action,
389 static void compose_template_activate_cb(GtkWidget *widget,
392 static void compose_ext_editor_cb (GtkAction *action,
395 static gint compose_delete_cb (GtkWidget *widget,
399 static void compose_undo_cb (GtkAction *action,
401 static void compose_redo_cb (GtkAction *action,
403 static void compose_cut_cb (GtkAction *action,
405 static void compose_copy_cb (GtkAction *action,
407 static void compose_paste_cb (GtkAction *action,
409 static void compose_paste_as_quote_cb (GtkAction *action,
411 static void compose_paste_no_wrap_cb (GtkAction *action,
413 static void compose_paste_wrap_cb (GtkAction *action,
415 static void compose_allsel_cb (GtkAction *action,
418 static void compose_advanced_action_cb (GtkAction *action,
421 static void compose_grab_focus_cb (GtkWidget *widget,
424 static void compose_changed_cb (GtkTextBuffer *textbuf,
427 static void compose_wrap_cb (GtkAction *action,
429 static void compose_wrap_all_cb (GtkAction *action,
431 static void compose_find_cb (GtkAction *action,
433 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
435 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
438 static void compose_toggle_ruler_cb (GtkToggleAction *action,
440 static void compose_toggle_sign_cb (GtkToggleAction *action,
442 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
444 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
445 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
446 static void activate_privacy_system (Compose *compose,
447 PrefsAccount *account,
449 static void compose_use_signing(Compose *compose, gboolean use_signing);
450 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
451 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
453 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
455 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
456 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
457 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
459 static void compose_attach_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_insert_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
475 static void compose_header_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
484 static gboolean compose_drag_drop (GtkWidget *widget,
485 GdkDragContext *drag_context,
487 guint time, gpointer user_data);
489 static void text_inserted (GtkTextBuffer *buffer,
494 static Compose *compose_generic_reply(MsgInfo *msginfo,
495 ComposeQuoteMode quote_mode,
499 gboolean followup_and_reply_to,
502 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
503 ComposeHeaderEntry *headerentry);
504 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
506 ComposeHeaderEntry *headerentry);
507 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
508 ComposeHeaderEntry *headerentry);
510 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
512 static void compose_allow_user_actions (Compose *compose, gboolean allow);
514 static void compose_nothing_cb (GtkAction *action, gpointer data)
520 static void compose_check_all (GtkAction *action, gpointer data);
521 static void compose_highlight_all (GtkAction *action, gpointer data);
522 static void compose_check_backwards (GtkAction *action, gpointer data);
523 static void compose_check_forwards_go (GtkAction *action, gpointer data);
526 static gint compose_defer_auto_save_draft (Compose *compose);
527 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
529 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
532 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
533 FolderItem *folder_item);
535 static void compose_attach_update_label(Compose *compose);
537 static GtkActionEntry compose_popup_entries[] =
539 {"Compose", NULL, "Compose" },
540 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
541 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
542 {"Compose/---", NULL, "---", NULL, NULL, NULL },
543 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
546 static GtkActionEntry compose_entries[] =
548 {"Menu", NULL, "Menu" },
550 {"Message", NULL, N_("_Message") },
551 {"Edit", NULL, N_("_Edit") },
553 {"Spelling", NULL, N_("_Spelling") },
555 {"Options", NULL, N_("_Options") },
556 {"Tools", NULL, N_("_Tools") },
557 {"Help", NULL, N_("_Help") },
559 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
560 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
561 {"Message/---", NULL, "---" },
563 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
564 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
565 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
566 /* {"Message/---", NULL, "---" }, */
567 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
568 /* {"Message/---", NULL, "---" }, */
569 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
572 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
573 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
574 {"Edit/---", NULL, "---" },
576 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
577 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
578 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
580 {"Edit/SpecialPaste", NULL, N_("Special paste") },
581 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
582 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
583 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
585 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
587 {"Edit/Advanced", NULL, N_("A_dvanced") },
588 {"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*/
589 {"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*/
590 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
591 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
592 {"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*/
593 {"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*/
594 {"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*/
595 {"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*/
596 {"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*/
597 {"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*/
598 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
599 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
600 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
601 {"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*/
603 /* {"Edit/---", NULL, "---" }, */
604 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
608 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
613 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
614 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
615 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
616 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
618 {"Spelling/---", NULL, "---" },
619 {"Spelling/Options", NULL, N_("_Options") },
624 {"Options/ReplyMode", NULL, N_("Reply _mode") },
625 {"Options/---", NULL, "---" },
626 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
627 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
629 /* {"Options/---", NULL, "---" }, */
631 {"Options/Priority", NULL, N_("_Priority") },
633 {"Options/Encoding", NULL, N_("Character _encoding") },
634 {"Options/Encoding/---", NULL, "---" },
635 #define ENC_ACTION(cs_char,c_char,string) \
636 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
638 {"Options/Encoding/Western", NULL, N_("Western European") },
639 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
640 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
641 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
642 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
643 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
644 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
645 {"Options/Encoding/Korean", NULL, N_("Korean") },
646 {"Options/Encoding/Thai", NULL, N_("Thai") },
649 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
651 {"Tools/Template", NULL, N_("_Template") },
652 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
653 {"Tools/Actions", NULL, N_("Actio_ns") },
654 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
660 static GtkToggleActionEntry compose_toggle_entries[] =
662 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
663 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
664 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
665 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
666 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
667 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
668 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
671 static GtkRadioActionEntry compose_radio_rm_entries[] =
673 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
674 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
675 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
676 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
679 static GtkRadioActionEntry compose_radio_prio_entries[] =
681 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
682 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
683 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
684 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
688 static GtkRadioActionEntry compose_radio_enc_entries[] =
690 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
724 static GtkTargetEntry compose_mime_types[] =
726 {"text/uri-list", 0, 0},
727 {"UTF8_STRING", 0, 0},
731 static gboolean compose_put_existing_to_front(MsgInfo *info)
733 GList *compose_list = compose_get_compose_list();
737 for (elem = compose_list; elem != NULL && elem->data != NULL;
739 Compose *c = (Compose*)elem->data;
741 if (!c->targetinfo || !c->targetinfo->msgid ||
745 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
746 gtkut_window_popup(c->window);
754 static GdkColor quote_color1 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_color2 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_color3 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor quote_bgcolor1 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
763 static GdkColor quote_bgcolor2 =
764 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor3 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor signature_color = {
775 static GdkColor uri_color = {
782 static void compose_create_tags(GtkTextView *text, Compose *compose)
784 GtkTextBuffer *buffer;
785 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
791 buffer = gtk_text_view_get_buffer(text);
793 if (prefs_common.enable_color) {
794 /* grab the quote colors, converting from an int to a GdkColor */
795 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
797 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
799 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
807 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
809 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
812 signature_color = quote_color1 = quote_color2 = quote_color3 =
813 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
816 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
817 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
818 "foreground-gdk", "e_color1,
819 "paragraph-background-gdk", "e_bgcolor1,
821 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
822 "foreground-gdk", "e_color2,
823 "paragraph-background-gdk", "e_bgcolor2,
825 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
826 "foreground-gdk", "e_color3,
827 "paragraph-background-gdk", "e_bgcolor3,
830 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
831 "foreground-gdk", "e_color1,
833 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
834 "foreground-gdk", "e_color2,
836 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
837 "foreground-gdk", "e_color3,
841 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
842 "foreground-gdk", &signature_color,
845 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
846 "foreground-gdk", &uri_color,
848 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
849 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
851 color[0] = quote_color1;
852 color[1] = quote_color2;
853 color[2] = quote_color3;
854 color[3] = quote_bgcolor1;
855 color[4] = quote_bgcolor2;
856 color[5] = quote_bgcolor3;
857 color[6] = signature_color;
858 color[7] = uri_color;
859 cmap = gdk_drawable_get_colormap(compose->window->window);
860 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
862 for (i = 0; i < 8; i++) {
863 if (success[i] == FALSE) {
866 g_warning("Compose: color allocation failed.\n");
867 style = gtk_widget_get_style(GTK_WIDGET(text));
868 quote_color1 = quote_color2 = quote_color3 =
869 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
870 signature_color = uri_color = black;
875 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
876 GPtrArray *attach_files)
878 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
881 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
883 return compose_generic_new(account, mailto, item, NULL, NULL);
886 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
888 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
891 #define SCROLL_TO_CURSOR(compose) { \
892 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
893 gtk_text_view_get_buffer( \
894 GTK_TEXT_VIEW(compose->text))); \
895 gtk_text_view_scroll_mark_onscreen( \
896 GTK_TEXT_VIEW(compose->text), \
900 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
903 if (folderidentifier) {
904 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
905 prefs_common.compose_save_to_history = add_history(
906 prefs_common.compose_save_to_history, folderidentifier);
907 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
908 prefs_common.compose_save_to_history);
911 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
912 if (folderidentifier)
913 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
915 gtk_entry_set_text(GTK_ENTRY(entry), "");
918 static gchar *compose_get_save_to(Compose *compose)
921 gchar *result = NULL;
922 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
923 result = gtk_editable_get_chars(entry, 0, -1);
926 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
927 prefs_common.compose_save_to_history = add_history(
928 prefs_common.compose_save_to_history, result);
929 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
930 prefs_common.compose_save_to_history);
935 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
936 GPtrArray *attach_files, GList *listAddress )
939 GtkTextView *textview;
940 GtkTextBuffer *textbuf;
942 const gchar *subject_format = NULL;
943 const gchar *body_format = NULL;
944 gchar *mailto_from = NULL;
945 PrefsAccount *mailto_account = NULL;
946 MsgInfo* dummyinfo = NULL;
947 MailField mfield = NO_FIELD_PRESENT;
951 /* check if mailto defines a from */
952 if (mailto && *mailto != '\0') {
953 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
954 /* mailto defines a from, check if we can get account prefs from it,
955 if not, the account prefs will be guessed using other ways, but we'll keep
958 mailto_account = account_find_from_address(mailto_from, TRUE);
960 account = mailto_account;
963 /* if no account prefs set from mailto, set if from folder prefs (if any) */
964 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
965 account = account_find_from_id(item->prefs->default_account);
967 /* if no account prefs set, fallback to the current one */
968 if (!account) account = cur_account;
969 cm_return_val_if_fail(account != NULL, NULL);
971 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
973 /* override from name if mailto asked for it */
975 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
978 /* override from name according to folder properties */
979 if (item && item->prefs &&
980 item->prefs->compose_with_format &&
981 item->prefs->compose_override_from_format &&
982 *item->prefs->compose_override_from_format != '\0') {
987 dummyinfo = compose_msginfo_new_from_compose(compose);
989 /* decode \-escape sequences in the internal representation of the quote format */
990 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
991 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
994 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
997 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
999 quote_fmt_scan_string(tmp);
1002 buf = quote_fmt_get_buffer();
1004 alertpanel_error(_("New message From format error."));
1006 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1007 quote_fmt_reset_vartable();
1012 compose->replyinfo = NULL;
1013 compose->fwdinfo = NULL;
1015 textview = GTK_TEXT_VIEW(compose->text);
1016 textbuf = gtk_text_view_get_buffer(textview);
1017 compose_create_tags(textview, compose);
1019 undo_block(compose->undostruct);
1021 compose_set_dictionaries_from_folder_prefs(compose, item);
1024 if (account->auto_sig)
1025 compose_insert_sig(compose, FALSE);
1026 gtk_text_buffer_get_start_iter(textbuf, &iter);
1027 gtk_text_buffer_place_cursor(textbuf, &iter);
1029 if (account->protocol != A_NNTP) {
1030 if (mailto && *mailto != '\0') {
1031 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1033 } else if (item && item->prefs) {
1034 if (item->prefs->enable_default_bcc) {
1035 compose_entry_append(compose, item->prefs->default_bcc,
1036 COMPOSE_BCC, PREF_FOLDER);
1038 if (item->prefs->enable_default_cc) {
1039 compose_entry_append(compose, item->prefs->default_cc,
1040 COMPOSE_CC, PREF_FOLDER);
1042 if (item->prefs->enable_default_replyto) {
1043 compose_entry_append(compose, item->prefs->default_replyto,
1044 COMPOSE_REPLYTO, PREF_FOLDER);
1046 if (item->prefs->enable_default_to) {
1047 compose_entry_append(compose, item->prefs->default_to,
1048 COMPOSE_TO, PREF_FOLDER);
1049 compose_entry_mark_default_to(compose, item->prefs->default_to);
1052 if (item && item->ret_rcpt) {
1053 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1056 if (mailto && *mailto != '\0') {
1057 if (!strchr(mailto, '@'))
1058 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1060 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1061 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1062 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1063 mfield = TO_FIELD_PRESENT;
1066 * CLAWS: just don't allow return receipt request, even if the user
1067 * may want to send an email. simple but foolproof.
1069 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1071 compose_add_field_list( compose, listAddress );
1073 if (item && item->prefs && item->prefs->compose_with_format) {
1074 subject_format = item->prefs->compose_subject_format;
1075 body_format = item->prefs->compose_body_format;
1076 } else if (account->compose_with_format) {
1077 subject_format = account->compose_subject_format;
1078 body_format = account->compose_body_format;
1079 } else if (prefs_common.compose_with_format) {
1080 subject_format = prefs_common.compose_subject_format;
1081 body_format = prefs_common.compose_body_format;
1084 if (subject_format || body_format) {
1087 && *subject_format != '\0' )
1089 gchar *subject = NULL;
1094 dummyinfo = compose_msginfo_new_from_compose(compose);
1096 /* decode \-escape sequences in the internal representation of the quote format */
1097 tmp = malloc(strlen(subject_format)+1);
1098 pref_get_unescaped_pref(tmp, subject_format);
1100 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1102 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1103 compose->gtkaspell);
1105 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1107 quote_fmt_scan_string(tmp);
1110 buf = quote_fmt_get_buffer();
1112 alertpanel_error(_("New message subject format error."));
1114 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1115 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1116 quote_fmt_reset_vartable();
1120 mfield = SUBJECT_FIELD_PRESENT;
1124 && *body_format != '\0' )
1127 GtkTextBuffer *buffer;
1128 GtkTextIter start, end;
1132 dummyinfo = compose_msginfo_new_from_compose(compose);
1134 text = GTK_TEXT_VIEW(compose->text);
1135 buffer = gtk_text_view_get_buffer(text);
1136 gtk_text_buffer_get_start_iter(buffer, &start);
1137 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1138 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1140 compose_quote_fmt(compose, dummyinfo,
1142 NULL, tmp, FALSE, TRUE,
1143 _("The body of the \"New message\" template has an error at line %d."));
1144 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1145 quote_fmt_reset_vartable();
1149 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1150 gtkaspell_highlight_all(compose->gtkaspell);
1152 mfield = BODY_FIELD_PRESENT;
1156 procmsg_msginfo_free( dummyinfo );
1162 for (i = 0; i < attach_files->len; i++) {
1163 file = g_ptr_array_index(attach_files, i);
1164 compose_attach_append(compose, file, file, NULL);
1168 compose_show_first_last_header(compose, TRUE);
1170 /* Set save folder */
1171 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1172 gchar *folderidentifier;
1174 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1175 folderidentifier = folder_item_get_identifier(item);
1176 compose_set_save_to(compose, folderidentifier);
1177 g_free(folderidentifier);
1180 /* Place cursor according to provided input (mfield) */
1182 case NO_FIELD_PRESENT:
1183 if (compose->header_last)
1184 gtk_widget_grab_focus(compose->header_last->entry);
1186 case TO_FIELD_PRESENT:
1187 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1189 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1192 gtk_widget_grab_focus(compose->subject_entry);
1194 case SUBJECT_FIELD_PRESENT:
1195 textview = GTK_TEXT_VIEW(compose->text);
1198 textbuf = gtk_text_view_get_buffer(textview);
1201 mark = gtk_text_buffer_get_insert(textbuf);
1202 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1203 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1205 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1206 * only defers where it comes to the variable body
1207 * is not null. If no body is present compose->text
1208 * will be null in which case you cannot place the
1209 * cursor inside the component so. An empty component
1210 * is therefore created before placing the cursor
1212 case BODY_FIELD_PRESENT:
1213 gtk_widget_grab_focus(compose->text);
1217 undo_unblock(compose->undostruct);
1219 if (prefs_common.auto_exteditor)
1220 compose_exec_ext_editor(compose);
1222 compose->draft_timeout_tag = -1;
1223 SCROLL_TO_CURSOR(compose);
1225 compose->modified = FALSE;
1226 compose_set_title(compose);
1228 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1233 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1234 gboolean override_pref, const gchar *system)
1236 const gchar *privacy = NULL;
1238 cm_return_if_fail(compose != NULL);
1239 cm_return_if_fail(account != NULL);
1241 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1246 else if (account->default_privacy_system
1247 && strlen(account->default_privacy_system)) {
1248 privacy = account->default_privacy_system;
1250 GSList *privacy_avail = privacy_get_system_ids();
1251 if (privacy_avail && g_slist_length(privacy_avail)) {
1252 privacy = (gchar *)(privacy_avail->data);
1255 if (privacy != NULL) {
1257 g_free(compose->privacy_system);
1258 compose->privacy_system = NULL;
1260 if (compose->privacy_system == NULL)
1261 compose->privacy_system = g_strdup(privacy);
1262 else if (*(compose->privacy_system) == '\0') {
1263 g_free(compose->privacy_system);
1264 compose->privacy_system = g_strdup(privacy);
1266 compose_update_privacy_system_menu_item(compose, FALSE);
1267 compose_use_encryption(compose, TRUE);
1271 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1273 const gchar *privacy = NULL;
1277 else if (account->default_privacy_system
1278 && strlen(account->default_privacy_system)) {
1279 privacy = account->default_privacy_system;
1281 GSList *privacy_avail = privacy_get_system_ids();
1282 if (privacy_avail && g_slist_length(privacy_avail)) {
1283 privacy = (gchar *)(privacy_avail->data);
1287 if (privacy != NULL) {
1289 g_free(compose->privacy_system);
1290 compose->privacy_system = NULL;
1292 if (compose->privacy_system == NULL)
1293 compose->privacy_system = g_strdup(privacy);
1294 compose_update_privacy_system_menu_item(compose, FALSE);
1295 compose_use_signing(compose, TRUE);
1299 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1303 Compose *compose = NULL;
1305 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1307 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1308 cm_return_val_if_fail(msginfo != NULL, NULL);
1310 list_len = g_slist_length(msginfo_list);
1314 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1315 FALSE, prefs_common.default_reply_list, FALSE, body);
1317 case COMPOSE_REPLY_WITH_QUOTE:
1318 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1319 FALSE, prefs_common.default_reply_list, FALSE, body);
1321 case COMPOSE_REPLY_WITHOUT_QUOTE:
1322 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1323 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1325 case COMPOSE_REPLY_TO_SENDER:
1326 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1327 FALSE, FALSE, TRUE, body);
1329 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1330 compose = compose_followup_and_reply_to(msginfo,
1331 COMPOSE_QUOTE_CHECK,
1332 FALSE, FALSE, body);
1334 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1335 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1336 FALSE, FALSE, TRUE, body);
1338 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1339 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1340 FALSE, FALSE, TRUE, NULL);
1342 case COMPOSE_REPLY_TO_ALL:
1343 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1344 TRUE, FALSE, FALSE, body);
1346 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1347 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1348 TRUE, FALSE, FALSE, body);
1350 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1351 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1352 TRUE, FALSE, FALSE, NULL);
1354 case COMPOSE_REPLY_TO_LIST:
1355 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1356 FALSE, TRUE, FALSE, body);
1358 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1359 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1360 FALSE, TRUE, FALSE, body);
1362 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1363 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1364 FALSE, TRUE, FALSE, NULL);
1366 case COMPOSE_FORWARD:
1367 if (prefs_common.forward_as_attachment) {
1368 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1371 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1375 case COMPOSE_FORWARD_INLINE:
1376 /* check if we reply to more than one Message */
1377 if (list_len == 1) {
1378 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1381 /* more messages FALL THROUGH */
1382 case COMPOSE_FORWARD_AS_ATTACH:
1383 compose = compose_forward_multiple(NULL, msginfo_list);
1385 case COMPOSE_REDIRECT:
1386 compose = compose_redirect(NULL, msginfo, FALSE);
1389 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1392 if (compose == NULL) {
1393 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1397 compose->rmode = mode;
1398 switch (compose->rmode) {
1400 case COMPOSE_REPLY_WITH_QUOTE:
1401 case COMPOSE_REPLY_WITHOUT_QUOTE:
1402 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1403 debug_print("reply mode Normal\n");
1404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1405 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1407 case COMPOSE_REPLY_TO_SENDER:
1408 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1409 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1410 debug_print("reply mode Sender\n");
1411 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1413 case COMPOSE_REPLY_TO_ALL:
1414 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1415 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1416 debug_print("reply mode All\n");
1417 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1419 case COMPOSE_REPLY_TO_LIST:
1420 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1421 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1422 debug_print("reply mode List\n");
1423 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1431 static Compose *compose_reply(MsgInfo *msginfo,
1432 ComposeQuoteMode quote_mode,
1438 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1439 to_sender, FALSE, body);
1442 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1443 ComposeQuoteMode quote_mode,
1448 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1449 to_sender, TRUE, body);
1452 static void compose_extract_original_charset(Compose *compose)
1454 MsgInfo *info = NULL;
1455 if (compose->replyinfo) {
1456 info = compose->replyinfo;
1457 } else if (compose->fwdinfo) {
1458 info = compose->fwdinfo;
1459 } else if (compose->targetinfo) {
1460 info = compose->targetinfo;
1463 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1464 MimeInfo *partinfo = mimeinfo;
1465 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1466 partinfo = procmime_mimeinfo_next(partinfo);
1468 compose->orig_charset =
1469 g_strdup(procmime_mimeinfo_get_parameter(
1470 partinfo, "charset"));
1472 procmime_mimeinfo_free_all(mimeinfo);
1476 #define SIGNAL_BLOCK(buffer) { \
1477 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1478 G_CALLBACK(compose_changed_cb), \
1480 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(text_inserted), \
1485 #define SIGNAL_UNBLOCK(buffer) { \
1486 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1487 G_CALLBACK(compose_changed_cb), \
1489 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1490 G_CALLBACK(text_inserted), \
1494 static Compose *compose_generic_reply(MsgInfo *msginfo,
1495 ComposeQuoteMode quote_mode,
1496 gboolean to_all, gboolean to_ml,
1498 gboolean followup_and_reply_to,
1502 PrefsAccount *account = NULL;
1503 GtkTextView *textview;
1504 GtkTextBuffer *textbuf;
1505 gboolean quote = FALSE;
1506 const gchar *qmark = NULL;
1507 const gchar *body_fmt = NULL;
1508 gchar *s_system = NULL;
1510 cm_return_val_if_fail(msginfo != NULL, NULL);
1511 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1513 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1515 cm_return_val_if_fail(account != NULL, NULL);
1517 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1519 compose->updating = TRUE;
1521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1522 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1524 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1525 if (!compose->replyinfo)
1526 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1528 compose_extract_original_charset(compose);
1530 if (msginfo->folder && msginfo->folder->ret_rcpt)
1531 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1533 /* Set save folder */
1534 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1535 gchar *folderidentifier;
1537 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1538 folderidentifier = folder_item_get_identifier(msginfo->folder);
1539 compose_set_save_to(compose, folderidentifier);
1540 g_free(folderidentifier);
1543 if (compose_parse_header(compose, msginfo) < 0) {
1544 compose->updating = FALSE;
1545 compose_destroy(compose);
1549 /* override from name according to folder properties */
1550 if (msginfo->folder && msginfo->folder->prefs &&
1551 msginfo->folder->prefs->reply_with_format &&
1552 msginfo->folder->prefs->reply_override_from_format &&
1553 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1558 /* decode \-escape sequences in the internal representation of the quote format */
1559 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1560 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1563 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1564 compose->gtkaspell);
1566 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1568 quote_fmt_scan_string(tmp);
1571 buf = quote_fmt_get_buffer();
1573 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1575 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1576 quote_fmt_reset_vartable();
1581 textview = (GTK_TEXT_VIEW(compose->text));
1582 textbuf = gtk_text_view_get_buffer(textview);
1583 compose_create_tags(textview, compose);
1585 undo_block(compose->undostruct);
1587 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1590 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1591 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1592 /* use the reply format of folder (if enabled), or the account's one
1593 (if enabled) or fallback to the global reply format, which is always
1594 enabled (even if empty), and use the relevant quotemark */
1596 if (msginfo->folder && msginfo->folder->prefs &&
1597 msginfo->folder->prefs->reply_with_format) {
1598 qmark = msginfo->folder->prefs->reply_quotemark;
1599 body_fmt = msginfo->folder->prefs->reply_body_format;
1601 } else if (account->reply_with_format) {
1602 qmark = account->reply_quotemark;
1603 body_fmt = account->reply_body_format;
1606 qmark = prefs_common.quotemark;
1607 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1608 body_fmt = gettext(prefs_common.quotefmt);
1615 /* empty quotemark is not allowed */
1616 if (qmark == NULL || *qmark == '\0')
1618 compose_quote_fmt(compose, compose->replyinfo,
1619 body_fmt, qmark, body, FALSE, TRUE,
1620 _("The body of the \"Reply\" template has an error at line %d."));
1621 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1622 quote_fmt_reset_vartable();
1624 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1625 gtkaspell_highlight_all(compose->gtkaspell);
1629 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1630 compose_force_encryption(compose, account, FALSE, s_system);
1633 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1634 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1635 compose_force_signing(compose, account, s_system);
1639 SIGNAL_BLOCK(textbuf);
1641 if (account->auto_sig)
1642 compose_insert_sig(compose, FALSE);
1644 compose_wrap_all(compose);
1646 SIGNAL_UNBLOCK(textbuf);
1648 gtk_widget_grab_focus(compose->text);
1650 undo_unblock(compose->undostruct);
1652 if (prefs_common.auto_exteditor)
1653 compose_exec_ext_editor(compose);
1655 compose->modified = FALSE;
1656 compose_set_title(compose);
1658 compose->updating = FALSE;
1659 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1660 SCROLL_TO_CURSOR(compose);
1662 if (compose->deferred_destroy) {
1663 compose_destroy(compose);
1671 #define INSERT_FW_HEADER(var, hdr) \
1672 if (msginfo->var && *msginfo->var) { \
1673 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1674 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1675 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1678 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1679 gboolean as_attach, const gchar *body,
1680 gboolean no_extedit,
1684 GtkTextView *textview;
1685 GtkTextBuffer *textbuf;
1688 cm_return_val_if_fail(msginfo != NULL, NULL);
1689 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1692 !(account = compose_guess_forward_account_from_msginfo
1694 account = cur_account;
1696 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1698 compose->updating = TRUE;
1699 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1700 if (!compose->fwdinfo)
1701 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1703 compose_extract_original_charset(compose);
1705 if (msginfo->subject && *msginfo->subject) {
1706 gchar *buf, *buf2, *p;
1708 buf = p = g_strdup(msginfo->subject);
1709 p += subject_get_prefix_length(p);
1710 memmove(buf, p, strlen(p) + 1);
1712 buf2 = g_strdup_printf("Fw: %s", buf);
1713 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1719 /* override from name according to folder properties */
1720 if (msginfo->folder && msginfo->folder->prefs &&
1721 msginfo->folder->prefs->forward_with_format &&
1722 msginfo->folder->prefs->forward_override_from_format &&
1723 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1727 MsgInfo *full_msginfo = NULL;
1730 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1732 full_msginfo = procmsg_msginfo_copy(msginfo);
1734 /* decode \-escape sequences in the internal representation of the quote format */
1735 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1736 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1739 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1740 compose->gtkaspell);
1742 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1744 quote_fmt_scan_string(tmp);
1747 buf = quote_fmt_get_buffer();
1749 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1751 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1752 quote_fmt_reset_vartable();
1755 procmsg_msginfo_free(full_msginfo);
1758 textview = GTK_TEXT_VIEW(compose->text);
1759 textbuf = gtk_text_view_get_buffer(textview);
1760 compose_create_tags(textview, compose);
1762 undo_block(compose->undostruct);
1766 msgfile = procmsg_get_message_file(msginfo);
1767 if (!is_file_exist(msgfile))
1768 g_warning("%s: file not exist\n", msgfile);
1770 compose_attach_append(compose, msgfile, msgfile,
1775 const gchar *qmark = NULL;
1776 const gchar *body_fmt = NULL;
1777 MsgInfo *full_msginfo;
1779 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1780 body_fmt = gettext(prefs_common.fw_quotefmt);
1784 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1786 full_msginfo = procmsg_msginfo_copy(msginfo);
1788 /* use the forward format of folder (if enabled), or the account's one
1789 (if enabled) or fallback to the global forward format, which is always
1790 enabled (even if empty), and use the relevant quotemark */
1791 if (msginfo->folder && msginfo->folder->prefs &&
1792 msginfo->folder->prefs->forward_with_format) {
1793 qmark = msginfo->folder->prefs->forward_quotemark;
1794 body_fmt = msginfo->folder->prefs->forward_body_format;
1796 } else if (account->forward_with_format) {
1797 qmark = account->forward_quotemark;
1798 body_fmt = account->forward_body_format;
1801 qmark = prefs_common.fw_quotemark;
1802 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1803 body_fmt = gettext(prefs_common.fw_quotefmt);
1808 /* empty quotemark is not allowed */
1809 if (qmark == NULL || *qmark == '\0')
1812 compose_quote_fmt(compose, full_msginfo,
1813 body_fmt, qmark, body, FALSE, TRUE,
1814 _("The body of the \"Forward\" template has an error at line %d."));
1815 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1816 quote_fmt_reset_vartable();
1817 compose_attach_parts(compose, msginfo);
1819 procmsg_msginfo_free(full_msginfo);
1821 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1822 gtkaspell_highlight_all(compose->gtkaspell);
1826 SIGNAL_BLOCK(textbuf);
1828 if (account->auto_sig)
1829 compose_insert_sig(compose, FALSE);
1831 compose_wrap_all(compose);
1833 SIGNAL_UNBLOCK(textbuf);
1835 gtk_text_buffer_get_start_iter(textbuf, &iter);
1836 gtk_text_buffer_place_cursor(textbuf, &iter);
1838 gtk_widget_grab_focus(compose->header_last->entry);
1840 if (!no_extedit && prefs_common.auto_exteditor)
1841 compose_exec_ext_editor(compose);
1844 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1845 gchar *folderidentifier;
1847 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1848 folderidentifier = folder_item_get_identifier(msginfo->folder);
1849 compose_set_save_to(compose, folderidentifier);
1850 g_free(folderidentifier);
1853 undo_unblock(compose->undostruct);
1855 compose->modified = FALSE;
1856 compose_set_title(compose);
1858 compose->updating = FALSE;
1859 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1860 SCROLL_TO_CURSOR(compose);
1862 if (compose->deferred_destroy) {
1863 compose_destroy(compose);
1867 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1872 #undef INSERT_FW_HEADER
1874 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1877 GtkTextView *textview;
1878 GtkTextBuffer *textbuf;
1882 gboolean single_mail = TRUE;
1884 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1886 if (g_slist_length(msginfo_list) > 1)
1887 single_mail = FALSE;
1889 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1890 if (((MsgInfo *)msginfo->data)->folder == NULL)
1893 /* guess account from first selected message */
1895 !(account = compose_guess_forward_account_from_msginfo
1896 (msginfo_list->data)))
1897 account = cur_account;
1899 cm_return_val_if_fail(account != NULL, NULL);
1901 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1902 if (msginfo->data) {
1903 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1904 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1908 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1909 g_warning("no msginfo_list");
1913 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1915 compose->updating = TRUE;
1917 /* override from name according to folder properties */
1918 if (msginfo_list->data) {
1919 MsgInfo *msginfo = msginfo_list->data;
1921 if (msginfo->folder && msginfo->folder->prefs &&
1922 msginfo->folder->prefs->forward_with_format &&
1923 msginfo->folder->prefs->forward_override_from_format &&
1924 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1929 /* decode \-escape sequences in the internal representation of the quote format */
1930 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1931 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1935 compose->gtkaspell);
1937 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1939 quote_fmt_scan_string(tmp);
1942 buf = quote_fmt_get_buffer();
1944 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1946 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1947 quote_fmt_reset_vartable();
1953 textview = GTK_TEXT_VIEW(compose->text);
1954 textbuf = gtk_text_view_get_buffer(textview);
1955 compose_create_tags(textview, compose);
1957 undo_block(compose->undostruct);
1958 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1959 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1961 if (!is_file_exist(msgfile))
1962 g_warning("%s: file not exist\n", msgfile);
1964 compose_attach_append(compose, msgfile, msgfile,
1970 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1971 if (info->subject && *info->subject) {
1972 gchar *buf, *buf2, *p;
1974 buf = p = g_strdup(info->subject);
1975 p += subject_get_prefix_length(p);
1976 memmove(buf, p, strlen(p) + 1);
1978 buf2 = g_strdup_printf("Fw: %s", buf);
1979 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1985 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1986 _("Fw: multiple emails"));
1989 SIGNAL_BLOCK(textbuf);
1991 if (account->auto_sig)
1992 compose_insert_sig(compose, FALSE);
1994 compose_wrap_all(compose);
1996 SIGNAL_UNBLOCK(textbuf);
1998 gtk_text_buffer_get_start_iter(textbuf, &iter);
1999 gtk_text_buffer_place_cursor(textbuf, &iter);
2001 gtk_widget_grab_focus(compose->header_last->entry);
2002 undo_unblock(compose->undostruct);
2003 compose->modified = FALSE;
2004 compose_set_title(compose);
2006 compose->updating = FALSE;
2007 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2008 SCROLL_TO_CURSOR(compose);
2010 if (compose->deferred_destroy) {
2011 compose_destroy(compose);
2015 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2020 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2022 GtkTextIter start = *iter;
2023 GtkTextIter end_iter;
2024 int start_pos = gtk_text_iter_get_offset(&start);
2026 if (!compose->account->sig_sep)
2029 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2030 start_pos+strlen(compose->account->sig_sep));
2032 /* check sig separator */
2033 str = gtk_text_iter_get_text(&start, &end_iter);
2034 if (!strcmp(str, compose->account->sig_sep)) {
2036 /* check end of line (\n) */
2037 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2038 start_pos+strlen(compose->account->sig_sep));
2039 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2040 start_pos+strlen(compose->account->sig_sep)+1);
2041 tmp = gtk_text_iter_get_text(&start, &end_iter);
2042 if (!strcmp(tmp,"\n")) {
2054 static void compose_colorize_signature(Compose *compose)
2056 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2058 GtkTextIter end_iter;
2059 gtk_text_buffer_get_start_iter(buffer, &iter);
2060 while (gtk_text_iter_forward_line(&iter))
2061 if (compose_is_sig_separator(compose, buffer, &iter)) {
2062 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2063 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2067 #define BLOCK_WRAP() { \
2068 prev_autowrap = compose->autowrap; \
2069 buffer = gtk_text_view_get_buffer( \
2070 GTK_TEXT_VIEW(compose->text)); \
2071 compose->autowrap = FALSE; \
2073 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2074 G_CALLBACK(compose_changed_cb), \
2076 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2077 G_CALLBACK(text_inserted), \
2080 #define UNBLOCK_WRAP() { \
2081 compose->autowrap = prev_autowrap; \
2082 if (compose->autowrap) { \
2083 gint old = compose->draft_timeout_tag; \
2084 compose->draft_timeout_tag = -2; \
2085 compose_wrap_all(compose); \
2086 compose->draft_timeout_tag = old; \
2089 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2090 G_CALLBACK(compose_changed_cb), \
2092 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2093 G_CALLBACK(text_inserted), \
2097 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2099 Compose *compose = NULL;
2100 PrefsAccount *account = NULL;
2101 GtkTextView *textview;
2102 GtkTextBuffer *textbuf;
2106 gchar buf[BUFFSIZE];
2107 gboolean use_signing = FALSE;
2108 gboolean use_encryption = FALSE;
2109 gchar *privacy_system = NULL;
2110 int priority = PRIORITY_NORMAL;
2111 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2112 gboolean autowrap = prefs_common.autowrap;
2113 gboolean autoindent = prefs_common.auto_indent;
2115 cm_return_val_if_fail(msginfo != NULL, NULL);
2116 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2118 if (compose_put_existing_to_front(msginfo)) {
2122 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2123 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2124 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2125 gchar queueheader_buf[BUFFSIZE];
2128 /* Select Account from queue headers */
2129 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2130 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2131 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2132 account = account_find_from_id(id);
2134 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2135 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2136 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2137 account = account_find_from_id(id);
2139 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2140 sizeof(queueheader_buf), "NAID:")) {
2141 id = atoi(&queueheader_buf[strlen("NAID:")]);
2142 account = account_find_from_id(id);
2144 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2145 sizeof(queueheader_buf), "MAID:")) {
2146 id = atoi(&queueheader_buf[strlen("MAID:")]);
2147 account = account_find_from_id(id);
2149 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2150 sizeof(queueheader_buf), "S:")) {
2151 account = account_find_from_address(queueheader_buf, FALSE);
2153 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2154 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2155 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2156 use_signing = param;
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2160 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2161 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2162 use_signing = param;
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2167 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2168 use_encryption = param;
2170 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2171 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2172 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2173 use_encryption = param;
2175 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2177 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2180 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2181 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2182 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2185 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2187 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2191 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2193 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2194 sizeof(queueheader_buf), "X-Priority: ")) {
2195 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "RMID:")) {
2200 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2201 if (tokens[0] && tokens[1] && tokens[2]) {
2202 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2203 if (orig_item != NULL) {
2204 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2209 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2210 sizeof(queueheader_buf), "FMID:")) {
2211 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2212 if (tokens[0] && tokens[1] && tokens[2]) {
2213 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2214 if (orig_item != NULL) {
2215 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2221 account = msginfo->folder->folder->account;
2224 if (!account && prefs_common.reedit_account_autosel) {
2225 gchar from[BUFFSIZE];
2226 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2227 extract_address(from);
2228 account = account_find_from_address(from, FALSE);
2232 account = cur_account;
2234 cm_return_val_if_fail(account != NULL, NULL);
2236 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2238 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2239 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2240 compose->autowrap = autowrap;
2241 compose->replyinfo = replyinfo;
2242 compose->fwdinfo = fwdinfo;
2244 compose->updating = TRUE;
2245 compose->priority = priority;
2247 if (privacy_system != NULL) {
2248 compose->privacy_system = privacy_system;
2249 compose_use_signing(compose, use_signing);
2250 compose_use_encryption(compose, use_encryption);
2251 compose_update_privacy_system_menu_item(compose, FALSE);
2253 activate_privacy_system(compose, account, FALSE);
2256 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2258 compose_extract_original_charset(compose);
2260 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2261 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2262 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2263 gchar queueheader_buf[BUFFSIZE];
2265 /* Set message save folder */
2266 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2267 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2268 compose_set_save_to(compose, &queueheader_buf[4]);
2270 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2271 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2273 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2278 if (compose_parse_header(compose, msginfo) < 0) {
2279 compose->updating = FALSE;
2280 compose_destroy(compose);
2283 compose_reedit_set_entry(compose, msginfo);
2285 textview = GTK_TEXT_VIEW(compose->text);
2286 textbuf = gtk_text_view_get_buffer(textview);
2287 compose_create_tags(textview, compose);
2289 mark = gtk_text_buffer_get_insert(textbuf);
2290 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2292 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2293 G_CALLBACK(compose_changed_cb),
2296 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2297 fp = procmime_get_first_encrypted_text_content(msginfo);
2299 compose_force_encryption(compose, account, TRUE, NULL);
2302 fp = procmime_get_first_text_content(msginfo);
2305 g_warning("Can't get text part\n");
2309 gboolean prev_autowrap = compose->autowrap;
2310 GtkTextBuffer *buffer = textbuf;
2312 while (fgets(buf, sizeof(buf), fp) != NULL) {
2314 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2320 compose_attach_parts(compose, msginfo);
2322 compose_colorize_signature(compose);
2324 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2325 G_CALLBACK(compose_changed_cb),
2328 gtk_widget_grab_focus(compose->text);
2330 if (prefs_common.auto_exteditor) {
2331 compose_exec_ext_editor(compose);
2333 compose->modified = FALSE;
2334 compose_set_title(compose);
2336 compose->updating = FALSE;
2337 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2338 SCROLL_TO_CURSOR(compose);
2340 if (compose->deferred_destroy) {
2341 compose_destroy(compose);
2345 compose->sig_str = account_get_signature_str(compose->account);
2347 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2352 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2359 cm_return_val_if_fail(msginfo != NULL, NULL);
2362 account = account_get_reply_account(msginfo,
2363 prefs_common.reply_account_autosel);
2364 cm_return_val_if_fail(account != NULL, NULL);
2366 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2368 compose->updating = TRUE;
2370 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2371 compose->replyinfo = NULL;
2372 compose->fwdinfo = NULL;
2374 compose_show_first_last_header(compose, TRUE);
2376 gtk_widget_grab_focus(compose->header_last->entry);
2378 filename = procmsg_get_message_file(msginfo);
2380 if (filename == NULL) {
2381 compose->updating = FALSE;
2382 compose_destroy(compose);
2387 compose->redirect_filename = filename;
2389 /* Set save folder */
2390 item = msginfo->folder;
2391 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2392 gchar *folderidentifier;
2394 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2395 folderidentifier = folder_item_get_identifier(item);
2396 compose_set_save_to(compose, folderidentifier);
2397 g_free(folderidentifier);
2400 compose_attach_parts(compose, msginfo);
2402 if (msginfo->subject)
2403 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2405 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2407 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2408 _("The body of the \"Redirect\" template has an error at line %d."));
2409 quote_fmt_reset_vartable();
2410 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2412 compose_colorize_signature(compose);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2417 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2424 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2426 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2428 if (compose->toolbar->draft_btn)
2429 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2430 if (compose->toolbar->insert_btn)
2431 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2432 if (compose->toolbar->attach_btn)
2433 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2434 if (compose->toolbar->sig_btn)
2435 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2436 if (compose->toolbar->exteditor_btn)
2437 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2438 if (compose->toolbar->linewrap_current_btn)
2439 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2440 if (compose->toolbar->linewrap_all_btn)
2441 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2443 compose->modified = FALSE;
2444 compose_set_title(compose);
2445 compose->updating = FALSE;
2446 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2447 SCROLL_TO_CURSOR(compose);
2449 if (compose->deferred_destroy) {
2450 compose_destroy(compose);
2454 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2459 GList *compose_get_compose_list(void)
2461 return compose_list;
2464 void compose_entry_append(Compose *compose, const gchar *address,
2465 ComposeEntryType type, ComposePrefType pref_type)
2467 const gchar *header;
2469 gboolean in_quote = FALSE;
2470 if (!address || *address == '\0') return;
2477 header = N_("Bcc:");
2479 case COMPOSE_REPLYTO:
2480 header = N_("Reply-To:");
2482 case COMPOSE_NEWSGROUPS:
2483 header = N_("Newsgroups:");
2485 case COMPOSE_FOLLOWUPTO:
2486 header = N_( "Followup-To:");
2488 case COMPOSE_INREPLYTO:
2489 header = N_( "In-Reply-To:");
2496 header = prefs_common_translated_header_name(header);
2498 cur = begin = (gchar *)address;
2500 /* we separate the line by commas, but not if we're inside a quoted
2502 while (*cur != '\0') {
2504 in_quote = !in_quote;
2505 if (*cur == ',' && !in_quote) {
2506 gchar *tmp = g_strdup(begin);
2508 tmp[cur-begin]='\0';
2511 while (*tmp == ' ' || *tmp == '\t')
2513 compose_add_header_entry(compose, header, tmp, pref_type);
2520 gchar *tmp = g_strdup(begin);
2522 tmp[cur-begin]='\0';
2525 while (*tmp == ' ' || *tmp == '\t')
2527 compose_add_header_entry(compose, header, tmp, pref_type);
2532 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2534 static GdkColor yellow;
2535 static GdkColor black;
2536 static gboolean yellow_initialised = FALSE;
2540 if (!yellow_initialised) {
2541 gdk_color_parse("#f5f6be", &yellow);
2542 gdk_color_parse("#000000", &black);
2543 yellow_initialised = gdk_colormap_alloc_color(
2544 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2545 yellow_initialised &= gdk_colormap_alloc_color(
2546 gdk_colormap_get_system(), &black, FALSE, TRUE);
2549 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2550 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2551 if (gtk_entry_get_text(entry) &&
2552 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2553 if (yellow_initialised) {
2554 gtk_widget_modify_base(
2555 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2556 GTK_STATE_NORMAL, &yellow);
2557 gtk_widget_modify_text(
2558 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2559 GTK_STATE_NORMAL, &black);
2565 void compose_toolbar_cb(gint action, gpointer data)
2567 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2568 Compose *compose = (Compose*)toolbar_item->parent;
2570 cm_return_if_fail(compose != NULL);
2574 compose_send_cb(NULL, compose);
2577 compose_send_later_cb(NULL, compose);
2580 compose_draft(compose, COMPOSE_QUIT_EDITING);
2583 compose_insert_file_cb(NULL, compose);
2586 compose_attach_cb(NULL, compose);
2589 compose_insert_sig(compose, FALSE);
2592 compose_ext_editor_cb(NULL, compose);
2594 case A_LINEWRAP_CURRENT:
2595 compose_beautify_paragraph(compose, NULL, TRUE);
2597 case A_LINEWRAP_ALL:
2598 compose_wrap_all_full(compose, TRUE);
2601 compose_address_cb(NULL, compose);
2604 case A_CHECK_SPELLING:
2605 compose_check_all(NULL, compose);
2613 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2618 gchar *subject = NULL;
2622 gchar **attach = NULL;
2623 gchar *inreplyto = NULL;
2624 MailField mfield = NO_FIELD_PRESENT;
2626 /* get mailto parts but skip from */
2627 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2630 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2631 mfield = TO_FIELD_PRESENT;
2634 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2636 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2638 if (!g_utf8_validate (subject, -1, NULL)) {
2639 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2640 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2643 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2645 mfield = SUBJECT_FIELD_PRESENT;
2648 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2649 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2652 gboolean prev_autowrap = compose->autowrap;
2654 compose->autowrap = FALSE;
2656 mark = gtk_text_buffer_get_insert(buffer);
2657 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2659 if (!g_utf8_validate (body, -1, NULL)) {
2660 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2661 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2664 gtk_text_buffer_insert(buffer, &iter, body, -1);
2666 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2668 compose->autowrap = prev_autowrap;
2669 if (compose->autowrap)
2670 compose_wrap_all(compose);
2671 mfield = BODY_FIELD_PRESENT;
2675 gint i = 0, att = 0;
2676 gchar *warn_files = NULL;
2677 while (attach[i] != NULL) {
2678 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2679 if (utf8_filename) {
2680 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2681 gchar *tmp = g_strdup_printf("%s%s\n",
2682 warn_files?warn_files:"",
2688 g_free(utf8_filename);
2690 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2695 alertpanel_notice(ngettext(
2696 "The following file has been attached: \n%s",
2697 "The following files have been attached: \n%s", att), warn_files);
2702 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2715 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2717 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2718 {"Cc:", NULL, TRUE},
2719 {"References:", NULL, FALSE},
2720 {"Bcc:", NULL, TRUE},
2721 {"Newsgroups:", NULL, TRUE},
2722 {"Followup-To:", NULL, TRUE},
2723 {"List-Post:", NULL, FALSE},
2724 {"X-Priority:", NULL, FALSE},
2725 {NULL, NULL, FALSE}};
2741 cm_return_val_if_fail(msginfo != NULL, -1);
2743 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2744 procheader_get_header_fields(fp, hentry);
2747 if (hentry[H_REPLY_TO].body != NULL) {
2748 if (hentry[H_REPLY_TO].body[0] != '\0') {
2750 conv_unmime_header(hentry[H_REPLY_TO].body,
2753 g_free(hentry[H_REPLY_TO].body);
2754 hentry[H_REPLY_TO].body = NULL;
2756 if (hentry[H_CC].body != NULL) {
2757 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2758 g_free(hentry[H_CC].body);
2759 hentry[H_CC].body = NULL;
2761 if (hentry[H_REFERENCES].body != NULL) {
2762 if (compose->mode == COMPOSE_REEDIT)
2763 compose->references = hentry[H_REFERENCES].body;
2765 compose->references = compose_parse_references
2766 (hentry[H_REFERENCES].body, msginfo->msgid);
2767 g_free(hentry[H_REFERENCES].body);
2769 hentry[H_REFERENCES].body = NULL;
2771 if (hentry[H_BCC].body != NULL) {
2772 if (compose->mode == COMPOSE_REEDIT)
2774 conv_unmime_header(hentry[H_BCC].body, NULL);
2775 g_free(hentry[H_BCC].body);
2776 hentry[H_BCC].body = NULL;
2778 if (hentry[H_NEWSGROUPS].body != NULL) {
2779 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2780 hentry[H_NEWSGROUPS].body = NULL;
2782 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2783 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2784 compose->followup_to =
2785 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2788 g_free(hentry[H_FOLLOWUP_TO].body);
2789 hentry[H_FOLLOWUP_TO].body = NULL;
2791 if (hentry[H_LIST_POST].body != NULL) {
2792 gchar *to = NULL, *start = NULL;
2794 extract_address(hentry[H_LIST_POST].body);
2795 if (hentry[H_LIST_POST].body[0] != '\0') {
2796 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2798 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2799 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2802 g_free(compose->ml_post);
2803 compose->ml_post = to;
2806 g_free(hentry[H_LIST_POST].body);
2807 hentry[H_LIST_POST].body = NULL;
2810 /* CLAWS - X-Priority */
2811 if (compose->mode == COMPOSE_REEDIT)
2812 if (hentry[H_X_PRIORITY].body != NULL) {
2815 priority = atoi(hentry[H_X_PRIORITY].body);
2816 g_free(hentry[H_X_PRIORITY].body);
2818 hentry[H_X_PRIORITY].body = NULL;
2820 if (priority < PRIORITY_HIGHEST ||
2821 priority > PRIORITY_LOWEST)
2822 priority = PRIORITY_NORMAL;
2824 compose->priority = priority;
2827 if (compose->mode == COMPOSE_REEDIT) {
2828 if (msginfo->inreplyto && *msginfo->inreplyto)
2829 compose->inreplyto = g_strdup(msginfo->inreplyto);
2833 if (msginfo->msgid && *msginfo->msgid)
2834 compose->inreplyto = g_strdup(msginfo->msgid);
2836 if (!compose->references) {
2837 if (msginfo->msgid && *msginfo->msgid) {
2838 if (msginfo->inreplyto && *msginfo->inreplyto)
2839 compose->references =
2840 g_strdup_printf("<%s>\n\t<%s>",
2844 compose->references =
2845 g_strconcat("<", msginfo->msgid, ">",
2847 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2848 compose->references =
2849 g_strconcat("<", msginfo->inreplyto, ">",
2857 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2859 GSList *ref_id_list, *cur;
2863 ref_id_list = references_list_append(NULL, ref);
2864 if (!ref_id_list) return NULL;
2865 if (msgid && *msgid)
2866 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2871 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2872 /* "<" + Message-ID + ">" + CR+LF+TAB */
2873 len += strlen((gchar *)cur->data) + 5;
2875 if (len > MAX_REFERENCES_LEN) {
2876 /* remove second message-ID */
2877 if (ref_id_list && ref_id_list->next &&
2878 ref_id_list->next->next) {
2879 g_free(ref_id_list->next->data);
2880 ref_id_list = g_slist_remove
2881 (ref_id_list, ref_id_list->next->data);
2883 slist_free_strings(ref_id_list);
2884 g_slist_free(ref_id_list);
2891 new_ref = g_string_new("");
2892 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2893 if (new_ref->len > 0)
2894 g_string_append(new_ref, "\n\t");
2895 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2898 slist_free_strings(ref_id_list);
2899 g_slist_free(ref_id_list);
2901 new_ref_str = new_ref->str;
2902 g_string_free(new_ref, FALSE);
2907 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2908 const gchar *fmt, const gchar *qmark,
2909 const gchar *body, gboolean rewrap,
2910 gboolean need_unescape,
2911 const gchar *err_msg)
2913 MsgInfo* dummyinfo = NULL;
2914 gchar *quote_str = NULL;
2916 gboolean prev_autowrap;
2917 const gchar *trimmed_body = body;
2918 gint cursor_pos = -1;
2919 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2920 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2925 SIGNAL_BLOCK(buffer);
2928 dummyinfo = compose_msginfo_new_from_compose(compose);
2929 msginfo = dummyinfo;
2932 if (qmark != NULL) {
2934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2935 compose->gtkaspell);
2937 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2939 quote_fmt_scan_string(qmark);
2942 buf = quote_fmt_get_buffer();
2944 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2946 Xstrdup_a(quote_str, buf, goto error)
2949 if (fmt && *fmt != '\0') {
2952 while (*trimmed_body == '\n')
2956 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2957 compose->gtkaspell);
2959 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2961 if (need_unescape) {
2964 /* decode \-escape sequences in the internal representation of the quote format */
2965 tmp = malloc(strlen(fmt)+1);
2966 pref_get_unescaped_pref(tmp, fmt);
2967 quote_fmt_scan_string(tmp);
2971 quote_fmt_scan_string(fmt);
2975 buf = quote_fmt_get_buffer();
2977 gint line = quote_fmt_get_line();
2978 alertpanel_error(err_msg, line);
2984 prev_autowrap = compose->autowrap;
2985 compose->autowrap = FALSE;
2987 mark = gtk_text_buffer_get_insert(buffer);
2988 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2989 if (g_utf8_validate(buf, -1, NULL)) {
2990 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2992 gchar *tmpout = NULL;
2993 tmpout = conv_codeset_strdup
2994 (buf, conv_get_locale_charset_str_no_utf8(),
2996 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2998 tmpout = g_malloc(strlen(buf)*2+1);
2999 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3001 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3005 cursor_pos = quote_fmt_get_cursor_pos();
3006 if (cursor_pos == -1)
3007 cursor_pos = gtk_text_iter_get_offset(&iter);
3008 compose->set_cursor_pos = cursor_pos;
3010 gtk_text_buffer_get_start_iter(buffer, &iter);
3011 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3012 gtk_text_buffer_place_cursor(buffer, &iter);
3014 compose->autowrap = prev_autowrap;
3015 if (compose->autowrap && rewrap)
3016 compose_wrap_all(compose);
3023 SIGNAL_UNBLOCK(buffer);
3025 procmsg_msginfo_free( dummyinfo );
3030 /* if ml_post is of type addr@host and from is of type
3031 * addr-anything@host, return TRUE
3033 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3035 gchar *left_ml = NULL;
3036 gchar *right_ml = NULL;
3037 gchar *left_from = NULL;
3038 gchar *right_from = NULL;
3039 gboolean result = FALSE;
3041 if (!ml_post || !from)
3044 left_ml = g_strdup(ml_post);
3045 if (strstr(left_ml, "@")) {
3046 right_ml = strstr(left_ml, "@")+1;
3047 *(strstr(left_ml, "@")) = '\0';
3050 left_from = g_strdup(from);
3051 if (strstr(left_from, "@")) {
3052 right_from = strstr(left_from, "@")+1;
3053 *(strstr(left_from, "@")) = '\0';
3056 if (left_ml && left_from && right_ml && right_from
3057 && !strncmp(left_from, left_ml, strlen(left_ml))
3058 && !strcmp(right_from, right_ml)) {
3067 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3068 gboolean to_all, gboolean to_ml,
3070 gboolean followup_and_reply_to)
3072 GSList *cc_list = NULL;
3075 gchar *replyto = NULL;
3076 gchar *ac_email = NULL;
3078 gboolean reply_to_ml = FALSE;
3079 gboolean default_reply_to = FALSE;
3081 cm_return_if_fail(compose->account != NULL);
3082 cm_return_if_fail(msginfo != NULL);
3084 reply_to_ml = to_ml && compose->ml_post;
3086 default_reply_to = msginfo->folder &&
3087 msginfo->folder->prefs->enable_default_reply_to;
3089 if (compose->account->protocol != A_NNTP) {
3090 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3091 if (msginfo->folder->prefs->enable_default_replyto) {
3092 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3093 COMPOSE_REPLYTO, PREF_FOLDER);
3095 if (msginfo->folder->prefs->enable_default_bcc) {
3096 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3097 COMPOSE_BCC, PREF_FOLDER);
3099 if (msginfo->folder->prefs->enable_default_cc) {
3100 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3101 COMPOSE_CC, PREF_FOLDER);
3104 if (reply_to_ml && !default_reply_to) {
3106 gboolean is_subscr = is_subscription(compose->ml_post,
3109 /* normal answer to ml post with a reply-to */
3110 compose_entry_append(compose,
3112 COMPOSE_TO, PREF_ML);
3113 if (compose->replyto)
3114 compose_entry_append(compose,
3116 COMPOSE_CC, PREF_ML);
3118 /* answer to subscription confirmation */
3119 if (compose->replyto)
3120 compose_entry_append(compose,
3122 COMPOSE_TO, PREF_ML);
3123 else if (msginfo->from)
3124 compose_entry_append(compose,
3126 COMPOSE_TO, PREF_ML);
3129 else if (!(to_all || to_sender) && default_reply_to) {
3130 compose_entry_append(compose,
3131 msginfo->folder->prefs->default_reply_to,
3132 COMPOSE_TO, PREF_FOLDER);
3133 compose_entry_mark_default_to(compose,
3134 msginfo->folder->prefs->default_reply_to);
3139 Xstrdup_a(tmp1, msginfo->from, return);
3140 extract_address(tmp1);
3141 if (to_all || to_sender ||
3142 !account_find_from_address(tmp1, FALSE))
3143 compose_entry_append(compose,
3144 (compose->replyto && !to_sender)
3145 ? compose->replyto :
3146 msginfo->from ? msginfo->from : "",
3147 COMPOSE_TO, PREF_NONE);
3148 else if (!to_all && !to_sender) {
3149 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3150 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3151 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3152 if (compose->replyto) {
3153 compose_entry_append(compose,
3155 COMPOSE_TO, PREF_NONE);