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);
549 static void compose_attach_update_label(Compose *compose);
551 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
553 static GtkItemFactoryEntry compose_popup_entries[] =
555 {N_("/_Add..."), NULL, compose_attach_cb, 0, NULL},
556 {N_("/_Remove"), NULL, compose_attach_remove_selected, 0, NULL},
557 {"/---", NULL, NULL, 0, "<Separator>"},
558 {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
561 static GtkItemFactoryEntry compose_entries[] =
563 {N_("/_Message"), NULL, NULL, 0, "<Branch>"},
564 {N_("/_Message/S_end"), "<control>Return",
565 compose_send_cb, 0, NULL},
566 {N_("/_Message/Send _later"), "<shift><control>S",
567 compose_send_later_cb, 0, NULL},
568 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
569 {N_("/_Message/_Attach file"), "<control>M", compose_attach_cb, 0, NULL},
570 {N_("/_Message/_Insert file"), "<control>I", compose_insert_file_cb, 0, NULL},
571 {N_("/_Message/Insert si_gnature"), "<control>G", compose_insert_sig_cb, 0, NULL},
572 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
573 {N_("/_Message/_Save"),
574 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
575 {N_("/_Message/---"), NULL, NULL, 0, "<Separator>"},
576 {N_("/_Message/_Close"), "<control>W", compose_close_cb, 0, NULL},
578 {N_("/_Edit"), NULL, NULL, 0, "<Branch>"},
579 {N_("/_Edit/_Undo"), "<control>Z", compose_undo_cb, 0, NULL},
580 {N_("/_Edit/_Redo"), "<control>Y", compose_redo_cb, 0, NULL},
581 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
582 {N_("/_Edit/Cu_t"), "<control>X", compose_cut_cb, 0, NULL},
583 {N_("/_Edit/_Copy"), "<control>C", compose_copy_cb, 0, NULL},
584 {N_("/_Edit/_Paste"), "<control>V", compose_paste_cb, 0, NULL},
585 {N_("/_Edit/Special paste"), NULL, NULL, 0, "<Branch>"},
586 {N_("/_Edit/Special paste/as _quotation"),
587 NULL, compose_paste_as_quote_cb, 0, NULL},
588 {N_("/_Edit/Special paste/_wrapped"),
589 NULL, compose_paste_wrap_cb, 0, NULL},
590 {N_("/_Edit/Special paste/_unwrapped"),
591 NULL, compose_paste_no_wrap_cb, 0, NULL},
592 {N_("/_Edit/Select _all"), "<control>A", compose_allsel_cb, 0, NULL},
593 {N_("/_Edit/A_dvanced"), NULL, NULL, 0, "<Branch>"},
594 {N_("/_Edit/A_dvanced/Move a character backward"),
596 compose_advanced_action_cb,
597 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
599 {N_("/_Edit/A_dvanced/Move a character forward"),
601 compose_advanced_action_cb,
602 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
604 {N_("/_Edit/A_dvanced/Move a word backward"),
606 compose_advanced_action_cb,
607 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
609 {N_("/_Edit/A_dvanced/Move a word forward"),
611 compose_advanced_action_cb,
612 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
614 {N_("/_Edit/A_dvanced/Move to beginning of line"),
615 NULL, /* "<control>A" */
616 compose_advanced_action_cb,
617 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
619 {N_("/_Edit/A_dvanced/Move to end of line"),
621 compose_advanced_action_cb,
622 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
624 {N_("/_Edit/A_dvanced/Move to previous line"),
626 compose_advanced_action_cb,
627 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
629 {N_("/_Edit/A_dvanced/Move to next line"),
631 compose_advanced_action_cb,
632 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
634 {N_("/_Edit/A_dvanced/Delete a character backward"),
636 compose_advanced_action_cb,
637 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
639 {N_("/_Edit/A_dvanced/Delete a character forward"),
641 compose_advanced_action_cb,
642 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
644 {N_("/_Edit/A_dvanced/Delete a word backward"),
645 NULL, /* "<control>W" */
646 compose_advanced_action_cb,
647 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
649 {N_("/_Edit/A_dvanced/Delete a word forward"),
650 NULL, /* "<alt>D", */
651 compose_advanced_action_cb,
652 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
654 {N_("/_Edit/A_dvanced/Delete line"),
656 compose_advanced_action_cb,
657 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
659 {N_("/_Edit/A_dvanced/Delete entire line"),
661 compose_advanced_action_cb,
662 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
664 {N_("/_Edit/A_dvanced/Delete to end of line"),
666 compose_advanced_action_cb,
667 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
669 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
671 "<control>F", compose_find_cb, 0, NULL},
672 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
673 {N_("/_Edit/_Wrap current paragraph"),
674 "<control>L", compose_wrap_cb, 0, NULL},
675 {N_("/_Edit/Wrap all long _lines"),
676 "<control><alt>L", compose_wrap_cb, 1, NULL},
677 {N_("/_Edit/Aut_o wrapping"), "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
678 {N_("/_Edit/---"), NULL, NULL, 0, "<Separator>"},
679 {N_("/_Edit/Edit with e_xternal editor"),
680 "<shift><control>X", compose_ext_editor_cb, 0, NULL},
682 {N_("/_Spelling"), NULL, NULL, 0, "<Branch>"},
683 {N_("/_Spelling/_Check all or check selection"),
684 NULL, compose_check_all, 0, NULL},
685 {N_("/_Spelling/_Highlight all misspelled words"),
686 NULL, compose_highlight_all, 0, NULL},
687 {N_("/_Spelling/Check _backwards misspelled word"),
688 NULL, compose_check_backwards , 0, NULL},
689 {N_("/_Spelling/_Forward to next misspelled word"),
690 NULL, compose_check_forwards_go, 0, NULL},
691 {N_("/_Spelling/---"), NULL, NULL, 0, "<Separator>"},
692 {N_("/_Spelling/Options"),
693 NULL, NULL, 0, "<Branch>"},
695 {N_("/_Options"), NULL, NULL, 0, "<Branch>"},
696 {N_("/_Options/Reply _mode"), NULL, NULL, 0, "<Branch>"},
697 {N_("/_Options/Reply _mode/_Normal"), NULL, compose_reply_change_mode, COMPOSE_REPLY, "<RadioItem>"},
698 {N_("/_Options/Reply _mode/_All"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
699 {N_("/_Options/Reply _mode/_Sender"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
700 {N_("/_Options/Reply _mode/_Mailing-list"), NULL, compose_reply_change_mode, COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
701 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
702 {N_("/_Options/Privacy _System"), NULL, NULL, 0, "<Branch>"},
703 {N_("/_Options/Privacy _System/None"), NULL, NULL, 0, "<RadioItem>"},
704 {N_("/_Options/Si_gn"), NULL, compose_toggle_sign_cb , 0, "<ToggleItem>"},
705 {N_("/_Options/_Encrypt"), NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
706 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
707 {N_("/_Options/_Priority"), NULL, NULL, 0, "<Branch>"},
708 {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
709 {N_("/_Options/Priority/Hi_gh"), NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
710 {N_("/_Options/Priority/_Normal"), NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
711 {N_("/_Options/Priority/Lo_w"), NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
712 {N_("/_Options/Priority/_Lowest"), NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
713 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
714 {N_("/_Options/_Request Return Receipt"), NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
715 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
716 {N_("/_Options/Remo_ve references"), NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
717 {N_("/_Options/---"), NULL, NULL, 0, "<Separator>"},
719 #define ENC_ACTION(action) \
720 NULL, compose_set_encoding_cb, action, \
721 "/Options/Character encoding/Automatic"
723 {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
724 {N_("/_Options/Character _encoding/_Automatic"),
725 NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
726 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
728 {N_("/_Options/Character _encoding/7bit ASCII (US-ASC_II)"),
729 ENC_ACTION(C_US_ASCII)},
730 {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
731 ENC_ACTION(C_UTF_8)},
732 {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
734 {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
735 {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
736 ENC_ACTION(C_ISO_8859_1)},
737 {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
738 ENC_ACTION(C_ISO_8859_15)},
739 {N_("/_Options/Character _encoding/Western European/Windows-1252"),
740 ENC_ACTION(C_WINDOWS_1252)},
742 {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
743 ENC_ACTION(C_ISO_8859_2)},
745 {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
746 {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
747 ENC_ACTION(C_ISO_8859_13)},
748 {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
749 ENC_ACTION(C_ISO_8859_4)},
751 {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
752 ENC_ACTION(C_ISO_8859_7)},
754 {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
755 {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
756 ENC_ACTION(C_ISO_8859_8)},
757 {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
758 ENC_ACTION(C_WINDOWS_1255)},
760 {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
761 {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
762 ENC_ACTION(C_ISO_8859_6)},
763 {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
764 ENC_ACTION(C_CP1256)},
766 {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
767 ENC_ACTION(C_ISO_8859_9)},
769 {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
770 {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
771 ENC_ACTION(C_ISO_8859_5)},
772 {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
773 ENC_ACTION(C_KOI8_R)},
774 {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
775 ENC_ACTION(C_KOI8_U)},
776 {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
777 ENC_ACTION(C_WINDOWS_1251)},
779 {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
780 {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
781 ENC_ACTION(C_ISO_2022_JP)},
782 {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
783 ENC_ACTION(C_ISO_2022_JP_2)},
784 {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
785 ENC_ACTION(C_EUC_JP)},
786 {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
787 ENC_ACTION(C_SHIFT_JIS)},
789 {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
790 {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
791 ENC_ACTION(C_GB2312)},
792 {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
794 {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
796 {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
797 ENC_ACTION(C_EUC_TW)},
799 {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
800 {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
801 ENC_ACTION(C_EUC_KR)},
802 {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
803 ENC_ACTION(C_ISO_2022_KR)},
805 {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
806 {N_("/_Options/Character _encoding/Thai/TIS-620"),
807 ENC_ACTION(C_TIS_620)},
808 {N_("/_Options/Character _encoding/Thai/Windows-874"),
809 ENC_ACTION(C_WINDOWS_874)},
811 {N_("/_Tools"), NULL, NULL, 0, "<Branch>"},
812 {N_("/_Tools/Show _ruler"), NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
813 {N_("/_Tools/_Address book"), "<shift><control>A", compose_address_cb , 0, NULL},
814 {N_("/_Tools/_Template"), NULL, NULL, 0, "<Branch>"},
815 {N_("/_Tools/Actio_ns"), NULL, NULL, 0, "<Branch>"},
816 {N_("/_Help"), NULL, NULL, 0, "<Branch>"},
817 {N_("/_Help/_About"), NULL, about_show, 0, NULL}
820 static GtkTargetEntry compose_mime_types[] =
822 {"text/uri-list", 0, 0},
823 {"UTF8_STRING", 0, 0},
827 static gboolean compose_put_existing_to_front(MsgInfo *info)
829 GList *compose_list = compose_get_compose_list();
833 for (elem = compose_list; elem != NULL && elem->data != NULL;
835 Compose *c = (Compose*)elem->data;
837 if (!c->targetinfo || !c->targetinfo->msgid ||
841 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
842 gtkut_window_popup(c->window);
850 static GdkColor quote_color1 =
851 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
852 static GdkColor quote_color2 =
853 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
854 static GdkColor quote_color3 =
855 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
857 static GdkColor quote_bgcolor1 =
858 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
859 static GdkColor quote_bgcolor2 =
860 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
861 static GdkColor quote_bgcolor3 =
862 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
864 static GdkColor signature_color = {
871 static GdkColor uri_color = {
878 static void compose_create_tags(GtkTextView *text, Compose *compose)
880 GtkTextBuffer *buffer;
881 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
887 buffer = gtk_text_view_get_buffer(text);
889 if (prefs_common.enable_color) {
890 /* grab the quote colors, converting from an int to a GdkColor */
891 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
893 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
895 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
897 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
899 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
901 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
903 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
905 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
908 signature_color = quote_color1 = quote_color2 = quote_color3 =
909 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
912 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
913 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
914 "foreground-gdk", "e_color1,
915 "paragraph-background-gdk", "e_bgcolor1,
917 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
918 "foreground-gdk", "e_color2,
919 "paragraph-background-gdk", "e_bgcolor2,
921 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
922 "foreground-gdk", "e_color3,
923 "paragraph-background-gdk", "e_bgcolor3,
926 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
927 "foreground-gdk", "e_color1,
929 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
930 "foreground-gdk", "e_color2,
932 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
933 "foreground-gdk", "e_color3,
937 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
938 "foreground-gdk", &signature_color,
941 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
942 "foreground-gdk", &uri_color,
944 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
945 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
947 color[0] = quote_color1;
948 color[1] = quote_color2;
949 color[2] = quote_color3;
950 color[3] = quote_bgcolor1;
951 color[4] = quote_bgcolor2;
952 color[5] = quote_bgcolor3;
953 color[6] = signature_color;
954 color[7] = uri_color;
955 cmap = gdk_drawable_get_colormap(compose->window->window);
956 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
958 for (i = 0; i < 8; i++) {
959 if (success[i] == FALSE) {
962 g_warning("Compose: color allocation failed.\n");
963 style = gtk_widget_get_style(GTK_WIDGET(text));
964 quote_color1 = quote_color2 = quote_color3 =
965 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
966 signature_color = uri_color = black;
971 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
972 GPtrArray *attach_files)
974 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
977 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
979 return compose_generic_new(account, mailto, item, NULL, NULL);
982 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
984 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
987 #define SCROLL_TO_CURSOR(compose) { \
988 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
989 gtk_text_view_get_buffer( \
990 GTK_TEXT_VIEW(compose->text))); \
991 gtk_text_view_scroll_mark_onscreen( \
992 GTK_TEXT_VIEW(compose->text), \
996 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
997 GPtrArray *attach_files, GList *listAddress )
1000 GtkTextView *textview;
1001 GtkTextBuffer *textbuf;
1003 GtkItemFactory *ifactory;
1004 const gchar *subject_format = NULL;
1005 const gchar *body_format = NULL;
1007 if (item && item->prefs && item->prefs->enable_default_account)
1008 account = account_find_from_id(item->prefs->default_account);
1010 if (!account) account = cur_account;
1011 g_return_val_if_fail(account != NULL, NULL);
1013 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1015 ifactory = gtk_item_factory_from_widget(compose->menubar);
1017 compose->replyinfo = NULL;
1018 compose->fwdinfo = NULL;
1020 textview = GTK_TEXT_VIEW(compose->text);
1021 textbuf = gtk_text_view_get_buffer(textview);
1022 compose_create_tags(textview, compose);
1024 undo_block(compose->undostruct);
1026 compose_set_dictionaries_from_folder_prefs(compose, item);
1029 if (account->auto_sig)
1030 compose_insert_sig(compose, FALSE);
1031 gtk_text_buffer_get_start_iter(textbuf, &iter);
1032 gtk_text_buffer_place_cursor(textbuf, &iter);
1034 if (account->protocol != A_NNTP) {
1035 if (mailto && *mailto != '\0') {
1036 compose_entries_set(compose, mailto);
1038 } else if (item && item->prefs->enable_default_to) {
1039 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1040 compose_entry_mark_default_to(compose, item->prefs->default_to);
1042 if (item && item->ret_rcpt) {
1043 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1047 compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1048 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1049 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1052 * CLAWS: just don't allow return receipt request, even if the user
1053 * may want to send an email. simple but foolproof.
1055 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE);
1057 compose_add_field_list( compose, listAddress );
1059 if (item && item->prefs && item->prefs->compose_with_format) {
1060 subject_format = item->prefs->compose_subject_format;
1061 body_format = item->prefs->compose_body_format;
1062 } else if (account->compose_with_format) {
1063 subject_format = account->compose_subject_format;
1064 body_format = account->compose_body_format;
1065 } else if (prefs_common.compose_with_format) {
1066 subject_format = prefs_common.compose_subject_format;
1067 body_format = prefs_common.compose_body_format;
1070 if (subject_format || body_format) {
1071 MsgInfo* dummyinfo = NULL;
1074 && *subject_format != '\0' )
1076 gchar *subject = NULL;
1080 dummyinfo = compose_msginfo_new_from_compose(compose);
1082 /* decode \-escape sequences in the internal representation of the quote format */
1083 tmp = malloc(strlen(subject_format)+1);
1084 pref_get_unescaped_pref(tmp, subject_format);
1086 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1089 compose->gtkaspell);
1091 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1093 quote_fmt_scan_string(tmp);
1096 buf = quote_fmt_get_buffer();
1098 alertpanel_error(_("New message subject format error."));
1100 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1101 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1102 quote_fmt_reset_vartable();
1109 && *body_format != '\0' )
1112 GtkTextBuffer *buffer;
1113 GtkTextIter start, end;
1116 if ( dummyinfo == NULL )
1117 dummyinfo = compose_msginfo_new_from_compose(compose);
1119 text = GTK_TEXT_VIEW(compose->text);
1120 buffer = gtk_text_view_get_buffer(text);
1121 gtk_text_buffer_get_start_iter(buffer, &start);
1122 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1123 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1125 compose_quote_fmt(compose, dummyinfo,
1127 NULL, tmp, FALSE, TRUE,
1128 _("New message body format error at line %d."));
1129 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1130 quote_fmt_reset_vartable();
1135 procmsg_msginfo_free( dummyinfo );
1142 for (i = 0; i < attach_files->len; i++) {
1143 file = g_ptr_array_index(attach_files, i);
1144 compose_attach_append(compose, file, file, NULL);
1148 compose_show_first_last_header(compose, TRUE);
1150 /* Set save folder */
1151 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1152 gchar *folderidentifier;
1154 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1155 folderidentifier = folder_item_get_identifier(item);
1156 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1157 g_free(folderidentifier);
1160 gtk_widget_grab_focus(compose->header_last->entry);
1162 undo_unblock(compose->undostruct);
1164 if (prefs_common.auto_exteditor)
1165 compose_exec_ext_editor(compose);
1167 compose->draft_timeout_tag = -1;
1168 SCROLL_TO_CURSOR(compose);
1170 compose->modified = FALSE;
1171 compose_set_title(compose);
1175 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1176 gboolean override_pref)
1178 gchar *privacy = NULL;
1180 g_return_if_fail(compose != NULL);
1181 g_return_if_fail(account != NULL);
1183 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1186 if (account->default_privacy_system
1187 && strlen(account->default_privacy_system)) {
1188 privacy = account->default_privacy_system;
1190 GSList *privacy_avail = privacy_get_system_ids();
1191 if (privacy_avail && g_slist_length(privacy_avail)) {
1192 privacy = (gchar *)(privacy_avail->data);
1195 if (privacy != NULL) {
1196 if (compose->privacy_system == NULL)
1197 compose->privacy_system = g_strdup(privacy);
1198 compose_update_privacy_system_menu_item(compose, FALSE);
1199 compose_use_encryption(compose, TRUE);
1203 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1205 gchar *privacy = NULL;
1207 if (account->default_privacy_system
1208 && strlen(account->default_privacy_system)) {
1209 privacy = account->default_privacy_system;
1211 GSList *privacy_avail = privacy_get_system_ids();
1212 if (privacy_avail && g_slist_length(privacy_avail)) {
1213 privacy = (gchar *)(privacy_avail->data);
1216 if (privacy != NULL) {
1217 if (compose->privacy_system == NULL)
1218 compose->privacy_system = g_strdup(privacy);
1219 compose_update_privacy_system_menu_item(compose, FALSE);
1220 compose_use_signing(compose, TRUE);
1224 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1228 Compose *compose = NULL;
1229 GtkItemFactory *ifactory = NULL;
1231 g_return_val_if_fail(msginfo_list != NULL, NULL);
1233 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1234 g_return_val_if_fail(msginfo != NULL, NULL);
1236 list_len = g_slist_length(msginfo_list);
1240 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1241 FALSE, prefs_common.default_reply_list, FALSE, body);
1243 case COMPOSE_REPLY_WITH_QUOTE:
1244 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1245 FALSE, prefs_common.default_reply_list, FALSE, body);
1247 case COMPOSE_REPLY_WITHOUT_QUOTE:
1248 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1249 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1251 case COMPOSE_REPLY_TO_SENDER:
1252 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1253 FALSE, FALSE, TRUE, body);
1255 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1256 compose = compose_followup_and_reply_to(msginfo,
1257 COMPOSE_QUOTE_CHECK,
1258 FALSE, FALSE, body);
1260 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1261 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1262 FALSE, FALSE, TRUE, body);
1264 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1265 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1266 FALSE, FALSE, TRUE, NULL);
1268 case COMPOSE_REPLY_TO_ALL:
1269 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1270 TRUE, FALSE, FALSE, body);
1272 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1273 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1274 TRUE, FALSE, FALSE, body);
1276 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1277 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1278 TRUE, FALSE, FALSE, NULL);
1280 case COMPOSE_REPLY_TO_LIST:
1281 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1282 FALSE, TRUE, FALSE, body);
1284 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1285 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1286 FALSE, TRUE, FALSE, body);
1288 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1289 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1290 FALSE, TRUE, FALSE, NULL);
1292 case COMPOSE_FORWARD:
1293 if (prefs_common.forward_as_attachment) {
1294 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1297 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1301 case COMPOSE_FORWARD_INLINE:
1302 /* check if we reply to more than one Message */
1303 if (list_len == 1) {
1304 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1307 /* more messages FALL THROUGH */
1308 case COMPOSE_FORWARD_AS_ATTACH:
1309 compose = compose_forward_multiple(NULL, msginfo_list);
1311 case COMPOSE_REDIRECT:
1312 compose = compose_redirect(NULL, msginfo, FALSE);
1315 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1318 if (compose == NULL) {
1319 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1322 ifactory = gtk_item_factory_from_widget(compose->menubar);
1324 compose->rmode = mode;
1325 switch (compose->rmode) {
1327 case COMPOSE_REPLY_WITH_QUOTE:
1328 case COMPOSE_REPLY_WITHOUT_QUOTE:
1329 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1330 debug_print("reply mode Normal\n");
1331 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1332 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1334 case COMPOSE_REPLY_TO_SENDER:
1335 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1336 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1337 debug_print("reply mode Sender\n");
1338 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1340 case COMPOSE_REPLY_TO_ALL:
1341 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1342 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1343 debug_print("reply mode All\n");
1344 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1346 case COMPOSE_REPLY_TO_LIST:
1347 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1348 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1349 debug_print("reply mode List\n");
1350 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1358 static Compose *compose_reply(MsgInfo *msginfo,
1359 ComposeQuoteMode quote_mode,
1365 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1366 to_sender, FALSE, body);
1369 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1370 ComposeQuoteMode quote_mode,
1375 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1376 to_sender, TRUE, body);
1379 static void compose_extract_original_charset(Compose *compose)
1381 MsgInfo *info = NULL;
1382 if (compose->replyinfo) {
1383 info = compose->replyinfo;
1384 } else if (compose->fwdinfo) {
1385 info = compose->fwdinfo;
1386 } else if (compose->targetinfo) {
1387 info = compose->targetinfo;
1390 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1391 MimeInfo *partinfo = mimeinfo;
1392 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1393 partinfo = procmime_mimeinfo_next(partinfo);
1395 compose->orig_charset =
1396 g_strdup(procmime_mimeinfo_get_parameter(
1397 partinfo, "charset"));
1399 procmime_mimeinfo_free_all(mimeinfo);
1403 #define SIGNAL_BLOCK(buffer) { \
1404 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1405 G_CALLBACK(compose_changed_cb), \
1407 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1408 G_CALLBACK(text_inserted), \
1412 #define SIGNAL_UNBLOCK(buffer) { \
1413 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1414 G_CALLBACK(compose_changed_cb), \
1416 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1417 G_CALLBACK(text_inserted), \
1421 static Compose *compose_generic_reply(MsgInfo *msginfo,
1422 ComposeQuoteMode quote_mode,
1423 gboolean to_all, gboolean to_ml,
1425 gboolean followup_and_reply_to,
1428 GtkItemFactory *ifactory;
1430 PrefsAccount *account = NULL;
1431 GtkTextView *textview;
1432 GtkTextBuffer *textbuf;
1433 gboolean quote = FALSE;
1434 const gchar *qmark = NULL;
1435 const gchar *body_fmt = NULL;
1437 g_return_val_if_fail(msginfo != NULL, NULL);
1438 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1440 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1442 g_return_val_if_fail(account != NULL, NULL);
1444 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1446 compose->updating = TRUE;
1448 ifactory = gtk_item_factory_from_widget(compose->menubar);
1450 menu_set_active(ifactory, "/Options/Remove references", FALSE);
1451 menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1453 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1454 if (!compose->replyinfo)
1455 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1457 compose_extract_original_charset(compose);
1459 if (msginfo->folder && msginfo->folder->ret_rcpt)
1460 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1462 /* Set save folder */
1463 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1464 gchar *folderidentifier;
1466 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1467 folderidentifier = folder_item_get_identifier(msginfo->folder);
1468 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1469 g_free(folderidentifier);
1472 if (compose_parse_header(compose, msginfo) < 0) return NULL;
1474 textview = (GTK_TEXT_VIEW(compose->text));
1475 textbuf = gtk_text_view_get_buffer(textview);
1476 compose_create_tags(textview, compose);
1478 undo_block(compose->undostruct);
1480 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1483 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1484 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1485 /* use the reply format of folder (if enabled), or the account's one
1486 (if enabled) or fallback to the global reply format, which is always
1487 enabled (even if empty), and use the relevant quotemark */
1489 if (msginfo->folder && msginfo->folder->prefs &&
1490 msginfo->folder->prefs->reply_with_format) {
1491 qmark = msginfo->folder->prefs->reply_quotemark;
1492 body_fmt = msginfo->folder->prefs->reply_body_format;
1494 } else if (account->reply_with_format) {
1495 qmark = account->reply_quotemark;
1496 body_fmt = account->reply_body_format;
1499 qmark = prefs_common.quotemark;
1500 body_fmt = prefs_common.quotefmt;
1505 /* empty quotemark is not allowed */
1506 if (qmark == NULL || *qmark == '\0')
1508 compose_quote_fmt(compose, compose->replyinfo,
1509 body_fmt, qmark, body, FALSE, TRUE,
1510 _("Message reply format error at line %d."));
1511 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1512 quote_fmt_reset_vartable();
1515 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1516 compose_force_encryption(compose, account, FALSE);
1519 SIGNAL_BLOCK(textbuf);
1521 if (account->auto_sig)
1522 compose_insert_sig(compose, FALSE);
1524 compose_wrap_all(compose);
1526 SIGNAL_UNBLOCK(textbuf);
1528 gtk_widget_grab_focus(compose->text);
1530 undo_unblock(compose->undostruct);
1532 if (prefs_common.auto_exteditor)
1533 compose_exec_ext_editor(compose);
1535 compose->modified = FALSE;
1536 compose_set_title(compose);
1538 compose->updating = FALSE;
1539 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1540 SCROLL_TO_CURSOR(compose);
1542 if (compose->deferred_destroy) {
1543 compose_destroy(compose);
1550 #define INSERT_FW_HEADER(var, hdr) \
1551 if (msginfo->var && *msginfo->var) { \
1552 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1553 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1554 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1557 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1558 gboolean as_attach, const gchar *body,
1559 gboolean no_extedit,
1563 GtkTextView *textview;
1564 GtkTextBuffer *textbuf;
1567 g_return_val_if_fail(msginfo != NULL, NULL);
1568 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1571 !(account = compose_guess_forward_account_from_msginfo
1573 account = cur_account;
1575 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1577 compose->updating = TRUE;
1578 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1579 if (!compose->fwdinfo)
1580 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1582 compose_extract_original_charset(compose);
1584 if (msginfo->subject && *msginfo->subject) {
1585 gchar *buf, *buf2, *p;
1587 buf = p = g_strdup(msginfo->subject);
1588 p += subject_get_prefix_length(p);
1589 memmove(buf, p, strlen(p) + 1);
1591 buf2 = g_strdup_printf("Fw: %s", buf);
1592 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1598 textview = GTK_TEXT_VIEW(compose->text);
1599 textbuf = gtk_text_view_get_buffer(textview);
1600 compose_create_tags(textview, compose);
1602 undo_block(compose->undostruct);
1606 msgfile = procmsg_get_message_file(msginfo);
1607 if (!is_file_exist(msgfile))
1608 g_warning("%s: file not exist\n", msgfile);
1610 compose_attach_append(compose, msgfile, msgfile,
1615 const gchar *qmark = NULL;
1616 const gchar *body_fmt = prefs_common.fw_quotefmt;
1617 MsgInfo *full_msginfo;
1619 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1621 full_msginfo = procmsg_msginfo_copy(msginfo);
1623 /* use the forward format of folder (if enabled), or the account's one
1624 (if enabled) or fallback to the global forward format, which is always
1625 enabled (even if empty), and use the relevant quotemark */
1626 if (msginfo->folder && msginfo->folder->prefs &&
1627 msginfo->folder->prefs->forward_with_format) {
1628 qmark = msginfo->folder->prefs->forward_quotemark;
1629 body_fmt = msginfo->folder->prefs->forward_body_format;
1631 } else if (account->forward_with_format) {
1632 qmark = account->forward_quotemark;
1633 body_fmt = account->forward_body_format;
1636 qmark = prefs_common.fw_quotemark;
1637 body_fmt = prefs_common.fw_quotefmt;
1640 /* empty quotemark is not allowed */
1641 if (qmark == NULL || *qmark == '\0')
1644 compose_quote_fmt(compose, full_msginfo,
1645 body_fmt, qmark, body, FALSE, TRUE,
1646 _("Message forward format error at line %d."));
1647 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1648 quote_fmt_reset_vartable();
1649 compose_attach_parts(compose, msginfo);
1651 procmsg_msginfo_free(full_msginfo);
1654 SIGNAL_BLOCK(textbuf);
1656 if (account->auto_sig)
1657 compose_insert_sig(compose, FALSE);
1659 compose_wrap_all(compose);
1661 SIGNAL_UNBLOCK(textbuf);
1663 gtk_text_buffer_get_start_iter(textbuf, &iter);
1664 gtk_text_buffer_place_cursor(textbuf, &iter);
1666 gtk_widget_grab_focus(compose->header_last->entry);
1668 if (!no_extedit && prefs_common.auto_exteditor)
1669 compose_exec_ext_editor(compose);
1672 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1673 gchar *folderidentifier;
1675 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1676 folderidentifier = folder_item_get_identifier(msginfo->folder);
1677 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1678 g_free(folderidentifier);
1681 undo_unblock(compose->undostruct);
1683 compose->modified = FALSE;
1684 compose_set_title(compose);
1686 compose->updating = FALSE;
1687 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1688 SCROLL_TO_CURSOR(compose);
1690 if (compose->deferred_destroy) {
1691 compose_destroy(compose);
1698 #undef INSERT_FW_HEADER
1700 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1703 GtkTextView *textview;
1704 GtkTextBuffer *textbuf;
1708 gboolean single_mail = TRUE;
1710 g_return_val_if_fail(msginfo_list != NULL, NULL);
1712 if (g_slist_length(msginfo_list) > 1)
1713 single_mail = FALSE;
1715 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1716 if (((MsgInfo *)msginfo->data)->folder == NULL)
1719 /* guess account from first selected message */
1721 !(account = compose_guess_forward_account_from_msginfo
1722 (msginfo_list->data)))
1723 account = cur_account;
1725 g_return_val_if_fail(account != NULL, NULL);
1727 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1728 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1729 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1732 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1734 compose->updating = TRUE;
1736 textview = GTK_TEXT_VIEW(compose->text);
1737 textbuf = gtk_text_view_get_buffer(textview);
1738 compose_create_tags(textview, compose);
1740 undo_block(compose->undostruct);
1741 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1742 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1744 if (!is_file_exist(msgfile))
1745 g_warning("%s: file not exist\n", msgfile);
1747 compose_attach_append(compose, msgfile, msgfile,
1753 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1754 if (info->subject && *info->subject) {
1755 gchar *buf, *buf2, *p;
1757 buf = p = g_strdup(info->subject);
1758 p += subject_get_prefix_length(p);
1759 memmove(buf, p, strlen(p) + 1);
1761 buf2 = g_strdup_printf("Fw: %s", buf);
1762 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1768 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1769 _("Fw: multiple emails"));
1772 SIGNAL_BLOCK(textbuf);
1774 if (account->auto_sig)
1775 compose_insert_sig(compose, FALSE);
1777 compose_wrap_all(compose);
1779 SIGNAL_UNBLOCK(textbuf);
1781 gtk_text_buffer_get_start_iter(textbuf, &iter);
1782 gtk_text_buffer_place_cursor(textbuf, &iter);
1784 gtk_widget_grab_focus(compose->header_last->entry);
1785 undo_unblock(compose->undostruct);
1786 compose->modified = FALSE;
1787 compose_set_title(compose);
1789 compose->updating = FALSE;
1790 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1791 SCROLL_TO_CURSOR(compose);
1793 if (compose->deferred_destroy) {
1794 compose_destroy(compose);
1801 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1803 GtkTextIter start = *iter;
1804 GtkTextIter end_iter;
1805 int start_pos = gtk_text_iter_get_offset(&start);
1807 if (!compose->account->sig_sep)
1810 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1811 start_pos+strlen(compose->account->sig_sep));
1813 /* check sig separator */
1814 str = gtk_text_iter_get_text(&start, &end_iter);
1815 if (!strcmp(str, compose->account->sig_sep)) {
1817 /* check end of line (\n) */
1818 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1819 start_pos+strlen(compose->account->sig_sep));
1820 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1821 start_pos+strlen(compose->account->sig_sep)+1);
1822 tmp = gtk_text_iter_get_text(&start, &end_iter);
1823 if (!strcmp(tmp,"\n")) {
1835 static void compose_colorize_signature(Compose *compose)
1837 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1839 GtkTextIter end_iter;
1840 gtk_text_buffer_get_start_iter(buffer, &iter);
1841 while (gtk_text_iter_forward_line(&iter))
1842 if (compose_is_sig_separator(compose, buffer, &iter)) {
1843 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1844 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1848 #define BLOCK_WRAP() { \
1849 prev_autowrap = compose->autowrap; \
1850 buffer = gtk_text_view_get_buffer( \
1851 GTK_TEXT_VIEW(compose->text)); \
1852 compose->autowrap = FALSE; \
1854 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1855 G_CALLBACK(compose_changed_cb), \
1857 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1858 G_CALLBACK(text_inserted), \
1861 #define UNBLOCK_WRAP() { \
1862 compose->autowrap = prev_autowrap; \
1863 if (compose->autowrap) { \
1864 gint old = compose->draft_timeout_tag; \
1865 compose->draft_timeout_tag = -2; \
1866 compose_wrap_all(compose); \
1867 compose->draft_timeout_tag = old; \
1870 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1871 G_CALLBACK(compose_changed_cb), \
1873 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1874 G_CALLBACK(text_inserted), \
1878 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1880 Compose *compose = NULL;
1881 PrefsAccount *account = NULL;
1882 GtkTextView *textview;
1883 GtkTextBuffer *textbuf;
1887 gchar buf[BUFFSIZE];
1888 gboolean use_signing = FALSE;
1889 gboolean use_encryption = FALSE;
1890 gchar *privacy_system = NULL;
1891 int priority = PRIORITY_NORMAL;
1892 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1894 g_return_val_if_fail(msginfo != NULL, NULL);
1895 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1897 if (compose_put_existing_to_front(msginfo)) {
1901 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1902 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1903 gchar queueheader_buf[BUFFSIZE];
1906 /* Select Account from queue headers */
1907 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1908 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1909 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1910 account = account_find_from_id(id);
1912 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1913 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1914 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1915 account = account_find_from_id(id);
1917 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1918 sizeof(queueheader_buf), "NAID:")) {
1919 id = atoi(&queueheader_buf[strlen("NAID:")]);
1920 account = account_find_from_id(id);
1922 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1923 sizeof(queueheader_buf), "MAID:")) {
1924 id = atoi(&queueheader_buf[strlen("MAID:")]);
1925 account = account_find_from_id(id);
1927 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1928 sizeof(queueheader_buf), "S:")) {
1929 account = account_find_from_address(queueheader_buf);
1931 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1932 sizeof(queueheader_buf), "X-Claws-Sign:")) {
1933 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1934 use_signing = param;
1937 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1938 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1939 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1940 use_signing = param;
1943 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1944 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1945 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1946 use_encryption = param;
1948 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1949 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1950 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1951 use_encryption = param;
1953 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1954 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1955 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1957 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1958 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1959 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1961 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1962 sizeof(queueheader_buf), "X-Priority: ")) {
1963 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1966 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1967 sizeof(queueheader_buf), "RMID:")) {
1968 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1969 if (tokens[0] && tokens[1] && tokens[2]) {
1970 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1971 if (orig_item != NULL) {
1972 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1977 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1978 sizeof(queueheader_buf), "FMID:")) {
1979 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1980 if (tokens[0] && tokens[1] && tokens[2]) {
1981 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1982 if (orig_item != NULL) {
1983 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1989 account = msginfo->folder->folder->account;
1992 if (!account && prefs_common.reedit_account_autosel) {
1993 gchar from[BUFFSIZE];
1994 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1995 extract_address(from);
1996 account = account_find_from_address(from);
2000 account = cur_account;
2002 g_return_val_if_fail(account != NULL, NULL);
2004 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2006 compose->replyinfo = replyinfo;
2007 compose->fwdinfo = fwdinfo;
2009 compose->updating = TRUE;
2010 compose->priority = priority;
2012 if (privacy_system != NULL) {
2013 compose->privacy_system = privacy_system;
2014 compose_use_signing(compose, use_signing);
2015 compose_use_encryption(compose, use_encryption);
2016 compose_update_privacy_system_menu_item(compose, FALSE);
2018 activate_privacy_system(compose, account, FALSE);
2021 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2023 compose_extract_original_charset(compose);
2025 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2026 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2027 gchar queueheader_buf[BUFFSIZE];
2029 /* Set message save folder */
2030 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2033 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2034 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2035 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2037 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2038 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2040 GtkItemFactory *ifactory;
2041 ifactory = gtk_item_factory_from_widget(compose->menubar);
2042 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2047 if (compose_parse_header(compose, msginfo) < 0) {
2048 compose->updating = FALSE;
2049 compose_destroy(compose);
2052 compose_reedit_set_entry(compose, msginfo);
2054 textview = GTK_TEXT_VIEW(compose->text);
2055 textbuf = gtk_text_view_get_buffer(textview);
2056 compose_create_tags(textview, compose);
2058 mark = gtk_text_buffer_get_insert(textbuf);
2059 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2061 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2062 G_CALLBACK(compose_changed_cb),
2065 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2066 fp = procmime_get_first_encrypted_text_content(msginfo);
2068 compose_force_encryption(compose, account, TRUE);
2071 fp = procmime_get_first_text_content(msginfo);
2074 g_warning("Can't get text part\n");
2078 gboolean prev_autowrap = compose->autowrap;
2079 GtkTextBuffer *buffer = textbuf;
2081 while (fgets(buf, sizeof(buf), fp) != NULL) {
2083 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2089 compose_attach_parts(compose, msginfo);
2091 compose_colorize_signature(compose);
2093 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2094 G_CALLBACK(compose_changed_cb),
2097 gtk_widget_grab_focus(compose->text);
2099 if (prefs_common.auto_exteditor) {
2100 compose_exec_ext_editor(compose);
2102 compose->modified = FALSE;
2103 compose_set_title(compose);
2105 compose->updating = FALSE;
2106 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2107 SCROLL_TO_CURSOR(compose);
2109 if (compose->deferred_destroy) {
2110 compose_destroy(compose);
2114 compose->sig_str = compose_get_signature_str(compose);
2119 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2124 GtkItemFactory *ifactory;
2127 g_return_val_if_fail(msginfo != NULL, NULL);
2130 account = account_get_reply_account(msginfo,
2131 prefs_common.reply_account_autosel);
2132 g_return_val_if_fail(account != NULL, NULL);
2134 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2136 compose->updating = TRUE;
2138 ifactory = gtk_item_factory_from_widget(compose->menubar);
2139 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2140 compose->replyinfo = NULL;
2141 compose->fwdinfo = NULL;
2143 compose_show_first_last_header(compose, TRUE);
2145 gtk_widget_grab_focus(compose->header_last->entry);
2147 filename = procmsg_get_message_file(msginfo);
2149 if (filename == NULL) {
2150 compose->updating = FALSE;
2151 compose_destroy(compose);
2156 compose->redirect_filename = filename;
2158 /* Set save folder */
2159 item = msginfo->folder;
2160 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2161 gchar *folderidentifier;
2163 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2164 folderidentifier = folder_item_get_identifier(item);
2165 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2166 g_free(folderidentifier);
2169 compose_attach_parts(compose, msginfo);
2171 if (msginfo->subject)
2172 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2174 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2176 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2177 _("Message redirect format error at line %d."));
2178 quote_fmt_reset_vartable();
2179 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2181 compose_colorize_signature(compose);
2183 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2184 menu_set_sensitive(ifactory, "/Add...", FALSE);
2185 menu_set_sensitive(ifactory, "/Remove", FALSE);
2186 menu_set_sensitive(ifactory, "/Properties...", FALSE);
2188 ifactory = gtk_item_factory_from_widget(compose->menubar);
2189 menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2190 menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2191 menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2192 menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2193 menu_set_sensitive(ifactory, "/Edit", FALSE);
2194 menu_set_sensitive(ifactory, "/Options", FALSE);
2195 menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2196 menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2198 if (compose->toolbar->draft_btn)
2199 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2200 if (compose->toolbar->insert_btn)
2201 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2202 if (compose->toolbar->attach_btn)
2203 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2204 if (compose->toolbar->sig_btn)
2205 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2206 if (compose->toolbar->exteditor_btn)
2207 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2208 if (compose->toolbar->linewrap_current_btn)
2209 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2210 if (compose->toolbar->linewrap_all_btn)
2211 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2213 compose->modified = FALSE;
2214 compose_set_title(compose);
2215 compose->updating = FALSE;
2216 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2217 SCROLL_TO_CURSOR(compose);
2219 if (compose->deferred_destroy) {
2220 compose_destroy(compose);
2227 GList *compose_get_compose_list(void)
2229 return compose_list;
2232 void compose_entry_append(Compose *compose, const gchar *address,
2233 ComposeEntryType type)
2235 const gchar *header;
2237 gboolean in_quote = FALSE;
2238 if (!address || *address == '\0') return;
2245 header = N_("Bcc:");
2247 case COMPOSE_REPLYTO:
2248 header = N_("Reply-To:");
2250 case COMPOSE_NEWSGROUPS:
2251 header = N_("Newsgroups:");
2253 case COMPOSE_FOLLOWUPTO:
2254 header = N_( "Followup-To:");
2261 header = prefs_common_translated_header_name(header);
2263 cur = begin = (gchar *)address;
2265 /* we separate the line by commas, but not if we're inside a quoted
2267 while (*cur != '\0') {
2269 in_quote = !in_quote;
2270 if (*cur == ',' && !in_quote) {
2271 gchar *tmp = g_strdup(begin);
2273 tmp[cur-begin]='\0';
2276 while (*tmp == ' ' || *tmp == '\t')
2278 compose_add_header_entry(compose, header, tmp);
2285 gchar *tmp = g_strdup(begin);
2287 tmp[cur-begin]='\0';
2290 while (*tmp == ' ' || *tmp == '\t')
2292 compose_add_header_entry(compose, header, tmp);
2297 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2299 static GdkColor yellow;
2300 static GdkColor black;
2301 static gboolean yellow_initialised = FALSE;
2305 if (!yellow_initialised) {
2306 gdk_color_parse("#f5f6be", &yellow);
2307 gdk_color_parse("#000000", &black);
2308 yellow_initialised = gdk_colormap_alloc_color(
2309 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2310 yellow_initialised &= gdk_colormap_alloc_color(
2311 gdk_colormap_get_system(), &black, FALSE, TRUE);
2314 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2315 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2316 if (gtk_entry_get_text(entry) &&
2317 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2318 if (yellow_initialised) {
2319 gtk_widget_modify_base(
2320 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2321 GTK_STATE_NORMAL, &yellow);
2322 gtk_widget_modify_text(
2323 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2324 GTK_STATE_NORMAL, &black);
2330 void compose_toolbar_cb(gint action, gpointer data)
2332 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2333 Compose *compose = (Compose*)toolbar_item->parent;
2335 g_return_if_fail(compose != NULL);
2339 compose_send_cb(compose, 0, NULL);
2342 compose_send_later_cb(compose, 0, NULL);
2345 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2348 compose_insert_file_cb(compose, 0, NULL);
2351 compose_attach_cb(compose, 0, NULL);
2354 compose_insert_sig(compose, FALSE);
2357 compose_ext_editor_cb(compose, 0, NULL);
2359 case A_LINEWRAP_CURRENT:
2360 compose_beautify_paragraph(compose, NULL, TRUE);
2362 case A_LINEWRAP_ALL:
2363 compose_wrap_all_full(compose, TRUE);
2366 compose_address_cb(compose, 0, NULL);
2369 case A_CHECK_SPELLING:
2370 compose_check_all(compose);
2378 static void compose_entries_set(Compose *compose, const gchar *mailto)
2383 gchar *subject = NULL;
2387 gchar **attach = NULL;
2389 scan_mailto_url(mailto, &to, &cc, &bcc, &subject, &body, &attach);
2392 compose_entry_append(compose, to, COMPOSE_TO);
2394 compose_entry_append(compose, cc, COMPOSE_CC);
2396 compose_entry_append(compose, bcc, COMPOSE_BCC);
2398 if (!g_utf8_validate (subject, -1, NULL)) {
2399 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2400 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2403 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2407 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2408 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2411 gboolean prev_autowrap = compose->autowrap;
2413 compose->autowrap = FALSE;
2415 mark = gtk_text_buffer_get_insert(buffer);
2416 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2418 if (!g_utf8_validate (body, -1, NULL)) {
2419 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2420 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2423 gtk_text_buffer_insert(buffer, &iter, body, -1);
2425 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2427 compose->autowrap = prev_autowrap;
2428 if (compose->autowrap)
2429 compose_wrap_all(compose);
2434 while (attach[i] != NULL) {
2435 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2436 if (utf8_filename) {
2437 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2438 alertpanel_notice(_("The file '%s' has been attached."), utf8_filename);
2440 g_free(utf8_filename);
2442 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2455 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2457 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2458 {"Cc:", NULL, TRUE},
2459 {"References:", NULL, FALSE},
2460 {"Bcc:", NULL, TRUE},
2461 {"Newsgroups:", NULL, TRUE},
2462 {"Followup-To:", NULL, TRUE},
2463 {"List-Post:", NULL, FALSE},
2464 {"X-Priority:", NULL, FALSE},
2465 {NULL, NULL, FALSE}};
2481 g_return_val_if_fail(msginfo != NULL, -1);
2483 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2484 procheader_get_header_fields(fp, hentry);
2487 if (hentry[H_REPLY_TO].body != NULL) {
2488 if (hentry[H_REPLY_TO].body[0] != '\0') {
2490 conv_unmime_header(hentry[H_REPLY_TO].body,
2493 g_free(hentry[H_REPLY_TO].body);
2494 hentry[H_REPLY_TO].body = NULL;
2496 if (hentry[H_CC].body != NULL) {
2497 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2498 g_free(hentry[H_CC].body);
2499 hentry[H_CC].body = NULL;
2501 if (hentry[H_REFERENCES].body != NULL) {
2502 if (compose->mode == COMPOSE_REEDIT)
2503 compose->references = hentry[H_REFERENCES].body;
2505 compose->references = compose_parse_references
2506 (hentry[H_REFERENCES].body, msginfo->msgid);
2507 g_free(hentry[H_REFERENCES].body);
2509 hentry[H_REFERENCES].body = NULL;
2511 if (hentry[H_BCC].body != NULL) {
2512 if (compose->mode == COMPOSE_REEDIT)
2514 conv_unmime_header(hentry[H_BCC].body, NULL);
2515 g_free(hentry[H_BCC].body);
2516 hentry[H_BCC].body = NULL;
2518 if (hentry[H_NEWSGROUPS].body != NULL) {
2519 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2520 hentry[H_NEWSGROUPS].body = NULL;
2522 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2523 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2524 compose->followup_to =
2525 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2528 g_free(hentry[H_FOLLOWUP_TO].body);
2529 hentry[H_FOLLOWUP_TO].body = NULL;
2531 if (hentry[H_LIST_POST].body != NULL) {
2534 extract_address(hentry[H_LIST_POST].body);
2535 if (hentry[H_LIST_POST].body[0] != '\0') {
2536 scan_mailto_url(hentry[H_LIST_POST].body,
2537 &to, NULL, NULL, NULL, NULL, NULL);
2539 g_free(compose->ml_post);
2540 compose->ml_post = to;
2543 g_free(hentry[H_LIST_POST].body);
2544 hentry[H_LIST_POST].body = NULL;
2547 /* CLAWS - X-Priority */
2548 if (compose->mode == COMPOSE_REEDIT)
2549 if (hentry[H_X_PRIORITY].body != NULL) {
2552 priority = atoi(hentry[H_X_PRIORITY].body);
2553 g_free(hentry[H_X_PRIORITY].body);
2555 hentry[H_X_PRIORITY].body = NULL;
2557 if (priority < PRIORITY_HIGHEST ||
2558 priority > PRIORITY_LOWEST)
2559 priority = PRIORITY_NORMAL;
2561 compose->priority = priority;
2564 if (compose->mode == COMPOSE_REEDIT) {
2565 if (msginfo->inreplyto && *msginfo->inreplyto)
2566 compose->inreplyto = g_strdup(msginfo->inreplyto);
2570 if (msginfo->msgid && *msginfo->msgid)
2571 compose->inreplyto = g_strdup(msginfo->msgid);
2573 if (!compose->references) {
2574 if (msginfo->msgid && *msginfo->msgid) {
2575 if (msginfo->inreplyto && *msginfo->inreplyto)
2576 compose->references =
2577 g_strdup_printf("<%s>\n\t<%s>",
2581 compose->references =
2582 g_strconcat("<", msginfo->msgid, ">",
2584 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2585 compose->references =
2586 g_strconcat("<", msginfo->inreplyto, ">",
2594 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2596 GSList *ref_id_list, *cur;
2600 ref_id_list = references_list_append(NULL, ref);
2601 if (!ref_id_list) return NULL;
2602 if (msgid && *msgid)
2603 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2608 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2609 /* "<" + Message-ID + ">" + CR+LF+TAB */
2610 len += strlen((gchar *)cur->data) + 5;
2612 if (len > MAX_REFERENCES_LEN) {
2613 /* remove second message-ID */
2614 if (ref_id_list && ref_id_list->next &&
2615 ref_id_list->next->next) {
2616 g_free(ref_id_list->next->data);
2617 ref_id_list = g_slist_remove
2618 (ref_id_list, ref_id_list->next->data);
2620 slist_free_strings(ref_id_list);
2621 g_slist_free(ref_id_list);
2628 new_ref = g_string_new("");
2629 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2630 if (new_ref->len > 0)
2631 g_string_append(new_ref, "\n\t");
2632 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2635 slist_free_strings(ref_id_list);
2636 g_slist_free(ref_id_list);
2638 new_ref_str = new_ref->str;
2639 g_string_free(new_ref, FALSE);
2644 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2645 const gchar *fmt, const gchar *qmark,
2646 const gchar *body, gboolean rewrap,
2647 gboolean need_unescape,
2648 const gchar *err_msg)
2650 MsgInfo* dummyinfo = NULL;
2651 gchar *quote_str = NULL;
2653 gboolean prev_autowrap;
2654 const gchar *trimmed_body = body;
2655 gint cursor_pos = -1;
2656 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2657 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2662 SIGNAL_BLOCK(buffer);
2665 dummyinfo = compose_msginfo_new_from_compose(compose);
2666 msginfo = dummyinfo;
2669 if (qmark != NULL) {
2671 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2672 compose->gtkaspell);
2674 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2676 quote_fmt_scan_string(qmark);
2679 buf = quote_fmt_get_buffer();
2681 alertpanel_error(_("Quote mark format error."));
2683 Xstrdup_a(quote_str, buf, goto error)
2686 if (fmt && *fmt != '\0') {
2689 while (*trimmed_body == '\n')
2693 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2694 compose->gtkaspell);
2696 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2698 if (need_unescape) {
2701 /* decode \-escape sequences in the internal representation of the quote format */
2702 tmp = malloc(strlen(fmt)+1);
2703 pref_get_unescaped_pref(tmp, fmt);
2704 quote_fmt_scan_string(tmp);
2708 quote_fmt_scan_string(fmt);
2712 buf = quote_fmt_get_buffer();
2714 gint line = quote_fmt_get_line();
2715 alertpanel_error(err_msg, line);
2721 prev_autowrap = compose->autowrap;
2722 compose->autowrap = FALSE;
2724 mark = gtk_text_buffer_get_insert(buffer);
2725 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2726 if (g_utf8_validate(buf, -1, NULL)) {
2727 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2729 gchar *tmpout = NULL;
2730 tmpout = conv_codeset_strdup
2731 (buf, conv_get_locale_charset_str_no_utf8(),
2733 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2735 tmpout = g_malloc(strlen(buf)*2+1);
2736 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2738 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2742 cursor_pos = quote_fmt_get_cursor_pos();
2743 compose->set_cursor_pos = cursor_pos;
2744 if (cursor_pos == -1) {
2747 gtk_text_buffer_get_start_iter(buffer, &iter);
2748 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2749 gtk_text_buffer_place_cursor(buffer, &iter);
2751 compose->autowrap = prev_autowrap;
2752 if (compose->autowrap && rewrap)
2753 compose_wrap_all(compose);
2760 SIGNAL_UNBLOCK(buffer);
2762 procmsg_msginfo_free( dummyinfo );
2767 /* if ml_post is of type addr@host and from is of type
2768 * addr-anything@host, return TRUE
2770 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2772 gchar *left_ml = NULL;
2773 gchar *right_ml = NULL;
2774 gchar *left_from = NULL;
2775 gchar *right_from = NULL;
2776 gboolean result = FALSE;
2778 if (!ml_post || !from)
2781 left_ml = g_strdup(ml_post);
2782 if (strstr(left_ml, "@")) {
2783 right_ml = strstr(left_ml, "@")+1;
2784 *(strstr(left_ml, "@")) = '\0';
2787 left_from = g_strdup(from);
2788 if (strstr(left_from, "@")) {
2789 right_from = strstr(left_from, "@")+1;
2790 *(strstr(left_from, "@")) = '\0';
2793 if (left_ml && left_from && right_ml && right_from
2794 && !strncmp(left_from, left_ml, strlen(left_ml))
2795 && !strcmp(right_from, right_ml)) {
2804 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2806 gchar *my_addr1, *my_addr2;
2808 if (!addr1 || !addr2)
2811 Xstrdup_a(my_addr1, addr1, return FALSE);
2812 Xstrdup_a(my_addr2, addr2, return FALSE);
2814 extract_address(my_addr1);
2815 extract_address(my_addr2);
2817 return !strcasecmp(my_addr1, my_addr2);
2820 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2821 gboolean to_all, gboolean to_ml,
2823 gboolean followup_and_reply_to)
2825 GSList *cc_list = NULL;
2828 gchar *replyto = NULL;
2829 GHashTable *to_table;
2831 gboolean reply_to_ml = FALSE;
2832 gboolean default_reply_to = FALSE;
2834 g_return_if_fail(compose->account != NULL);
2835 g_return_if_fail(msginfo != NULL);
2837 reply_to_ml = to_ml && compose->ml_post;
2839 default_reply_to = msginfo->folder &&
2840 msginfo->folder->prefs->enable_default_reply_to;
2842 if (compose->account->protocol != A_NNTP) {
2843 if (reply_to_ml && !default_reply_to) {
2845 gboolean is_subscr = is_subscription(compose->ml_post,
2848 /* normal answer to ml post with a reply-to */
2849 compose_entry_append(compose,
2852 if (compose->replyto
2853 && !same_address(compose->ml_post, compose->replyto))
2854 compose_entry_append(compose,
2858 /* answer to subscription confirmation */
2859 if (compose->replyto)
2860 compose_entry_append(compose,
2863 else if (msginfo->from)
2864 compose_entry_append(compose,
2869 else if (!(to_all || to_sender) && default_reply_to) {
2870 compose_entry_append(compose,
2871 msginfo->folder->prefs->default_reply_to,
2873 compose_entry_mark_default_to(compose,
2874 msginfo->folder->prefs->default_reply_to);
2879 Xstrdup_a(tmp1, msginfo->from, return);
2880 extract_address(tmp1);
2881 if (to_all || to_sender ||
2882 !account_find_from_address(tmp1))
2883 compose_entry_append(compose,
2884 (compose->replyto && !to_sender)
2885 ? compose->replyto :
2886 msginfo->from ? msginfo->from : "",
2888 else if (!to_all && !to_sender) {
2889 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2890 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2891 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2892 compose_entry_append(compose,
2893 msginfo->from ? msginfo->from : "",
2896 /* replying to own mail, use original recp */
2897 compose_entry_append(compose,
2898 msginfo->to ? msginfo->to : "",
2900 compose_entry_append(compose,
2901 msginfo->cc ? msginfo->cc : "",
2907 if (to_sender || (compose->followup_to &&
2908 !strncmp(compose->followup_to, "poster", 6)))
2909 compose_entry_append
2911 (compose->replyto ? compose->replyto :
2912 msginfo->from ? msginfo->from : ""),
2915 else if (followup_and_reply_to || to_all) {
2916 compose_entry_append
2918 (compose->replyto ? compose->replyto :
2919 msginfo->from ? msginfo->from : ""),
2922 compose_entry_append
2924 compose->followup_to ? compose->followup_to :
2925 compose->newsgroups ? compose->newsgroups : "",
2926 COMPOSE_NEWSGROUPS);
2929 compose_entry_append
2931 compose->followup_to ? compose->followup_to :
2932 compose->newsgroups ? compose->newsgroups : "",
2933 COMPOSE_NEWSGROUPS);
2936 if (msginfo->subject && *msginfo->subject) {
2940 buf = p = g_strdup(msginfo->subject);
2941 p += subject_get_prefix_length(p);
2942 memmove(buf, p, strlen(p) + 1);
2944 buf2 = g_strdup_printf("Re: %s", buf);
2945 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2950 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2952 if (to_ml && compose->ml_post) return;
2953 if (!to_all || compose->account->protocol == A_NNTP) return;
2955 if (compose->replyto) {
2956 Xstrdup_a(replyto, compose->replyto, return);
2957 extract_address(replyto);
2959 if (msginfo->from) {
2960 Xstrdup_a(from, msginfo->from, return);
2961 extract_address(from);
2964 if (replyto && from)
2965 cc_list = address_list_append_with_comments(cc_list, from);
2966 if (to_all && msginfo->folder &&
2967 msginfo->folder->prefs->enable_default_reply_to)
2968 cc_list = address_list_append_with_comments(cc_list,
2969 msginfo->folder->prefs->default_reply_to);
2970 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2971 cc_list = address_list_append_with_comments(cc_list, compose->cc);
2973 to_table = g_hash_table_new(g_str_hash, g_str_equal);
2975 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2976 if (compose->account) {
2977 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2978 GINT_TO_POINTER(1));
2980 /* remove address on To: and that of current account */
2981 for (cur = cc_list; cur != NULL; ) {
2982 GSList *next = cur->next;
2985 addr = g_utf8_strdown(cur->data, -1);
2986 extract_address(addr);
2988 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
2989 cc_list = g_slist_remove(cc_list, cur->data);
2991 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
2995 hash_free_strings(to_table);
2996 g_hash_table_destroy(to_table);
2999 for (cur = cc_list; cur != NULL; cur = cur->next)
3000 compose_entry_append(compose, (gchar *)cur->data,
3002 slist_free_strings(cc_list);
3003 g_slist_free(cc_list);
3008 #define SET_ENTRY(entry, str) \
3011 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3014 #define SET_ADDRESS(type, str) \
3017 compose_entry_append(compose, str, type); \
3020 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3022 g_return_if_fail(msginfo != NULL);
3024 SET_ENTRY(subject_entry, msginfo->subject);
3025 SET_ENTRY(from_name, msginfo->from);
3026 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3027 SET_ADDRESS(COMPOSE_CC, compose->cc);
3028 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3029 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3030 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3031 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3033 compose_update_priority_menu_item(compose);
3034 compose_update_privacy_system_menu_item(compose, FALSE);
3035 compose_show_first_last_header(compose, TRUE);
3041 static void compose_insert_sig(Compose *compose, gboolean replace)
3043 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3044 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3046 GtkTextIter iter, iter_end;
3048 gboolean prev_autowrap;
3049 gboolean found = FALSE;
3050 gboolean exists = FALSE;
3052 g_return_if_fail(compose->account != NULL);
3056 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3057 G_CALLBACK(compose_changed_cb),
3060 mark = gtk_text_buffer_get_insert(buffer);
3061 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3062 cur_pos = gtk_text_iter_get_offset (&iter);
3064 gtk_text_buffer_get_end_iter(buffer, &iter);
3066 exists = (compose->sig_str != NULL);
3069 GtkTextIter first_iter, start_iter, end_iter;
3071 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3073 if (!exists || compose->sig_str[0] == '\0')
3076 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3077 compose->signature_tag);
3080 /* include previous \n\n */
3081 gtk_text_iter_backward_chars(&first_iter, 2);
3082 start_iter = first_iter;
3083 end_iter = first_iter;
3085 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3086 compose->signature_tag);
3087 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3088 compose->signature_tag);
3090 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3096 g_free(compose->sig_str);
3097 compose->sig_str = compose_get_signature_str(compose);
3099 cur_pos = gtk_text_iter_get_offset(&iter);
3101 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3102 g_free(compose->sig_str);
3103 compose->sig_str = NULL;
3105 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3107 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3108 gtk_text_iter_forward_chars(&iter, 2);
3109 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3110 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3112 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3113 cur_pos = gtk_text_buffer_get_char_count (buffer);
3115 /* put the cursor where it should be
3116 * either where the quote_fmt says, either before the signature */
3117 if (compose->set_cursor_pos < 0)
3118 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3120 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3121 compose->set_cursor_pos);
3123 gtk_text_buffer_place_cursor(buffer, &iter);
3124 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3125 G_CALLBACK(compose_changed_cb),
3131 static gchar *compose_get_signature_str(Compose *compose)
3133 gchar *sig_body = NULL;
3134 gchar *sig_str = NULL;
3135 gchar *utf8_sig_str = NULL;
3137 g_return_val_if_fail(compose->account != NULL, NULL);
3139 if (!compose->account->sig_path)
3142 if (compose->account->sig_type == SIG_FILE) {
3143 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3144 g_warning("can't open signature file: %s\n",
3145 compose->account->sig_path);
3150 if (compose->account->sig_type == SIG_COMMAND)
3151 sig_body = get_command_output(compose->account->sig_path);
3155 tmp = file_read_to_str(compose->account->sig_path);
3158 sig_body = normalize_newlines(tmp);
3162 if (compose->account->sig_sep) {
3163 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3167 sig_str = g_strconcat("\n\n", sig_body, NULL);
3170 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3171 utf8_sig_str = sig_str;
3173 utf8_sig_str = conv_codeset_strdup
3174 (sig_str, conv_get_locale_charset_str_no_utf8(),