2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 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>
33 #include <gtk/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkvpaned.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkeditable.h>
43 #include <gtk/gtkwindow.h>
44 #include <gtk/gtksignal.h>
45 #include <gtk/gtkvbox.h>
46 #include <gtk/gtkcontainer.h>
47 #include <gtk/gtkhandlebox.h>
48 #include <gtk/gtktoolbar.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtklabel.h>
52 #include <gtk/gtkscrolledwindow.h>
53 #include <gtk/gtktreeview.h>
54 #include <gtk/gtkliststore.h>
55 #include <gtk/gtktreeselection.h>
56 #include <gtk/gtktreemodel.h>
58 #include <gtk/gtkdnd.h>
59 #include <gtk/gtkclipboard.h>
60 #include <pango/pango-break.h>
65 #include <sys/types.h>
71 # include <sys/wait.h>
75 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
79 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
86 #include "mainwindow.h"
88 #include "addressbook.h"
89 #include "folderview.h"
92 #include "stock_pixmap.h"
93 #include "send_message.h"
96 #include "customheader.h"
97 #include "prefs_common.h"
98 #include "prefs_account.h"
102 #include "procheader.h"
103 #include "procmime.h"
104 #include "statusbar.h"
107 #include "quoted-printable.h"
108 #include "codeconv.h"
110 #include "gtkutils.h"
112 #include "alertpanel.h"
113 #include "manage_window.h"
114 #include "gtkshruler.h"
116 #include "addr_compl.h"
117 #include "quote_fmt.h"
119 #include "foldersel.h"
122 #include "message_search.h"
123 #include "combobox.h"
138 #define N_ATTACH_COLS (N_COL_COLUMNS)
142 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
143 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
144 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
145 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
146 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
147 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
148 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
149 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
150 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
151 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
152 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
153 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
154 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
155 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
156 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
157 } ComposeCallAdvancedAction;
161 PRIORITY_HIGHEST = 1,
170 COMPOSE_INSERT_SUCCESS,
171 COMPOSE_INSERT_READ_ERROR,
172 COMPOSE_INSERT_INVALID_CHARACTER,
173 COMPOSE_INSERT_NO_FILE
174 } ComposeInsertResult;
178 COMPOSE_WRITE_FOR_SEND,
179 COMPOSE_WRITE_FOR_STORE
184 COMPOSE_QUOTE_FORCED,
189 #define B64_LINE_SIZE 57
190 #define B64_BUFFSIZE 77
192 #define MAX_REFERENCES_LEN 999
194 static GList *compose_list = NULL;
196 static Compose *compose_generic_new (PrefsAccount *account,
199 GPtrArray *attach_files,
200 GList *listAddress );
202 static Compose *compose_create (PrefsAccount *account,
207 static void compose_entry_mark_default_to (Compose *compose,
208 const gchar *address);
209 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
210 ComposeQuoteMode quote_mode,
214 static Compose *compose_forward_multiple (PrefsAccount *account,
215 GSList *msginfo_list);
216 static Compose *compose_reply (MsgInfo *msginfo,
217 ComposeQuoteMode quote_mode,
222 static Compose *compose_reply_mode (ComposeMode mode,
223 GSList *msginfo_list,
225 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
226 static void compose_update_privacy_systems_menu(Compose *compose);
228 static GtkWidget *compose_account_option_menu_create
230 static void compose_set_out_encoding (Compose *compose);
231 static void compose_set_template_menu (Compose *compose);
232 static void compose_template_apply (Compose *compose,
235 static void compose_destroy (Compose *compose);
237 static void compose_entries_set (Compose *compose,
238 const gchar *mailto);
239 static gint compose_parse_header (Compose *compose,
241 static gchar *compose_parse_references (const gchar *ref,
244 static gchar *compose_quote_fmt (Compose *compose,
250 gboolean need_unescape,
251 const gchar *err_msg);
253 static void compose_reply_set_entry (Compose *compose,
259 followup_and_reply_to);
260 static void compose_reedit_set_entry (Compose *compose,
263 static void compose_insert_sig (Compose *compose,
265 static gchar *compose_get_signature_str (Compose *compose);
266 static ComposeInsertResult compose_insert_file (Compose *compose,
269 static gboolean compose_attach_append (Compose *compose,
272 const gchar *content_type);
273 static void compose_attach_parts (Compose *compose,
276 static gboolean compose_beautify_paragraph (Compose *compose,
277 GtkTextIter *par_iter,
279 static void compose_wrap_all (Compose *compose);
280 static void compose_wrap_all_full (Compose *compose,
283 static void compose_set_title (Compose *compose);
284 static void compose_select_account (Compose *compose,
285 PrefsAccount *account,
288 static PrefsAccount *compose_current_mail_account(void);
289 /* static gint compose_send (Compose *compose); */
290 static gboolean compose_check_for_valid_recipient
292 static gboolean compose_check_entries (Compose *compose,
293 gboolean check_everything);
294 static gint compose_write_to_file (Compose *compose,
297 gboolean attach_parts);
298 static gint compose_write_body_to_file (Compose *compose,
300 static gint compose_remove_reedit_target (Compose *compose,
302 static void compose_remove_draft (Compose *compose);
303 static gint compose_queue_sub (Compose *compose,
307 gboolean check_subject,
308 gboolean remove_reedit_target);
309 static void compose_add_attachments (Compose *compose,
311 static gchar *compose_get_header (Compose *compose);
313 static void compose_convert_header (Compose *compose,
318 gboolean addr_field);
320 static void compose_attach_info_free (AttachInfo *ainfo);
321 static void compose_attach_remove_selected (Compose *compose);
323 static void compose_attach_property (Compose *compose);
324 static void compose_attach_property_create (gboolean *cancelled);
325 static void attach_property_ok (GtkWidget *widget,
326 gboolean *cancelled);
327 static void attach_property_cancel (GtkWidget *widget,
328 gboolean *cancelled);
329 static gint attach_property_delete_event (GtkWidget *widget,
331 gboolean *cancelled);
332 static gboolean attach_property_key_pressed (GtkWidget *widget,
334 gboolean *cancelled);
336 static void compose_exec_ext_editor (Compose *compose);
338 static gint compose_exec_ext_editor_real (const gchar *file);
339 static gboolean compose_ext_editor_kill (Compose *compose);
340 static gboolean compose_input_cb (GIOChannel *source,
341 GIOCondition condition,
343 static void compose_set_ext_editor_sensitive (Compose *compose,
345 #endif /* G_OS_UNIX */
347 static void compose_undo_state_changed (UndoMain *undostruct,
352 static void compose_create_header_entry (Compose *compose);
353 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
360 static void compose_add_field_list ( Compose *compose,
361 GList *listAddress );
363 /* callback functions */
365 static gboolean compose_edit_size_alloc (GtkEditable *widget,
366 GtkAllocation *allocation,
367 GtkSHRuler *shruler);
368 static void account_activated (GtkComboBox *optmenu,
370 static void attach_selected (GtkTreeView *tree_view,
371 GtkTreePath *tree_path,
372 GtkTreeViewColumn *column,
374 static gboolean attach_button_pressed (GtkWidget *widget,
375 GdkEventButton *event,
377 static gboolean attach_key_pressed (GtkWidget *widget,
380 static void compose_send_cb (gpointer data,
383 static void compose_send_later_cb (gpointer data,
387 static void compose_draft_cb (gpointer data,
391 static void compose_attach_cb (gpointer data,
394 static void compose_insert_file_cb (gpointer data,
397 static void compose_insert_sig_cb (gpointer data,
401 static void compose_close_cb (gpointer data,
405 static void compose_set_encoding_cb (gpointer data,
409 static void compose_address_cb (gpointer data,
412 static void compose_template_activate_cb(GtkWidget *widget,
415 static void compose_ext_editor_cb (gpointer data,
419 static gint compose_delete_cb (GtkWidget *widget,
423 static void compose_undo_cb (Compose *compose);
424 static void compose_redo_cb (Compose *compose);
425 static void compose_cut_cb (Compose *compose);
426 static void compose_copy_cb (Compose *compose);
427 static void compose_paste_cb (Compose *compose);
428 static void compose_paste_as_quote_cb (Compose *compose);
429 static void compose_paste_no_wrap_cb (Compose *compose);
430 static void compose_paste_wrap_cb (Compose *compose);
431 static void compose_allsel_cb (Compose *compose);
433 static void compose_advanced_action_cb (Compose *compose,
434 ComposeCallAdvancedAction action);
436 static void compose_grab_focus_cb (GtkWidget *widget,
439 static void compose_changed_cb (GtkTextBuffer *textbuf,
442 static void compose_wrap_cb (gpointer data,
445 static void compose_find_cb (gpointer data,
448 static void compose_toggle_autowrap_cb (gpointer data,
452 static void compose_toggle_ruler_cb (gpointer data,
455 static void compose_toggle_sign_cb (gpointer data,
458 static void compose_toggle_encrypt_cb (gpointer data,
461 static void compose_set_privacy_system_cb(GtkWidget *widget,
463 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
464 static void activate_privacy_system (Compose *compose,
465 PrefsAccount *account,
467 static void compose_use_signing(Compose *compose, gboolean use_signing);
468 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
469 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
471 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
473 static void compose_set_priority_cb (gpointer data,
476 static void compose_reply_change_mode (gpointer data,
480 static void compose_attach_drag_received_cb (GtkWidget *widget,
481 GdkDragContext *drag_context,
484 GtkSelectionData *data,
488 static void compose_insert_drag_received_cb (GtkWidget *widget,
489 GdkDragContext *drag_context,
492 GtkSelectionData *data,
496 static void compose_header_drag_received_cb (GtkWidget *widget,
497 GdkDragContext *drag_context,
500 GtkSelectionData *data,
505 static gboolean compose_drag_drop (GtkWidget *widget,
506 GdkDragContext *drag_context,
508 guint time, gpointer user_data);
510 static void text_inserted (GtkTextBuffer *buffer,
515 static Compose *compose_generic_reply(MsgInfo *msginfo,
516 ComposeQuoteMode quote_mode,
520 gboolean followup_and_reply_to,
523 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
524 ComposeHeaderEntry *headerentry);
525 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
527 ComposeHeaderEntry *headerentry);
529 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
531 static void compose_allow_user_actions (Compose *compose, gboolean allow);
534 static void compose_check_all (Compose *compose);
535 static void compose_highlight_all (Compose *compose);
536 static void compose_check_backwards (Compose *compose);
537 static void compose_check_forwards_go (Compose *compose);
540 static gint compose_defer_auto_save_draft (Compose *compose);
541 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
543 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
546 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
547 FolderItem *folder_item);
550 static GtkItemFactoryEntry compose_popup_entries[] =
552 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
553 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
554 {"/---", NULL, NULL, 0, "<Separator>"},
555 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
558 static GtkItemFactoryEntry compose_entries[] =
560 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
561 {N_("/_Message/S_end"), "<control>Return",
562 compose_send_cb, 0, NULL},
563 {N_("/_Message/Send _later"), "<shift><control>S",
564 compose_send_later_cb, 0, NULL},
565 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
566 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
567 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
568 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
569 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
570 {N_("/_Message/_Save"),
571 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
572 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
573 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
575 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
576 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
577 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
578 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
579 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
580 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
581 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
582 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
583 {N_("/_Edit/Special paste/as _quotation"),
584 NULL, compose_paste_as_quote_cb, 0, NULL},
585 {N_("/_Edit/Special paste/_wrapped"),
586 NULL, compose_paste_wrap_cb, 0, NULL},
587 {N_("/_Edit/Special paste/_unwrapped"),
588 NULL, compose_paste_no_wrap_cb, 0, NULL},
589 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
590 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
591 {N_("/_Edit/A_dvanced/Move a character backward"),
593 compose_advanced_action_cb,
594 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
596 {N_("/_Edit/A_dvanced/Move a character forward"),
598 compose_advanced_action_cb,
599 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
601 {N_("/_Edit/A_dvanced/Move a word backward"),
603 compose_advanced_action_cb,
604 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
606 {N_("/_Edit/A_dvanced/Move a word forward"),
608 compose_advanced_action_cb,
609 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
611 {N_("/_Edit/A_dvanced/Move to beginning of line"),
612 NULL, /* "<control>A" */
613 compose_advanced_action_cb,
614 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
616 {N_("/_Edit/A_dvanced/Move to end of line"),
618 compose_advanced_action_cb,
619 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
621 {N_("/_Edit/A_dvanced/Move to previous line"),
623 compose_advanced_action_cb,
624 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
626 {N_("/_Edit/A_dvanced/Move to next line"),
628 compose_advanced_action_cb,
629 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
631 {N_("/_Edit/A_dvanced/Delete a character backward"),
633 compose_advanced_action_cb,
634 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
636 {N_("/_Edit/A_dvanced/Delete a character forward"),
638 compose_advanced_action_cb,
639 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
641 {N_("/_Edit/A_dvanced/Delete a word backward"),
642 NULL, /* "<control>W" */
643 compose_advanced_action_cb,
644 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
646 {N_("/_Edit/A_dvanced/Delete a word forward"),
647 NULL, /* "<alt>D", */
648 compose_advanced_action_cb,
649 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
651 {N_("/_Edit/A_dvanced/Delete line"),
653 compose_advanced_action_cb,
654 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
656 {N_("/_Edit/A_dvanced/Delete entire line"),
658 compose_advanced_action_cb,
659 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
661 {N_("/_Edit/A_dvanced/Delete to end of line"),
663 compose_advanced_action_cb,
664 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
666 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
668 "<control>F", compose_find_cb, 0, NULL},
669 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
670 {N_("/_Edit/_Wrap current paragraph"),
671 "<control>L", compose_wrap_cb, 0, NULL},
672 {N_("/_Edit/Wrap all long _lines"),
673 "<control><alt>L", compose_wrap_cb, 1, NULL},
674 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
675 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
676 {N_("/_Edit/Edit with e_xternal editor"),
677 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
679 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
680 {N_("/_Spelling/_Check all or check selection"),
681 NULL, compose_check_all, 0, NULL},
682 {N_("/_Spelling/_Highlight all misspelled words"),
683 NULL, compose_highlight_all, 0, NULL},
684 {N_("/_Spelling/Check _backwards misspelled word"),
685 NULL, compose_check_backwards , 0, NULL},
686 {N_("/_Spelling/_Forward to next misspelled word"),
687 NULL, compose_check_forwards_go, 0, NULL},
688 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
689 {N_("/_Spelling/Options"),
690 NULL, NULL, 0, "<Branch>"},
692 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
693 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
694 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
695 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
696 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
697 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
698 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
699 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
700 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
701 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
702 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
703 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
704 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
705 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
706 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
707 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
708 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
709 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
710 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
711 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
712 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
713 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
714 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
716 #define ENC_ACTION(action) \
717 NULL, compose_set_encoding_cb, action, \
718 "/Options/Character encoding/Automatic"
720 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
721 {N_("/_Options/Character _encoding/_Automatic"),
722 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
723 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
725 {N_("/_Options/Character _encoding/7bit ascii (US-ASC_II)"),
726 ENC_ACTION(C_US_ASCII)},
727 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
728 ENC_ACTION(C_UTF_8)},
729 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
731 {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
732 {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
733 ENC_ACTION(C_ISO_8859_1)},
734 {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
735 ENC_ACTION(C_ISO_8859_15)},
736 {N_("/_Options/Character _encoding/Western European/Windows-1252"),
737 ENC_ACTION(C_WINDOWS_1252)},
739 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
740 ENC_ACTION(C_ISO_8859_2)},
742 {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
743 {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
744 ENC_ACTION(C_ISO_8859_13)},
745 {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
746 ENC_ACTION(C_ISO_8859_4)},
748 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
749 ENC_ACTION(C_ISO_8859_7)},
751 {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
752 {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
753 ENC_ACTION(C_ISO_8859_8)},
754 {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
755 ENC_ACTION(C_WINDOWS_1255)},
757 {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
758 {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
759 ENC_ACTION(C_ISO_8859_6)},
760 {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
761 ENC_ACTION(C_CP1256)},
763 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
764 ENC_ACTION(C_ISO_8859_9)},
766 {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
767 {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
768 ENC_ACTION(C_ISO_8859_5)},
769 {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
770 ENC_ACTION(C_KOI8_R)},
771 {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
772 ENC_ACTION(C_KOI8_U)},
773 {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
774 ENC_ACTION(C_WINDOWS_1251)},
776 {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
777 {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
778 ENC_ACTION(C_ISO_2022_JP)},
779 {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
780 ENC_ACTION(C_ISO_2022_JP_2)},
781 {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
782 ENC_ACTION(C_EUC_JP)},
783 {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
784 ENC_ACTION(C_SHIFT_JIS)},
786 {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
787 {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
788 ENC_ACTION(C_GB2312)},
789 {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
791 {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
793 {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
794 ENC_ACTION(C_EUC_TW)},
796 {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
797 {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
798 ENC_ACTION(C_EUC_KR)},
799 {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
800 ENC_ACTION(C_ISO_2022_KR)},
802 {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
803 {N_("/_Options/Character _encoding/Thai/TIS-620"),
804 ENC_ACTION(C_TIS_620)},
805 {N_("/_Options/Character _encoding/Thai/Windows-874"),
806 ENC_ACTION(C_WINDOWS_874)},
808 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
809 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
810 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
811 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
812 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
813 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
814 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
817 static GtkTargetEntry compose_mime_types[] =
819 {"text/uri-list", 0, 0},
820 {"UTF8_STRING", 0, 0},
824 static gboolean compose_put_existing_to_front(MsgInfo *info)
826 GList *compose_list = compose_get_compose_list();
830 for (elem = compose_list; elem != NULL && elem->data != NULL;
832 Compose *c = (Compose*)elem->data;
834 if (!c->targetinfo || !c->targetinfo->msgid ||
838 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
839 gtkut_window_popup(c->window);
847 static GdkColor quote_color1 =
848 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
849 static GdkColor quote_color2 =
850 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
851 static GdkColor quote_color3 =
852 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
854 static GdkColor quote_bgcolor1 =
855 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
856 static GdkColor quote_bgcolor2 =
857 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
858 static GdkColor quote_bgcolor3 =
859 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
861 static GdkColor signature_color = {
868 static GdkColor uri_color = {
875 static void compose_create_tags(GtkTextView *text, Compose *compose)
877 GtkTextBuffer *buffer;
878 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
884 buffer = gtk_text_view_get_buffer(text);
886 if (prefs_common.enable_color) {
887 /* grab the quote colors, converting from an int to a GdkColor */
888 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
890 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
892 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
894 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
896 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
898 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
900 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
902 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
905 signature_color = quote_color1 = quote_color2 = quote_color3 =
906 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
909 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
910 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
911 "foreground-gdk", "e_color1,
912 "paragraph-background-gdk", "e_bgcolor1,
914 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
915 "foreground-gdk", "e_color2,
916 "paragraph-background-gdk", "e_bgcolor2,
918 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
919 "foreground-gdk", "e_color3,
920 "paragraph-background-gdk", "e_bgcolor3,
923 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
924 "foreground-gdk", "e_color1,
926 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
927 "foreground-gdk", "e_color2,
929 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
930 "foreground-gdk", "e_color3,
934 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
935 "foreground-gdk", &signature_color,
938 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
939 "foreground-gdk", &uri_color,
941 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
942 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
944 color[0] = quote_color1;
945 color[1] = quote_color2;
946 color[2] = quote_color3;
947 color[3] = quote_bgcolor1;
948 color[4] = quote_bgcolor2;
949 color[5] = quote_bgcolor3;
950 color[6] = signature_color;
951 color[7] = uri_color;
952 cmap = gdk_drawable_get_colormap(compose->window->window);
953 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
955 for (i = 0; i < 8; i++) {
956 if (success[i] == FALSE) {
959 g_warning("Compose: color allocation failed.\n");
960 style = gtk_widget_get_style(GTK_WIDGET(text));
961 quote_color1 = quote_color2 = quote_color3 =
962 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
963 signature_color = uri_color = black;
968 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
969 GPtrArray *attach_files)
971 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
974 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
976 return compose_generic_new(account, mailto, item, NULL, NULL);
979 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
981 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
984 #define SCROLL_TO_CURSOR(compose) { \
985 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
986 gtk_text_view_get_buffer( \
987 GTK_TEXT_VIEW(compose->text))); \
988 gtk_text_view_scroll_mark_onscreen( \
989 GTK_TEXT_VIEW(compose->text), \
993 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
994 GPtrArray *attach_files, GList *listAddress )
997 GtkTextView *textview;
998 GtkTextBuffer *textbuf;
1000 GtkItemFactory *ifactory;
1001 const gchar *subject_format = NULL;
1002 const gchar *body_format = NULL;
1004 if (item && item->prefs && item->prefs->enable_default_account)
1005 account = account_find_from_id(item->prefs->default_account);
1007 if (!account) account = cur_account;
1008 g_return_val_if_fail(account != NULL, NULL);
1010 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1012 ifactory = gtk_item_factory_from_widget(compose->menubar);
1014 compose->replyinfo = NULL;
1015 compose->fwdinfo = NULL;
1017 textview = GTK_TEXT_VIEW(compose->text);
1018 textbuf = gtk_text_view_get_buffer(textview);
1019 compose_create_tags(textview, compose);
1021 undo_block(compose->undostruct);
1023 compose_set_dictionaries_from_folder_prefs(compose, item);
1026 if (account->auto_sig)
1027 compose_insert_sig(compose, FALSE);
1028 gtk_text_buffer_get_start_iter(textbuf, &iter);
1029 gtk_text_buffer_place_cursor(textbuf, &iter);
1031 if (account->protocol != A_NNTP) {
1032 if (mailto && *mailto != '\0') {
1033 compose_entries_set(compose, mailto);
1035 } else if (item && item->prefs->enable_default_to) {
1036 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1037 compose_entry_mark_default_to(compose, item->prefs->default_to);
1039 if (item && item->ret_rcpt) {
1040 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1044 compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1045 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1046 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1049 * CLAWS: just don't allow return receipt request, even if the user
1050 * may want to send an email. simple but foolproof.
1052 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1054 compose_add_field_list( compose, listAddress );
1056 if (item && item->prefs && item->prefs->compose_with_format) {
1057 subject_format = item->prefs->compose_subject_format;
1058 body_format = item->prefs->compose_body_format;
1059 } else if (account->compose_with_format) {
1060 subject_format = account->compose_subject_format;
1061 body_format = account->compose_body_format;
1062 } else if (prefs_common.compose_with_format) {
1063 subject_format = prefs_common.compose_subject_format;
1064 body_format = prefs_common.compose_body_format;
1067 if (subject_format || body_format) {
1068 MsgInfo* dummyinfo = NULL;
1071 && *subject_format != '\0' )
1073 gchar *subject = NULL;
1077 dummyinfo = compose_msginfo_new_from_compose(compose);
1079 /* decode \-escape sequences in the internal representation of the quote format */
1080 tmp = malloc(strlen(subject_format)+1);
1081 pref_get_unescaped_pref(tmp, subject_format);
1083 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1085 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1086 compose->gtkaspell);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1090 quote_fmt_scan_string(tmp);
1093 buf = quote_fmt_get_buffer();
1095 alertpanel_error(_("New message subject format error."));
1097 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1098 quote_fmt_reset_vartable();
1105 && *body_format != '\0' )
1108 GtkTextBuffer *buffer;
1109 GtkTextIter start, end;
1112 if ( dummyinfo == NULL )
1113 dummyinfo = compose_msginfo_new_from_compose(compose);
1115 text = GTK_TEXT_VIEW(compose->text);
1116 buffer = gtk_text_view_get_buffer(text);
1117 gtk_text_buffer_get_start_iter(buffer, &start);
1118 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1119 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1121 compose_quote_fmt(compose, dummyinfo,
1123 NULL, tmp, FALSE, TRUE,
1124 _("New message body format error at line %d."));
1125 quote_fmt_reset_vartable();
1130 procmsg_msginfo_free( dummyinfo );
1137 for (i = 0; i < attach_files->len; i++) {
1138 file = g_ptr_array_index(attach_files, i);
1139 compose_attach_append(compose, file, file, NULL);
1143 compose_show_first_last_header(compose, TRUE);
1145 /* Set save folder */
1146 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1147 gchar *folderidentifier;
1149 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1150 folderidentifier = folder_item_get_identifier(item);
1151 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1152 g_free(folderidentifier);
1155 gtk_widget_grab_focus(compose->header_last->entry);
1157 undo_unblock(compose->undostruct);
1159 if (prefs_common.auto_exteditor)
1160 compose_exec_ext_editor(compose);
1162 compose->draft_timeout_tag = -1;
1163 SCROLL_TO_CURSOR(compose);
1165 compose->modified = FALSE;
1166 compose_set_title(compose);
1170 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1171 gboolean override_pref)
1173 gchar *privacy = NULL;
1175 g_return_if_fail(compose != NULL);
1176 g_return_if_fail(account != NULL);
1178 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1181 if (account->default_privacy_system
1182 && strlen(account->default_privacy_system)) {
1183 privacy = account->default_privacy_system;
1185 GSList *privacy_avail = privacy_get_system_ids();
1186 if (privacy_avail && g_slist_length(privacy_avail)) {
1187 privacy = (gchar *)(privacy_avail->data);
1190 if (privacy != NULL) {
1191 if (compose->privacy_system == NULL)
1192 compose->privacy_system = g_strdup(privacy);
1193 compose_update_privacy_system_menu_item(compose, FALSE);
1194 compose_use_encryption(compose, TRUE);
1198 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1200 gchar *privacy = NULL;
1202 if (account->default_privacy_system
1203 && strlen(account->default_privacy_system)) {
1204 privacy = account->default_privacy_system;
1206 GSList *privacy_avail = privacy_get_system_ids();
1207 if (privacy_avail && g_slist_length(privacy_avail)) {
1208 privacy = (gchar *)(privacy_avail->data);
1211 if (privacy != NULL) {
1212 if (compose->privacy_system == NULL)
1213 compose->privacy_system = g_strdup(privacy);
1214 compose_update_privacy_system_menu_item(compose, FALSE);
1215 compose_use_signing(compose, TRUE);
1219 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1223 Compose *compose = NULL;
1224 GtkItemFactory *ifactory = NULL;
1226 g_return_val_if_fail(msginfo_list != NULL, NULL);
1228 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1229 g_return_val_if_fail(msginfo != NULL, NULL);
1231 list_len = g_slist_length(msginfo_list);
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1236 FALSE, prefs_common.default_reply_list, FALSE, body);
1238 case COMPOSE_REPLY_WITH_QUOTE:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1240 FALSE, prefs_common.default_reply_list, FALSE, body);
1242 case COMPOSE_REPLY_WITHOUT_QUOTE:
1243 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1244 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1246 case COMPOSE_REPLY_TO_SENDER:
1247 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1248 FALSE, FALSE, TRUE, body);
1250 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1251 compose = compose_followup_and_reply_to(msginfo,
1252 COMPOSE_QUOTE_CHECK,
1253 FALSE, FALSE, body);
1255 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1256 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1257 FALSE, FALSE, TRUE, body);
1259 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1260 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1261 FALSE, FALSE, TRUE, NULL);
1263 case COMPOSE_REPLY_TO_ALL:
1264 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1265 TRUE, FALSE, FALSE, body);
1267 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1268 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1269 TRUE, FALSE, FALSE, body);
1271 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1272 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1273 TRUE, FALSE, FALSE, NULL);
1275 case COMPOSE_REPLY_TO_LIST:
1276 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1277 FALSE, TRUE, FALSE, body);
1279 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1280 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1281 FALSE, TRUE, FALSE, body);
1283 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1284 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1285 FALSE, TRUE, FALSE, NULL);
1287 case COMPOSE_FORWARD:
1288 if (prefs_common.forward_as_attachment) {
1289 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1292 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1296 case COMPOSE_FORWARD_INLINE:
1297 /* check if we reply to more than one Message */
1298 if (list_len == 1) {
1299 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1302 /* more messages FALL THROUGH */
1303 case COMPOSE_FORWARD_AS_ATTACH:
1304 compose = compose_forward_multiple(NULL, msginfo_list);
1306 case COMPOSE_REDIRECT:
1307 compose = compose_redirect(NULL, msginfo, FALSE);
1310 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1313 if (compose == NULL) {
1314 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1317 ifactory = gtk_item_factory_from_widget(compose->menubar);
1319 compose->rmode = mode;
1320 switch (compose->rmode) {
1322 case COMPOSE_REPLY_WITH_QUOTE:
1323 case COMPOSE_REPLY_WITHOUT_QUOTE:
1324 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1325 debug_print("reply mode Normal\n");
1326 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1327 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1329 case COMPOSE_REPLY_TO_SENDER:
1330 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1331 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1332 debug_print("reply mode Sender\n");
1333 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1335 case COMPOSE_REPLY_TO_ALL:
1336 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1337 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1338 debug_print("reply mode All\n");
1339 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1341 case COMPOSE_REPLY_TO_LIST:
1342 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1343 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1344 debug_print("reply mode List\n");
1345 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1353 static Compose *compose_reply(MsgInfo *msginfo,
1354 ComposeQuoteMode quote_mode,
1360 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1361 to_sender, FALSE, body);
1364 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1365 ComposeQuoteMode quote_mode,
1370 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1371 to_sender, TRUE, body);
1374 static void compose_extract_original_charset(Compose *compose)
1376 MsgInfo *info = NULL;
1377 if (compose->replyinfo) {
1378 info = compose->replyinfo;
1379 } else if (compose->fwdinfo) {
1380 info = compose->fwdinfo;
1381 } else if (compose->targetinfo) {
1382 info = compose->targetinfo;
1385 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1386 MimeInfo *partinfo = mimeinfo;
1387 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1388 partinfo = procmime_mimeinfo_next(partinfo);
1390 compose->orig_charset =
1391 g_strdup(procmime_mimeinfo_get_parameter(
1392 partinfo, "charset"));
1394 procmime_mimeinfo_free_all(mimeinfo);
1398 #define SIGNAL_BLOCK(buffer) { \
1399 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1400 G_CALLBACK(compose_changed_cb), \
1402 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1403 G_CALLBACK(text_inserted), \
1407 #define SIGNAL_UNBLOCK(buffer) { \
1408 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1409 G_CALLBACK(compose_changed_cb), \
1411 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1412 G_CALLBACK(text_inserted), \
1416 static Compose *compose_generic_reply(MsgInfo *msginfo,
1417 ComposeQuoteMode quote_mode,
1418 gboolean to_all, gboolean to_ml,
1420 gboolean followup_and_reply_to,
1423 GtkItemFactory *ifactory;
1425 PrefsAccount *account = NULL;
1426 GtkTextView *textview;
1427 GtkTextBuffer *textbuf;
1428 gboolean quote = FALSE;
1429 const gchar *qmark = NULL;
1430 const gchar *body_fmt = NULL;
1432 g_return_val_if_fail(msginfo != NULL, NULL);
1433 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1435 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1437 g_return_val_if_fail(account != NULL, NULL);
1439 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1441 compose->updating = TRUE;
1443 ifactory = gtk_item_factory_from_widget(compose->menubar);
1445 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1446 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1448 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1449 if (!compose->replyinfo)
1450 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1452 compose_extract_original_charset(compose);
1454 if (msginfo->folder && msginfo->folder->ret_rcpt)
1455 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1457 /* Set save folder */
1458 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1459 gchar *folderidentifier;
1461 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1462 folderidentifier = folder_item_get_identifier(msginfo->folder);
1463 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1464 g_free(folderidentifier);
1467 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1469 textview = (GTK_TEXT_VIEW(compose->text));
1470 textbuf = gtk_text_view_get_buffer(textview);
1471 compose_create_tags(textview, compose);
1473 undo_block(compose->undostruct);
1475 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1478 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1479 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1480 /* use the reply format of folder (if enabled), or the account's one
1481 (if enabled) or fallback to the global reply format, which is always
1482 enabled (even if empty), and use the relevant quotemark */
1484 if (msginfo->folder && msginfo->folder->prefs &&
1485 msginfo->folder->prefs->reply_with_format) {
1486 qmark = msginfo->folder->prefs->reply_quotemark;
1487 body_fmt = msginfo->folder->prefs->reply_body_format;
1489 } else if (account->reply_with_format) {
1490 qmark = account->reply_quotemark;
1491 body_fmt = account->reply_body_format;
1494 qmark = prefs_common.quotemark;
1495 body_fmt = prefs_common.quotefmt;
1500 /* empty quotemark is not allowed */
1501 if (qmark == NULL || *qmark == '\0')
1503 compose_quote_fmt(compose, compose->replyinfo,
1504 body_fmt, qmark, body, FALSE, TRUE,
1505 _("Message reply format error at line %d."));
1506 quote_fmt_reset_vartable();
1509 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1510 compose_force_encryption(compose, account, FALSE);
1513 SIGNAL_BLOCK(textbuf);
1515 if (account->auto_sig)
1516 compose_insert_sig(compose, FALSE);
1518 compose_wrap_all(compose);
1520 SIGNAL_UNBLOCK(textbuf);
1522 gtk_widget_grab_focus(compose->text);
1524 undo_unblock(compose->undostruct);
1526 if (prefs_common.auto_exteditor)
1527 compose_exec_ext_editor(compose);
1529 compose->modified = FALSE;
1530 compose_set_title(compose);
1532 compose->updating = FALSE;
1533 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1534 SCROLL_TO_CURSOR(compose);
1536 if (compose->deferred_destroy) {
1537 compose_destroy(compose);
1544 #define INSERT_FW_HEADER(var, hdr) \
1545 if (msginfo->var && *msginfo->var) { \
1546 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1547 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1548 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1551 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1552 gboolean as_attach, const gchar *body,
1553 gboolean no_extedit,
1557 GtkTextView *textview;
1558 GtkTextBuffer *textbuf;
1561 g_return_val_if_fail(msginfo != NULL, NULL);
1562 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1565 !(account = compose_guess_forward_account_from_msginfo
1567 account = cur_account;
1569 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1571 compose->updating = TRUE;
1572 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1573 if (!compose->fwdinfo)
1574 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1576 compose_extract_original_charset(compose);
1578 if (msginfo->subject && *msginfo->subject) {
1579 gchar *buf, *buf2, *p;
1581 buf = p = g_strdup(msginfo->subject);
1582 p += subject_get_prefix_length(p);
1583 memmove(buf, p, strlen(p) + 1);
1585 buf2 = g_strdup_printf("Fw: %s", buf);
1586 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1592 textview = GTK_TEXT_VIEW(compose->text);
1593 textbuf = gtk_text_view_get_buffer(textview);
1594 compose_create_tags(textview, compose);
1596 undo_block(compose->undostruct);
1600 msgfile = procmsg_get_message_file(msginfo);
1601 if (!is_file_exist(msgfile))
1602 g_warning("%s: file not exist\n", msgfile);
1604 compose_attach_append(compose, msgfile, msgfile,
1609 const gchar *qmark = NULL;
1610 const gchar *body_fmt = prefs_common.fw_quotefmt;
1611 MsgInfo *full_msginfo;
1613 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1615 full_msginfo = procmsg_msginfo_copy(msginfo);
1617 /* use the forward format of folder (if enabled), or the account's one
1618 (if enabled) or fallback to the global forward format, which is always
1619 enabled (even if empty), and use the relevant quotemark */
1620 if (msginfo->folder && msginfo->folder->prefs &&
1621 msginfo->folder->prefs->forward_with_format) {
1622 qmark = msginfo->folder->prefs->forward_quotemark;
1623 body_fmt = msginfo->folder->prefs->forward_body_format;
1625 } else if (account->forward_with_format) {
1626 qmark = account->forward_quotemark;
1627 body_fmt = account->forward_body_format;
1630 qmark = prefs_common.fw_quotemark;
1631 body_fmt = prefs_common.fw_quotefmt;
1634 /* empty quotemark is not allowed */
1635 if (qmark == NULL || *qmark == '\0')
1638 compose_quote_fmt(compose, full_msginfo,
1639 body_fmt, qmark, body, FALSE, TRUE,
1640 _("Message forward format error at line %d."));
1641 quote_fmt_reset_vartable();
1642 compose_attach_parts(compose, msginfo);
1644 procmsg_msginfo_free(full_msginfo);
1647 SIGNAL_BLOCK(textbuf);
1649 if (account->auto_sig)
1650 compose_insert_sig(compose, FALSE);
1652 compose_wrap_all(compose);
1654 SIGNAL_UNBLOCK(textbuf);
1656 gtk_text_buffer_get_start_iter(textbuf, &iter);
1657 gtk_text_buffer_place_cursor(textbuf, &iter);
1659 gtk_widget_grab_focus(compose->header_last->entry);
1661 if (!no_extedit && prefs_common.auto_exteditor)
1662 compose_exec_ext_editor(compose);
1665 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1666 gchar *folderidentifier;
1668 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1669 folderidentifier = folder_item_get_identifier(msginfo->folder);
1670 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1671 g_free(folderidentifier);
1674 undo_unblock(compose->undostruct);
1676 compose->modified = FALSE;
1677 compose_set_title(compose);
1679 compose->updating = FALSE;
1680 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1681 SCROLL_TO_CURSOR(compose);
1683 if (compose->deferred_destroy) {
1684 compose_destroy(compose);
1691 #undef INSERT_FW_HEADER
1693 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1696 GtkTextView *textview;
1697 GtkTextBuffer *textbuf;
1701 gboolean single_mail = TRUE;
1703 g_return_val_if_fail(msginfo_list != NULL, NULL);
1705 if (g_slist_length(msginfo_list) > 1)
1706 single_mail = FALSE;
1708 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1709 if (((MsgInfo *)msginfo->data)->folder == NULL)
1712 /* guess account from first selected message */
1714 !(account = compose_guess_forward_account_from_msginfo
1715 (msginfo_list->data)))
1716 account = cur_account;
1718 g_return_val_if_fail(account != NULL, NULL);
1720 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1721 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1722 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1725 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1727 compose->updating = TRUE;
1729 textview = GTK_TEXT_VIEW(compose->text);
1730 textbuf = gtk_text_view_get_buffer(textview);
1731 compose_create_tags(textview, compose);
1733 undo_block(compose->undostruct);
1734 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1735 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1737 if (!is_file_exist(msgfile))
1738 g_warning("%s: file not exist\n", msgfile);
1740 compose_attach_append(compose, msgfile, msgfile,
1746 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1747 if (info->subject && *info->subject) {
1748 gchar *buf, *buf2, *p;
1750 buf = p = g_strdup(info->subject);
1751 p += subject_get_prefix_length(p);
1752 memmove(buf, p, strlen(p) + 1);
1754 buf2 = g_strdup_printf("Fw: %s", buf);
1755 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1761 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1762 _("Fw: multiple emails"));
1765 SIGNAL_BLOCK(textbuf);
1767 if (account->auto_sig)
1768 compose_insert_sig(compose, FALSE);
1770 compose_wrap_all(compose);
1772 SIGNAL_UNBLOCK(textbuf);
1774 gtk_text_buffer_get_start_iter(textbuf, &iter);
1775 gtk_text_buffer_place_cursor(textbuf, &iter);
1777 gtk_widget_grab_focus(compose->header_last->entry);
1778 undo_unblock(compose->undostruct);
1779 compose->modified = FALSE;
1780 compose_set_title(compose);
1782 compose->updating = FALSE;
1783 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1784 SCROLL_TO_CURSOR(compose);
1786 if (compose->deferred_destroy) {
1787 compose_destroy(compose);
1794 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1796 GtkTextIter start = *iter;
1797 GtkTextIter end_iter;
1798 int start_pos = gtk_text_iter_get_offset(&start);
1800 if (!compose->account->sig_sep)
1803 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1804 start_pos+strlen(compose->account->sig_sep));
1806 /* check sig separator */
1807 str = gtk_text_iter_get_text(&start, &end_iter);
1808 if (!strcmp(str, compose->account->sig_sep)) {
1810 /* check end of line (\n) */
1811 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1812 start_pos+strlen(compose->account->sig_sep));
1813 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1814 start_pos+strlen(compose->account->sig_sep)+1);
1815 tmp = gtk_text_iter_get_text(&start, &end_iter);
1816 if (!strcmp(tmp,"\n")) {
1828 static void compose_colorize_signature(Compose *compose)
1830 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1832 GtkTextIter end_iter;
1833 gtk_text_buffer_get_start_iter(buffer, &iter);
1834 while (gtk_text_iter_forward_line(&iter))
1835 if (compose_is_sig_separator(compose, buffer, &iter)) {
1836 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1837 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1841 #define BLOCK_WRAP() { \
1842 prev_autowrap = compose->autowrap; \
1843 buffer = gtk_text_view_get_buffer( \
1844 GTK_TEXT_VIEW(compose->text)); \
1845 compose->autowrap = FALSE; \
1847 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1848 G_CALLBACK(compose_changed_cb), \
1850 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1851 G_CALLBACK(text_inserted), \
1854 #define UNBLOCK_WRAP() { \
1855 compose->autowrap = prev_autowrap; \
1856 if (compose->autowrap) { \
1857 gint old = compose->draft_timeout_tag; \
1858 compose->draft_timeout_tag = -2; \
1859 compose_wrap_all(compose); \
1860 compose->draft_timeout_tag = old; \
1863 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1864 G_CALLBACK(compose_changed_cb), \
1866 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1867 G_CALLBACK(text_inserted), \
1871 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1873 Compose *compose = NULL;
1874 PrefsAccount *account = NULL;
1875 GtkTextView *textview;
1876 GtkTextBuffer *textbuf;
1880 gchar buf[BUFFSIZE];
1881 gboolean use_signing = FALSE;
1882 gboolean use_encryption = FALSE;
1883 gchar *privacy_system = NULL;
1884 int priority = PRIORITY_NORMAL;
1885 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1887 g_return_val_if_fail(msginfo != NULL, NULL);
1888 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1890 if (compose_put_existing_to_front(msginfo)) {
1894 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1895 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1896 gchar queueheader_buf[BUFFSIZE];
1899 /* Select Account from queue headers */
1900 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1901 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1902 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1903 account = account_find_from_id(id);
1905 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1906 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1907 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1908 account = account_find_from_id(id);
1910 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1911 sizeof(queueheader_buf), "NAID:")) {
1912 id = atoi(&queueheader_buf[strlen("NAID:")]);
1913 account = account_find_from_id(id);
1915 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1916 sizeof(queueheader_buf), "MAID:")) {
1917 id = atoi(&queueheader_buf[strlen("MAID:")]);
1918 account = account_find_from_id(id);
1920 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1921 sizeof(queueheader_buf), "S:")) {
1922 account = account_find_from_address(queueheader_buf);
1924 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1925 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1926 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1927 use_signing = param;
1930 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1931 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1932 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1933 use_signing = param;
1936 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1937 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1938 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1939 use_encryption = param;
1941 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1942 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1943 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1944 use_encryption = param;
1946 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1947 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1948 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1950 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1951 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1952 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1954 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1955 sizeof(queueheader_buf), "X-Priority: ")) {
1956 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1959 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1960 sizeof(queueheader_buf), "RMID:")) {
1961 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1962 if (tokens[0] && tokens[1] && tokens[2]) {
1963 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1964 if (orig_item != NULL) {
1965 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1970 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1971 sizeof(queueheader_buf), "FMID:")) {
1972 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1973 if (tokens[0] && tokens[1] && tokens[2]) {
1974 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1975 if (orig_item != NULL) {
1976 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1982 account = msginfo->folder->folder->account;
1985 if (!account && prefs_common.reedit_account_autosel) {
1986 gchar from[BUFFSIZE];
1987 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1988 extract_address(from);
1989 account = account_find_from_address(from);
1993 account = cur_account;
1995 g_return_val_if_fail(account != NULL, NULL);
1997 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
1999 compose->replyinfo = replyinfo;
2000 compose->fwdinfo = fwdinfo;
2002 compose->updating = TRUE;
2003 compose->priority = priority;
2005 if (privacy_system != NULL) {
2006 compose->privacy_system = privacy_system;
2007 compose_use_signing(compose, use_signing);
2008 compose_use_encryption(compose, use_encryption);
2009 compose_update_privacy_system_menu_item(compose, FALSE);
2011 activate_privacy_system(compose, account, FALSE);
2014 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2016 compose_extract_original_charset(compose);
2018 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2019 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2020 gchar queueheader_buf[BUFFSIZE];
2022 /* Set message save folder */
2023 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2026 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2027 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2028 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2030 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2031 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2033 GtkItemFactory *ifactory;
2034 ifactory = gtk_item_factory_from_widget(compose->menubar);
2035 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2040 if (compose_parse_header(compose, msginfo) < 0) {
2041 compose->updating = FALSE;
2042 compose_destroy(compose);
2045 compose_reedit_set_entry(compose, msginfo);
2047 textview = GTK_TEXT_VIEW(compose->text);
2048 textbuf = gtk_text_view_get_buffer(textview);
2049 compose_create_tags(textview, compose);
2051 mark = gtk_text_buffer_get_insert(textbuf);
2052 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2054 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2055 G_CALLBACK(compose_changed_cb),
2058 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2059 fp = procmime_get_first_encrypted_text_content(msginfo);
2061 compose_force_encryption(compose, account, TRUE);
2064 fp = procmime_get_first_text_content(msginfo);
2067 g_warning("Can't get text part\n");
2071 gboolean prev_autowrap = compose->autowrap;
2072 GtkTextBuffer *buffer = textbuf;
2074 while (fgets(buf, sizeof(buf), fp) != NULL) {
2076 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2082 compose_attach_parts(compose, msginfo);
2084 compose_colorize_signature(compose);
2086 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2087 G_CALLBACK(compose_changed_cb),
2090 gtk_widget_grab_focus(compose->text);
2092 if (prefs_common.auto_exteditor) {
2093 compose_exec_ext_editor(compose);
2095 compose->modified = FALSE;
2096 compose_set_title(compose);
2098 compose->updating = FALSE;
2099 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2100 SCROLL_TO_CURSOR(compose);
2102 if (compose->deferred_destroy) {
2103 compose_destroy(compose);
2107 compose->sig_str = compose_get_signature_str(compose);
2112 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2117 GtkItemFactory *ifactory;
2120 g_return_val_if_fail(msginfo != NULL, NULL);
2123 account = account_get_reply_account(msginfo,
2124 prefs_common.reply_account_autosel);
2125 g_return_val_if_fail(account != NULL, NULL);
2127 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2129 compose->updating = TRUE;
2131 ifactory = gtk_item_factory_from_widget(compose->menubar);
2132 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2133 compose->replyinfo = NULL;
2134 compose->fwdinfo = NULL;
2136 compose_show_first_last_header(compose, TRUE);
2138 gtk_widget_grab_focus(compose->header_last->entry);
2140 filename = procmsg_get_message_file(msginfo);
2142 if (filename == NULL) {
2143 compose->updating = FALSE;
2144 compose_destroy(compose);
2149 compose->redirect_filename = filename;
2151 /* Set save folder */
2152 item = msginfo->folder;
2153 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2154 gchar *folderidentifier;
2156 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2157 folderidentifier = folder_item_get_identifier(item);
2158 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2159 g_free(folderidentifier);
2162 compose_attach_parts(compose, msginfo);
2164 if (msginfo->subject)
2165 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2167 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2169 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2170 _("Message redirect format error at line %d."));
2171 quote_fmt_reset_vartable();
2172 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2174 compose_colorize_signature(compose);
2176 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2177 menu_set_sensitive(ifactory, "/Add...", FALSE);
2178 menu_set_sensitive(ifactory, "/Remove", FALSE);
2179 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2181 ifactory = gtk_item_factory_from_widget(compose->menubar);
2182 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2183 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2184 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2185 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2186 menu_set_sensitive(ifactory, "/Edit", FALSE);
2187 menu_set_sensitive(ifactory, "/Options", FALSE);
2188 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2189 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2191 if (compose->toolbar->draft_btn)
2192 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2193 if (compose->toolbar->insert_btn)
2194 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2195 if (compose->toolbar->attach_btn)
2196 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2197 if (compose->toolbar->sig_btn)
2198 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2199 if (compose->toolbar->exteditor_btn)
2200 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2201 if (compose->toolbar->linewrap_current_btn)
2202 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2203 if (compose->toolbar->linewrap_all_btn)
2204 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2206 compose->modified = FALSE;
2207 compose_set_title(compose);
2208 compose->updating = FALSE;
2209 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2210 SCROLL_TO_CURSOR(compose);
2212 if (compose->deferred_destroy) {
2213 compose_destroy(compose);
2220 GList *compose_get_compose_list(void)
2222 return compose_list;
2225 void compose_entry_append(Compose *compose, const gchar *address,
2226 ComposeEntryType type)
2228 const gchar *header;
2230 gboolean in_quote = FALSE;
2231 if (!address || *address == '\0') return;
2238 header = N_("Bcc:");
2240 case COMPOSE_REPLYTO:
2241 header = N_("Reply-To:");
2243 case COMPOSE_NEWSGROUPS:
2244 header = N_("Newsgroups:");
2246 case COMPOSE_FOLLOWUPTO:
2247 header = N_( "Followup-To:");
2254 header = prefs_common_translated_header_name(header);
2256 cur = begin = (gchar *)address;
2258 /* we separate the line by commas, but not if we're inside a quoted
2260 while (*cur != '\0') {
2262 in_quote = !in_quote;
2263 if (*cur == ',' && !in_quote) {
2264 gchar *tmp = g_strdup(begin);
2266 tmp[cur-begin]='\0';
2269 while (*tmp == ' ' || *tmp == '\t')
2271 compose_add_header_entry(compose, header, tmp);
2278 gchar *tmp = g_strdup(begin);
2280 tmp[cur-begin]='\0';
2283 while (*tmp == ' ' || *tmp == '\t')
2285 compose_add_header_entry(compose, header, tmp);
2290 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2292 static GdkColor yellow;
2293 static GdkColor black;
2294 static gboolean yellow_initialised = FALSE;
2298 if (!yellow_initialised) {
2299 gdk_color_parse("#f5f6be", &yellow);
2300 gdk_color_parse("#000000", &black);
2301 yellow_initialised = gdk_colormap_alloc_color(
2302 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2303 yellow_initialised &= gdk_colormap_alloc_color(
2304 gdk_colormap_get_system(), &black, FALSE, TRUE);
2307 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2308 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2309 if (gtk_entry_get_text(entry) &&
2310 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2311 if (yellow_initialised) {
2312 gtk_widget_modify_base(
2313 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2314 GTK_STATE_NORMAL, &yellow);
2315 gtk_widget_modify_text(
2316 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2317 GTK_STATE_NORMAL, &black);
2323 void compose_toolbar_cb(gint action, gpointer data)
2325 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2326 Compose *compose = (Compose*)toolbar_item->parent;
2328 g_return_if_fail(compose != NULL);
2332 compose_send_cb(compose, 0, NULL);
2335 compose_send_later_cb(compose, 0, NULL);
2338 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2341 compose_insert_file_cb(compose, 0, NULL);
2344 compose_attach_cb(compose, 0, NULL);
2347 compose_insert_sig(compose, FALSE);
2350 compose_ext_editor_cb(compose, 0, NULL);
2352 case A_LINEWRAP_CURRENT:
2353 compose_beautify_paragraph(compose, NULL, TRUE);
2355 case A_LINEWRAP_ALL:
2356 compose_wrap_all_full(compose, TRUE);
2359 compose_address_cb(compose, 0, NULL);
2362 case A_CHECK_SPELLING:
2363 compose_check_all(compose);
2371 static void compose_entries_set(Compose *compose, const gchar *mailto)
2375 gchar *subject = NULL;
2379 gchar *attach = NULL;
2381 scan_mailto_url(mailto, &to, &cc, NULL, &subject, &body, &attach);
2384 compose_entry_append(compose, to, COMPOSE_TO);
2386 compose_entry_append(compose, cc, COMPOSE_CC);
2388 if (!g_utf8_validate (subject, -1, NULL)) {
2389 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2390 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2393 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2397 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2398 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2401 gboolean prev_autowrap = compose->autowrap;
2403 compose->autowrap = FALSE;
2405 mark = gtk_text_buffer_get_insert(buffer);
2406 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2408 if (!g_utf8_validate (body, -1, NULL)) {
2409 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2410 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2413 gtk_text_buffer_insert(buffer, &iter, body, -1);
2415 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2417 compose->autowrap = prev_autowrap;
2418 if (compose->autowrap)
2419 compose_wrap_all(compose);
2423 gchar *utf8_filename = conv_filename_to_utf8(attach);
2424 if (utf8_filename) {
2425 if (compose_attach_append(compose, attach, utf8_filename, NULL)) {
2426 alertpanel_notice(_("The file '%s' has been attached."), attach);
2428 g_free(utf8_filename);
2430 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2440 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2442 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2443 {"Cc:", NULL, TRUE},
2444 {"References:", NULL, FALSE},
2445 {"Bcc:", NULL, TRUE},
2446 {"Newsgroups:", NULL, TRUE},
2447 {"Followup-To:", NULL, TRUE},
2448 {"List-Post:", NULL, FALSE},
2449 {"X-Priority:", NULL, FALSE},
2450 {NULL, NULL, FALSE}};
2466 g_return_val_if_fail(msginfo != NULL, -1);
2468 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2469 procheader_get_header_fields(fp, hentry);
2472 if (hentry[H_REPLY_TO].body != NULL) {
2473 if (hentry[H_REPLY_TO].body[0] != '\0') {
2475 conv_unmime_header(hentry[H_REPLY_TO].body,
2478 g_free(hentry[H_REPLY_TO].body);
2479 hentry[H_REPLY_TO].body = NULL;
2481 if (hentry[H_CC].body != NULL) {
2482 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2483 g_free(hentry[H_CC].body);
2484 hentry[H_CC].body = NULL;
2486 if (hentry[H_REFERENCES].body != NULL) {
2487 if (compose->mode == COMPOSE_REEDIT)
2488 compose->references = hentry[H_REFERENCES].body;
2490 compose->references = compose_parse_references
2491 (hentry[H_REFERENCES].body, msginfo->msgid);
2492 g_free(hentry[H_REFERENCES].body);
2494 hentry[H_REFERENCES].body = NULL;
2496 if (hentry[H_BCC].body != NULL) {
2497 if (compose->mode == COMPOSE_REEDIT)
2499 conv_unmime_header(hentry[H_BCC].body, NULL);
2500 g_free(hentry[H_BCC].body);
2501 hentry[H_BCC].body = NULL;
2503 if (hentry[H_NEWSGROUPS].body != NULL) {
2504 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2505 hentry[H_NEWSGROUPS].body = NULL;
2507 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2508 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2509 compose->followup_to =
2510 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2513 g_free(hentry[H_FOLLOWUP_TO].body);
2514 hentry[H_FOLLOWUP_TO].body = NULL;
2516 if (hentry[H_LIST_POST].body != NULL) {
2519 extract_address(hentry[H_LIST_POST].body);
2520 if (hentry[H_LIST_POST].body[0] != '\0') {
2521 scan_mailto_url(hentry[H_LIST_POST].body,
2522 &to, NULL, NULL, NULL, NULL, NULL);
2524 g_free(compose->ml_post);
2525 compose->ml_post = to;
2528 g_free(hentry[H_LIST_POST].body);
2529 hentry[H_LIST_POST].body = NULL;
2532 /* CLAWS - X-Priority */
2533 if (compose->mode == COMPOSE_REEDIT)
2534 if (hentry[H_X_PRIORITY].body != NULL) {
2537 priority = atoi(hentry[H_X_PRIORITY].body);
2538 g_free(hentry[H_X_PRIORITY].body);
2540 hentry[H_X_PRIORITY].body = NULL;
2542 if (priority < PRIORITY_HIGHEST ||
2543 priority > PRIORITY_LOWEST)
2544 priority = PRIORITY_NORMAL;
2546 compose->priority = priority;
2549 if (compose->mode == COMPOSE_REEDIT) {
2550 if (msginfo->inreplyto && *msginfo->inreplyto)
2551 compose->inreplyto = g_strdup(msginfo->inreplyto);
2555 if (msginfo->msgid && *msginfo->msgid)
2556 compose->inreplyto = g_strdup(msginfo->msgid);
2558 if (!compose->references) {
2559 if (msginfo->msgid && *msginfo->msgid) {
2560 if (msginfo->inreplyto && *msginfo->inreplyto)
2561 compose->references =
2562 g_strdup_printf("<%s>\n\t<%s>",
2566 compose->references =
2567 g_strconcat("<", msginfo->msgid, ">",
2569 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2570 compose->references =
2571 g_strconcat("<", msginfo->inreplyto, ">",
2579 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2581 GSList *ref_id_list, *cur;
2585 ref_id_list = references_list_append(NULL, ref);
2586 if (!ref_id_list) return NULL;
2587 if (msgid && *msgid)
2588 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2593 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2594 /* "<" + Message-ID + ">" + CR+LF+TAB */
2595 len += strlen((gchar *)cur->data) + 5;
2597 if (len > MAX_REFERENCES_LEN) {
2598 /* remove second message-ID */
2599 if (ref_id_list && ref_id_list->next &&
2600 ref_id_list->next->next) {
2601 g_free(ref_id_list->next->data);
2602 ref_id_list = g_slist_remove
2603 (ref_id_list, ref_id_list->next->data);
2605 slist_free_strings(ref_id_list);
2606 g_slist_free(ref_id_list);
2613 new_ref = g_string_new("");
2614 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2615 if (new_ref->len > 0)
2616 g_string_append(new_ref, "\n\t");
2617 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2620 slist_free_strings(ref_id_list);
2621 g_slist_free(ref_id_list);
2623 new_ref_str = new_ref->str;
2624 g_string_free(new_ref, FALSE);
2629 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2630 const gchar *fmt, const gchar *qmark,
2631 const gchar *body, gboolean rewrap,
2632 gboolean need_unescape,
2633 const gchar *err_msg)
2635 MsgInfo* dummyinfo = NULL;
2636 gchar *quote_str = NULL;
2638 gboolean prev_autowrap;
2639 const gchar *trimmed_body = body;
2640 gint cursor_pos = -1;
2641 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2642 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2647 SIGNAL_BLOCK(buffer);
2650 dummyinfo = compose_msginfo_new_from_compose(compose);
2651 msginfo = dummyinfo;
2654 if (qmark != NULL) {
2656 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2657 compose->gtkaspell);
2659 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2661 quote_fmt_scan_string(qmark);
2664 buf = quote_fmt_get_buffer();
2666 alertpanel_error(_("Quote mark format error."));
2668 Xstrdup_a(quote_str, buf, goto error)
2671 if (fmt && *fmt != '\0') {
2674 while (*trimmed_body == '\n')
2678 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2679 compose->gtkaspell);
2681 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2683 if (need_unescape) {
2686 /* decode \-escape sequences in the internal representation of the quote format */
2687 tmp = malloc(strlen(fmt)+1);
2688 pref_get_unescaped_pref(tmp, fmt);
2689 quote_fmt_scan_string(tmp);
2693 quote_fmt_scan_string(fmt);
2697 buf = quote_fmt_get_buffer();
2699 gint line = quote_fmt_get_line();
2700 alertpanel_error(err_msg, line);
2706 prev_autowrap = compose->autowrap;
2707 compose->autowrap = FALSE;
2709 mark = gtk_text_buffer_get_insert(buffer);
2710 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2711 if (g_utf8_validate(buf, -1, NULL)) {
2712 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2714 gchar *tmpout = NULL;
2715 tmpout = conv_codeset_strdup
2716 (buf, conv_get_locale_charset_str_no_utf8(),
2718 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2720 tmpout = g_malloc(strlen(buf)*2+1);
2721 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2723 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2727 cursor_pos = quote_fmt_get_cursor_pos();
2728 compose->set_cursor_pos = cursor_pos;
2729 if (cursor_pos == -1) {
2732 gtk_text_buffer_get_start_iter(buffer, &iter);
2733 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2734 gtk_text_buffer_place_cursor(buffer, &iter);
2736 compose->autowrap = prev_autowrap;
2737 if (compose->autowrap && rewrap)
2738 compose_wrap_all(compose);
2745 SIGNAL_UNBLOCK(buffer);
2747 procmsg_msginfo_free( dummyinfo );
2752 /* if ml_post is of type addr@host and from is of type
2753 * addr-anything@host, return TRUE
2755 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2757 gchar *left_ml = NULL;
2758 gchar *right_ml = NULL;
2759 gchar *left_from = NULL;
2760 gchar *right_from = NULL;
2761 gboolean result = FALSE;
2763 if (!ml_post || !from)
2766 left_ml = g_strdup(ml_post);
2767 if (strstr(left_ml, "@")) {
2768 right_ml = strstr(left_ml, "@")+1;
2769 *(strstr(left_ml, "@")) = '\0';
2772 left_from = g_strdup(from);
2773 if (strstr(left_from, "@")) {
2774 right_from = strstr(left_from, "@")+1;
2775 *(strstr(left_from, "@")) = '\0';
2778 if (left_ml && left_from && right_ml && right_from
2779 && !strncmp(left_from, left_ml, strlen(left_ml))
2780 && !strcmp(right_from, right_ml)) {
2789 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2791 gchar *my_addr1, *my_addr2;
2793 if (!addr1 || !addr2)
2796 Xstrdup_a(my_addr1, addr1, return FALSE);
2797 Xstrdup_a(my_addr2, addr2, return FALSE);
2799 extract_address(my_addr1);
2800 extract_address(my_addr2);
2802 return !strcasecmp(my_addr1, my_addr2);
2805 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2806 gboolean to_all, gboolean to_ml,
2808 gboolean followup_and_reply_to)
2810 GSList *cc_list = NULL;
2813 gchar *replyto = NULL;
2814 GHashTable *to_table;
2816 gboolean reply_to_ml = FALSE;
2817 gboolean default_reply_to = FALSE;
2819 g_return_if_fail(compose->account != NULL);
2820 g_return_if_fail(msginfo != NULL);
2822 reply_to_ml = to_ml && compose->ml_post;
2824 default_reply_to = msginfo->folder &&
2825 msginfo->folder->prefs->enable_default_reply_to;
2827 if (compose->account->protocol != A_NNTP) {
2828 if (reply_to_ml && !default_reply_to) {
2830 gboolean is_subscr = is_subscription(compose->ml_post,
2833 /* normal answer to ml post with a reply-to */
2834 compose_entry_append(compose,
2837 if (compose->replyto
2838 && !same_address(compose->ml_post, compose->replyto))
2839 compose_entry_append(compose,
2843 /* answer to subscription confirmation */
2844 if (compose->replyto)
2845 compose_entry_append(compose,
2848 else if (msginfo->from)
2849 compose_entry_append(compose,
2854 else if (!(to_all || to_sender) && default_reply_to) {
2855 compose_entry_append(compose,
2856 msginfo->folder->prefs->default_reply_to,
2858 compose_entry_mark_default_to(compose,
2859 msginfo->folder->prefs->default_reply_to);
2864 Xstrdup_a(tmp1, msginfo->from, return);
2865 extract_address(tmp1);
2866 if (to_all || to_sender ||
2867 !account_find_from_address(tmp1))
2868 compose_entry_append(compose,
2869 (compose->replyto && !to_sender)
2870 ? compose->replyto :
2871 msginfo->from ? msginfo->from : "",
2873 else if (!to_all && !to_sender) {
2874 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2875 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2876 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2877 compose_entry_append(compose,
2878 msginfo->from ? msginfo->from : "",
2881 /* replying to own mail, use original recp */
2882 compose_entry_append(compose,
2883 msginfo->to ? msginfo->to : "",
2885 compose_entry_append(compose,
2886 msginfo->cc ? msginfo->cc : "",
2892 if (to_sender || (compose->followup_to &&
2893 !strncmp(compose->followup_to, "poster", 6)))
2894 compose_entry_append
2896 (compose->replyto ? compose->replyto :
2897 msginfo->from ? msginfo->from : ""),
2900 else if (followup_and_reply_to || to_all) {
2901 compose_entry_append
2903 (compose->replyto ? compose->replyto :
2904 msginfo->from ? msginfo->from : ""),
2907 compose_entry_append
2909 compose->followup_to ? compose->followup_to :
2910 compose->newsgroups ? compose->newsgroups : "",
2911 COMPOSE_NEWSGROUPS);
2914 compose_entry_append
2916 compose->followup_to ? compose->followup_to :
2917 compose->newsgroups ? compose->newsgroups : "",
2918 COMPOSE_NEWSGROUPS);
2921 if (msginfo->subject && *msginfo->subject) {
2925 buf = p = g_strdup(msginfo->subject);
2926 p += subject_get_prefix_length(p);
2927 memmove(buf, p, strlen(p) + 1);
2929 buf2 = g_strdup_printf("Re: %s", buf);
2930 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2935 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2937 if (to_ml && compose->ml_post) return;
2938 if (!to_all || compose->account->protocol == A_NNTP) return;
2940 if (compose->replyto) {
2941 Xstrdup_a(replyto, compose->replyto, return);
2942 extract_address(replyto);
2944 if (msginfo->from) {
2945 Xstrdup_a(from, msginfo->from, return);
2946 extract_address(from);
2949 if (replyto && from)
2950 cc_list = address_list_append_with_comments(cc_list, from);
2951 if (to_all && msginfo->folder &&
2952 msginfo->folder->prefs->enable_default_reply_to)
2953 cc_list = address_list_append_with_comments(cc_list,
2954 msginfo->folder->prefs->default_reply_to);
2955 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2956 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2958 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2960 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2961 if (compose->account) {
2962 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2963 GINT_TO_POINTER(1));
2965 /* remove address on To: and that of current account */
2966 for (cur = cc_list; cur != NULL; ) {
2967 GSList *next = cur->next;
2970 addr = g_utf8_strdown(cur->data, -1);
2971 extract_address(addr);
2973 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2974 cc_list = g_slist_remove(cc_list, cur->data);
2976 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2980 hash_free_strings(to_table);
2981 g_hash_table_destroy(to_table);
2984 for (cur = cc_list; cur != NULL; cur = cur->next)
2985 compose_entry_append(compose, (gchar *)cur->data,
2987 slist_free_strings(cc_list);
2988 g_slist_free(cc_list);
2993 #define SET_ENTRY(entry, str) \
2996 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
2999 #define SET_ADDRESS(type, str) \
3002 compose_entry_append(compose, str, type); \
3005 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3007 g_return_if_fail(msginfo != NULL);
3009 SET_ENTRY(subject_entry, msginfo->subject);
3010 SET_ENTRY(from_name, msginfo->from);
3011 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3012 SET_ADDRESS(COMPOSE_CC, compose->cc);
3013 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3014 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3015 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3016 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3018 compose_update_priority_menu_item(compose);
3019 compose_update_privacy_system_menu_item(compose, FALSE);
3020 compose_show_first_last_header(compose, TRUE);
3026 static void compose_insert_sig(Compose *compose, gboolean replace)
3028 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3029 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3031 GtkTextIter iter, iter_end;
3033 gboolean prev_autowrap;
3034 gboolean found = FALSE;
3035 gboolean exists = FALSE;
3037 g_return_if_fail(compose->account != NULL);
3041 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3042 G_CALLBACK(compose_changed_cb),
3045 mark = gtk_text_buffer_get_insert(buffer);
3046 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3047 cur_pos = gtk_text_iter_get_offset (&iter);
3049 gtk_text_buffer_get_end_iter(buffer, &iter);
3051 exists = (compose->sig_str != NULL);
3054 GtkTextIter first_iter, start_iter, end_iter;
3056 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3058 if (!exists || compose->sig_str[0] == '\0')
3061 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3062 compose->signature_tag);
3065 /* include previous \n\n */
3066 gtk_text_iter_backward_chars(&first_iter, 2);
3067 start_iter = first_iter;
3068 end_iter = first_iter;
3070 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3071 compose->signature_tag);
3072 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3073 compose->signature_tag);
3075 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3081 g_free(compose->sig_str);
3082 compose->sig_str = compose_get_signature_str(compose);
3084 cur_pos = gtk_text_iter_get_offset(&iter);
3086 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3087 g_free(compose->sig_str);
3088 compose->sig_str = NULL;
3090 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3092 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3093 gtk_text_iter_forward_chars(&iter, 2);
3094 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3095 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3097 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3098 cur_pos = gtk_text_buffer_get_char_count (buffer);
3100 /* put the cursor where it should be
3101 * either where the quote_fmt says, either before the signature */
3102 if (compose->set_cursor_pos < 0)
3103 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3105 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3106 compose->set_cursor_pos);
3108 gtk_text_buffer_place_cursor(buffer, &iter);
3109 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3110 G_CALLBACK(compose_changed_cb),
3116 static gchar *compose_get_signature_str(Compose *compose)
3118 gchar *sig_body = NULL;
3119 gchar *sig_str = NULL;
3120 gchar *utf8_sig_str = NULL;
3122 g_return_val_if_fail(compose->account != NULL, NULL);
3124 if (!compose->account->sig_path)
3127 if (compose->account->sig_type == SIG_FILE) {
3128 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3129 g_warning("can't open signature file: %s\n",
3130 compose->account->sig_path);
3135 if (compose->account->sig_type == SIG_COMMAND)
3136 sig_body = get_command_output(compose->account->sig_path);
3140 tmp = file_read_to_str(compose->account->sig_path);
3143 sig_body = normalize_newlines(tmp);
3147 if (compose->account->sig_sep) {
3148 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3152 sig_str = g_strconcat("\n\n", sig_body, NULL);
3155 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3156 utf8_sig_str = sig_str;
3158 utf8_sig_str = conv_codeset_strdup
3159 (sig_str, conv_get_locale_charset_str_no_utf8(),
3165 return utf8_sig_str;
3168 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3171 GtkTextBuffer *buffer;
3174 const gchar *cur_encoding;
3175 gchar buf[BUFFSIZE];
3178 gboolean prev_autowrap;
3179 gboolean badtxt = FALSE;