2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2015 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
87 #include "quoted-printable.h"
91 #include "gtkshruler.h"
93 #include "alertpanel.h"
94 #include "manage_window.h"
96 #include "folder_item_prefs.h"
97 #include "addr_compl.h"
98 #include "quote_fmt.h"
100 #include "foldersel.h"
103 #include "message_search.h"
104 #include "combobox.h"
108 #include "autofaces.h"
109 #include "spell_entry.h"
122 #define N_ATTACH_COLS (N_COL_COLUMNS)
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
140 } ComposeCallAdvancedAction;
144 PRIORITY_HIGHEST = 1,
153 COMPOSE_INSERT_SUCCESS,
154 COMPOSE_INSERT_READ_ERROR,
155 COMPOSE_INSERT_INVALID_CHARACTER,
156 COMPOSE_INSERT_NO_FILE
157 } ComposeInsertResult;
161 COMPOSE_WRITE_FOR_SEND,
162 COMPOSE_WRITE_FOR_STORE
167 COMPOSE_QUOTE_FORCED,
174 SUBJECT_FIELD_PRESENT,
179 #define B64_LINE_SIZE 57
180 #define B64_BUFFSIZE 77
182 #define MAX_REFERENCES_LEN 999
184 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
185 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
187 static GList *compose_list = NULL;
188 static GSList *extra_headers = NULL;
190 static Compose *compose_generic_new (PrefsAccount *account,
194 GList *listAddress );
196 static Compose *compose_create (PrefsAccount *account,
201 static void compose_entry_mark_default_to (Compose *compose,
202 const gchar *address);
203 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
204 ComposeQuoteMode quote_mode,
208 static Compose *compose_forward_multiple (PrefsAccount *account,
209 GSList *msginfo_list);
210 static Compose *compose_reply (MsgInfo *msginfo,
211 ComposeQuoteMode quote_mode,
216 static Compose *compose_reply_mode (ComposeMode mode,
217 GSList *msginfo_list,
219 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
220 static void compose_update_privacy_systems_menu(Compose *compose);
222 static GtkWidget *compose_account_option_menu_create
224 static void compose_set_out_encoding (Compose *compose);
225 static void compose_set_template_menu (Compose *compose);
226 static void compose_destroy (Compose *compose);
228 static MailField compose_entries_set (Compose *compose,
230 ComposeEntryType to_type);
231 static gint compose_parse_header (Compose *compose,
233 static gint compose_parse_manual_headers (Compose *compose,
235 HeaderEntry *entries);
236 static gchar *compose_parse_references (const gchar *ref,
239 static gchar *compose_quote_fmt (Compose *compose,
245 gboolean need_unescape,
246 const gchar *err_msg);
248 static void compose_reply_set_entry (Compose *compose,
254 followup_and_reply_to);
255 static void compose_reedit_set_entry (Compose *compose,
258 static void compose_insert_sig (Compose *compose,
260 static ComposeInsertResult compose_insert_file (Compose *compose,
263 static gboolean compose_attach_append (Compose *compose,
266 const gchar *content_type,
267 const gchar *charset);
268 static void compose_attach_parts (Compose *compose,
271 static gboolean compose_beautify_paragraph (Compose *compose,
272 GtkTextIter *par_iter,
274 static void compose_wrap_all (Compose *compose);
275 static void compose_wrap_all_full (Compose *compose,
278 static void compose_set_title (Compose *compose);
279 static void compose_select_account (Compose *compose,
280 PrefsAccount *account,
283 static PrefsAccount *compose_current_mail_account(void);
284 /* static gint compose_send (Compose *compose); */
285 static gboolean compose_check_for_valid_recipient
287 static gboolean compose_check_entries (Compose *compose,
288 gboolean check_everything);
289 static gint compose_write_to_file (Compose *compose,
292 gboolean attach_parts);
293 static gint compose_write_body_to_file (Compose *compose,
295 static gint compose_remove_reedit_target (Compose *compose,
297 static void compose_remove_draft (Compose *compose);
298 static gint compose_queue_sub (Compose *compose,
302 gboolean check_subject,
303 gboolean remove_reedit_target);
304 static int compose_add_attachments (Compose *compose,
306 static gchar *compose_get_header (Compose *compose);
307 static gchar *compose_get_manual_headers_info (Compose *compose);
309 static void compose_convert_header (Compose *compose,
314 gboolean addr_field);
316 static void compose_attach_info_free (AttachInfo *ainfo);
317 static void compose_attach_remove_selected (GtkAction *action,
320 static void compose_template_apply (Compose *compose,
323 static void compose_attach_property (GtkAction *action,
325 static void compose_attach_property_create (gboolean *cancelled);
326 static void attach_property_ok (GtkWidget *widget,
327 gboolean *cancelled);
328 static void attach_property_cancel (GtkWidget *widget,
329 gboolean *cancelled);
330 static gint attach_property_delete_event (GtkWidget *widget,
332 gboolean *cancelled);
333 static gboolean attach_property_key_pressed (GtkWidget *widget,
335 gboolean *cancelled);
337 static void compose_exec_ext_editor (Compose *compose);
339 static gint compose_exec_ext_editor_real (const gchar *file);
340 static gboolean compose_ext_editor_kill (Compose *compose);
341 static gboolean compose_input_cb (GIOChannel *source,
342 GIOCondition condition,
344 static void compose_set_ext_editor_sensitive (Compose *compose,
346 #endif /* G_OS_UNIX */
348 static void compose_undo_state_changed (UndoMain *undostruct,
353 static void compose_create_header_entry (Compose *compose);
354 static void compose_add_header_entry (Compose *compose, const gchar *header,
355 gchar *text, ComposePrefType pref_type);
356 static void compose_remove_header_entries(Compose *compose);
358 static void compose_update_priority_menu_item(Compose * compose);
360 static void compose_spell_menu_changed (void *data);
361 static void compose_dict_changed (void *data);
363 static void compose_add_field_list ( Compose *compose,
364 GList *listAddress );
366 /* callback functions */
368 static void compose_notebook_size_alloc (GtkNotebook *notebook,
369 GtkAllocation *allocation,
371 static gboolean compose_edit_size_alloc (GtkEditable *widget,
372 GtkAllocation *allocation,
373 GtkSHRuler *shruler);
374 static void account_activated (GtkComboBox *optmenu,
376 static void attach_selected (GtkTreeView *tree_view,
377 GtkTreePath *tree_path,
378 GtkTreeViewColumn *column,
380 static gboolean attach_button_pressed (GtkWidget *widget,
381 GdkEventButton *event,
383 static gboolean attach_key_pressed (GtkWidget *widget,
386 static void compose_send_cb (GtkAction *action, gpointer data);
387 static void compose_send_later_cb (GtkAction *action, gpointer data);
389 static void compose_save_cb (GtkAction *action,
392 static void compose_attach_cb (GtkAction *action,
394 static void compose_insert_file_cb (GtkAction *action,
396 static void compose_insert_sig_cb (GtkAction *action,
398 static void compose_replace_sig_cb (GtkAction *action,
401 static void compose_close_cb (GtkAction *action,
403 static void compose_print_cb (GtkAction *action,
406 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
408 static void compose_address_cb (GtkAction *action,
410 static void about_show_cb (GtkAction *action,
412 static void compose_template_activate_cb(GtkWidget *widget,
415 static void compose_ext_editor_cb (GtkAction *action,
418 static gint compose_delete_cb (GtkWidget *widget,
422 static void compose_undo_cb (GtkAction *action,
424 static void compose_redo_cb (GtkAction *action,
426 static void compose_cut_cb (GtkAction *action,
428 static void compose_copy_cb (GtkAction *action,
430 static void compose_paste_cb (GtkAction *action,
432 static void compose_paste_as_quote_cb (GtkAction *action,
434 static void compose_paste_no_wrap_cb (GtkAction *action,
436 static void compose_paste_wrap_cb (GtkAction *action,
438 static void compose_allsel_cb (GtkAction *action,
441 static void compose_advanced_action_cb (GtkAction *action,
444 static void compose_grab_focus_cb (GtkWidget *widget,
447 static void compose_changed_cb (GtkTextBuffer *textbuf,
450 static void compose_wrap_cb (GtkAction *action,
452 static void compose_wrap_all_cb (GtkAction *action,
454 static void compose_find_cb (GtkAction *action,
456 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
458 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
461 static void compose_toggle_ruler_cb (GtkToggleAction *action,
463 static void compose_toggle_sign_cb (GtkToggleAction *action,
465 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
467 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
468 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
469 static void activate_privacy_system (Compose *compose,
470 PrefsAccount *account,
472 static void compose_use_signing(Compose *compose, gboolean use_signing);
473 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
474 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
476 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
478 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
479 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
480 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
482 static void compose_attach_drag_received_cb (GtkWidget *widget,
483 GdkDragContext *drag_context,
486 GtkSelectionData *data,
490 static void compose_insert_drag_received_cb (GtkWidget *widget,
491 GdkDragContext *drag_context,
494 GtkSelectionData *data,
498 static void compose_header_drag_received_cb (GtkWidget *widget,
499 GdkDragContext *drag_context,
502 GtkSelectionData *data,
507 static gboolean compose_drag_drop (GtkWidget *widget,
508 GdkDragContext *drag_context,
510 guint time, gpointer user_data);
511 static gboolean completion_set_focus_to_subject
516 static void text_inserted (GtkTextBuffer *buffer,
521 static Compose *compose_generic_reply(MsgInfo *msginfo,
522 ComposeQuoteMode quote_mode,
526 gboolean followup_and_reply_to,
529 static void compose_headerentry_changed_cb (GtkWidget *entry,
530 ComposeHeaderEntry *headerentry);
531 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
533 ComposeHeaderEntry *headerentry);
534 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
535 ComposeHeaderEntry *headerentry);
537 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
539 static void compose_allow_user_actions (Compose *compose, gboolean allow);
541 static void compose_nothing_cb (GtkAction *action, gpointer data)
547 static void compose_check_all (GtkAction *action, gpointer data);
548 static void compose_highlight_all (GtkAction *action, gpointer data);
549 static void compose_check_backwards (GtkAction *action, gpointer data);
550 static void compose_check_forwards_go (GtkAction *action, gpointer data);
553 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
555 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
558 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
559 FolderItem *folder_item);
561 static void compose_attach_update_label(Compose *compose);
562 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
563 gboolean respect_default_to);
564 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
565 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
567 static GtkActionEntry compose_popup_entries[] =
569 {"Compose", NULL, "Compose" },
570 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
571 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
572 {"Compose/---", NULL, "---", NULL, NULL, NULL },
573 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
576 static GtkActionEntry compose_entries[] =
578 {"Menu", NULL, "Menu" },
580 {"Message", NULL, N_("_Message") },
581 {"Edit", NULL, N_("_Edit") },
583 {"Spelling", NULL, N_("_Spelling") },
585 {"Options", NULL, N_("_Options") },
586 {"Tools", NULL, N_("_Tools") },
587 {"Help", NULL, N_("_Help") },
589 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
590 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
591 {"Message/---", NULL, "---" },
593 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
594 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
595 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
596 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
597 /* {"Message/---", NULL, "---" }, */
598 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
599 /* {"Message/---", NULL, "---" }, */
600 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
601 /* {"Message/---", NULL, "---" }, */
602 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
605 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
606 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
607 {"Edit/---", NULL, "---" },
609 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
610 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
611 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
613 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
614 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
615 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
616 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
618 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
620 {"Edit/Advanced", NULL, N_("A_dvanced") },
621 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
622 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
623 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
624 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
625 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
626 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
627 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
628 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
629 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
630 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
631 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
632 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
633 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
634 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
639 /* {"Edit/---", NULL, "---" }, */
640 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
641 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
642 /* {"Edit/---", NULL, "---" }, */
643 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
646 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
647 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
648 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
649 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
651 {"Spelling/---", NULL, "---" },
652 {"Spelling/Options", NULL, N_("_Options") },
657 {"Options/ReplyMode", NULL, N_("Reply _mode") },
658 {"Options/---", NULL, "---" },
659 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
660 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
662 /* {"Options/---", NULL, "---" }, */
664 {"Options/Priority", NULL, N_("_Priority") },
666 {"Options/Encoding", NULL, N_("Character _encoding") },
667 {"Options/Encoding/---", NULL, "---" },
668 #define ENC_ACTION(cs_char,c_char,string) \
669 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
671 {"Options/Encoding/Western", NULL, N_("Western European") },
672 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
673 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
674 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
675 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
676 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
677 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
678 {"Options/Encoding/Korean", NULL, N_("Korean") },
679 {"Options/Encoding/Thai", NULL, N_("Thai") },
682 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
684 {"Tools/Template", NULL, N_("_Template") },
685 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
686 {"Tools/Actions", NULL, N_("Actio_ns") },
687 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
690 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
693 static GtkToggleActionEntry compose_toggle_entries[] =
695 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
696 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
697 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
698 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
699 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
700 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
701 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
704 static GtkRadioActionEntry compose_radio_rm_entries[] =
706 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
707 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
708 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
709 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
712 static GtkRadioActionEntry compose_radio_prio_entries[] =
714 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
715 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
716 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
717 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
718 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
721 static GtkRadioActionEntry compose_radio_enc_entries[] =
723 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
758 static GtkTargetEntry compose_mime_types[] =
760 {"text/uri-list", 0, 0},
761 {"UTF8_STRING", 0, 0},
765 static gboolean compose_put_existing_to_front(MsgInfo *info)
767 const GList *compose_list = compose_get_compose_list();
768 const GList *elem = NULL;
771 for (elem = compose_list; elem != NULL && elem->data != NULL;
773 Compose *c = (Compose*)elem->data;
775 if (!c->targetinfo || !c->targetinfo->msgid ||
779 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
780 gtkut_window_popup(c->window);
788 static GdkColor quote_color1 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_color2 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_color3 =
793 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor quote_bgcolor1 =
796 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
797 static GdkColor quote_bgcolor2 =
798 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
799 static GdkColor quote_bgcolor3 =
800 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
802 static GdkColor signature_color = {
809 static GdkColor uri_color = {
816 static void compose_create_tags(GtkTextView *text, Compose *compose)
818 GtkTextBuffer *buffer;
819 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
820 #if !GTK_CHECK_VERSION(2, 24, 0)
827 buffer = gtk_text_view_get_buffer(text);
829 if (prefs_common.enable_color) {
830 /* grab the quote colors, converting from an int to a GdkColor */
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
833 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
835 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
837 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
839 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
841 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
843 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
845 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
848 signature_color = quote_color1 = quote_color2 = quote_color3 =
849 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
852 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
853 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
854 "foreground-gdk", "e_color1,
855 "paragraph-background-gdk", "e_bgcolor1,
857 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
858 "foreground-gdk", "e_color2,
859 "paragraph-background-gdk", "e_bgcolor2,
861 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
862 "foreground-gdk", "e_color3,
863 "paragraph-background-gdk", "e_bgcolor3,
866 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
867 "foreground-gdk", "e_color1,
869 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
870 "foreground-gdk", "e_color2,
872 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
873 "foreground-gdk", "e_color3,
877 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
878 "foreground-gdk", &signature_color,
881 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
882 "foreground-gdk", &uri_color,
884 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
885 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
887 #if !GTK_CHECK_VERSION(2, 24, 0)
888 color[0] = quote_color1;
889 color[1] = quote_color2;
890 color[2] = quote_color3;
891 color[3] = quote_bgcolor1;
892 color[4] = quote_bgcolor2;
893 color[5] = quote_bgcolor3;
894 color[6] = signature_color;
895 color[7] = uri_color;
897 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
898 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
900 for (i = 0; i < 8; i++) {
901 if (success[i] == FALSE) {
902 g_warning("Compose: color allocation failed.");
903 quote_color1 = quote_color2 = quote_color3 =
904 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
905 signature_color = uri_color = black;
911 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
914 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
917 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
919 return compose_generic_new(account, mailto, item, NULL, NULL);
922 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
924 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
927 #define SCROLL_TO_CURSOR(compose) { \
928 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
929 gtk_text_view_get_buffer( \
930 GTK_TEXT_VIEW(compose->text))); \
931 gtk_text_view_scroll_mark_onscreen( \
932 GTK_TEXT_VIEW(compose->text), \
936 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
939 if (folderidentifier) {
940 #if !GTK_CHECK_VERSION(2, 24, 0)
941 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
943 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
945 prefs_common.compose_save_to_history = add_history(
946 prefs_common.compose_save_to_history, folderidentifier);
947 #if !GTK_CHECK_VERSION(2, 24, 0)
948 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
949 prefs_common.compose_save_to_history);
951 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
952 prefs_common.compose_save_to_history);
956 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
957 if (folderidentifier)
958 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
960 gtk_entry_set_text(GTK_ENTRY(entry), "");
963 static gchar *compose_get_save_to(Compose *compose)
966 gchar *result = NULL;
967 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
968 result = gtk_editable_get_chars(entry, 0, -1);
971 #if !GTK_CHECK_VERSION(2, 24, 0)
972 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
974 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
976 prefs_common.compose_save_to_history = add_history(
977 prefs_common.compose_save_to_history, result);
978 #if !GTK_CHECK_VERSION(2, 24, 0)
979 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
980 prefs_common.compose_save_to_history);
982 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
983 prefs_common.compose_save_to_history);
989 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
990 GList *attach_files, GList *listAddress )
993 GtkTextView *textview;
994 GtkTextBuffer *textbuf;
996 const gchar *subject_format = NULL;
997 const gchar *body_format = NULL;
998 gchar *mailto_from = NULL;
999 PrefsAccount *mailto_account = NULL;
1000 MsgInfo* dummyinfo = NULL;
1001 gint cursor_pos = -1;
1002 MailField mfield = NO_FIELD_PRESENT;
1006 /* check if mailto defines a from */
1007 if (mailto && *mailto != '\0') {
1008 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1009 /* mailto defines a from, check if we can get account prefs from it,
1010 if not, the account prefs will be guessed using other ways, but we'll keep
1013 mailto_account = account_find_from_address(mailto_from, TRUE);
1014 if (mailto_account == NULL) {
1016 Xstrdup_a(tmp_from, mailto_from, return NULL);
1017 extract_address(tmp_from);
1018 mailto_account = account_find_from_address(tmp_from, TRUE);
1022 account = mailto_account;
1025 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1026 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1027 account = account_find_from_id(item->prefs->default_account);
1029 /* if no account prefs set, fallback to the current one */
1030 if (!account) account = cur_account;
1031 cm_return_val_if_fail(account != NULL, NULL);
1033 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1035 /* override from name if mailto asked for it */
1037 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1038 g_free(mailto_from);
1040 /* override from name according to folder properties */
1041 if (item && item->prefs &&
1042 item->prefs->compose_with_format &&
1043 item->prefs->compose_override_from_format &&
1044 *item->prefs->compose_override_from_format != '\0') {
1049 dummyinfo = compose_msginfo_new_from_compose(compose);
1051 /* decode \-escape sequences in the internal representation of the quote format */
1052 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1053 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1056 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1057 compose->gtkaspell);
1059 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1061 quote_fmt_scan_string(tmp);
1064 buf = quote_fmt_get_buffer();
1066 alertpanel_error(_("New message From format error."));
1068 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1069 quote_fmt_reset_vartable();
1074 compose->replyinfo = NULL;
1075 compose->fwdinfo = NULL;
1077 textview = GTK_TEXT_VIEW(compose->text);
1078 textbuf = gtk_text_view_get_buffer(textview);
1079 compose_create_tags(textview, compose);
1081 undo_block(compose->undostruct);
1083 compose_set_dictionaries_from_folder_prefs(compose, item);
1086 if (account->auto_sig)
1087 compose_insert_sig(compose, FALSE);
1088 gtk_text_buffer_get_start_iter(textbuf, &iter);
1089 gtk_text_buffer_place_cursor(textbuf, &iter);
1091 if (account->protocol != A_NNTP) {
1092 if (mailto && *mailto != '\0') {
1093 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1096 compose_set_folder_prefs(compose, item, TRUE);
1098 if (item && item->ret_rcpt) {
1099 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1102 if (mailto && *mailto != '\0') {
1103 if (!strchr(mailto, '@'))
1104 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1106 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1107 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1108 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1109 mfield = TO_FIELD_PRESENT;
1112 * CLAWS: just don't allow return receipt request, even if the user
1113 * may want to send an email. simple but foolproof.
1115 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1117 compose_add_field_list( compose, listAddress );
1119 if (item && item->prefs && item->prefs->compose_with_format) {
1120 subject_format = item->prefs->compose_subject_format;
1121 body_format = item->prefs->compose_body_format;
1122 } else if (account->compose_with_format) {
1123 subject_format = account->compose_subject_format;
1124 body_format = account->compose_body_format;
1125 } else if (prefs_common.compose_with_format) {
1126 subject_format = prefs_common.compose_subject_format;
1127 body_format = prefs_common.compose_body_format;
1130 if (subject_format || body_format) {
1133 && *subject_format != '\0' )
1135 gchar *subject = NULL;
1140 dummyinfo = compose_msginfo_new_from_compose(compose);
1142 /* decode \-escape sequences in the internal representation of the quote format */
1143 tmp = g_malloc(strlen(subject_format)+1);
1144 pref_get_unescaped_pref(tmp, subject_format);
1146 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1148 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1149 compose->gtkaspell);
1151 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1153 quote_fmt_scan_string(tmp);
1156 buf = quote_fmt_get_buffer();
1158 alertpanel_error(_("New message subject format error."));
1160 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1161 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1162 quote_fmt_reset_vartable();
1166 mfield = SUBJECT_FIELD_PRESENT;
1170 && *body_format != '\0' )
1173 GtkTextBuffer *buffer;
1174 GtkTextIter start, end;
1178 dummyinfo = compose_msginfo_new_from_compose(compose);
1180 text = GTK_TEXT_VIEW(compose->text);
1181 buffer = gtk_text_view_get_buffer(text);
1182 gtk_text_buffer_get_start_iter(buffer, &start);
1183 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1184 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1186 compose_quote_fmt(compose, dummyinfo,
1188 NULL, tmp, FALSE, TRUE,
1189 _("The body of the \"New message\" template has an error at line %d."));
1190 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1191 quote_fmt_reset_vartable();
1195 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1196 gtkaspell_highlight_all(compose->gtkaspell);
1198 mfield = BODY_FIELD_PRESENT;
1202 procmsg_msginfo_free( dummyinfo );
1208 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1209 ainfo = (AttachInfo *) curr->data;
1210 compose_attach_append(compose, ainfo->file, ainfo->file,
1211 ainfo->content_type, ainfo->charset);
1215 compose_show_first_last_header(compose, TRUE);
1217 /* Set save folder */
1218 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1219 gchar *folderidentifier;
1221 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1222 folderidentifier = folder_item_get_identifier(item);
1223 compose_set_save_to(compose, folderidentifier);
1224 g_free(folderidentifier);
1227 /* Place cursor according to provided input (mfield) */
1229 case NO_FIELD_PRESENT:
1230 if (compose->header_last)
1231 gtk_widget_grab_focus(compose->header_last->entry);
1233 case TO_FIELD_PRESENT:
1234 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1236 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1239 gtk_widget_grab_focus(compose->subject_entry);
1241 case SUBJECT_FIELD_PRESENT:
1242 textview = GTK_TEXT_VIEW(compose->text);
1245 textbuf = gtk_text_view_get_buffer(textview);
1248 mark = gtk_text_buffer_get_insert(textbuf);
1249 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1250 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1252 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1253 * only defers where it comes to the variable body
1254 * is not null. If no body is present compose->text
1255 * will be null in which case you cannot place the
1256 * cursor inside the component so. An empty component
1257 * is therefore created before placing the cursor
1259 case BODY_FIELD_PRESENT:
1260 cursor_pos = quote_fmt_get_cursor_pos();
1261 if (cursor_pos == -1)
1262 gtk_widget_grab_focus(compose->header_last->entry);
1264 gtk_widget_grab_focus(compose->text);
1268 undo_unblock(compose->undostruct);
1270 if (prefs_common.auto_exteditor)
1271 compose_exec_ext_editor(compose);
1273 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1275 SCROLL_TO_CURSOR(compose);
1277 compose->modified = FALSE;
1278 compose_set_title(compose);
1280 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1285 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1286 gboolean override_pref, const gchar *system)
1288 const gchar *privacy = NULL;
1290 cm_return_if_fail(compose != NULL);
1291 cm_return_if_fail(account != NULL);
1293 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1296 if (account->default_privacy_system && strlen(account->default_privacy_system))
1297 privacy = account->default_privacy_system;
1301 GSList *privacy_avail = privacy_get_system_ids();
1302 if (privacy_avail && g_slist_length(privacy_avail)) {
1303 privacy = (gchar *)(privacy_avail->data);
1306 if (privacy != NULL) {
1308 g_free(compose->privacy_system);
1309 compose->privacy_system = NULL;
1310 g_free(compose->encdata);
1311 compose->encdata = NULL;
1313 if (compose->privacy_system == NULL)
1314 compose->privacy_system = g_strdup(privacy);
1315 else if (*(compose->privacy_system) == '\0') {
1316 g_free(compose->privacy_system);
1317 g_free(compose->encdata);
1318 compose->encdata = NULL;
1319 compose->privacy_system = g_strdup(privacy);
1321 compose_update_privacy_system_menu_item(compose, FALSE);
1322 compose_use_encryption(compose, TRUE);
1326 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1328 const gchar *privacy = NULL;
1330 if (account->default_privacy_system && strlen(account->default_privacy_system))
1331 privacy = account->default_privacy_system;
1335 GSList *privacy_avail = privacy_get_system_ids();
1336 if (privacy_avail && g_slist_length(privacy_avail)) {
1337 privacy = (gchar *)(privacy_avail->data);
1341 if (privacy != NULL) {
1343 g_free(compose->privacy_system);
1344 compose->privacy_system = NULL;
1345 g_free(compose->encdata);
1346 compose->encdata = NULL;
1348 if (compose->privacy_system == NULL)
1349 compose->privacy_system = g_strdup(privacy);
1350 compose_update_privacy_system_menu_item(compose, FALSE);
1351 compose_use_signing(compose, TRUE);
1355 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1359 Compose *compose = NULL;
1361 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1363 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1364 cm_return_val_if_fail(msginfo != NULL, NULL);
1366 list_len = g_slist_length(msginfo_list);
1370 case COMPOSE_REPLY_TO_ADDRESS:
1371 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1372 FALSE, prefs_common.default_reply_list, FALSE, body);
1374 case COMPOSE_REPLY_WITH_QUOTE:
1375 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1376 FALSE, prefs_common.default_reply_list, FALSE, body);
1378 case COMPOSE_REPLY_WITHOUT_QUOTE:
1379 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1380 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1382 case COMPOSE_REPLY_TO_SENDER:
1383 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1384 FALSE, FALSE, TRUE, body);
1386 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1387 compose = compose_followup_and_reply_to(msginfo,
1388 COMPOSE_QUOTE_CHECK,
1389 FALSE, FALSE, body);
1391 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1392 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1393 FALSE, FALSE, TRUE, body);
1395 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1396 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1397 FALSE, FALSE, TRUE, NULL);
1399 case COMPOSE_REPLY_TO_ALL:
1400 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1401 TRUE, FALSE, FALSE, body);
1403 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1404 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1405 TRUE, FALSE, FALSE, body);
1407 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1408 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1409 TRUE, FALSE, FALSE, NULL);
1411 case COMPOSE_REPLY_TO_LIST:
1412 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1413 FALSE, TRUE, FALSE, body);
1415 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1416 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1417 FALSE, TRUE, FALSE, body);
1419 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1420 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1421 FALSE, TRUE, FALSE, NULL);
1423 case COMPOSE_FORWARD:
1424 if (prefs_common.forward_as_attachment) {
1425 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1428 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1432 case COMPOSE_FORWARD_INLINE:
1433 /* check if we reply to more than one Message */
1434 if (list_len == 1) {
1435 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1438 /* more messages FALL THROUGH */
1439 case COMPOSE_FORWARD_AS_ATTACH:
1440 compose = compose_forward_multiple(NULL, msginfo_list);
1442 case COMPOSE_REDIRECT:
1443 compose = compose_redirect(NULL, msginfo, FALSE);
1446 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1449 if (compose == NULL) {
1450 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1454 compose->rmode = mode;
1455 switch (compose->rmode) {
1457 case COMPOSE_REPLY_WITH_QUOTE:
1458 case COMPOSE_REPLY_WITHOUT_QUOTE:
1459 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1460 debug_print("reply mode Normal\n");
1461 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1462 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1464 case COMPOSE_REPLY_TO_SENDER:
1465 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1466 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1467 debug_print("reply mode Sender\n");
1468 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1470 case COMPOSE_REPLY_TO_ALL:
1471 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1472 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1473 debug_print("reply mode All\n");
1474 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1476 case COMPOSE_REPLY_TO_LIST:
1477 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1478 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1479 debug_print("reply mode List\n");
1480 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1482 case COMPOSE_REPLY_TO_ADDRESS:
1483 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1491 static Compose *compose_reply(MsgInfo *msginfo,
1492 ComposeQuoteMode quote_mode,
1498 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1499 to_sender, FALSE, body);
1502 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1503 ComposeQuoteMode quote_mode,
1508 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1509 to_sender, TRUE, body);
1512 static void compose_extract_original_charset(Compose *compose)
1514 MsgInfo *info = NULL;
1515 if (compose->replyinfo) {
1516 info = compose->replyinfo;
1517 } else if (compose->fwdinfo) {
1518 info = compose->fwdinfo;
1519 } else if (compose->targetinfo) {
1520 info = compose->targetinfo;
1523 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1524 MimeInfo *partinfo = mimeinfo;
1525 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1526 partinfo = procmime_mimeinfo_next(partinfo);
1528 compose->orig_charset =
1529 g_strdup(procmime_mimeinfo_get_parameter(
1530 partinfo, "charset"));
1532 procmime_mimeinfo_free_all(mimeinfo);
1536 #define SIGNAL_BLOCK(buffer) { \
1537 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1538 G_CALLBACK(compose_changed_cb), \
1540 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1541 G_CALLBACK(text_inserted), \
1545 #define SIGNAL_UNBLOCK(buffer) { \
1546 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1547 G_CALLBACK(compose_changed_cb), \
1549 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1550 G_CALLBACK(text_inserted), \
1554 static Compose *compose_generic_reply(MsgInfo *msginfo,
1555 ComposeQuoteMode quote_mode,
1556 gboolean to_all, gboolean to_ml,
1558 gboolean followup_and_reply_to,
1562 PrefsAccount *account = NULL;
1563 GtkTextView *textview;
1564 GtkTextBuffer *textbuf;
1565 gboolean quote = FALSE;
1566 const gchar *qmark = NULL;
1567 const gchar *body_fmt = NULL;
1568 gchar *s_system = NULL;
1570 cm_return_val_if_fail(msginfo != NULL, NULL);
1571 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1573 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1575 cm_return_val_if_fail(account != NULL, NULL);
1577 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1579 compose->updating = TRUE;
1581 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1582 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1584 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1585 if (!compose->replyinfo)
1586 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1588 compose_extract_original_charset(compose);
1590 if (msginfo->folder && msginfo->folder->ret_rcpt)
1591 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1593 /* Set save folder */
1594 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1595 gchar *folderidentifier;
1597 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1598 folderidentifier = folder_item_get_identifier(msginfo->folder);
1599 compose_set_save_to(compose, folderidentifier);
1600 g_free(folderidentifier);
1603 if (compose_parse_header(compose, msginfo) < 0) {
1604 compose->updating = FALSE;
1605 compose_destroy(compose);
1609 /* override from name according to folder properties */
1610 if (msginfo->folder && msginfo->folder->prefs &&
1611 msginfo->folder->prefs->reply_with_format &&
1612 msginfo->folder->prefs->reply_override_from_format &&
1613 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1618 /* decode \-escape sequences in the internal representation of the quote format */
1619 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1620 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1623 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1624 compose->gtkaspell);
1626 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1628 quote_fmt_scan_string(tmp);
1631 buf = quote_fmt_get_buffer();
1633 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1635 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1636 quote_fmt_reset_vartable();
1641 textview = (GTK_TEXT_VIEW(compose->text));
1642 textbuf = gtk_text_view_get_buffer(textview);
1643 compose_create_tags(textview, compose);
1645 undo_block(compose->undostruct);
1647 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1648 gtkaspell_block_check(compose->gtkaspell);
1651 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1652 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1653 /* use the reply format of folder (if enabled), or the account's one
1654 (if enabled) or fallback to the global reply format, which is always
1655 enabled (even if empty), and use the relevant quotemark */
1657 if (msginfo->folder && msginfo->folder->prefs &&
1658 msginfo->folder->prefs->reply_with_format) {
1659 qmark = msginfo->folder->prefs->reply_quotemark;
1660 body_fmt = msginfo->folder->prefs->reply_body_format;
1662 } else if (account->reply_with_format) {
1663 qmark = account->reply_quotemark;
1664 body_fmt = account->reply_body_format;
1667 qmark = prefs_common.quotemark;
1668 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1669 body_fmt = gettext(prefs_common.quotefmt);
1676 /* empty quotemark is not allowed */
1677 if (qmark == NULL || *qmark == '\0')
1679 compose_quote_fmt(compose, compose->replyinfo,
1680 body_fmt, qmark, body, FALSE, TRUE,
1681 _("The body of the \"Reply\" template has an error at line %d."));
1682 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1683 quote_fmt_reset_vartable();
1686 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1687 compose_force_encryption(compose, account, FALSE, s_system);
1690 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1691 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1692 compose_force_signing(compose, account, s_system);
1696 SIGNAL_BLOCK(textbuf);
1698 if (account->auto_sig)
1699 compose_insert_sig(compose, FALSE);
1701 compose_wrap_all(compose);
1704 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1705 gtkaspell_highlight_all(compose->gtkaspell);
1706 gtkaspell_unblock_check(compose->gtkaspell);
1708 SIGNAL_UNBLOCK(textbuf);
1710 gtk_widget_grab_focus(compose->text);
1712 undo_unblock(compose->undostruct);
1714 if (prefs_common.auto_exteditor)
1715 compose_exec_ext_editor(compose);
1717 compose->modified = FALSE;
1718 compose_set_title(compose);
1720 compose->updating = FALSE;
1721 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1722 SCROLL_TO_CURSOR(compose);
1724 if (compose->deferred_destroy) {
1725 compose_destroy(compose);
1733 #define INSERT_FW_HEADER(var, hdr) \
1734 if (msginfo->var && *msginfo->var) { \
1735 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1736 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1737 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1740 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1741 gboolean as_attach, const gchar *body,
1742 gboolean no_extedit,
1746 GtkTextView *textview;
1747 GtkTextBuffer *textbuf;
1748 gint cursor_pos = -1;
1751 cm_return_val_if_fail(msginfo != NULL, NULL);
1752 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1755 !(account = compose_guess_forward_account_from_msginfo
1757 account = cur_account;
1759 if (!prefs_common.forward_as_attachment)
1760 mode = COMPOSE_FORWARD_INLINE;
1762 mode = COMPOSE_FORWARD;
1763 compose = compose_create(account, msginfo->folder, mode, batch);
1765 compose->updating = TRUE;
1766 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1767 if (!compose->fwdinfo)
1768 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1770 compose_extract_original_charset(compose);
1772 if (msginfo->subject && *msginfo->subject) {
1773 gchar *buf, *buf2, *p;
1775 buf = p = g_strdup(msginfo->subject);
1776 p += subject_get_prefix_length(p);
1777 memmove(buf, p, strlen(p) + 1);
1779 buf2 = g_strdup_printf("Fw: %s", buf);
1780 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1786 /* override from name according to folder properties */
1787 if (msginfo->folder && msginfo->folder->prefs &&
1788 msginfo->folder->prefs->forward_with_format &&
1789 msginfo->folder->prefs->forward_override_from_format &&
1790 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1794 MsgInfo *full_msginfo = NULL;
1797 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1799 full_msginfo = procmsg_msginfo_copy(msginfo);
1801 /* decode \-escape sequences in the internal representation of the quote format */
1802 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1803 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1806 gtkaspell_block_check(compose->gtkaspell);
1807 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1808 compose->gtkaspell);
1810 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1812 quote_fmt_scan_string(tmp);
1815 buf = quote_fmt_get_buffer();
1817 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1819 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1820 quote_fmt_reset_vartable();
1823 procmsg_msginfo_free(full_msginfo);
1826 textview = GTK_TEXT_VIEW(compose->text);
1827 textbuf = gtk_text_view_get_buffer(textview);
1828 compose_create_tags(textview, compose);
1830 undo_block(compose->undostruct);
1834 msgfile = procmsg_get_message_file(msginfo);
1835 if (!is_file_exist(msgfile))
1836 g_warning("%s: file does not exist", msgfile);
1838 compose_attach_append(compose, msgfile, msgfile,
1839 "message/rfc822", NULL);
1843 const gchar *qmark = NULL;
1844 const gchar *body_fmt = NULL;
1845 MsgInfo *full_msginfo;
1847 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1849 full_msginfo = procmsg_msginfo_copy(msginfo);
1851 /* use the forward format of folder (if enabled), or the account's one
1852 (if enabled) or fallback to the global forward format, which is always
1853 enabled (even if empty), and use the relevant quotemark */
1854 if (msginfo->folder && msginfo->folder->prefs &&
1855 msginfo->folder->prefs->forward_with_format) {
1856 qmark = msginfo->folder->prefs->forward_quotemark;
1857 body_fmt = msginfo->folder->prefs->forward_body_format;
1859 } else if (account->forward_with_format) {
1860 qmark = account->forward_quotemark;
1861 body_fmt = account->forward_body_format;
1864 qmark = prefs_common.fw_quotemark;
1865 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1866 body_fmt = gettext(prefs_common.fw_quotefmt);
1871 /* empty quotemark is not allowed */
1872 if (qmark == NULL || *qmark == '\0')
1875 compose_quote_fmt(compose, full_msginfo,
1876 body_fmt, qmark, body, FALSE, TRUE,
1877 _("The body of the \"Forward\" template has an error at line %d."));
1878 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1879 quote_fmt_reset_vartable();
1880 compose_attach_parts(compose, msginfo);
1882 procmsg_msginfo_free(full_msginfo);
1885 SIGNAL_BLOCK(textbuf);
1887 if (account->auto_sig)
1888 compose_insert_sig(compose, FALSE);
1890 compose_wrap_all(compose);
1893 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1894 gtkaspell_highlight_all(compose->gtkaspell);
1895 gtkaspell_unblock_check(compose->gtkaspell);
1897 SIGNAL_UNBLOCK(textbuf);
1899 cursor_pos = quote_fmt_get_cursor_pos();
1900 if (cursor_pos == -1)
1901 gtk_widget_grab_focus(compose->header_last->entry);
1903 gtk_widget_grab_focus(compose->text);
1905 if (!no_extedit && prefs_common.auto_exteditor)
1906 compose_exec_ext_editor(compose);
1909 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1910 gchar *folderidentifier;
1912 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1913 folderidentifier = folder_item_get_identifier(msginfo->folder);
1914 compose_set_save_to(compose, folderidentifier);
1915 g_free(folderidentifier);
1918 undo_unblock(compose->undostruct);
1920 compose->modified = FALSE;
1921 compose_set_title(compose);
1923 compose->updating = FALSE;
1924 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1925 SCROLL_TO_CURSOR(compose);
1927 if (compose->deferred_destroy) {
1928 compose_destroy(compose);
1932 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1937 #undef INSERT_FW_HEADER
1939 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1942 GtkTextView *textview;
1943 GtkTextBuffer *textbuf;
1947 gboolean single_mail = TRUE;
1949 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1951 if (g_slist_length(msginfo_list) > 1)
1952 single_mail = FALSE;
1954 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1955 if (((MsgInfo *)msginfo->data)->folder == NULL)
1958 /* guess account from first selected message */
1960 !(account = compose_guess_forward_account_from_msginfo
1961 (msginfo_list->data)))
1962 account = cur_account;
1964 cm_return_val_if_fail(account != NULL, NULL);
1966 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1967 if (msginfo->data) {
1968 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1969 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1973 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1974 g_warning("no msginfo_list");
1978 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1980 compose->updating = TRUE;
1982 /* override from name according to folder properties */
1983 if (msginfo_list->data) {
1984 MsgInfo *msginfo = msginfo_list->data;
1986 if (msginfo->folder && msginfo->folder->prefs &&
1987 msginfo->folder->prefs->forward_with_format &&
1988 msginfo->folder->prefs->forward_override_from_format &&
1989 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1994 /* decode \-escape sequences in the internal representation of the quote format */
1995 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1996 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1999 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2000 compose->gtkaspell);
2002 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2004 quote_fmt_scan_string(tmp);
2007 buf = quote_fmt_get_buffer();
2009 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2011 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2012 quote_fmt_reset_vartable();
2018 textview = GTK_TEXT_VIEW(compose->text);
2019 textbuf = gtk_text_view_get_buffer(textview);
2020 compose_create_tags(textview, compose);
2022 undo_block(compose->undostruct);
2023 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2024 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2026 if (!is_file_exist(msgfile))
2027 g_warning("%s: file does not exist", msgfile);
2029 compose_attach_append(compose, msgfile, msgfile,
2030 "message/rfc822", NULL);
2035 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2036 if (info->subject && *info->subject) {
2037 gchar *buf, *buf2, *p;
2039 buf = p = g_strdup(info->subject);
2040 p += subject_get_prefix_length(p);
2041 memmove(buf, p, strlen(p) + 1);
2043 buf2 = g_strdup_printf("Fw: %s", buf);
2044 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2050 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2051 _("Fw: multiple emails"));
2054 SIGNAL_BLOCK(textbuf);
2056 if (account->auto_sig)
2057 compose_insert_sig(compose, FALSE);
2059 compose_wrap_all(compose);
2061 SIGNAL_UNBLOCK(textbuf);
2063 gtk_text_buffer_get_start_iter(textbuf, &iter);
2064 gtk_text_buffer_place_cursor(textbuf, &iter);
2066 gtk_widget_grab_focus(compose->header_last->entry);
2067 undo_unblock(compose->undostruct);
2068 compose->modified = FALSE;
2069 compose_set_title(compose);
2071 compose->updating = FALSE;
2072 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2073 SCROLL_TO_CURSOR(compose);
2075 if (compose->deferred_destroy) {
2076 compose_destroy(compose);
2080 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2085 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2087 GtkTextIter start = *iter;
2088 GtkTextIter end_iter;
2089 int start_pos = gtk_text_iter_get_offset(&start);
2091 if (!compose->account->sig_sep)
2094 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2095 start_pos+strlen(compose->account->sig_sep));
2097 /* check sig separator */
2098 str = gtk_text_iter_get_text(&start, &end_iter);
2099 if (!strcmp(str, compose->account->sig_sep)) {
2101 /* check end of line (\n) */
2102 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2103 start_pos+strlen(compose->account->sig_sep));
2104 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2105 start_pos+strlen(compose->account->sig_sep)+1);
2106 tmp = gtk_text_iter_get_text(&start, &end_iter);
2107 if (!strcmp(tmp,"\n")) {
2119 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2121 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2122 Compose *compose = (Compose *)data;
2123 FolderItem *old_item = NULL;
2124 FolderItem *new_item = NULL;
2125 gchar *old_id, *new_id;
2127 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2128 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2131 old_item = hookdata->item;
2132 new_item = hookdata->item2;
2134 old_id = folder_item_get_identifier(old_item);
2135 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2137 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2138 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2139 compose->targetinfo->folder = new_item;
2142 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2143 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2144 compose->replyinfo->folder = new_item;
2147 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2148 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2149 compose->fwdinfo->folder = new_item;
2157 static void compose_colorize_signature(Compose *compose)
2159 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2161 GtkTextIter end_iter;
2162 gtk_text_buffer_get_start_iter(buffer, &iter);
2163 while (gtk_text_iter_forward_line(&iter))
2164 if (compose_is_sig_separator(compose, buffer, &iter)) {
2165 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2166 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2170 #define BLOCK_WRAP() { \
2171 prev_autowrap = compose->autowrap; \
2172 buffer = gtk_text_view_get_buffer( \
2173 GTK_TEXT_VIEW(compose->text)); \
2174 compose->autowrap = FALSE; \
2176 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2177 G_CALLBACK(compose_changed_cb), \
2179 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2180 G_CALLBACK(text_inserted), \
2183 #define UNBLOCK_WRAP() { \
2184 compose->autowrap = prev_autowrap; \
2185 if (compose->autowrap) { \
2186 gint old = compose->draft_timeout_tag; \
2187 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2188 compose_wrap_all(compose); \
2189 compose->draft_timeout_tag = old; \
2192 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2193 G_CALLBACK(compose_changed_cb), \
2195 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2196 G_CALLBACK(text_inserted), \
2200 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2202 Compose *compose = NULL;
2203 PrefsAccount *account = NULL;
2204 GtkTextView *textview;
2205 GtkTextBuffer *textbuf;
2209 gchar buf[BUFFSIZE];
2210 gboolean use_signing = FALSE;
2211 gboolean use_encryption = FALSE;
2212 gchar *privacy_system = NULL;
2213 int priority = PRIORITY_NORMAL;
2214 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2215 gboolean autowrap = prefs_common.autowrap;
2216 gboolean autoindent = prefs_common.auto_indent;
2217 HeaderEntry *manual_headers = NULL;
2219 cm_return_val_if_fail(msginfo != NULL, NULL);
2220 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2222 if (compose_put_existing_to_front(msginfo)) {
2226 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2227 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2228 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2229 gchar queueheader_buf[BUFFSIZE];
2232 /* Select Account from queue headers */
2233 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2234 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2235 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2236 account = account_find_from_id(id);
2238 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2239 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2240 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2241 account = account_find_from_id(id);
2243 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2244 sizeof(queueheader_buf), "NAID:")) {
2245 id = atoi(&queueheader_buf[strlen("NAID:")]);
2246 account = account_find_from_id(id);
2248 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2249 sizeof(queueheader_buf), "MAID:")) {
2250 id = atoi(&queueheader_buf[strlen("MAID:")]);
2251 account = account_find_from_id(id);
2253 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2254 sizeof(queueheader_buf), "S:")) {
2255 account = account_find_from_address(queueheader_buf, FALSE);
2257 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2258 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2259 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2260 use_signing = param;
2263 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2264 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2265 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2266 use_signing = param;
2269 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2270 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2271 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2272 use_encryption = param;
2274 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2275 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2276 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2277 use_encryption = param;
2279 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2280 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2281 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2284 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2285 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2286 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2289 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2290 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2291 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2293 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2294 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2295 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2297 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2298 sizeof(queueheader_buf), "X-Priority: ")) {
2299 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2302 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2303 sizeof(queueheader_buf), "RMID:")) {
2304 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2305 if (tokens[0] && tokens[1] && tokens[2]) {
2306 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2307 if (orig_item != NULL) {
2308 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2313 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2314 sizeof(queueheader_buf), "FMID:")) {
2315 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2316 if (tokens[0] && tokens[1] && tokens[2]) {
2317 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2318 if (orig_item != NULL) {
2319 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2324 /* Get manual headers */
2325 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2326 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2327 if (*listmh != '\0') {
2328 debug_print("Got manual headers: %s\n", listmh);
2329 manual_headers = procheader_entries_from_str(listmh);
2334 account = msginfo->folder->folder->account;
2337 if (!account && prefs_common.reedit_account_autosel) {
2338 gchar from[BUFFSIZE];
2339 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2340 extract_address(from);
2341 account = account_find_from_address(from, FALSE);
2345 account = cur_account;
2347 cm_return_val_if_fail(account != NULL, NULL);
2349 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2351 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2352 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2353 compose->autowrap = autowrap;
2354 compose->replyinfo = replyinfo;
2355 compose->fwdinfo = fwdinfo;
2357 compose->updating = TRUE;
2358 compose->priority = priority;
2360 if (privacy_system != NULL) {
2361 compose->privacy_system = privacy_system;
2362 compose_use_signing(compose, use_signing);
2363 compose_use_encryption(compose, use_encryption);
2364 compose_update_privacy_system_menu_item(compose, FALSE);
2366 activate_privacy_system(compose, account, FALSE);
2369 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2371 compose_extract_original_charset(compose);
2373 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2374 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2375 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2376 gchar queueheader_buf[BUFFSIZE];
2378 /* Set message save folder */
2379 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2380 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2381 compose_set_save_to(compose, &queueheader_buf[4]);
2383 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2384 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2386 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2391 if (compose_parse_header(compose, msginfo) < 0) {
2392 compose->updating = FALSE;
2393 compose_destroy(compose);
2396 compose_reedit_set_entry(compose, msginfo);
2398 textview = GTK_TEXT_VIEW(compose->text);
2399 textbuf = gtk_text_view_get_buffer(textview);
2400 compose_create_tags(textview, compose);
2402 mark = gtk_text_buffer_get_insert(textbuf);
2403 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2405 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2406 G_CALLBACK(compose_changed_cb),
2409 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2410 fp = procmime_get_first_encrypted_text_content(msginfo);
2412 compose_force_encryption(compose, account, TRUE, NULL);
2415 fp = procmime_get_first_text_content(msginfo);
2418 g_warning("Can't get text part");
2422 gboolean prev_autowrap;
2423 GtkTextBuffer *buffer;
2425 while (fgets(buf, sizeof(buf), fp) != NULL) {
2427 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2433 compose_attach_parts(compose, msginfo);
2435 compose_colorize_signature(compose);
2437 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2438 G_CALLBACK(compose_changed_cb),
2441 if (manual_headers != NULL) {
2442 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2443 procheader_entries_free(manual_headers);
2444 compose->updating = FALSE;
2445 compose_destroy(compose);
2448 procheader_entries_free(manual_headers);
2451 gtk_widget_grab_focus(compose->text);
2453 if (prefs_common.auto_exteditor) {
2454 compose_exec_ext_editor(compose);
2456 compose->modified = FALSE;
2457 compose_set_title(compose);
2459 compose->updating = FALSE;
2460 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2461 SCROLL_TO_CURSOR(compose);
2463 if (compose->deferred_destroy) {
2464 compose_destroy(compose);
2468 compose->sig_str = account_get_signature_str(compose->account);
2470 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2475 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2482 cm_return_val_if_fail(msginfo != NULL, NULL);
2485 account = account_get_reply_account(msginfo,
2486 prefs_common.reply_account_autosel);
2487 cm_return_val_if_fail(account != NULL, NULL);
2489 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2491 compose->updating = TRUE;
2493 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2494 compose->replyinfo = NULL;
2495 compose->fwdinfo = NULL;
2497 compose_show_first_last_header(compose, TRUE);
2499 gtk_widget_grab_focus(compose->header_last->entry);
2501 filename = procmsg_get_message_file(msginfo);
2503 if (filename == NULL) {
2504 compose->updating = FALSE;
2505 compose_destroy(compose);
2510 compose->redirect_filename = filename;
2512 /* Set save folder */
2513 item = msginfo->folder;
2514 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2515 gchar *folderidentifier;
2517 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2518 folderidentifier = folder_item_get_identifier(item);
2519 compose_set_save_to(compose, folderidentifier);
2520 g_free(folderidentifier);
2523 compose_attach_parts(compose, msginfo);
2525 if (msginfo->subject)
2526 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2528 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2530 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2531 _("The body of the \"Redirect\" template has an error at line %d."));
2532 quote_fmt_reset_vartable();
2533 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2535 compose_colorize_signature(compose);
2538 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2539 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2540 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2542 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2543 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2545 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2546 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2547 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2548 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2549 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2550 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2552 if (compose->toolbar->draft_btn)
2553 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2554 if (compose->toolbar->insert_btn)
2555 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2556 if (compose->toolbar->attach_btn)
2557 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2558 if (compose->toolbar->sig_btn)
2559 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2560 if (compose->toolbar->exteditor_btn)
2561 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2562 if (compose->toolbar->linewrap_current_btn)
2563 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2564 if (compose->toolbar->linewrap_all_btn)
2565 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2567 compose->modified = FALSE;
2568 compose_set_title(compose);
2569 compose->updating = FALSE;
2570 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2571 SCROLL_TO_CURSOR(compose);
2573 if (compose->deferred_destroy) {
2574 compose_destroy(compose);
2578 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2583 const GList *compose_get_compose_list(void)
2585 return compose_list;
2588 void compose_entry_append(Compose *compose, const gchar *address,
2589 ComposeEntryType type, ComposePrefType pref_type)
2591 const gchar *header;
2593 gboolean in_quote = FALSE;
2594 if (!address || *address == '\0') return;
2601 header = N_("Bcc:");
2603 case COMPOSE_REPLYTO:
2604 header = N_("Reply-To:");
2606 case COMPOSE_NEWSGROUPS:
2607 header = N_("Newsgroups:");
2609 case COMPOSE_FOLLOWUPTO:
2610 header = N_( "Followup-To:");
2612 case COMPOSE_INREPLYTO:
2613 header = N_( "In-Reply-To:");
2620 header = prefs_common_translated_header_name(header);
2622 cur = begin = (gchar *)address;
2624 /* we separate the line by commas, but not if we're inside a quoted
2626 while (*cur != '\0') {
2628 in_quote = !in_quote;
2629 if (*cur == ',' && !in_quote) {
2630 gchar *tmp = g_strdup(begin);
2632 tmp[cur-begin]='\0';
2635 while (*tmp == ' ' || *tmp == '\t')
2637 compose_add_header_entry(compose, header, tmp, pref_type);
2644 gchar *tmp = g_strdup(begin);
2646 tmp[cur-begin]='\0';
2647 while (*tmp == ' ' || *tmp == '\t')
2649 compose_add_header_entry(compose, header, tmp, pref_type);
2654 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2656 #if !GTK_CHECK_VERSION(3, 0, 0)
2657 static GdkColor yellow;
2658 static GdkColor black;
2659 static gboolean yellow_initialised = FALSE;
2661 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2662 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2667 #if !GTK_CHECK_VERSION(3, 0, 0)
2668 if (!yellow_initialised) {
2669 gdk_color_parse("#f5f6be", &yellow);
2670 gdk_color_parse("#000000", &black);
2671 yellow_initialised = gdk_colormap_alloc_color(
2672 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2673 yellow_initialised &= gdk_colormap_alloc_color(
2674 gdk_colormap_get_system(), &black, FALSE, TRUE);
2678 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2679 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2680 if (gtk_entry_get_text(entry) &&
2681 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2682 #if !GTK_CHECK_VERSION(3, 0, 0)
2683 if (yellow_initialised) {
2685 gtk_widget_modify_base(
2686 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2687 GTK_STATE_NORMAL, &yellow);
2688 gtk_widget_modify_text(
2689 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2690 GTK_STATE_NORMAL, &black);
2691 #if !GTK_CHECK_VERSION(3, 0, 0)
2698 void compose_toolbar_cb(gint action, gpointer data)
2700 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2701 Compose *compose = (Compose*)toolbar_item->parent;
2703 cm_return_if_fail(compose != NULL);
2707 compose_send_cb(NULL, compose);
2710 compose_send_later_cb(NULL, compose);
2713 compose_draft(compose, COMPOSE_QUIT_EDITING);
2716 compose_insert_file_cb(NULL, compose);
2719 compose_attach_cb(NULL, compose);
2722 compose_insert_sig(compose, FALSE);
2725 compose_insert_sig(compose, TRUE);
2728 compose_ext_editor_cb(NULL, compose);
2730 case A_LINEWRAP_CURRENT:
2731 compose_beautify_paragraph(compose, NULL, TRUE);
2733 case A_LINEWRAP_ALL:
2734 compose_wrap_all_full(compose, TRUE);
2737 compose_address_cb(NULL, compose);
2740 case A_CHECK_SPELLING:
2741 compose_check_all(NULL, compose);
2749 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2754 gchar *subject = NULL;
2758 gchar **attach = NULL;
2759 gchar *inreplyto = NULL;
2760 MailField mfield = NO_FIELD_PRESENT;
2762 /* get mailto parts but skip from */
2763 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2766 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2767 mfield = TO_FIELD_PRESENT;
2770 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2772 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2774 if (!g_utf8_validate (subject, -1, NULL)) {
2775 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2776 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2779 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2781 mfield = SUBJECT_FIELD_PRESENT;
2784 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2785 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2788 gboolean prev_autowrap = compose->autowrap;
2790 compose->autowrap = FALSE;
2792 mark = gtk_text_buffer_get_insert(buffer);
2793 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2795 if (!g_utf8_validate (body, -1, NULL)) {
2796 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2797 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2800 gtk_text_buffer_insert(buffer, &iter, body, -1);
2802 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2804 compose->autowrap = prev_autowrap;
2805 if (compose->autowrap)
2806 compose_wrap_all(compose);
2807 mfield = BODY_FIELD_PRESENT;
2811 gint i = 0, att = 0;
2812 gchar *warn_files = NULL;
2813 while (attach[i] != NULL) {
2814 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2815 if (utf8_filename) {
2816 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2817 gchar *tmp = g_strdup_printf("%s%s\n",
2818 warn_files?warn_files:"",
2824 g_free(utf8_filename);
2826 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2831 alertpanel_notice(ngettext(
2832 "The following file has been attached: \n%s",
2833 "The following files have been attached: \n%s", att), warn_files);
2838 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2851 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2853 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2854 {"Cc:", NULL, TRUE},
2855 {"References:", NULL, FALSE},
2856 {"Bcc:", NULL, TRUE},
2857 {"Newsgroups:", NULL, TRUE},
2858 {"Followup-To:", NULL, TRUE},
2859 {"List-Post:", NULL, FALSE},
2860 {"X-Priority:", NULL, FALSE},
2861 {NULL, NULL, FALSE}};
2877 cm_return_val_if_fail(msginfo != NULL, -1);
2879 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2880 procheader_get_header_fields(fp, hentry);
2883 if (hentry[H_REPLY_TO].body != NULL) {
2884 if (hentry[H_REPLY_TO].body[0] != '\0') {
2886 conv_unmime_header(hentry[H_REPLY_TO].body,
2889 g_free(hentry[H_REPLY_TO].body);
2890 hentry[H_REPLY_TO].body = NULL;
2892 if (hentry[H_CC].body != NULL) {
2893 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2894 g_free(hentry[H_CC].body);
2895 hentry[H_CC].body = NULL;
2897 if (hentry[H_REFERENCES].body != NULL) {
2898 if (compose->mode == COMPOSE_REEDIT)
2899 compose->references = hentry[H_REFERENCES].body;
2901 compose->references = compose_parse_references
2902 (hentry[H_REFERENCES].body, msginfo->msgid);
2903 g_free(hentry[H_REFERENCES].body);
2905 hentry[H_REFERENCES].body = NULL;
2907 if (hentry[H_BCC].body != NULL) {
2908 if (compose->mode == COMPOSE_REEDIT)
2910 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2911 g_free(hentry[H_BCC].body);
2912 hentry[H_BCC].body = NULL;
2914 if (hentry[H_NEWSGROUPS].body != NULL) {
2915 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2916 hentry[H_NEWSGROUPS].body = NULL;
2918 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2919 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2920 compose->followup_to =
2921 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2924 g_free(hentry[H_FOLLOWUP_TO].body);
2925 hentry[H_FOLLOWUP_TO].body = NULL;
2927 if (hentry[H_LIST_POST].body != NULL) {
2928 gchar *to = NULL, *start = NULL;
2930 extract_address(hentry[H_LIST_POST].body);
2931 if (hentry[H_LIST_POST].body[0] != '\0') {
2932 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2934 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2935 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2938 g_free(compose->ml_post);
2939 compose->ml_post = to;
2942 g_free(hentry[H_LIST_POST].body);
2943 hentry[H_LIST_POST].body = NULL;
2946 /* CLAWS - X-Priority */
2947 if (compose->mode == COMPOSE_REEDIT)
2948 if (hentry[H_X_PRIORITY].body != NULL) {
2951 priority = atoi(hentry[H_X_PRIORITY].body);
2952 g_free(hentry[H_X_PRIORITY].body);
2954 hentry[H_X_PRIORITY].body = NULL;
2956 if (priority < PRIORITY_HIGHEST ||
2957 priority > PRIORITY_LOWEST)
2958 priority = PRIORITY_NORMAL;
2960 compose->priority = priority;
2963 if (compose->mode == COMPOSE_REEDIT) {
2964 if (msginfo->inreplyto && *msginfo->inreplyto)
2965 compose->inreplyto = g_strdup(msginfo->inreplyto);
2969 if (msginfo->msgid && *msginfo->msgid)
2970 compose->inreplyto = g_strdup(msginfo->msgid);
2972 if (!compose->references) {
2973 if (msginfo->msgid && *msginfo->msgid) {
2974 if (msginfo->inreplyto && *msginfo->inreplyto)
2975 compose->references =
2976 g_strdup_printf("<%s>\n\t<%s>",
2980 compose->references =
2981 g_strconcat("<", msginfo->msgid, ">",
2983 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2984 compose->references =
2985 g_strconcat("<", msginfo->inreplyto, ">",
2993 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2998 cm_return_val_if_fail(msginfo != NULL, -1);
3000 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3001 procheader_get_header_fields(fp, entries);
3005 while (he != NULL && he->name != NULL) {
3007 GtkListStore *model = NULL;
3009 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3010 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3011 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3012 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3013 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3020 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3022 GSList *ref_id_list, *cur;
3026 ref_id_list = references_list_append(NULL, ref);
3027 if (!ref_id_list) return NULL;
3028 if (msgid && *msgid)
3029 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3034 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3035 /* "<" + Message-ID + ">" + CR+LF+TAB */
3036 len += strlen((gchar *)cur->data) + 5;
3038 if (len > MAX_REFERENCES_LEN) {
3039 /* remove second message-ID */
3040 if (ref_id_list && ref_id_list->next &&
3041 ref_id_list->next->next) {
3042 g_free(ref_id_list->next->data);
3043 ref_id_list = g_slist_remove
3044 (ref_id_list, ref_id_list->next->data);
3046 slist_free_strings_full(ref_id_list);
3053 new_ref = g_string_new("");
3054 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3055 if (new_ref->len > 0)
3056 g_string_append(new_ref, "\n\t");
3057 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3060 slist_free_strings_full(ref_id_list);
3062 new_ref_str = new_ref->str;
3063 g_string_free(new_ref, FALSE);
3068 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3069 const gchar *fmt, const gchar *qmark,
3070 const gchar *body, gboolean rewrap,
3071 gboolean need_unescape,
3072 const gchar *err_msg)
3074 MsgInfo* dummyinfo = NULL;
3075 gchar *quote_str = NULL;
3077 gboolean prev_autowrap;
3078 const gchar *trimmed_body = body;
3079 gint cursor_pos = -1;
3080 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3081 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3086 SIGNAL_BLOCK(buffer);
3089 dummyinfo = compose_msginfo_new_from_compose(compose);
3090 msginfo = dummyinfo;
3093 if (qmark != NULL) {
3095 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3096 compose->gtkaspell);
3098 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3100 quote_fmt_scan_string(qmark);
3103 buf = quote_fmt_get_buffer();
3105 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3107 Xstrdup_a(quote_str, buf, goto error)
3110 if (fmt && *fmt != '\0') {
3113 while (*trimmed_body == '\n')
3117 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3118 compose->gtkaspell);
3120 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3122 if (need_unescape) {
3125 /* decode \-escape sequences in the internal representation of the quote format */
3126 tmp = g_malloc(strlen(fmt)+1);
3127 pref_get_unescaped_pref(tmp, fmt);
3128 quote_fmt_scan_string(tmp);
3132 quote_fmt_scan_string(fmt);
3136 buf = quote_fmt_get_buffer();
3138 gint line = quote_fmt_get_line();
3139 alertpanel_error(err_msg, line);
3145 prev_autowrap = compose->autowrap;
3146 compose->autowrap = FALSE;
3148 mark = gtk_text_buffer_get_insert(buffer);
3149 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3150 if (g_utf8_validate(buf, -1, NULL)) {
3151 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3153 gchar *tmpout = NULL;
3154 tmpout = conv_codeset_strdup
3155 (buf, conv_get_locale_charset_str_no_utf8(),
3157 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3159 tmpout = g_malloc(strlen(buf)*2+1);
3160 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3162 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3166 cursor_pos = quote_fmt_get_cursor_pos();
3167 if (cursor_pos == -1)
3168 cursor_pos = gtk_text_iter_get_offset(&iter);
3169 compose->set_cursor_pos = cursor_pos;
3171 gtk_text_buffer_get_start_iter(buffer, &iter);
3172 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3173 gtk_text_buffer_place_cursor(buffer, &iter);
3175 compose->autowrap = prev_autowrap;
3176 if (compose->autowrap && rewrap)
3177 compose_wrap_all(compose);
3184 SIGNAL_UNBLOCK(buffer);
3186 procmsg_msginfo_free( dummyinfo );
3191 /* if ml_post is of type addr@host and from is of type
3192 * addr-anything@host, return TRUE
3194 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3196 gchar *left_ml = NULL;
3197 gchar *right_ml = NULL;
3198 gchar *left_from = NULL;
3199 gchar *right_from = NULL;
3200 gboolean result = FALSE;
3202 if (!ml_post || !from)
3205 left_ml = g_strdup(ml_post);
3206 if (strstr(left_ml, "@")) {
3207 right_ml = strstr(left_ml, "@")+1;
3208 *(strstr(left_ml, "@")) = '\0';
3211 left_from = g_strdup(from);
3212 if (strstr(left_from, "@")) {
3213 right_from = strstr(left_from, "@")+1;
3214 *(strstr(left_from, "@")) = '\0';
3217 if (right_ml && right_from
3218 && !strncmp(left_from, left_ml, strlen(left_ml))
3219 && !strcmp(right_from, right_ml)) {
3228 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3229 gboolean respect_default_to)
3233 if (!folder || !folder->prefs)
3236 if (respect_default_to && folder->prefs->enable_default_to) {
3237 compose_entry_append(compose, folder->prefs->default_to,
3238 COMPOSE_TO, PREF_FOLDER);
3239 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3241 if (folder->prefs->enable_default_cc)
3242 compose_entry_append(compose, folder->prefs->default_cc,
3243 COMPOSE_CC, PREF_FOLDER);
3244 if (folder->prefs->enable_default_bcc)
3245 compose_entry_append(compose, folder->prefs->default_bcc,
3246 COMPOSE_BCC, PREF_FOLDER);
3247 if (folder->prefs->enable_default_replyto)
3248 compose_entry_append(compose, folder->prefs->default_replyto,
3249 COMPOSE_REPLYTO, PREF_FOLDER);
3252 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3257 if (!compose || !msginfo)
3260 if (msginfo->subject && *msginfo->subject) {
3261 buf = p = g_strdup(msginfo->subject);
3262 p += subject_get_prefix_length(p);
3263 memmove(buf, p, strlen(p) + 1);
3265 buf2 = g_strdup_printf("Re: %s", buf);
3266 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3271 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3274 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3275 gboolean to_all, gboolean to_ml,
3277 gboolean followup_and_reply_to)
3279 GSList *cc_list = NULL;
3282 gchar *replyto = NULL;
3283 gchar *ac_email = NULL;
3285 gboolean reply_to_ml = FALSE;
3286 gboolean default_reply_to = FALSE;
3288 cm_return_if_fail(compose->account != NULL);
3289 cm_return_if_fail(msginfo != NULL);
3291 reply_to_ml = to_ml && compose->ml_post;
3293 default_reply_to = msginfo->folder &&
3294 msginfo->folder->prefs->enable_default_reply_to;
3296 if (compose->account->protocol != A_NNTP) {
3297 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3299 if (reply_to_ml && !default_reply_to) {
3301 gboolean is_subscr = is_subscription(compose->ml_post,
3304 /* normal answer to ml post with a reply-to */
3305 compose_entry_append(compose,
3307 COMPOSE_TO, PREF_ML);
3308 if (compose->replyto)
3309 compose_entry_append(compose,
3311 COMPOSE_CC, PREF_ML);
3313 /* answer to subscription confirmation */
3314 if (compose->replyto)
3315 compose_entry_append(compose,
3317 COMPOSE_TO, PREF_ML);
3318 else if (msginfo->from)
3319 compose_entry_append(compose,
3321 COMPOSE_TO, PREF_ML);
3324 else if (!(to_all || to_sender) && default_reply_to) {
3325 compose_entry_append(compose,
3326 msginfo->folder->prefs->default_reply_to,
3327 COMPOSE_TO, PREF_FOLDER);
3328 compose_entry_mark_default_to(compose,
3329 msginfo->folder->prefs->default_reply_to);
3335 compose_entry_append(compose, msginfo->from,
3336 COMPOSE_TO, PREF_NONE);
3338 Xstrdup_a(tmp1, msginfo->from, return);
3339 extract_address(tmp1);
3340 compose_entry_append(compose,
3341 (!account_find_from_address(tmp1, FALSE))
3344 COMPOSE_TO, PREF_NONE);
3346 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3347 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3348 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3349 if (compose->replyto) {
3350 compose_entry_append(compose,
3352 COMPOSE_TO, PREF_NONE);
3354 compose_entry_append(compose,
3355 msginfo->from ? msginfo->from : "",
3356 COMPOSE_TO, PREF_NONE);
3359 /* replying to own mail, use original recp */
3360 compose_entry_append(compose,
3361 msginfo->to ? msginfo->to : "",
3362 COMPOSE_TO, PREF_NONE);
3363 compose_entry_append(compose,
3364 msginfo->cc ? msginfo->cc : "",
3365 COMPOSE_CC, PREF_NONE);
3370 if (to_sender || (compose->followup_to &&
3371 !strncmp(compose->followup_to, "poster", 6)))
3372 compose_entry_append
3374 (compose->replyto ? compose->replyto :
3375 msginfo->from ? msginfo->from : ""),
3376 COMPOSE_TO, PREF_NONE);
3378 else if (followup_and_reply_to || to_all) {
3379 compose_entry_append
3381 (compose->replyto ? compose->replyto :
3382 msginfo->from ? msginfo->from : ""),
3383 COMPOSE_TO, PREF_NONE);
3385 compose_entry_append
3387 compose->followup_to ? compose->followup_to :
3388 compose->newsgroups ? compose->newsgroups : "",
3389 COMPOSE_NEWSGROUPS, PREF_NONE);
3392 compose_entry_append
3394 compose->followup_to ? compose->followup_to :
3395 compose->newsgroups ? compose->newsgroups : "",
3396 COMPOSE_NEWSGROUPS, PREF_NONE);
3398 compose_reply_set_subject(compose, msginfo);
3400 if (to_ml && compose->ml_post) return;
3401 if (!to_all || compose->account->protocol == A_NNTP) return;
3403 if (compose->replyto) {
3404 Xstrdup_a(replyto, compose->replyto, return);
3405 extract_address(replyto);
3407 if (msginfo->from) {
3408 Xstrdup_a(from, msginfo->from, return);
3409 extract_address(from);
3412 if (replyto && from)
3413 cc_list = address_list_append_with_comments(cc_list, from);
3414 if (to_all && msginfo->folder &&
3415 msginfo->folder->prefs->enable_default_reply_to)
3416 cc_list = address_list_append_with_comments(cc_list,
3417 msginfo->folder->prefs->default_reply_to);
3418 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3419 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3421 ac_email = g_utf8_strdown(compose->account->address, -1);
3424 for (cur = cc_list; cur != NULL; cur = cur->next) {
3425 gchar *addr = g_utf8_strdown(cur->data, -1);
3426 extract_address(addr);
3428 if (strcmp(ac_email, addr))
3429 compose_entry_append(compose, (gchar *)cur->data,
3430 COMPOSE_CC, PREF_NONE);
3432 debug_print("Cc address same as compose account's, ignoring\n");
3437 slist_free_strings_full(cc_list);
3443 #define SET_ENTRY(entry, str) \
3446 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3449 #define SET_ADDRESS(type, str) \
3452 compose_entry_append(compose, str, type, PREF_NONE); \
3455 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3457 cm_return_if_fail(msginfo != NULL);
3459 SET_ENTRY(subject_entry, msginfo->subject);
3460 SET_ENTRY(from_name, msginfo->from);
3461 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3462 SET_ADDRESS(COMPOSE_CC, compose->cc);
3463 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3464 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3465 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3466 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3468 compose_update_priority_menu_item(compose);
3469 compose_update_privacy_system_menu_item(compose, FALSE);
3470 compose_show_first_last_header(compose, TRUE);
3476 static void compose_insert_sig(Compose *compose, gboolean replace)
3478 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3479 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3481 GtkTextIter iter, iter_end;
3482 gint cur_pos, ins_pos;
3483 gboolean prev_autowrap;
3484 gboolean found = FALSE;
3485 gboolean exists = FALSE;
3487 cm_return_if_fail(compose->account != NULL);
3491 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3492 G_CALLBACK(compose_changed_cb),
3495 mark = gtk_text_buffer_get_insert(buffer);
3496 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3497 cur_pos = gtk_text_iter_get_offset (&iter);
3500 gtk_text_buffer_get_end_iter(buffer, &iter);
3502 exists = (compose->sig_str != NULL);
3505 GtkTextIter first_iter, start_iter, end_iter;
3507 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3509 if (!exists || compose->sig_str[0] == '\0')
3512 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3513 compose->signature_tag);
3516 /* include previous \n\n */
3517 gtk_text_iter_backward_chars(&first_iter, 1);
3518 start_iter = first_iter;
3519 end_iter = first_iter;
3521 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3522 compose->signature_tag);
3523 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3524 compose->signature_tag);
3526 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3532 g_free(compose->sig_str);
3533 compose->sig_str = account_get_signature_str(compose->account);
3535 cur_pos = gtk_text_iter_get_offset(&iter);
3537 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3538 g_free(compose->sig_str);
3539 compose->sig_str = NULL;
3541 if (compose->sig_inserted == FALSE)
3542 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3543 compose->sig_inserted = TRUE;
3545 cur_pos = gtk_text_iter_get_offset(&iter);
3546 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3548 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3549 gtk_text_iter_forward_chars(&iter, 1);
3550 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3551 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3553 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3554 cur_pos = gtk_text_buffer_get_char_count (buffer);
3557 /* put the cursor where it should be
3558 * either where the quote_fmt says, either where it was */
3559 if (compose->set_cursor_pos < 0)
3560 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3562 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3563 compose->set_cursor_pos);
3565 compose->set_cursor_pos = -1;
3566 gtk_text_buffer_place_cursor(buffer, &iter);
3567 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3568 G_CALLBACK(compose_changed_cb),
3574 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3577 GtkTextBuffer *buffer;
3580 const gchar *cur_encoding;
3581 gchar buf[BUFFSIZE];
3584 gboolean prev_autowrap;
3587 GString *file_contents = NULL;
3588 ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3590 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3592 /* get the size of the file we are about to insert */
3593 ret = g_stat(file, &file_stat);
3595 gchar *shortfile = g_path_get_basename(file);
3596 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3598 return COMPOSE_INSERT_NO_FILE;
3599 } else if (prefs_common.warn_large_insert == TRUE) {
3601 /* ask user for confirmation if the file is large */
3602 if (prefs_common.warn_large_insert_size < 0 ||
3603 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3607 msg = g_strdup_printf(_("You are about to insert a file of %s "
3608 "in the message body. Are you sure you want to do that?"),
3609 to_human_readable(file_stat.st_size));
3610 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3611 g_strconcat("+", _("_Insert"), NULL), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3614 /* do we ask for confirmation next time? */
3615 if (aval & G_ALERTDISABLE) {
3616 /* no confirmation next time, disable feature in preferences */
3617 aval &= ~G_ALERTDISABLE;
3618 prefs_common.warn_large_insert = FALSE;
3621 /* abort file insertion if user canceled action */
3622 if (aval != G_ALERTALTERNATE) {
3623 return COMPOSE_INSERT_NO_FILE;
3629 if ((fp = g_fopen(file, "rb")) == NULL) {
3630 FILE_OP_ERROR(file, "fopen");
3631 return COMPOSE_INSERT_READ_ERROR;
3634 prev_autowrap = compose->autowrap;
3635 compose->autowrap = FALSE;
3637 text = GTK_TEXT_VIEW(compose->text);
3638 buffer = gtk_text_view_get_buffer(text);
3639 mark = gtk_text_buffer_get_insert(buffer);
3640 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3642 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3643 G_CALLBACK(text_inserted),
3646 cur_encoding = conv_get_locale_charset_str_no_utf8();
3648 file_contents = g_string_new("");
3649 while (fgets(buf, sizeof(buf), fp) != NULL) {
3652 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3653 str = g_strdup(buf);
3655 codeconv_set_strict(TRUE);
3656 str = conv_codeset_strdup
3657 (buf, cur_encoding, CS_INTERNAL);
3658 codeconv_set_strict(FALSE);
3661 result = COMPOSE_INSERT_INVALID_CHARACTER;
3667 /* strip <CR> if DOS/Windows file,
3668 replace <CR> with <LF> if Macintosh file. */
3671 if (len > 0 && str[len - 1] != '\n') {
3673 if (str[len] == '\r') str[len] = '\n';
3676 file_contents = g_string_append(file_contents, str);
3680 if (result == COMPOSE_INSERT_SUCCESS) {
3681 gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3683 compose_changed_cb(NULL, compose);
3684 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3685 G_CALLBACK(text_inserted),
3687 compose->autowrap = prev_autowrap;
3688 if (compose->autowrap)
3689 compose_wrap_all(compose);
3692 g_string_free(file_contents, TRUE);
3698 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3699 const gchar *filename,
3700 const gchar *content_type,
3701 const gchar *charset)
3709 GtkListStore *store;
3711 gboolean has_binary = FALSE;
3713 if (!is_file_exist(file)) {
3714 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3715 gboolean result = FALSE;
3716 if (file_from_uri && is_file_exist(file_from_uri)) {
3717 result = compose_attach_append(
3718 compose, file_from_uri,
3719 filename, content_type,
3722 g_free(file_from_uri);
3725 alertpanel_error("File %s doesn't exist\n", filename);
3728 if ((size = get_file_size(file)) < 0) {
3729 alertpanel_error("Can't get file size of %s\n", filename);
3733 /* In batch mode, we allow 0-length files to be attached no questions asked */
3734 if (size == 0 && !compose->batch) {
3735 gchar * msg = g_strdup_printf(_("File %s is empty."), filename);
3736 AlertValue aval = alertpanel_full(_("Empty file"), msg,
3737 GTK_STOCK_CANCEL, g_strconcat("+", _("_Attach anyway"), NULL), NULL, FALSE,
3738 NULL, ALERT_WARNING, G_ALERTDEFAULT);
3741 if (aval != G_ALERTALTERNATE) {
3745 if ((fp = g_fopen(file, "rb")) == NULL) {
3746 alertpanel_error(_("Can't read %s."), filename);
3751 ainfo = g_new0(AttachInfo, 1);
3752 auto_ainfo = g_auto_pointer_new_with_free
3753 (ainfo, (GFreeFunc) compose_attach_info_free);
3754 ainfo->file = g_strdup(file);
3757 ainfo->content_type = g_strdup(content_type);
3758 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3760 MsgFlags flags = {0, 0};
3762 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3763 ainfo->encoding = ENC_7BIT;
3765 ainfo->encoding = ENC_8BIT;
3767 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3768 if (msginfo && msginfo->subject)
3769 name = g_strdup(msginfo->subject);
3771 name = g_path_get_basename(filename ? filename : file);
3773 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3775 procmsg_msginfo_free(msginfo);
3777 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3778 ainfo->charset = g_strdup(charset);
3779 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3781 ainfo->encoding = ENC_BASE64;
3783 name = g_path_get_basename(filename ? filename : file);
3784 ainfo->name = g_strdup(name);
3788 ainfo->content_type = procmime_get_mime_type(file);
3789 if (!ainfo->content_type) {
3790 ainfo->content_type =
3791 g_strdup("application/octet-stream");
3792 ainfo->encoding = ENC_BASE64;
3793 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3795 procmime_get_encoding_for_text_file(file, &has_binary);
3797 ainfo->encoding = ENC_BASE64;
3798 name = g_path_get_basename(filename ? filename : file);
3799 ainfo->name = g_strdup(name);
3803 if (ainfo->name != NULL
3804 && !strcmp(ainfo->name, ".")) {
3805 g_free(ainfo->name);
3809 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3810 g_free(ainfo->content_type);
3811 ainfo->content_type = g_strdup("application/octet-stream");
3812 g_free(ainfo->charset);
3813 ainfo->charset = NULL;
3816 ainfo->size = (goffset)size;
3817 size_text = to_human_readable((goffset)size);
3819 store = GTK_LIST_STORE(gtk_tree_view_get_model
3820 (GTK_TREE_VIEW(compose->attach_clist)));
3822 gtk_list_store_append(store, &iter);
3823 gtk_list_store_set(store, &iter,
3824 COL_MIMETYPE, ainfo->content_type,
3825 COL_SIZE, size_text,
3826 COL_NAME, ainfo->name,
3827 COL_CHARSET, ainfo->charset,
3829 COL_AUTODATA, auto_ainfo,
3832 g_auto_pointer_free(auto_ainfo);
3833 compose_attach_update_label(compose);
3837 static void compose_use_signing(Compose *compose, gboolean use_signing)
3839 compose->use_signing = use_signing;
3840 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3843 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3845 compose->use_encryption = use_encryption;
3846 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3849 #define NEXT_PART_NOT_CHILD(info) \
3851 node = info->node; \
3852 while (node->children) \
3853 node = g_node_last_child(node); \
3854 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3857 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3861 MimeInfo *firsttext = NULL;
3862 MimeInfo *encrypted = NULL;
3865 const gchar *partname = NULL;
3867 mimeinfo = procmime_scan_message(msginfo);
3868 if (!mimeinfo) return;
3870 if (mimeinfo->node->children == NULL) {
3871 procmime_mimeinfo_free_all(mimeinfo);
3875 /* find first content part */
3876 child = (MimeInfo *) mimeinfo->node->children->data;
3877 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3878 child = (MimeInfo *)child->node->children->data;
3881 if (child->type == MIMETYPE_TEXT) {
3883 debug_print("First text part found\n");
3884 } else if (compose->mode == COMPOSE_REEDIT &&
3885 child->type == MIMETYPE_APPLICATION &&
3886 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3887 encrypted = (MimeInfo *)child->node->parent->data;
3890 child = (MimeInfo *) mimeinfo->node->children->data;
3891 while (child != NULL) {
3894 if (child == encrypted) {
3895 /* skip this part of tree */
3896 NEXT_PART_NOT_CHILD(child);
3900 if (child->type == MIMETYPE_MULTIPART) {
3901 /* get the actual content */
3902 child = procmime_mimeinfo_next(child);
3906 if (child == firsttext) {
3907 child = procmime_mimeinfo_next(child);
3911 outfile = procmime_get_tmp_file_name(child);
3912 if ((err = procmime_get_part(outfile, child)) < 0)
3913 g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err));
3915 gchar *content_type;
3917 content_type = procmime_get_content_type_str(child->type, child->subtype);
3919 /* if we meet a pgp signature, we don't attach it, but
3920 * we force signing. */
3921 if ((strcmp(content_type, "application/pgp-signature") &&
3922 strcmp(content_type, "application/pkcs7-signature") &&
3923 strcmp(content_type, "application/x-pkcs7-signature"))
3924 || compose->mode == COMPOSE_REDIRECT) {
3925 partname = procmime_mimeinfo_get_parameter(child, "filename");
3926 if (partname == NULL)
3927 partname = procmime_mimeinfo_get_parameter(child, "name");
3928 if (partname == NULL)
3930 compose_attach_append(compose, outfile,
3931 partname, content_type,
3932 procmime_mimeinfo_get_parameter(child, "charset"));
3934 compose_force_signing(compose, compose->account, NULL);
3936 g_free(content_type);
3939 NEXT_PART_NOT_CHILD(child);
3941 procmime_mimeinfo_free_all(mimeinfo);
3944 #undef NEXT_PART_NOT_CHILD
3949 WAIT_FOR_INDENT_CHAR,
3950 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3953 /* return indent length, we allow:
3954 indent characters followed by indent characters or spaces/tabs,
3955 alphabets and numbers immediately followed by indent characters,
3956 and the repeating sequences of the above
3957 If quote ends with multiple spaces, only the first one is included. */
3958 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3959 const GtkTextIter *start, gint *len)
3961 GtkTextIter iter = *start;
3965 IndentState state = WAIT_FOR_INDENT_CHAR;
3968 gint alnum_count = 0;
3969 gint space_count = 0;
3972 if (prefs_common.quote_chars == NULL) {
3976 while (!gtk_text_iter_ends_line(&iter)) {
3977 wc = gtk_text_iter_get_char(&iter);
3978 if (g_unichar_iswide(wc))
3980 clen = g_unichar_to_utf8(wc, ch);
3984 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3985 is_space = g_unichar_isspace(wc);
3987 if (state == WAIT_FOR_INDENT_CHAR) {
3988 if (!is_indent && !g_unichar_isalnum(wc))
3991 quote_len += alnum_count + space_count + 1;
3992 alnum_count = space_count = 0;
3993 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3996 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3997 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
4001 else if (is_indent) {
4002 quote_len += alnum_count + space_count + 1;
4003 alnum_count = space_count = 0;
4006 state = WAIT_FOR_INDENT_CHAR;
4010 gtk_text_iter_forward_char(&iter);
4013 if (quote_len > 0 && space_count > 0)
4019 if (quote_len > 0) {
4021 gtk_text_iter_forward_chars(&iter, quote_len);
4022 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
4028 /* return >0 if the line is itemized */
4029 static int compose_itemized_length(GtkTextBuffer *buffer,
4030 const GtkTextIter *start)
4032 GtkTextIter iter = *start;
4037 if (gtk_text_iter_ends_line(&iter))
4042 wc = gtk_text_iter_get_char(&iter);
4043 if (!g_unichar_isspace(wc))
4045 gtk_text_iter_forward_char(&iter);
4046 if (gtk_text_iter_ends_line(&iter))
4050 clen = g_unichar_to_utf8(wc, ch);
4054 if (!strchr("*-+", ch[0]))
4057 gtk_text_iter_forward_char(&iter);
4058 if (gtk_text_iter_ends_line(&iter))
4060 wc = gtk_text_iter_get_char(&iter);
4061 if (g_unichar_isspace(wc)) {
4067 /* return the string at the start of the itemization */
4068 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
4069 const GtkTextIter *start)
4071 GtkTextIter iter = *start;
4074 GString *item_chars = g_string_new("");
4077 if (gtk_text_iter_ends_line(&iter))
4082 wc = gtk_text_iter_get_char(&iter);
4083 if (!g_unichar_isspace(wc))
4085 gtk_text_iter_forward_char(&iter);
4086 if (gtk_text_iter_ends_line(&iter))
4088 g_string_append_unichar(item_chars, wc);
4091 str = item_chars->str;
4092 g_string_free(item_chars, FALSE);
4096 /* return the number of spaces at a line's start */
4097 static int compose_left_offset_length(GtkTextBuffer *buffer,
4098 const GtkTextIter *start)
4100 GtkTextIter iter = *start;
4103 if (gtk_text_iter_ends_line(&iter))
4107 wc = gtk_text_iter_get_char(&iter);
4108 if (!g_unichar_isspace(wc))
4111 gtk_text_iter_forward_char(&iter);
4112 if (gtk_text_iter_ends_line(&iter))
4116 gtk_text_iter_forward_char(&iter);
4117 if (gtk_text_iter_ends_line(&iter))
4122 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4123 const GtkTextIter *start,
4124 GtkTextIter *break_pos,
4128 GtkTextIter iter = *start, line_end = *start;
4129 PangoLogAttr *attrs;
4136 gboolean can_break = FALSE;
4137 gboolean do_break = FALSE;
4138 gboolean was_white = FALSE;
4139 gboolean prev_dont_break = FALSE;
4141 gtk_text_iter_forward_to_line_end(&line_end);
4142 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4143 len = g_utf8_strlen(str, -1);
4147 g_warning("compose_get_line_break_pos: len = 0!");
4151 /* g_print("breaking line: %d: %s (len = %d)\n",
4152 gtk_text_iter_get_line(&iter), str, len); */
4154 attrs = g_new(PangoLogAttr, len + 1);
4156 pango_default_break(str, -1, NULL, attrs, len + 1);
4160 /* skip quote and leading spaces */
4161 for (i = 0; *p != '\0' && i < len; i++) {
4164 wc = g_utf8_get_char(p);
4165 if (i >= quote_len && !g_unichar_isspace(wc))
4167 if (g_unichar_iswide(wc))
4169 else if (*p == '\t')
4173 p = g_utf8_next_char(p);
4176 for (; *p != '\0' && i < len; i++) {
4177 PangoLogAttr *attr = attrs + i;
4181 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4184 was_white = attr->is_white;
4186 /* don't wrap URI */
4187 if ((uri_len = get_uri_len(p)) > 0) {
4189 if (pos > 0 && col > max_col) {
4199 wc = g_utf8_get_char(p);
4200 if (g_unichar_iswide(wc)) {
4202 if (prev_dont_break && can_break && attr->is_line_break)
4204 } else if (*p == '\t')
4208 if (pos > 0 && col > max_col) {
4213 if (*p == '-' || *p == '/')
4214 prev_dont_break = TRUE;
4216 prev_dont_break = FALSE;
4218 p = g_utf8_next_char(p);
4222 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4227 *break_pos = *start;
4228 gtk_text_iter_set_line_offset(break_pos, pos);
4233 static gboolean compose_join_next_line(Compose *compose,
4234 GtkTextBuffer *buffer,
4236 const gchar *quote_str)
4238 GtkTextIter iter_ = *iter, cur, prev, next, end;
4239 PangoLogAttr attrs[3];
4241 gchar *next_quote_str;
4244 gboolean keep_cursor = FALSE;
4246 if (!gtk_text_iter_forward_line(&iter_) ||
4247 gtk_text_iter_ends_line(&iter_)) {
4250 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4252 if ((quote_str || next_quote_str) &&
4253 strcmp2(quote_str, next_quote_str) != 0) {
4254 g_free(next_quote_str);
4257 g_free(next_quote_str);
4260 if (quote_len > 0) {
4261 gtk_text_iter_forward_chars(&end, quote_len);
4262 if (gtk_text_iter_ends_line(&end)) {
4267 /* don't join itemized lines */
4268 if (compose_itemized_length(buffer, &end) > 0) {
4272 /* don't join signature separator */
4273 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4276 /* delete quote str */
4278 gtk_text_buffer_delete(buffer, &iter_, &end);
4280 /* don't join line breaks put by the user */
4282 gtk_text_iter_backward_char(&cur);
4283 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4284 gtk_text_iter_forward_char(&cur);
4288 gtk_text_iter_forward_char(&cur);
4289 /* delete linebreak and extra spaces */
4290 while (gtk_text_iter_backward_char(&cur)) {
4291 wc1 = gtk_text_iter_get_char(&cur);
4292 if (!g_unichar_isspace(wc1))
4297 while (!gtk_text_iter_ends_line(&cur)) {
4298 wc1 = gtk_text_iter_get_char(&cur);
4299 if (!g_unichar_isspace(wc1))
4301 gtk_text_iter_forward_char(&cur);
4304 if (!gtk_text_iter_equal(&prev, &next)) {
4307 mark = gtk_text_buffer_get_insert(buffer);
4308 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4309 if (gtk_text_iter_equal(&prev, &cur))
4311 gtk_text_buffer_delete(buffer, &prev, &next);
4315 /* insert space if required */
4316 gtk_text_iter_backward_char(&prev);
4317 wc1 = gtk_text_iter_get_char(&prev);
4318 wc2 = gtk_text_iter_get_char(&next);
4319 gtk_text_iter_forward_char(&next);
4320 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4321 pango_default_break(str, -1, NULL, attrs, 3);
4322 if (!attrs[1].is_line_break ||
4323 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4324 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4326 gtk_text_iter_backward_char(&iter_);
4327 gtk_text_buffer_place_cursor(buffer, &iter_);
4336 #define ADD_TXT_POS(bp_, ep_, pti_) \
4337 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4338 last = last->next; \
4339 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4340 last->next = NULL; \
4342 g_warning("alloc error scanning URIs"); \
4345 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4347 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4348 GtkTextBuffer *buffer;
4349 GtkTextIter iter, break_pos, end_of_line;
4350 gchar *quote_str = NULL;
4352 gboolean wrap_quote = force || prefs_common.linewrap_quote;
4353 gboolean prev_autowrap = compose->autowrap;
4354 gint startq_offset = -1, noq_offset = -1;
4355 gint uri_start = -1, uri_stop = -1;
4356 gint nouri_start = -1, nouri_stop = -1;
4357 gint num_blocks = 0;
4358 gint quotelevel = -1;
4359 gboolean modified = force;
4360 gboolean removed = FALSE;
4361 gboolean modified_before_remove = FALSE;
4363 gboolean start = TRUE;
4364 gint itemized_len = 0, rem_item_len = 0;
4365 gchar *itemized_chars = NULL;
4366 gboolean item_continuation = FALSE;
4371 if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
4375 compose->autowrap = FALSE;
4377 buffer = gtk_text_view_get_buffer(text);
4378 undo_wrapping(compose->undostruct, TRUE);
4383 mark = gtk_text_buffer_get_insert(buffer);
4384 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4388 if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
4389 if (gtk_text_iter_ends_line(&iter)) {
4390 while (gtk_text_iter_ends_line(&iter) &&
4391 gtk_text_iter_forward_line(&iter))
4394 while (gtk_text_iter_backward_line(&iter)) {
4395 if (gtk_text_iter_ends_line(&iter)) {
4396 gtk_text_iter_forward_line(&iter);
4402 /* move to line start */
4403 gtk_text_iter_set_line_offset(&iter, 0);
4406 itemized_len = compose_itemized_length(buffer, &iter);
4408 if (!itemized_len) {
4409 itemized_len = compose_left_offset_length(buffer, &iter);
4410 item_continuation = TRUE;
4414 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4416 /* go until paragraph end (empty line) */
4417 while (start || !gtk_text_iter_ends_line(&iter)) {
4418 gchar *scanpos = NULL;
4419 /* parse table - in order of priority */
4421 const gchar *needle; /* token */
4423 /* token search function */
4424 gchar *(*search) (const gchar *haystack,
4425 const gchar *needle);
4426 /* part parsing function */
4427 gboolean (*parse) (const gchar *start,
4428 const gchar *scanpos,
4432 /* part to URI function */
4433 gchar *(*build_uri) (const gchar *bp,
4437 static struct table parser[] = {
4438 {"http://", strcasestr, get_uri_part, make_uri_string},
4439 {"https://", strcasestr, get_uri_part, make_uri_string},
4440 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4441 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4442 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4443 {"www.", strcasestr, get_uri_part, make_http_string},
4444 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4445 {"@", strcasestr, get_email_part, make_email_string}
4447 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4448 gint last_index = PARSE_ELEMS;
4450 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4454 if (!prev_autowrap && num_blocks == 0) {
4456 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4457 G_CALLBACK(text_inserted),
4460 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4463 uri_start = uri_stop = -1;
4465 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4468 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4469 if (startq_offset == -1)
4470 startq_offset = gtk_text_iter_get_offset(&iter);
4471 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4472 if (quotelevel > 2) {
4473 /* recycle colors */
4474 if (prefs_common.recycle_quote_colors)
4483 if (startq_offset == -1)
4484 noq_offset = gtk_text_iter_get_offset(&iter);
4488 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4491 if (gtk_text_iter_ends_line(&iter)) {
4493 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4494 prefs_common.linewrap_len,
4496 GtkTextIter prev, next, cur;
4497 if (prev_autowrap != FALSE || force) {
4498 compose->automatic_break = TRUE;
4500 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4501 compose->automatic_break = FALSE;
4502 if (itemized_len && compose->autoindent) {
4503 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4504 if (!item_continuation)
4505 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4507 } else if (quote_str && wrap_quote) {
4508 compose->automatic_break = TRUE;
4510 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4511 compose->automatic_break = FALSE;
4512 if (itemized_len && compose->autoindent) {
4513 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4514 if (!item_continuation)
4515 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4519 /* remove trailing spaces */
4521 rem_item_len = itemized_len;
4522 while (compose->autoindent && rem_item_len-- > 0)
4523 gtk_text_iter_backward_char(&cur);
4524 gtk_text_iter_backward_char(&cur);
4527 while (!gtk_text_iter_starts_line(&cur)) {
4530 gtk_text_iter_backward_char(&cur);
4531 wc = gtk_text_iter_get_char(&cur);
4532 if (!g_unichar_isspace(wc))
4536 if (!gtk_text_iter_equal(&prev, &next)) {
4537 gtk_text_buffer_delete(buffer, &prev, &next);
4539 gtk_text_iter_forward_char(&break_pos);
4543 gtk_text_buffer_insert(buffer, &break_pos,
4547 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4549 /* move iter to current line start */
4550 gtk_text_iter_set_line_offset(&iter, 0);
4557 /* move iter to next line start */
4563 if (!prev_autowrap && num_blocks > 0) {
4565 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4566 G_CALLBACK(text_inserted),
4570 while (!gtk_text_iter_ends_line(&end_of_line)) {
4571 gtk_text_iter_forward_char(&end_of_line);
4573 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4575 nouri_start = gtk_text_iter_get_offset(&iter);
4576 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4578 walk_pos = gtk_text_iter_get_offset(&iter);
4579 /* FIXME: this looks phony. scanning for anything in the parse table */
4580 for (n = 0; n < PARSE_ELEMS; n++) {
4583 tmp = parser[n].search(walk, parser[n].needle);
4585 if (scanpos == NULL || tmp < scanpos) {
4594 /* check if URI can be parsed */
4595 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4596 (const gchar **)&ep, FALSE)
4597 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4601 strlen(parser[last_index].needle);
4604 uri_start = walk_pos + (bp - o_walk);
4605 uri_stop = walk_pos + (ep - o_walk);
4609 gtk_text_iter_forward_line(&iter);
4612 if (startq_offset != -1) {
4613 GtkTextIter startquote, endquote;
4614 gtk_text_buffer_get_iter_at_offset(
4615 buffer, &startquote, startq_offset);
4618 switch (quotelevel) {
4620 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4621 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4622 gtk_text_buffer_apply_tag_by_name(
4623 buffer, "quote0", &startquote, &endquote);
4624 gtk_text_buffer_remove_tag_by_name(
4625 buffer, "quote1", &startquote, &endquote);
4626 gtk_text_buffer_remove_tag_by_name(
4627 buffer, "quote2", &startquote, &endquote);
4632 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4633 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4634 gtk_text_buffer_apply_tag_by_name(
4635 buffer, "quote1", &startquote, &endquote);
4636 gtk_text_buffer_remove_tag_by_name(
4637 buffer, "quote0", &startquote, &endquote);
4638 gtk_text_buffer_remove_tag_by_name(
4639 buffer, "quote2", &startquote, &endquote);
4644 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4645 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4646 gtk_text_buffer_apply_tag_by_name(
4647 buffer, "quote2", &startquote, &endquote);
4648 gtk_text_buffer_remove_tag_by_name(
4649 buffer, "quote0", &startquote, &endquote);
4650 gtk_text_buffer_remove_tag_by_name(
4651 buffer, "quote1", &startquote, &endquote);
4657 } else if (noq_offset != -1) {
4658 GtkTextIter startnoquote, endnoquote;
4659 gtk_text_buffer_get_iter_at_offset(
4660 buffer, &startnoquote, noq_offset);
4663 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4664 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4665 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4666 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4667 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4668 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4669 gtk_text_buffer_remove_tag_by_name(
4670 buffer, "quote0", &startnoquote, &endnoquote);
4671 gtk_text_buffer_remove_tag_by_name(
4672 buffer, "quote1", &startnoquote, &endnoquote);
4673 gtk_text_buffer_remove_tag_by_name(
4674 buffer, "quote2", &startnoquote, &endnoquote);
4680 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4681 GtkTextIter nouri_start_iter, nouri_end_iter;
4682 gtk_text_buffer_get_iter_at_offset(
4683 buffer, &nouri_start_iter, nouri_start);
4684 gtk_text_buffer_get_iter_at_offset(
4685 buffer, &nouri_end_iter, nouri_stop);
4686 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4687 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4688 gtk_text_buffer_remove_tag_by_name(
4689 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4690 modified_before_remove = modified;
4695 if (uri_start >= 0 && uri_stop > 0) {
4696 GtkTextIter uri_start_iter, uri_end_iter, back;
4697 gtk_text_buffer_get_iter_at_offset(
4698 buffer, &uri_start_iter, uri_start);
4699 gtk_text_buffer_get_iter_at_offset(
4700 buffer, &uri_end_iter, uri_stop);
4701 back = uri_end_iter;
4702 gtk_text_iter_backward_char(&back);
4703 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4704 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4705 gtk_text_buffer_apply_tag_by_name(
4706 buffer, "link", &uri_start_iter, &uri_end_iter);
4708 if (removed && !modified_before_remove) {
4714 // debug_print("not modified, out after %d lines\n", lines);
4718 // debug_print("modified, out after %d lines\n", lines);
4720 g_free(itemized_chars);
4723 undo_wrapping(compose->undostruct, FALSE);
4724 compose->autowrap = prev_autowrap;
4729 void compose_action_cb(void *data)
4731 Compose *compose = (Compose *)data;
4732 compose_wrap_all(compose);
4735 static void compose_wrap_all(Compose *compose)
4737 compose_wrap_all_full(compose, FALSE);
4740 static void compose_wrap_all_full(Compose *compose, gboolean force)
4742 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4743 GtkTextBuffer *buffer;
4745 gboolean modified = TRUE;
4747 buffer = gtk_text_view_get_buffer(text);
4749 gtk_text_buffer_get_start_iter(buffer, &iter);
4751 undo_wrapping(compose->undostruct, TRUE);
4753 while (!gtk_text_iter_is_end(&iter) && modified)
4754 modified = compose_beautify_paragraph(compose, &iter, force);
4756 undo_wrapping(compose->undostruct, FALSE);
4760 static void compose_set_title(Compose *compose)
4766 edited = compose->modified ? _(" [Edited]") : "";
4768 subject = gtk_editable_get_chars(
4769 GTK_EDITABLE(compose->subject_entry), 0, -1);
4771 #ifndef GENERIC_UMPC
4772 if (subject && strlen(subject))
4773 str = g_strdup_printf(_("%s - Compose message%s"),
4776 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4778 str = g_strdup(_("Compose message"));
4781 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4787 * compose_current_mail_account:
4789 * Find a current mail account (the currently selected account, or the
4790 * default account, if a news account is currently selected). If a
4791 * mail account cannot be found, display an error message.
4793 * Return value: Mail account, or NULL if not found.
4795 static PrefsAccount *
4796 compose_current_mail_account(void)
4800 if (cur_account && cur_account->protocol != A_NNTP)
4803 ac = account_get_default();
4804 if (!ac || ac->protocol == A_NNTP) {
4805 alertpanel_error(_("Account for sending mail is not specified.\n"
4806 "Please select a mail account before sending."));
4813 #define QUOTE_IF_REQUIRED(out, str) \
4815 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4819 len = strlen(str) + 3; \
4820 if ((__tmp = alloca(len)) == NULL) { \
4821 g_warning("can't allocate memory"); \
4822 g_string_free(header, TRUE); \
4825 g_snprintf(__tmp, len, "\"%s\"", str); \
4830 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4831 g_warning("can't allocate memory"); \
4832 g_string_free(header, TRUE); \
4835 strcpy(__tmp, str); \
4841 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4843 if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) { \
4847 len = strlen(str) + 3; \
4848 if ((__tmp = alloca(len)) == NULL) { \
4849 g_warning("can't allocate memory"); \
4852 g_snprintf(__tmp, len, "\"%s\"", str); \
4857 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4858 g_warning("can't allocate memory"); \
4861 strcpy(__tmp, str); \
4867 static void compose_select_account(Compose *compose, PrefsAccount *account,
4870 gchar *from = NULL, *header = NULL;
4871 ComposeHeaderEntry *header_entry;
4872 #if GTK_CHECK_VERSION(2, 24, 0)
4876 cm_return_if_fail(account != NULL);
4878 compose->account = account;
4879 if (account->name && *account->name) {
4881 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4882 qbuf = escape_internal_quotes(buf, '"');
4883 from = g_strdup_printf("%s <%s>",
4884 qbuf, account->address);
4887 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4889 from = g_strdup_printf("<%s>",
4891 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4896 compose_set_title(compose);
4898 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4899 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4901 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4902 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4903 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4905 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4907 activate_privacy_system(compose, account, FALSE);
4909 if (!init && compose->mode != COMPOSE_REDIRECT) {
4910 undo_block(compose->undostruct);
4911 compose_insert_sig(compose, TRUE);
4912 undo_unblock(compose->undostruct);
4915 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4916 #if !GTK_CHECK_VERSION(2, 24, 0)
4917 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4919 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry->combo), &iter))
4920 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4921 header_entry->combo)), &iter, COMBOBOX_TEXT, &header, -1);
4924 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4925 if (account->protocol == A_NNTP) {
4926 if (!strcmp(header, _("To:")))
4927 combobox_select_by_text(
4928 GTK_COMBO_BOX(header_entry->combo),
4931 if (!strcmp(header, _("Newsgroups:")))
4932 combobox_select_by_text(
4933 GTK_COMBO_BOX(header_entry->combo),
4941 /* use account's dict info if set */
4942 if (compose->gtkaspell) {
4943 if (account->enable_default_dictionary)
4944 gtkaspell_change_dict(compose->gtkaspell,
4945 account->default_dictionary, FALSE);
4946 if (account->enable_default_alt_dictionary)
4947 gtkaspell_change_alt_dict(compose->gtkaspell,
4948 account->default_alt_dictionary);
4949 if (account->enable_default_dictionary
4950 || account->enable_default_alt_dictionary)
4951 compose_spell_menu_changed(compose);
4956 gboolean compose_check_for_valid_recipient(Compose *compose) {
4957 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4958 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4959 gboolean recipient_found = FALSE;
4963 /* free to and newsgroup list */
4964 slist_free_strings_full(compose->to_list);
4965 compose->to_list = NULL;
4967 slist_free_strings_full(compose->newsgroup_list);
4968 compose->newsgroup_list = NULL;
4970 /* search header entries for to and newsgroup entries */
4971 for (list = compose->header_list; list; list = list->next) {
4974 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4975 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4978 if (entry[0] != '\0') {
4979 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4980 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4981 compose->to_list = address_list_append(compose->to_list, entry);
4982 recipient_found = TRUE;
4985 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4986 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4987 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4988 recipient_found = TRUE;
4995 return recipient_found;
4998 static gboolean compose_check_for_set_recipients(Compose *compose)
5000 if (compose->account->set_autocc && compose->account->auto_cc) {
5001 gboolean found_other = FALSE;
5003 /* search header entries for to and newsgroup entries */
5004 for (list = compose->header_list; list; list = list->next) {
5007 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5008 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5011 if (strcmp(entry, compose->account->auto_cc)
5012 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
5022 if (compose->batch) {
5023 gtk_widget_show_all(compose->window);
5025 aval = alertpanel(_("Send"),
5026 _("The only recipient is the default CC address. Send anyway?"),
5027 GTK_STOCK_CANCEL, g_strconcat("+", _("_Send"), NULL), NULL);
5028 if (aval != G_ALERTALTERNATE)
5032 if (compose->account->set_autobcc && compose->account->auto_bcc) {
5033 gboolean found_other = FALSE;
5035 /* search header entries for to and newsgroup entries */
5036 for (list = compose->header_list; list; list = list->next) {
5039 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5040 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5043 if (strcmp(entry, compose->account->auto_bcc)
5044 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
5054 if (compose->batch) {
5055 gtk_widget_show_all(compose->window);
5057 aval = alertpanel(_("Send"),
5058 _("The only recipient is the default BCC address. Send anyway?"),
5059 GTK_STOCK_CANCEL, g_strconcat("+", _("_Send"), NULL), NULL);
5060 if (aval != G_ALERTALTERNATE)
5067 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
5071 if (compose_check_for_valid_recipient(compose) == FALSE) {
5072 if (compose->batch) {
5073 gtk_widget_show_all(compose->window);
5075 alertpanel_error(_("Recipient is not specified."));
5079 if (compose_check_for_set_recipients(compose) == FALSE) {
5083 if (!compose->batch && prefs_common.warn_empty_subj == TRUE) {
5084 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5085 if (*str == '\0' && check_everything == TRUE &&
5086 compose->mode != COMPOSE_REDIRECT) {
5088 gchar *button_label;
5091 if (compose->sending)
5092 button_label = g_strconcat("+", _("_Send"), NULL);
5094 button_label = g_strconcat("+", _("_Queue"), NULL);
5095 message = g_strdup_printf(_("Subject is empty. %s"),
5096 compose->sending?_("Send it anyway?"):
5097 _("Queue it anyway?"));
5099 aval = alertpanel_full(compose->sending?_("Send"):_("Send later"), message,
5100 GTK_STOCK_CANCEL, button_label, NULL, TRUE, NULL,
5101 ALERT_QUESTION, G_ALERTDEFAULT);
5103 if (aval & G_ALERTDISABLE) {
5104 aval &= ~G_ALERTDISABLE;
5105 prefs_common.warn_empty_subj = FALSE;
5107 if (aval != G_ALERTALTERNATE)
5112 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5118 gint compose_send(Compose *compose)
5121 FolderItem *folder = NULL;
5123 gchar *msgpath = NULL;
5124 gboolean discard_window = FALSE;
5125 gchar *errstr = NULL;
5126 gchar *tmsgid = NULL;
5127 MainWindow *mainwin = mainwindow_get_mainwindow();
5128 gboolean queued_removed = FALSE;
5130 if (prefs_common.send_dialog_invisible
5131 || compose->batch == TRUE)
5132 discard_window = TRUE;
5134 compose_allow_user_actions (compose, FALSE);
5135 compose->sending = TRUE;
5137 if (compose_check_entries(compose, TRUE) == FALSE) {
5138 if (compose->batch) {
5139 gtk_widget_show_all(compose->window);
5145 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5148 if (compose->batch) {
5149 gtk_widget_show_all(compose->window);
5152 alertpanel_error(_("Could not queue message for sending:\n\n"
5153 "Charset conversion failed."));
5154 } else if (val == -5) {
5155 alertpanel_error(_("Could not queue message for sending:\n\n"
5156 "Couldn't get recipient encryption key."));
5157 } else if (val == -6) {
5159 } else if (val == -3) {
5160 if (privacy_peek_error())
5161 alertpanel_error(_("Could not queue message for sending:\n\n"
5162 "Signature failed: %s"), privacy_get_error());
5163 } else if (val == -2 && errno != 0) {
5164 alertpanel_error(_("Could not queue message for sending:\n\n%s."), g_strerror(errno));
5166 alertpanel_error(_("Could not queue message for sending."));
5171 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5172 if (discard_window) {
5173 compose->sending = FALSE;
5174 compose_close(compose);
5175 /* No more compose access in the normal codepath
5176 * after this point! */
5181 alertpanel_error(_("The message was queued but could not be "
5182 "sent.\nUse \"Send queued messages\" from "
5183 "the main window to retry."));
5184 if (!discard_window) {
5191 if (msgpath == NULL) {
5192 msgpath = folder_item_fetch_msg(folder, msgnum);
5193 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5196 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5197 claws_unlink(msgpath);
5200 if (!discard_window) {
5202 if (!queued_removed)
5203 folder_item_remove_msg(folder, msgnum);
5204 folder_item_scan(folder);
5206 /* make sure we delete that */
5207 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5209 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5210 folder_item_remove_msg(folder, tmp->msgnum);
5211 procmsg_msginfo_free(tmp);
5218 if (!queued_removed)
5219 folder_item_remove_msg(folder, msgnum);
5220 folder_item_scan(folder);
5222 /* make sure we delete that */
5223 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5225 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5226 folder_item_remove_msg(folder, tmp->msgnum);
5227 procmsg_msginfo_free(tmp);
5230 if (!discard_window) {
5231 compose->sending = FALSE;
5232 compose_allow_user_actions (compose, TRUE);
5233 compose_close(compose);
5237 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5238 "the main window to retry."), errstr);
5241 alertpanel_error_log(_("The message was queued but could not be "
5242 "sent.\nUse \"Send queued messages\" from "
5243 "the main window to retry."));
5245 if (!discard_window) {
5254 toolbar_main_set_sensitive(mainwin);
5255 main_window_set_menu_sensitive(mainwin);
5261 compose_allow_user_actions (compose, TRUE);
5262 compose->sending = FALSE;
5263 compose->modified = TRUE;
5264 toolbar_main_set_sensitive(mainwin);
5265 main_window_set_menu_sensitive(mainwin);
5270 static gboolean compose_use_attach(Compose *compose)
5272 GtkTreeModel *model = gtk_tree_view_get_model
5273 (GTK_TREE_VIEW(compose->attach_clist));
5274 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5277 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5280 gchar buf[BUFFSIZE];
5282 gboolean first_to_address;
5283 gboolean first_cc_address;
5285 ComposeHeaderEntry *headerentry;
5286 const gchar *headerentryname;
5287 const gchar *cc_hdr;
5288 const gchar *to_hdr;
5289 gboolean err = FALSE;
5291 debug_print("Writing redirect header\n");
5293 cc_hdr = prefs_common_translated_header_name("Cc:");
5294 to_hdr = prefs_common_translated_header_name("To:");
5296 first_to_address = TRUE;
5297 for (list = compose->header_list; list; list = list->next) {
5298 headerentry = ((ComposeHeaderEntry *)list->data);
5299 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5301 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5302 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5303 Xstrdup_a(str, entstr, return -1);
5305 if (str[0] != '\0') {
5306 compose_convert_header
5307 (compose, buf, sizeof(buf), str,
5308 strlen("Resent-To") + 2, TRUE);
5310 if (first_to_address) {
5311 err |= (fprintf(fp, "Resent-To: ") < 0);
5312 first_to_address = FALSE;
5314 err |= (fprintf(fp, ",") < 0);
5316 err |= (fprintf(fp, "%s", buf) < 0);
5320 if (!first_to_address) {
5321 err |= (fprintf(fp, "\n") < 0);
5324 first_cc_address = TRUE;
5325 for (list = compose->header_list; list; list = list->next) {
5326 headerentry = ((ComposeHeaderEntry *)list->data);
5327 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5329 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5330 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5331 Xstrdup_a(str, strg, return -1);
5333 if (str[0] != '\0') {
5334 compose_convert_header
5335 (compose, buf, sizeof(buf), str,
5336 strlen("Resent-Cc") + 2, TRUE);
5338 if (first_cc_address) {
5339 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5340 first_cc_address = FALSE;
5342 err |= (fprintf(fp, ",") < 0);
5344 err |= (fprintf(fp, "%s", buf) < 0);
5348 if (!first_cc_address) {
5349 err |= (fprintf(fp, "\n") < 0);
5352 return (err ? -1:0);
5355 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5357 gchar buf[BUFFSIZE];
5359 const gchar *entstr;
5360 /* struct utsname utsbuf; */
5361 gboolean err = FALSE;
5363 cm_return_val_if_fail(fp != NULL, -1);
5364 cm_return_val_if_fail(compose->account != NULL, -1);
5365 cm_return_val_if_fail(compose->account->address != NULL, -1);
5368 get_rfc822_date(buf, sizeof(buf));
5369 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5372 if (compose->account->name && *compose->account->name) {
5373 compose_convert_header
5374 (compose, buf, sizeof(buf), compose->account->name,
5375 strlen("From: "), TRUE);
5376 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5377 buf, compose->account->address) < 0);
5379 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5382 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5383 if (*entstr != '\0') {
5384 Xstrdup_a(str, entstr, return -1);
5387 compose_convert_header(compose, buf, sizeof(buf), str,
5388 strlen("Subject: "), FALSE);
5389 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5393 /* Resent-Message-ID */
5394 if (compose->account->set_domain && compose->account->domain) {
5395 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5396 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5397 g_snprintf(buf, sizeof(buf), "%s",
5398 strchr(compose->account->address, '@') ?
5399 strchr(compose->account->address, '@')+1 :
5400 compose->account->address);
5402 g_snprintf(buf, sizeof(buf), "%s", "");
5405 if (compose->account->gen_msgid) {
5407 if (compose->account->msgid_with_addr) {
5408 addr = compose->account->address;
5410 generate_msgid(buf, sizeof(buf), addr);
5411 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5413 g_free(compose->msgid);
5414 compose->msgid = g_strdup(buf);
5416 compose->msgid = NULL;
5419 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5422 /* separator between header and body */
5423 err |= (fputs("\n", fp) == EOF);
5425 return (err ? -1:0);
5428 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5432 gchar buf[BUFFSIZE];
5434 gboolean skip = FALSE;
5435 gboolean err = FALSE;
5436 gchar *not_included[]={
5437 "Return-Path:", "Delivered-To:", "Received:",
5438 "Subject:", "X-UIDL:", "AF:",
5439 "NF:", "PS:", "SRH:",
5440 "SFN:", "DSR:", "MID:",
5441 "CFG:", "PT:", "S:",
5442 "RQ:", "SSV:", "NSV:",
5443 "SSH:", "R:", "MAID:",
5444 "NAID:", "RMID:", "FMID:",
5445 "SCF:", "RRCPT:", "NG:",
5446 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5447 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5448 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5449 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5450 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5453 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5454 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5458 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5460 for (i = 0; not_included[i] != NULL; i++) {
5461 if (g_ascii_strncasecmp(buf, not_included[i],
5462 strlen(not_included[i])) == 0) {
5469 if (fputs(buf, fdest) == -1)
5472 if (!prefs_common.redirect_keep_from) {
5473 if (g_ascii_strncasecmp(buf, "From:",
5474 strlen("From:")) == 0) {
5475 err |= (fputs(" (by way of ", fdest) == EOF);
5476 if (compose->account->name
5477 && *compose->account->name) {
5478 compose_convert_header
5479 (compose, buf, sizeof(buf),
5480 compose->account->name,
5483 err |= (fprintf(fdest, "%s <%s>",
5485 compose->account->address) < 0);
5487 err |= (fprintf(fdest, "%s",
5488 compose->account->address) < 0);
5489 err |= (fputs(")", fdest) == EOF);
5493 if (fputs("\n", fdest) == -1)
5500 if (compose_redirect_write_headers(compose, fdest))
5503 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5504 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5517 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5519 GtkTextBuffer *buffer;
5520 GtkTextIter start, end;
5521 gchar *chars, *tmp_enc_file, *content;
5523 const gchar *out_codeset;
5524 EncodingType encoding = ENC_UNKNOWN;
5525 MimeInfo *mimemsg, *mimetext;
5527 const gchar *src_codeset = CS_INTERNAL;
5528 gchar *from_addr = NULL;
5529 gchar *from_name = NULL;
5532 if (action == COMPOSE_WRITE_FOR_SEND)
5533 attach_parts = TRUE;
5535 /* create message MimeInfo */
5536 mimemsg = procmime_mimeinfo_new();
5537 mimemsg->type = MIMETYPE_MESSAGE;
5538 mimemsg->subtype = g_strdup("rfc822");
5539 mimemsg->content = MIMECONTENT_MEM;
5540 mimemsg->tmp = TRUE; /* must free content later */
5541 mimemsg->data.mem = compose_get_header(compose);
5543 /* Create text part MimeInfo */
5544 /* get all composed text */
5545 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5546 gtk_text_buffer_get_start_iter(buffer, &start);
5547 gtk_text_buffer_get_end_iter(buffer, &end);
5548 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5550 out_codeset = conv_get_charset_str(compose->out_encoding);
5552 if (!out_codeset && is_ascii_str(chars)) {
5553 out_codeset = CS_US_ASCII;
5554 } else if (prefs_common.outgoing_fallback_to_ascii &&
5555 is_ascii_str(chars)) {
5556 out_codeset = CS_US_ASCII;
5557 encoding = ENC_7BIT;
5561 gchar *test_conv_global_out = NULL;
5562 gchar *test_conv_reply = NULL;
5564 /* automatic mode. be automatic. */
5565 codeconv_set_strict(TRUE);
5567 out_codeset = conv_get_outgoing_charset_str();
5569 debug_print("trying to convert to %s\n", out_codeset);
5570 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5573 if (!test_conv_global_out && compose->orig_charset
5574 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5575 out_codeset = compose->orig_charset;
5576 debug_print("failure; trying to convert to %s\n", out_codeset);
5577 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5580 if (!test_conv_global_out && !test_conv_reply) {
5582 out_codeset = CS_INTERNAL;
5583 debug_print("failure; finally using %s\n", out_codeset);
5585 g_free(test_conv_global_out);
5586 g_free(test_conv_reply);
5587 codeconv_set_strict(FALSE);
5590 if (encoding == ENC_UNKNOWN) {
5591 if (prefs_common.encoding_method == CTE_BASE64)
5592 encoding = ENC_BASE64;
5593 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5594 encoding = ENC_QUOTED_PRINTABLE;
5595 else if (prefs_common.encoding_method == CTE_8BIT)
5596 encoding = ENC_8BIT;
5598 encoding = procmime_get_encoding_for_charset(out_codeset);
5601 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5602 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5604 if (action == COMPOSE_WRITE_FOR_SEND) {
5605 codeconv_set_strict(TRUE);
5606 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5607 codeconv_set_strict(FALSE);
5612 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5613 "to the specified %s charset.\n"
5614 "Send it as %s?"), out_codeset, src_codeset);
5615 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL,
5616 g_strconcat("+", _("_Send"), NULL), NULL, FALSE,
5617 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5620 if (aval != G_ALERTALTERNATE) {
5625 out_codeset = src_codeset;
5631 out_codeset = src_codeset;
5636 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5637 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5638 strstr(buf, "\nFrom ") != NULL) {
5639 encoding = ENC_QUOTED_PRINTABLE;
5643 mimetext = procmime_mimeinfo_new();
5644 mimetext->content = MIMECONTENT_MEM;
5645 mimetext->tmp = TRUE; /* must free content later */
5646 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5647 * and free the data, which we need later. */
5648 mimetext->data.mem = g_strdup(buf);
5649 mimetext->type = MIMETYPE_TEXT;
5650 mimetext->subtype = g_strdup("plain");
5651 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5652 g_strdup(out_codeset));
5654 /* protect trailing spaces when signing message */
5655 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5656 privacy_system_can_sign(compose->privacy_system)) {
5657 encoding = ENC_QUOTED_PRINTABLE;
5660 debug_print("main text: %zd bytes encoded as %s in %d\n",
5661 strlen(buf), out_codeset, encoding);
5663 /* check for line length limit */
5664 if (action == COMPOSE_WRITE_FOR_SEND &&
5665 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5666 check_line_length(buf, 1000, &line) < 0) {
5669 msg = g_strdup_printf
5670 (_("Line %d exceeds the line length limit (998 bytes).\n"
5671 "The contents of the message might be broken on the way to the delivery.\n"
5673 "Send it anyway?"), line + 1);
5674 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5676 if (aval != G_ALERTALTERNATE) {
5682 if (encoding != ENC_UNKNOWN)
5683 procmime_encode_content(mimetext, encoding);
5685 /* append attachment parts */
5686 if (compose_use_attach(compose) && attach_parts) {
5687 MimeInfo *mimempart;
5688 gchar *boundary = NULL;
5689 mimempart = procmime_mimeinfo_new();
5690 mimempart->content = MIMECONTENT_EMPTY;
5691 mimempart->type = MIMETYPE_MULTIPART;
5692 mimempart->subtype = g_strdup("mixed");
5696 boundary = generate_mime_boundary(NULL);
5697 } while (strstr(buf, boundary) != NULL);
5699 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5702 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5704 g_node_append(mimempart->node, mimetext->node);
5705 g_node_append(mimemsg->node, mimempart->node);
5707 if (compose_add_attachments(compose, mimempart) < 0)
5710 g_node_append(mimemsg->node, mimetext->node);
5714 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5715 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5716 /* extract name and address */
5717 if (strstr(spec, " <") && strstr(spec, ">")) {
5718 from_addr = g_strdup(strrchr(spec, '<')+1);
5719 *(strrchr(from_addr, '>')) = '\0';
5720 from_name = g_strdup(spec);
5721 *(strrchr(from_name, '<')) = '\0';
5728 /* sign message if sending */
5729 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5730 privacy_system_can_sign(compose->privacy_system))
5731 if (!privacy_sign(compose->privacy_system, mimemsg,
5732 compose->account, from_addr)) {
5740 if (compose->use_encryption) {
5741 if (compose->encdata != NULL &&
5742 strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
5744 /* First, write an unencrypted copy and save it to outbox, if
5745 * user wants that. */
5746 if (compose->account->save_encrypted_as_clear_text) {
5747 debug_print("saving sent message unencrypted...\n");
5748 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
5752 /* fp now points to a file with headers written,
5753 * let's make a copy. */
5755 content = file_read_stream_to_str(fp);
5757 str_write_to_file(content, tmp_enc_file);
5760 /* Now write the unencrypted body. */
5761 if ((tmpfp = g_fopen(tmp_enc_file, "a")) != NULL) {
5762 procmime_write_mimeinfo(mimemsg, tmpfp);
5765 outbox = folder_find_item_from_identifier(compose_get_save_to(compose));
5767 outbox = folder_get_default_outbox();
5769 procmsg_save_to_outbox(outbox, tmp_enc_file, TRUE);
5770 claws_unlink(tmp_enc_file);
5772 g_warning("Can't open file '%s'", tmp_enc_file);
5775 g_warning("couldn't get tempfile");
5778 if (!privacy_encrypt(compose->privacy_system, mimemsg, compose->encdata)) {
5779 debug_print("Couldn't encrypt mime structure: %s.\n",
5780 privacy_get_error());
5781 alertpanel_error(_("Couldn't encrypt the email: %s"),
5782 privacy_get_error());
5787 procmime_write_mimeinfo(mimemsg, fp);
5789 procmime_mimeinfo_free_all(mimemsg);
5794 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5796 GtkTextBuffer *buffer;
5797 GtkTextIter start, end;
5802 if ((fp = g_fopen(file, "wb")) == NULL) {
5803 FILE_OP_ERROR(file, "fopen");
5807 /* chmod for security */
5808 if (change_file_mode_rw(fp, file) < 0) {
5809 FILE_OP_ERROR(file, "chmod");
5810 g_warning("can't change file mode");
5813 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5814 gtk_text_buffer_get_start_iter(buffer, &start);
5815 gtk_text_buffer_get_end_iter(buffer, &end);
5816 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5818 chars = conv_codeset_strdup
5819 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5828 len = strlen(chars);
5829 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5830 FILE_OP_ERROR(file, "fwrite");
5839 if (fclose(fp) == EOF) {
5840 FILE_OP_ERROR(file, "fclose");
5847 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5850 MsgInfo *msginfo = compose->targetinfo;
5852 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5853 if (!msginfo) return -1;
5855 if (!force && MSG_IS_LOCKED(msginfo->flags))
5858 item = msginfo->folder;
5859 cm_return_val_if_fail(item != NULL, -1);
5861 if (procmsg_msg_exist(msginfo) &&
5862 (folder_has_parent_of_type(item, F_QUEUE) ||
5863 folder_has_parent_of_type(item, F_DRAFT)
5864 || msginfo == compose->autosaved_draft)) {
5865 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5866 g_warning("can't remove the old message");
5869 debug_print("removed reedit target %d\n", msginfo->msgnum);
5876 static void compose_remove_draft(Compose *compose)
5879 MsgInfo *msginfo = compose->targetinfo;
5880 drafts = account_get_special_folder(compose->account, F_DRAFT);
5882 if (procmsg_msg_exist(msginfo)) {
5883 folder_item_remove_msg(drafts, msginfo->msgnum);
5888 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5889 gboolean remove_reedit_target)
5891 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5894 static gboolean compose_warn_encryption(Compose *compose)
5896 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5897 AlertValue val = G_ALERTALTERNATE;
5899 if (warning == NULL)
5902 val = alertpanel_full(_("Encryption warning"), warning,
5903 GTK_STOCK_CANCEL, g_strconcat("+", _("C_ontinue"), NULL), NULL,
5904 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5905 if (val & G_ALERTDISABLE) {
5906 val &= ~G_ALERTDISABLE;
5907 if (val == G_ALERTALTERNATE)
5908 privacy_inhibit_encrypt_warning(compose->privacy_system,
5912 if (val == G_ALERTALTERNATE) {
5919 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5920 gchar **msgpath, gboolean check_subject,
5921 gboolean remove_reedit_target)
5928 PrefsAccount *mailac = NULL, *newsac = NULL;
5929 gboolean err = FALSE;
5931 debug_print("queueing message...\n");
5932 cm_return_val_if_fail(compose->account != NULL, -1);
5934 if (compose_check_entries(compose, check_subject) == FALSE) {
5935 if (compose->batch) {
5936 gtk_widget_show_all(compose->window);
5941 if (!compose->to_list && !compose->newsgroup_list) {
5942 g_warning("can't get recipient list.");
5946 if (compose->to_list) {
5947 if (compose->account->protocol != A_NNTP)
5948 mailac = compose->account;
5949 else if (cur_account && cur_account->protocol != A_NNTP)
5950 mailac = cur_account;
5951 else if (!(mailac = compose_current_mail_account())) {
5952 alertpanel_error(_("No account for sending mails available!"));
5957 if (compose->newsgroup_list) {
5958 if (compose->account->protocol == A_NNTP)
5959 newsac = compose->account;
5961 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5966 /* write queue header */
5967 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5968 G_DIR_SEPARATOR, compose, (guint) rand());
5969 debug_print("queuing to %s\n", tmp);
5970 if ((fp = g_fopen(tmp, "w+b")) == NULL) {
5971 FILE_OP_ERROR(tmp, "fopen");
5976 if (change_file_mode_rw(fp, tmp) < 0) {
5977 FILE_OP_ERROR(tmp, "chmod");
5978 g_warning("can't change file mode");
5981 /* queueing variables */
5982 err |= (fprintf(fp, "AF:\n") < 0);
5983 err |= (fprintf(fp, "NF:0\n") < 0);
5984 err |= (fprintf(fp, "PS:10\n") < 0);
5985 err |= (fprintf(fp, "SRH:1\n") < 0);
5986 err |= (fprintf(fp, "SFN:\n") < 0);
5987 err |= (fprintf(fp, "DSR:\n") < 0);
5989 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5991 err |= (fprintf(fp, "MID:\n") < 0);
5992 err |= (fprintf(fp, "CFG:\n") < 0);
5993 err |= (fprintf(fp, "PT:0\n") < 0);
5994 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5995 err |= (fprintf(fp, "RQ:\n") < 0);
5997 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5999 err |= (fprintf(fp, "SSV:\n") < 0);
6001 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
6003 err |= (fprintf(fp, "NSV:\n") < 0);
6004 err |= (fprintf(fp, "SSH:\n") < 0);
6005 /* write recepient list */
6006 if (compose->to_list) {
6007 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
6008 for (cur = compose->to_list->next; cur != NULL;
6010 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
6011 err |= (fprintf(fp, "\n") < 0);
6013 /* write newsgroup list */
6014 if (compose->newsgroup_list) {
6015 err |= (fprintf(fp, "NG:") < 0);
6016 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
6017 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
6018 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
6019 err |= (fprintf(fp, "\n") < 0);
6021 /* Sylpheed account IDs */
6023 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
6025 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
6028 if (compose->privacy_system != NULL) {
6029 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
6030 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
6031 if (compose->use_encryption) {
6032 if (!compose_warn_encryption(compose)) {
6038 if (mailac && mailac->encrypt_to_self) {
6039 GSList *tmp_list = g_slist_copy(compose->to_list);
6040 tmp_list = g_slist_append(tmp_list, compose->account->address);
6041 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
6042 g_slist_free(tmp_list);
6044 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
6046 if (compose->encdata != NULL) {
6047 if (strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
6048 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6049 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
6050 compose->encdata) < 0);
6051 } /* else we finally dont want to encrypt */
6053 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6054 /* and if encdata was null, it means there's been a problem in
6057 g_warning("failed to write queue message");
6066 /* Save copy folder */
6067 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6068 gchar *savefolderid;
6070 savefolderid = compose_get_save_to(compose);
6071 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
6072 g_free(savefolderid);
6074 /* Save copy folder */
6075 if (compose->return_receipt) {
6076 err |= (fprintf(fp, "RRCPT:1\n") < 0);
6078 /* Message-ID of message replying to */
6079 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
6080 gchar *folderid = NULL;
6082 if (compose->replyinfo->folder)
6083 folderid = folder_item_get_identifier(compose->replyinfo->folder);
6084 if (folderid == NULL)
6085 folderid = g_strdup("NULL");
6087 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
6090 /* Message-ID of message forwarding to */
6091 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
6092 gchar *folderid = NULL;
6094 if (compose->fwdinfo->folder)
6095 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
6096 if (folderid == NULL)
6097 folderid = g_strdup("NULL");
6099 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
6103 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
6104 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
6106 /* end of headers */
6107 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
6109 if (compose->redirect_filename != NULL) {
6110 if (compose_redirect_write_to_file(compose, fp) < 0) {
6118 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
6122 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6126 g_warning("failed to write queue message");
6132 if (fclose(fp) == EOF) {
6133 FILE_OP_ERROR(tmp, "fclose");
6139 if (item && *item) {
6142 queue = account_get_special_folder(compose->account, F_QUEUE);
6145 g_warning("can't find queue folder");
6150 folder_item_scan(queue);
6151 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6152 g_warning("can't queue the message");
6158 if (msgpath == NULL) {
6164 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6165 compose_remove_reedit_target(compose, FALSE);
6168 if ((msgnum != NULL) && (item != NULL)) {
6176 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6179 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6182 gchar *type, *subtype;
6183 GtkTreeModel *model;
6186 model = gtk_tree_view_get_model(tree_view);
6188 if (!gtk_tree_model_get_iter_first(model, &iter))
6191 gtk_tree_model_get(model, &iter,
6195 if (!is_file_exist(ainfo->file)) {
6196 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6197 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6198 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6200 if (val == G_ALERTDEFAULT) {
6205 if (g_stat(ainfo->file, &statbuf) < 0)
6208 mimepart = procmime_mimeinfo_new();
6209 mimepart->content = MIMECONTENT_FILE;
6210 mimepart->data.filename = g_strdup(ainfo->file);
6211 mimepart->tmp = FALSE; /* or we destroy our attachment */
6212 mimepart->offset = 0;
6213 mimepart->length = statbuf.st_size;
6215 type = g_strdup(ainfo->content_type);
6217 if (!strchr(type, '/')) {
6219 type = g_strdup("application/octet-stream");
6222 subtype = strchr(type, '/') + 1;
6223 *(subtype - 1) = '\0';
6224 mimepart->type = procmime_get_media_type(type);
6225 mimepart->subtype = g_strdup(subtype);
6228 if (mimepart->type == MIMETYPE_MESSAGE &&
6229 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6230 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6231 } else if (mimepart->type == MIMETYPE_TEXT) {
6232 if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6233 /* Text parts with no name come from multipart/alternative
6234 * forwards. Make sure the recipient won't look at the
6235 * original HTML part by mistake. */
6236 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6237 ainfo->name = g_strdup_printf(_("Original %s part"),
6241 g_hash_table_insert(mimepart->typeparameters,
6242 g_strdup("charset"), g_strdup(ainfo->charset));
6244 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6245 if (mimepart->type == MIMETYPE_APPLICATION &&
6246 !strcmp2(mimepart->subtype, "octet-stream"))
6247 g_hash_table_insert(mimepart->typeparameters,
6248 g_strdup("name"), g_strdup(ainfo->name));
6249 g_hash_table_insert(mimepart->dispositionparameters,
6250 g_strdup("filename"), g_strdup(ainfo->name));
6251 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6254 if (mimepart->type == MIMETYPE_MESSAGE
6255 || mimepart->type == MIMETYPE_MULTIPART)
6256 ainfo->encoding = ENC_BINARY;
6257 else if (compose->use_signing) {
6258 if (ainfo->encoding == ENC_7BIT)
6259 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6260 else if (ainfo->encoding == ENC_8BIT)
6261 ainfo->encoding = ENC_BASE64;
6266 procmime_encode_content(mimepart, ainfo->encoding);
6268 g_node_append(parent->node, mimepart->node);
6269 } while (gtk_tree_model_iter_next(model, &iter));
6274 static gchar *compose_quote_list_of_addresses(gchar *str)
6276 GSList *list = NULL, *item = NULL;
6277 gchar *qname = NULL, *faddr = NULL, *result = NULL;
6279 list = address_list_append_with_comments(list, str);
6280 for (item = list; item != NULL; item = item->next) {
6281 gchar *spec = item->data;
6282 gchar *endofname = strstr(spec, " <");
6283 if (endofname != NULL) {
6286 QUOTE_IF_REQUIRED_NORMAL(qname, spec, return NULL);
6287 qqname = escape_internal_quotes(qname, '"');
6289 if (*qname != *spec || qqname != qname) { /* has been quoted, compute new */
6290 gchar *addr = g_strdup(endofname);
6291 gchar *name = (qqname != qname)? qqname: g_strdup(qname);
6292 faddr = g_strconcat(name, addr, NULL);
6295 debug_print("new auto-quoted address: '%s'", faddr);
6299 result = g_strdup((faddr != NULL)? faddr: spec);
6301 result = g_strconcat(result,
6303 (faddr != NULL)? faddr: spec,
6306 if (faddr != NULL) {
6311 slist_free_strings_full(list);
6316 #define IS_IN_CUSTOM_HEADER(header) \
6317 (compose->account->add_customhdr && \
6318 custom_header_find(compose->account->customhdr_list, header) != NULL)
6320 static void compose_add_headerfield_from_headerlist(Compose *compose,
6322 const gchar *fieldname,
6323 const gchar *seperator)
6325 gchar *str, *fieldname_w_colon;
6326 gboolean add_field = FALSE;
6328 ComposeHeaderEntry *headerentry;
6329 const gchar *headerentryname;
6330 const gchar *trans_fieldname;
6333 if (IS_IN_CUSTOM_HEADER(fieldname))
6336 debug_print("Adding %s-fields\n", fieldname);
6338 fieldstr = g_string_sized_new(64);
6340 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6341 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6343 for (list = compose->header_list; list; list = list->next) {
6344 headerentry = ((ComposeHeaderEntry *)list->data);
6345 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6347 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6348 gchar * ustr = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6350 str = compose_quote_list_of_addresses(ustr);
6352 if (str != NULL && str[0] != '\0') {
6354 g_string_append(fieldstr, seperator);
6355 g_string_append(fieldstr, str);
6364 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6365 compose_convert_header
6366 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6367 strlen(fieldname) + 2, TRUE);
6368 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6372 g_free(fieldname_w_colon);
6373 g_string_free(fieldstr, TRUE);
6378 static gchar *compose_get_manual_headers_info(Compose *compose)
6380 GString *sh_header = g_string_new(" ");
6382 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6384 for (list = compose->header_list; list; list = list->next) {
6385 ComposeHeaderEntry *headerentry;
6388 gchar *headername_wcolon;
6389 const gchar *headername_trans;
6391 gboolean standard_header = FALSE;
6393 headerentry = ((ComposeHeaderEntry *)list->data);
6395 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6397 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6402 if (!strstr(tmp, ":")) {
6403 headername_wcolon = g_strconcat(tmp, ":", NULL);
6404 headername = g_strdup(tmp);
6406 headername_wcolon = g_strdup(tmp);
6407 headername = g_strdup(strtok(tmp, ":"));
6411 string = std_headers;
6412 while (*string != NULL) {
6413 headername_trans = prefs_common_translated_header_name(*string);
6414 if (!strcmp(headername_trans, headername_wcolon))
6415 standard_header = TRUE;
6418 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6419 g_string_append_printf(sh_header, "%s ", headername);
6421 g_free(headername_wcolon);
6423 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6424 return g_string_free(sh_header, FALSE);
6427 static gchar *compose_get_header(Compose *compose)
6429 gchar buf[BUFFSIZE];
6430 const gchar *entry_str;
6434 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6436 gchar *from_name = NULL, *from_address = NULL;
6439 cm_return_val_if_fail(compose->account != NULL, NULL);
6440 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6442 header = g_string_sized_new(64);
6445 get_rfc822_date(buf, sizeof(buf));
6446 g_string_append_printf(header, "Date: %s\n", buf);
6450 if (compose->account->name && *compose->account->name) {
6452 QUOTE_IF_REQUIRED(buf, compose->account->name);
6453 tmp = g_strdup_printf("%s <%s>",
6454 buf, compose->account->address);
6456 tmp = g_strdup_printf("%s",
6457 compose->account->address);
6459 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6460 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6462 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6463 from_address = g_strdup(compose->account->address);
6465 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6466 /* extract name and address */
6467 if (strstr(spec, " <") && strstr(spec, ">")) {
6468 from_address = g_strdup(strrchr(spec, '<')+1);
6469 *(strrchr(from_address, '>')) = '\0';
6470 from_name = g_strdup(spec);
6471 *(strrchr(from_name, '<')) = '\0';
6474 from_address = g_strdup(spec);
6481 if (from_name && *from_name) {
6483 compose_convert_header
6484 (compose, buf, sizeof(buf), from_name,
6485 strlen("From: "), TRUE);
6486 QUOTE_IF_REQUIRED(name, buf);
6487 qname = escape_internal_quotes(name, '"');
6489 g_string_append_printf(header, "From: %s <%s>\n",
6490 qname, from_address);
6494 g_string_append_printf(header, "From: %s\n", from_address);
6497 g_free(from_address);
6500 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6503 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6506 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6510 * If this account is a NNTP account remove Bcc header from
6511 * message body since it otherwise will be publicly shown
6513 if (compose->account->protocol != A_NNTP)
6514 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6517 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6519 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6522 compose_convert_header(compose, buf, sizeof(buf), str,
6523 strlen("Subject: "), FALSE);
6524 g_string_append_printf(header, "Subject: %s\n", buf);
6530 if (compose->account->set_domain && compose->account->domain) {
6531 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6532 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6533 g_snprintf(buf, sizeof(buf), "%s",
6534 strchr(compose->account->address, '@') ?
6535 strchr(compose->account->address, '@')+1 :
6536 compose->account->address);
6538 g_snprintf(buf, sizeof(buf), "%s", "");
6541 if (compose->account->gen_msgid) {
6543 if (compose->account->msgid_with_addr) {
6544 addr = compose->account->address;
6546 generate_msgid(buf, sizeof(buf), addr);
6547 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6549 g_free(compose->msgid);
6550 compose->msgid = g_strdup(buf);
6552 compose->msgid = NULL;
6555 if (compose->remove_references == FALSE) {
6557 if (compose->inreplyto && compose->to_list)
6558 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6561 if (compose->references)
6562 g_string_append_printf(header, "References: %s\n", compose->references);
6566 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6569 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6572 if (compose->account->organization &&
6573 strlen(compose->account->organization) &&
6574 !IS_IN_CUSTOM_HEADER("Organization")) {
6575 compose_convert_header(compose, buf, sizeof(buf),
6576 compose->account->organization,
6577 strlen("Organization: "), FALSE);
6578 g_string_append_printf(header, "Organization: %s\n", buf);
6581 /* Program version and system info */
6582 if (compose->account->gen_xmailer &&
6583 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6584 !compose->newsgroup_list) {
6585 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6587 gtk_major_version, gtk_minor_version, gtk_micro_version,
6590 if (compose->account->gen_xmailer &&
6591 g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6592 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6594 gtk_major_version, gtk_minor_version, gtk_micro_version,
6598 /* custom headers */
6599 if (compose->account->add_customhdr) {
6602 for (cur = compose->account->customhdr_list; cur != NULL;
6604 CustomHeader *chdr = (CustomHeader *)cur->data;
6606 if (custom_header_is_allowed(chdr->name)
6607 && chdr->value != NULL
6608 && *(chdr->value) != '\0') {
6609 compose_convert_header
6610 (compose, buf, sizeof(buf),
6612 strlen(chdr->name) + 2, FALSE);
6613 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6618 /* Automatic Faces and X-Faces */
6619 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6620 g_string_append_printf(header, "X-Face: %s\n", buf);
6622 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6623 g_string_append_printf(header, "X-Face: %s\n", buf);
6625 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6626 g_string_append_printf(header, "Face: %s\n", buf);
6628 else if (get_default_face (buf, sizeof(buf)) == 0) {
6629 g_string_append_printf(header, "Face: %s\n", buf);
6633 switch (compose->priority) {
6634 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6635 "X-Priority: 1 (Highest)\n");
6637 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6638 "X-Priority: 2 (High)\n");
6640 case PRIORITY_NORMAL: break;
6641 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6642 "X-Priority: 4 (Low)\n");
6644 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6645 "X-Priority: 5 (Lowest)\n");
6647 default: debug_print("compose: priority unknown : %d\n",
6651 /* Request Return Receipt */
6652 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6653 if (compose->return_receipt) {
6654 if (compose->account->name
6655 && *compose->account->name) {
6656 compose_convert_header(compose, buf, sizeof(buf),
6657 compose->account->name,
6658 strlen("Disposition-Notification-To: "),
6660 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6662 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6666 /* get special headers */
6667 for (list = compose->header_list; list; list = list->next) {
6668 ComposeHeaderEntry *headerentry;
6671 gchar *headername_wcolon;
6672 const gchar *headername_trans;
6675 gboolean standard_header = FALSE;
6677 headerentry = ((ComposeHeaderEntry *)list->data);
6679 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6681 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6686 if (!strstr(tmp, ":")) {
6687 headername_wcolon = g_strconcat(tmp, ":", NULL);
6688 headername = g_strdup(tmp);
6690 headername_wcolon = g_strdup(tmp);
6691 headername = g_strdup(strtok(tmp, ":"));
6695 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6696 Xstrdup_a(headervalue, entry_str, return NULL);
6697 subst_char(headervalue, '\r', ' ');
6698 subst_char(headervalue, '\n', ' ');
6699 string = std_headers;
6700 while (*string != NULL) {
6701 headername_trans = prefs_common_translated_header_name(*string);
6702 if (!strcmp(headername_trans, headername_wcolon))
6703 standard_header = TRUE;
6706 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6707 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6710 g_free(headername_wcolon);
6714 g_string_free(header, FALSE);
6719 #undef IS_IN_CUSTOM_HEADER
6721 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6722 gint header_len, gboolean addr_field)
6724 gchar *tmpstr = NULL;
6725 const gchar *out_codeset = NULL;
6727 cm_return_if_fail(src != NULL);
6728 cm_return_if_fail(dest != NULL);
6730 if (len < 1) return;
6732 tmpstr = g_strdup(src);
6734 subst_char(tmpstr, '\n', ' ');
6735 subst_char(tmpstr, '\r', ' ');
6738 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6739 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6740 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6745 codeconv_set_strict(TRUE);
6746 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6747 conv_get_charset_str(compose->out_encoding));
6748 codeconv_set_strict(FALSE);
6750 if (!dest || *dest == '\0') {
6751 gchar *test_conv_global_out = NULL;
6752 gchar *test_conv_reply = NULL;
6754 /* automatic mode. be automatic. */
6755 codeconv_set_strict(TRUE);
6757 out_codeset = conv_get_outgoing_charset_str();
6759 debug_print("trying to convert to %s\n", out_codeset);
6760 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6763 if (!test_conv_global_out && compose->orig_charset
6764 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6765 out_codeset = compose->orig_charset;
6766 debug_print("failure; trying to convert to %s\n", out_codeset);
6767 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6770 if (!test_conv_global_out && !test_conv_reply) {
6772 out_codeset = CS_INTERNAL;
6773 debug_print("finally using %s\n", out_codeset);
6775 g_free(test_conv_global_out);
6776 g_free(test_conv_reply);
6777 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6779 codeconv_set_strict(FALSE);
6784 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6788 cm_return_if_fail(user_data != NULL);
6790 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6791 g_strstrip(address);
6792 if (*address != '\0') {
6793 gchar *name = procheader_get_fromname(address);
6794 extract_address(address);
6795 #ifndef USE_NEW_ADDRBOOK
6796 addressbook_add_contact(name, address, NULL, NULL);
6798 debug_print("%s: %s\n", name, address);
6799 if (addressadd_selection(name, address, NULL, NULL)) {
6800 debug_print( "addressbook_add_contact - added\n" );
6807 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6809 GtkWidget *menuitem;
6812 cm_return_if_fail(menu != NULL);
6813 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6815 menuitem = gtk_separator_menu_item_new();
6816 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6817 gtk_widget_show(menuitem);
6819 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6820 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6822 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6823 g_strstrip(address);
6824 if (*address == '\0') {
6825 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6828 g_signal_connect(G_OBJECT(menuitem), "activate",
6829 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6830 gtk_widget_show(menuitem);
6833 void compose_add_extra_header(gchar *header, GtkListStore *model)
6836 if (strcmp(header, "")) {
6837 COMBOBOX_ADD(model, header, COMPOSE_TO);
6841 void compose_add_extra_header_entries(GtkListStore *model)
6845 gchar buf[BUFFSIZE];
6848 if (extra_headers == NULL) {
6849 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6850 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6851 debug_print("extra headers file not found\n");
6852 goto extra_headers_done;
6854 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6855 lastc = strlen(buf) - 1; /* remove trailing control chars */
6856 while (lastc >= 0 && buf[lastc] != ':')
6857 buf[lastc--] = '\0';
6858 if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6859 buf[lastc] = '\0'; /* remove trailing : for comparison */
6860 if (custom_header_is_allowed(buf)) {
6862 extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6865 g_message("disallowed extra header line: %s\n", buf);
6869 g_message("invalid extra header line: %s\n", buf);
6875 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6876 extra_headers = g_slist_reverse(extra_headers);
6878 g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6881 static void compose_create_header_entry(Compose *compose)
6883 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6890 const gchar *header = NULL;
6891 ComposeHeaderEntry *headerentry;
6892 gboolean standard_header = FALSE;
6893 GtkListStore *model;
6896 headerentry = g_new0(ComposeHeaderEntry, 1);
6898 /* Combo box model */
6899 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6900 #if !GTK_CHECK_VERSION(2, 24, 0)
6901 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6903 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6905 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6907 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6909 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6910 COMPOSE_NEWSGROUPS);
6911 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6913 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6914 COMPOSE_FOLLOWUPTO);
6915 compose_add_extra_header_entries(model);
6918 #if GTK_CHECK_VERSION(2, 24, 0)
6919 combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6920 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6921 gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6922 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6923 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6925 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6926 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6927 G_CALLBACK(compose_grab_focus_cb), compose);
6928 gtk_widget_show(combo);
6930 /* Putting only the combobox child into focus chain of its parent causes
6931 * the parent to be skipped when changing focus via Tab or Shift+Tab.
6932 * This eliminates need to pres Tab twice in order to really get from the
6933 * combobox to next widget. */
6935 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6936 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6939 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6940 compose->header_nextrow, compose->header_nextrow+1,
6941 GTK_SHRINK, GTK_FILL, 0, 0);
6942 if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
6943 const gchar *last_header_entry = gtk_entry_get_text(
6944 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6946 while (*string != NULL) {
6947 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6948 standard_header = TRUE;
6951 if (standard_header)
6952 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6954 if (!compose->header_last || !standard_header) {
6955 switch(compose->account->protocol) {
6957 header = prefs_common_translated_header_name("Newsgroups:");
6960 header = prefs_common_translated_header_name("To:");
6965 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6967 gtk_editable_set_editable(
6968 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo)))),
6969 prefs_common.type_any_header);
6971 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6972 G_CALLBACK(compose_grab_focus_cb), compose);
6974 /* Entry field with cleanup button */
6975 button = gtk_button_new();
6976 gtk_button_set_image(GTK_BUTTON(button),
6977 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6978 gtk_widget_show(button);
6979 CLAWS_SET_TIP(button,
6980 _("Delete entry contents"));
6981 entry = gtk_entry_new();
6982 gtk_widget_show(entry);
6983 CLAWS_SET_TIP(entry,
6984 _("Use <tab> to autocomplete from addressbook"));
6985 hbox = gtk_hbox_new (FALSE, 0);
6986 gtk_widget_show(hbox);
6987 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6988 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6989 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6990 compose->header_nextrow, compose->header_nextrow+1,
6991 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6993 g_signal_connect(G_OBJECT(entry), "key-press-event",
6994 G_CALLBACK(compose_headerentry_key_press_event_cb),
6996 g_signal_connect(G_OBJECT(entry), "changed",
6997 G_CALLBACK(compose_headerentry_changed_cb),
6999 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
7000 G_CALLBACK(compose_grab_focus_cb), compose);
7002 g_signal_connect(G_OBJECT(button), "clicked",
7003 G_CALLBACK(compose_headerentry_button_clicked_cb),
7007 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7008 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7009 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7010 g_signal_connect(G_OBJECT(entry), "drag_data_received",
7011 G_CALLBACK(compose_header_drag_received_cb),
7013 g_signal_connect(G_OBJECT(entry), "drag-drop",
7014 G_CALLBACK(compose_drag_drop),
7016 g_signal_connect(G_OBJECT(entry), "populate-popup",
7017 G_CALLBACK(compose_entry_popup_extend),
7020 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
7022 headerentry->compose = compose;
7023 headerentry->combo = combo;
7024 headerentry->entry = entry;
7025 headerentry->button = button;
7026 headerentry->hbox = hbox;
7027 headerentry->headernum = compose->header_nextrow;
7028 headerentry->type = PREF_NONE;
7030 compose->header_nextrow++;
7031 compose->header_last = headerentry;
7032 compose->header_list =
7033 g_slist_append(compose->header_list,
7037 static void compose_add_header_entry(Compose *compose, const gchar *header,
7038 gchar *text, ComposePrefType pref_type)
7040 ComposeHeaderEntry *last_header = compose->header_last;
7041 gchar *tmp = g_strdup(text), *email;
7042 gboolean replyto_hdr;
7044 replyto_hdr = (!strcasecmp(header,
7045 prefs_common_translated_header_name("Reply-To:")) ||
7047 prefs_common_translated_header_name("Followup-To:")) ||
7049 prefs_common_translated_header_name("In-Reply-To:")));
7051 extract_address(tmp);
7052 email = g_utf8_strdown(tmp, -1);
7054 if (replyto_hdr == FALSE &&
7055 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
7057 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7058 header, text, (gint) pref_type);
7064 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
7065 gtk_entry_set_text(GTK_ENTRY(
7066 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
7068 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
7069 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
7070 last_header->type = pref_type;
7072 if (replyto_hdr == FALSE)
7073 g_hash_table_insert(compose->email_hashtable, email,
7074 GUINT_TO_POINTER(1));
7081 static void compose_destroy_headerentry(Compose *compose,
7082 ComposeHeaderEntry *headerentry)
7084 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7087 extract_address(text);
7088 email = g_utf8_strdown(text, -1);
7089 g_hash_table_remove(compose->email_hashtable, email);
7093 gtk_widget_destroy(headerentry->combo);
7094 gtk_widget_destroy(headerentry->entry);
7095 gtk_widget_destroy(headerentry->button);
7096 gtk_widget_destroy(headerentry->hbox);
7097 g_free(headerentry);
7100 static void compose_remove_header_entries(Compose *compose)
7103 for (list = compose->header_list; list; list = list->next)
7104 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7106 compose->header_last = NULL;
7107 g_slist_free(compose->header_list);
7108 compose->header_list = NULL;
7109 compose->header_nextrow = 1;
7110 compose_create_header_entry(compose);
7113 static GtkWidget *compose_create_header(Compose *compose)
7115 GtkWidget *from_optmenu_hbox;
7116 GtkWidget *header_scrolledwin_main;
7117 GtkWidget *header_table_main;
7118 GtkWidget *header_scrolledwin;
7119 GtkWidget *header_table;
7121 /* parent with account selection and from header */
7122 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
7123 gtk_widget_show(header_scrolledwin_main);
7124 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7126 header_table_main = gtk_table_new(2, 2, FALSE);
7127 gtk_widget_show(header_table_main);
7128 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7129 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
7130 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
7132 from_optmenu_hbox = compose_account_option_menu_create(compose);
7133 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7134 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7136 /* child with header labels and entries */
7137 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7138 gtk_widget_show(header_scrolledwin);
7139 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7141 header_table = gtk_table_new(2, 2, FALSE);
7142 gtk_widget_show(header_table);
7143 gtk_container_set_border_width(GTK_CONTAINER(header_table), 0);
7144 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7145 gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table),
7146 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin)));
7147 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin))), GTK_SHADOW_NONE);
7149 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7150 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7152 compose->header_table = header_table;
7153 compose->header_list = NULL;
7154 compose->header_nextrow = 0;
7156 compose_create_header_entry(compose);
7158 compose->table = NULL;
7160 return header_scrolledwin_main;
7163 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7165 Compose *compose = (Compose *)data;
7166 GdkEventButton event;
7169 event.time = gtk_get_current_event_time();
7171 return attach_button_pressed(compose->attach_clist, &event, compose);
7174 static GtkWidget *compose_create_attach(Compose *compose)
7176 GtkWidget *attach_scrwin;
7177 GtkWidget *attach_clist;
7179 GtkListStore *store;
7180 GtkCellRenderer *renderer;
7181 GtkTreeViewColumn *column;
7182 GtkTreeSelection *selection;
7184 /* attachment list */
7185 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7186 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7187 GTK_POLICY_AUTOMATIC,
7188 GTK_POLICY_AUTOMATIC);
7189 gtk_widget_set_size_request(attach_scrwin, -1, 80);
7191 store = gtk_list_store_new(N_ATTACH_COLS,
7197 G_TYPE_AUTO_POINTER,
7199 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7200 (GTK_TREE_MODEL(store)));
7201 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7202 g_object_unref(store);
7204 renderer = gtk_cell_renderer_text_new();
7205 column = gtk_tree_view_column_new_with_attributes
7206 (_("Mime type"), renderer, "text",
7207 COL_MIMETYPE, NULL);
7208 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7210 renderer = gtk_cell_renderer_text_new();
7211 column = gtk_tree_view_column_new_with_attributes
7212 (_("Size"), renderer, "text",
7214 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7216 renderer = gtk_cell_renderer_text_new();
7217 column = gtk_tree_view_column_new_with_attributes
7218 (_("Name"), renderer, "text",
7220 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7222 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7223 prefs_common.use_stripes_everywhere);
7224 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7225 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7227 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7228 G_CALLBACK(attach_selected), compose);
7229 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7230 G_CALLBACK(attach_button_pressed), compose);
7231 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7232 G_CALLBACK(popup_attach_button_pressed), compose);
7233 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7234 G_CALLBACK(attach_key_pressed), compose);
7237 gtk_drag_dest_set(attach_clist,
7238 GTK_DEST_DEFAULT_ALL, compose_mime_types,
7239 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7240 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7241 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7242 G_CALLBACK(compose_attach_drag_received_cb),
7244 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7245 G_CALLBACK(compose_drag_drop),
7248 compose->attach_scrwin = attach_scrwin;
7249 compose->attach_clist = attach_clist;
7251 return attach_scrwin;
7254 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7255 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7257 static GtkWidget *compose_create_others(Compose *compose)
7260 GtkWidget *savemsg_checkbtn;
7261 GtkWidget *savemsg_combo;
7262 GtkWidget *savemsg_select;
7265 gchar *folderidentifier;
7267 /* Table for settings */
7268 table = gtk_table_new(3, 1, FALSE);
7269 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7270 gtk_widget_show(table);
7271 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7274 /* Save Message to folder */
7275 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7276 gtk_widget_show(savemsg_checkbtn);
7277 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7278 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7279 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7281 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7282 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7284 #if !GTK_CHECK_VERSION(2, 24, 0)
7285 savemsg_combo = gtk_combo_box_entry_new_text();
7287 savemsg_combo = gtk_combo_box_text_new_with_entry();
7289 compose->savemsg_checkbtn = savemsg_checkbtn;
7290 compose->savemsg_combo = savemsg_combo;
7291 gtk_widget_show(savemsg_combo);
7293 if (prefs_common.compose_save_to_history)
7294 #if !GTK_CHECK_VERSION(2, 24, 0)
7295 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7296 prefs_common.compose_save_to_history);
7298 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7299 prefs_common.compose_save_to_history);
7301 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7302 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7303 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7304 G_CALLBACK(compose_grab_focus_cb), compose);
7305 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7306 folderidentifier = folder_item_get_identifier(account_get_special_folder
7307 (compose->account, F_OUTBOX));
7308 compose_set_save_to(compose, folderidentifier);
7309 g_free(folderidentifier);
7312 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7313 gtk_widget_show(savemsg_select);
7314 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7315 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7316 G_CALLBACK(compose_savemsg_select_cb),
7322 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7324 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7325 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7328 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7333 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7336 path = folder_item_get_identifier(dest);
7338 compose_set_save_to(compose, path);
7342 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7343 GdkAtom clip, GtkTextIter *insert_place);
7346 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7350 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7352 if (event->button == 3) {
7354 GtkTextIter sel_start, sel_end;
7355 gboolean stuff_selected;
7357 /* move the cursor to allow GtkAspell to check the word
7358 * under the mouse */
7359 if (event->x && event->y) {
7360 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7361 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7363 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7366 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7367 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7370 stuff_selected = gtk_text_buffer_get_selection_bounds(
7372 &sel_start, &sel_end);
7374 gtk_text_buffer_place_cursor (buffer, &iter);
7375 /* reselect stuff */
7377 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7378 gtk_text_buffer_select_range(buffer,
7379 &sel_start, &sel_end);
7381 return FALSE; /* pass the event so that the right-click goes through */
7384 if (event->button == 2) {
7389 /* get the middle-click position to paste at the correct place */
7390 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7391 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7393 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7396 entry_paste_clipboard(compose, text,
7397 prefs_common.linewrap_pastes,
7398 GDK_SELECTION_PRIMARY, &iter);
7406 static void compose_spell_menu_changed(void *data)
7408 Compose *compose = (Compose *)data;
7410 GtkWidget *menuitem;
7411 GtkWidget *parent_item;
7412 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7415 if (compose->gtkaspell == NULL)
7418 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7419 "/Menu/Spelling/Options");
7421 /* setting the submenu removes /Spelling/Options from the factory
7422 * so we need to save it */
7424 if (parent_item == NULL) {
7425 parent_item = compose->aspell_options_menu;
7426 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7428 compose->aspell_options_menu = parent_item;
7430 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7432 spell_menu = g_slist_reverse(spell_menu);
7433 for (items = spell_menu;
7434 items; items = items->next) {
7435 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7436 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7437 gtk_widget_show(GTK_WIDGET(menuitem));
7439 g_slist_free(spell_menu);
7441 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7442 gtk_widget_show(parent_item);
7445 static void compose_dict_changed(void *data)
7447 Compose *compose = (Compose *) data;
7449 if(!compose->gtkaspell)
7451 if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7454 gtkaspell_highlight_all(compose->gtkaspell);
7455 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7459 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7461 Compose *compose = (Compose *)data;
7462 GdkEventButton event;
7465 event.time = gtk_get_current_event_time();
7469 return text_clicked(compose->text, &event, compose);
7472 static gboolean compose_force_window_origin = TRUE;
7473 static Compose *compose_create(PrefsAccount *account,
7482 GtkWidget *handlebox;
7484 GtkWidget *notebook;
7486 GtkWidget *attach_hbox;
7487 GtkWidget *attach_lab1;
7488 GtkWidget *attach_lab2;
7493 GtkWidget *subject_hbox;
7494 GtkWidget *subject_frame;
7495 GtkWidget *subject_entry;
7499 GtkWidget *edit_vbox;
7500 GtkWidget *ruler_hbox;
7502 GtkWidget *scrolledwin;
7504 GtkTextBuffer *buffer;
7505 GtkClipboard *clipboard;
7507 UndoMain *undostruct;
7509 GtkWidget *popupmenu;
7510 GtkWidget *tmpl_menu;
7511 GtkActionGroup *action_group = NULL;
7514 GtkAspell * gtkaspell = NULL;
7517 static GdkGeometry geometry;
7519 cm_return_val_if_fail(account != NULL, NULL);
7521 debug_print("Creating compose window...\n");
7522 compose = g_new0(Compose, 1);
7524 compose->batch = batch;
7525 compose->account = account;
7526 compose->folder = folder;
7528 compose->mutex = cm_mutex_new();
7529 compose->set_cursor_pos = -1;
7531 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7533 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7534 gtk_widget_set_size_request(window, prefs_common.compose_width,
7535 prefs_common.compose_height);
7537 if (!geometry.max_width) {
7538 geometry.max_width = gdk_screen_width();
7539 geometry.max_height = gdk_screen_height();
7542 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7543 &geometry, GDK_HINT_MAX_SIZE);
7544 if (!geometry.min_width) {
7545 geometry.min_width = 600;
7546 geometry.min_height = 440;
7548 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7549 &geometry, GDK_HINT_MIN_SIZE);
7551 #ifndef GENERIC_UMPC
7552 if (compose_force_window_origin)
7553 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7554 prefs_common.compose_y);
7556 g_signal_connect(G_OBJECT(window), "delete_event",
7557 G_CALLBACK(compose_delete_cb), compose);
7558 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7559 gtk_widget_realize(window);
7561 gtkut_widget_set_composer_icon(window);
7563 vbox = gtk_vbox_new(FALSE, 0);
7564 gtk_container_add(GTK_CONTAINER(window), vbox);
7566 compose->ui_manager = gtk_ui_manager_new();
7567 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7568 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7569 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7570 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7571 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7572 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7573 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7574 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7575 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7576 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7578 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7580 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7581 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7583 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7585 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7586 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7587 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7590 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7591 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7592 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7593 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7594 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7595 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7596 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7597 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7598 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7599 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7600 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7601 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7602 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7605 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7606 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7607 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7609 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7610 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7611 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7613 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7614 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7615 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7616 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7618 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7620 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7621 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7622 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7623 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7624 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7625 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7626 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7627 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7628 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7629 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7630 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7631 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7632 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7633 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7634 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7636 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7638 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7639 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7640 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7641 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7642 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7644 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7646 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7650 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7651 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7652 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7653 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7654 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7655 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7659 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7660 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7661 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7662 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7663 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7665 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7666 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7667 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7668 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7669 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7672 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7673 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7674 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7675 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7676 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7677 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7678 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7680 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7681 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7682 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7683 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7684 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7686 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7688 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7689 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7690 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7691 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7692 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7694 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7695 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_1, "Options/Encoding/Western/"CS_ISO_8859_1, GTK_UI_MANAGER_MENUITEM)
7696 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_15, "Options/Encoding/Western/"CS_ISO_8859_15, GTK_UI_MANAGER_MENUITEM)
7697 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7699 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7701 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7702 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_13, "Options/Encoding/Baltic/"CS_ISO_8859_13, GTK_UI_MANAGER_MENUITEM)
7703 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_4, "Options/Encoding/Baltic/"CS_ISO_8859_4, GTK_UI_MANAGER_MENUITEM)
7705 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7707 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7708 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_ISO_8859_8, "Options/Encoding/Hebrew/"CS_ISO_8859_8, GTK_UI_MANAGER_MENUITEM)
7709 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7711 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7712 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_ISO_8859_6, "Options/Encoding/Arabic/"CS_ISO_8859_6, GTK_UI_MANAGER_MENUITEM)
7713 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7715 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7717 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7718 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_ISO_8859_5, "Options/Encoding/Cyrillic/"CS_ISO_8859_5, GTK_UI_MANAGER_MENUITEM)
7719 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7720 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR, "Options/Encoding/Cyrillic/"CS_MACCYR, GTK_UI_MANAGER_MENUITEM)
7721 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7722 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7724 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7725 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP, "Options/Encoding/Japanese/"CS_ISO_2022_JP, GTK_UI_MANAGER_MENUITEM)
7726 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP_2, "Options/Encoding/Japanese/"CS_ISO_2022_JP_2, GTK_UI_MANAGER_MENUITEM)
7727 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7728 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7730 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7731 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7732 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7733 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7734 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7735 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7737 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7738 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7739 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_ISO_2022_KR, "Options/Encoding/Korean/"CS_ISO_2022_KR, GTK_UI_MANAGER_MENUITEM)
7741 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7742 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7743 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7747 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7748 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7749 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7750 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7751 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7752 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7755 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7757 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7758 gtk_widget_show_all(menubar);
7760 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7761 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7763 if (prefs_common.toolbar_detachable) {
7764 handlebox = gtk_handle_box_new();
7766 handlebox = gtk_hbox_new(FALSE, 0);
7768 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7770 gtk_widget_realize(handlebox);
7771 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7774 vbox2 = gtk_vbox_new(FALSE, 2);
7775 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7776 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7779 notebook = gtk_notebook_new();
7780 gtk_widget_show(notebook);
7782 /* header labels and entries */
7783 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7784 compose_create_header(compose),
7785 gtk_label_new_with_mnemonic(_("Hea_der")));
7786 /* attachment list */
7787 attach_hbox = gtk_hbox_new(FALSE, 0);
7788 gtk_widget_show(attach_hbox);
7790 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7791 gtk_widget_show(attach_lab1);
7792 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7794 attach_lab2 = gtk_label_new("");
7795 gtk_widget_show(attach_lab2);
7796 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7798 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7799 compose_create_attach(compose),
7802 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7803 compose_create_others(compose),
7804 gtk_label_new_with_mnemonic(_("Othe_rs")));
7807 subject_hbox = gtk_hbox_new(FALSE, 0);
7808 gtk_widget_show(subject_hbox);
7810 subject_frame = gtk_frame_new(NULL);
7811 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7812 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7813 gtk_widget_show(subject_frame);
7815 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7816 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7817 gtk_widget_show(subject);
7819 label = gtk_label_new_with_mnemonic(_("S_ubject:"));
7820 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7821 gtk_widget_show(label);
7824 subject_entry = claws_spell_entry_new();
7826 subject_entry = gtk_entry_new();
7828 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7829 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7830 G_CALLBACK(compose_grab_focus_cb), compose);
7831 gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
7832 gtk_widget_show(subject_entry);
7833 compose->subject_entry = subject_entry;
7834 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7836 edit_vbox = gtk_vbox_new(FALSE, 0);
7838 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7841 ruler_hbox = gtk_hbox_new(FALSE, 0);
7842 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7844 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7845 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7846 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7850 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7851 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7852 GTK_POLICY_AUTOMATIC,
7853 GTK_POLICY_AUTOMATIC);
7854 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7856 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7858 text = gtk_text_view_new();
7859 if (prefs_common.show_compose_margin) {
7860 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7861 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7863 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7864 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7865 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7866 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7867 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7869 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7870 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7871 G_CALLBACK(compose_edit_size_alloc),
7873 g_signal_connect(G_OBJECT(buffer), "changed",
7874 G_CALLBACK(compose_changed_cb), compose);
7875 g_signal_connect(G_OBJECT(text), "grab_focus",
7876 G_CALLBACK(compose_grab_focus_cb), compose);
7877 g_signal_connect(G_OBJECT(buffer), "insert_text",
7878 G_CALLBACK(text_inserted), compose);
7879 g_signal_connect(G_OBJECT(text), "button_press_event",
7880 G_CALLBACK(text_clicked), compose);
7881 g_signal_connect(G_OBJECT(text), "popup-menu",
7882 G_CALLBACK(compose_popup_menu), compose);
7883 g_signal_connect(G_OBJECT(subject_entry), "changed",
7884 G_CALLBACK(compose_changed_cb), compose);
7885 g_signal_connect(G_OBJECT(subject_entry), "activate",
7886 G_CALLBACK(compose_subject_entry_activated), compose);
7889 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7890 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7891 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7892 g_signal_connect(G_OBJECT(text), "drag_data_received",
7893 G_CALLBACK(compose_insert_drag_received_cb),
7895 g_signal_connect(G_OBJECT(text), "drag-drop",
7896 G_CALLBACK(compose_drag_drop),
7898 g_signal_connect(G_OBJECT(text), "key-press-event",
7899 G_CALLBACK(completion_set_focus_to_subject),
7901 gtk_widget_show_all(vbox);
7903 /* pane between attach clist and text */
7904 paned = gtk_vpaned_new();
7905 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7906 gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
7907 gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
7908 gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
7909 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7910 G_CALLBACK(compose_notebook_size_alloc), paned);
7912 gtk_widget_show_all(paned);
7915 if (prefs_common.textfont) {
7916 PangoFontDescription *font_desc;
7918 font_desc = pango_font_description_from_string
7919 (prefs_common.textfont);
7921 gtk_widget_modify_font(text, font_desc);
7922 pango_font_description_free(font_desc);
7926 gtk_action_group_add_actions(action_group, compose_popup_entries,
7927 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7930 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7932 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7935 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7937 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7938 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7939 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7941 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7943 undostruct = undo_init(text);
7944 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7947 address_completion_start(window);
7949 compose->window = window;
7950 compose->vbox = vbox;
7951 compose->menubar = menubar;
7952 compose->handlebox = handlebox;
7954 compose->vbox2 = vbox2;
7956 compose->paned = paned;
7958 compose->attach_label = attach_lab2;
7960 compose->notebook = notebook;
7961 compose->edit_vbox = edit_vbox;
7962 compose->ruler_hbox = ruler_hbox;
7963 compose->ruler = ruler;
7964 compose->scrolledwin = scrolledwin;
7965 compose->text = text;
7967 compose->focused_editable = NULL;
7969 compose->popupmenu = popupmenu;
7971 compose->tmpl_menu = tmpl_menu;
7973 compose->mode = mode;
7974 compose->rmode = mode;
7976 compose->targetinfo = NULL;
7977 compose->replyinfo = NULL;
7978 compose->fwdinfo = NULL;
7980 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7981 g_str_equal, (GDestroyNotify) g_free, NULL);
7983 compose->replyto = NULL;
7985 compose->bcc = NULL;
7986 compose->followup_to = NULL;
7988 compose->ml_post = NULL;
7990 compose->inreplyto = NULL;
7991 compose->references = NULL;
7992 compose->msgid = NULL;
7993 compose->boundary = NULL;
7995 compose->autowrap = prefs_common.autowrap;
7996 compose->autoindent = prefs_common.auto_indent;
7997 compose->use_signing = FALSE;
7998 compose->use_encryption = FALSE;
7999 compose->privacy_system = NULL;
8000 compose->encdata = NULL;
8002 compose->modified = FALSE;
8004 compose->return_receipt = FALSE;
8006 compose->to_list = NULL;
8007 compose->newsgroup_list = NULL;
8009 compose->undostruct = undostruct;
8011 compose->sig_str = NULL;
8013 compose->exteditor_file = NULL;
8014 compose->exteditor_pid = -1;
8015 compose->exteditor_tag = -1;
8016 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
8018 compose->folder_update_callback_id =
8019 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
8020 compose_update_folder_hook,
8021 (gpointer) compose);
8024 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
8025 if (mode != COMPOSE_REDIRECT) {
8026 if (prefs_common.enable_aspell && prefs_common.dictionary &&
8027 strcmp(prefs_common.dictionary, "")) {
8028 gtkaspell = gtkaspell_new(prefs_common.dictionary,
8029 prefs_common.alt_dictionary,
8030 conv_get_locale_charset_str(),
8031 prefs_common.misspelled_col,
8032 prefs_common.check_while_typing,
8033 prefs_common.recheck_when_changing_dict,
8034 prefs_common.use_alternate,
8035 prefs_common.use_both_dicts,
8036 GTK_TEXT_VIEW(text),
8037 GTK_WINDOW(compose->window),
8038 compose_dict_changed,
8039 compose_spell_menu_changed,
8042 alertpanel_error(_("Spell checker could not "
8044 gtkaspell_checkers_strerror());
8045 gtkaspell_checkers_reset_error();
8047 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
8051 compose->gtkaspell = gtkaspell;
8052 compose_spell_menu_changed(compose);
8053 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
8056 compose_select_account(compose, account, TRUE);
8058 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
8059 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
8061 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
8062 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8064 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
8065 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8067 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
8068 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8070 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
8071 if (account->protocol != A_NNTP)
8072 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8073 prefs_common_translated_header_name("To:"));
8075 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8076 prefs_common_translated_header_name("Newsgroups:"));
8078 #ifndef USE_NEW_ADDRBOOK
8079 addressbook_set_target_compose(compose);
8081 if (mode != COMPOSE_REDIRECT)
8082 compose_set_template_menu(compose);
8084 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8087 compose_list = g_list_append(compose_list, compose);
8089 if (!prefs_common.show_ruler)
8090 gtk_widget_hide(ruler_hbox);
8092 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8095 compose->priority = PRIORITY_NORMAL;
8096 compose_update_priority_menu_item(compose);
8098 compose_set_out_encoding(compose);
8101 compose_update_actions_menu(compose);
8103 /* Privacy Systems menu */
8104 compose_update_privacy_systems_menu(compose);
8106 activate_privacy_system(compose, account, TRUE);
8107 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8109 gtk_widget_realize(window);
8111 gtk_widget_show(window);
8117 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8122 GtkWidget *optmenubox;
8123 GtkWidget *fromlabel;
8126 GtkWidget *from_name = NULL;
8128 gint num = 0, def_menu = 0;
8130 accounts = account_get_list();
8131 cm_return_val_if_fail(accounts != NULL, NULL);
8133 optmenubox = gtk_event_box_new();
8134 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8135 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8137 hbox = gtk_hbox_new(FALSE, 4);
8138 from_name = gtk_entry_new();
8140 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8141 G_CALLBACK(compose_grab_focus_cb), compose);
8142 g_signal_connect_after(G_OBJECT(from_name), "activate",
8143 G_CALLBACK(from_name_activate_cb), optmenu);
8145 for (; accounts != NULL; accounts = accounts->next, num++) {
8146 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8147 gchar *name, *from = NULL;
8149 if (ac == compose->account) def_menu = num;
8151 name = g_markup_printf_escaped(_("<i>%s</i>"),
8154 if (ac == compose->account) {
8155 if (ac->name && *ac->name) {
8157 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8158 from = g_strdup_printf("%s <%s>",
8160 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8162 from = g_strdup_printf("%s",
8164 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8167 COMBOBOX_ADD(menu, name, ac->account_id);
8172 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8174 g_signal_connect(G_OBJECT(optmenu), "changed",
8175 G_CALLBACK(account_activated),
8177 g_signal_connect(G_OBJECT(from_name), "populate-popup",
8178 G_CALLBACK(compose_entry_popup_extend),
8181 fromlabel = gtk_label_new_with_mnemonic(_("_From:"));
8182 gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel), from_name);
8184 gtk_box_pack_start(GTK_BOX(hbox), fromlabel, FALSE, FALSE, 4);
8185 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8186 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8188 /* Putting only the GtkEntry into focus chain of parent hbox causes
8189 * the account selector combobox next to it to be unreachable when
8190 * navigating widgets in GtkTable with up/down arrow keys.
8191 * Note: gtk_widget_set_can_focus() was not enough. */
8193 l = g_list_prepend(l, from_name);
8194 gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8197 CLAWS_SET_TIP(optmenubox,
8198 _("Account to use for this email"));
8199 CLAWS_SET_TIP(from_name,
8200 _("Sender address to be used"));
8202 compose->account_combo = optmenu;
8203 compose->from_name = from_name;
8208 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8210 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8211 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8212 Compose *compose = (Compose *) data;
8214 compose->priority = value;
8218 static void compose_reply_change_mode(Compose *compose,
8221 gboolean was_modified = compose->modified;
8223 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8225 cm_return_if_fail(compose->replyinfo != NULL);
8227 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8229 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8231 if (action == COMPOSE_REPLY_TO_ALL)
8233 if (action == COMPOSE_REPLY_TO_SENDER)
8235 if (action == COMPOSE_REPLY_TO_LIST)
8238 compose_remove_header_entries(compose);
8239 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8240 if (compose->account->set_autocc && compose->account->auto_cc)
8241 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8243 if (compose->account->set_autobcc && compose->account->auto_bcc)
8244 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8246 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8247 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8248 compose_show_first_last_header(compose, TRUE);
8249 compose->modified = was_modified;
8250 compose_set_title(compose);
8253 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8255 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8256 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8257 Compose *compose = (Compose *) data;
8260 compose_reply_change_mode(compose, value);
8263 static void compose_update_priority_menu_item(Compose * compose)
8265 GtkWidget *menuitem = NULL;
8266 switch (compose->priority) {
8267 case PRIORITY_HIGHEST:
8268 menuitem = gtk_ui_manager_get_widget
8269 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8272 menuitem = gtk_ui_manager_get_widget
8273 (compose->ui_manager, "/Menu/Options/Priority/High");
8275 case PRIORITY_NORMAL:
8276 menuitem = gtk_ui_manager_get_widget
8277 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8280 menuitem = gtk_ui_manager_get_widget
8281 (compose->ui_manager, "/Menu/Options/Priority/Low");
8283 case PRIORITY_LOWEST:
8284 menuitem = gtk_ui_manager_get_widget
8285 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8288 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8291 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8293 Compose *compose = (Compose *) data;
8295 gboolean can_sign = FALSE, can_encrypt = FALSE;
8297 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8299 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8302 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8303 g_free(compose->privacy_system);
8304 compose->privacy_system = NULL;
8305 g_free(compose->encdata);
8306 compose->encdata = NULL;
8307 if (systemid != NULL) {
8308 compose->privacy_system = g_strdup(systemid);
8310 can_sign = privacy_system_can_sign(systemid);
8311 can_encrypt = privacy_system_can_encrypt(systemid);
8314 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8316 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8317 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8320 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8322 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8323 GtkWidget *menuitem = NULL;
8324 GList *children, *amenu;
8325 gboolean can_sign = FALSE, can_encrypt = FALSE;
8326 gboolean found = FALSE;
8328 if (compose->privacy_system != NULL) {
8330 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8331 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8332 cm_return_if_fail(menuitem != NULL);
8334 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8337 while (amenu != NULL) {
8338 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8339 if (systemid != NULL) {
8340 if (strcmp(systemid, compose->privacy_system) == 0 &&
8341 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8342 menuitem = GTK_WIDGET(amenu->data);
8344 can_sign = privacy_system_can_sign(systemid);
8345 can_encrypt = privacy_system_can_encrypt(systemid);
8349 } else if (strlen(compose->privacy_system) == 0 &&
8350 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8351 menuitem = GTK_WIDGET(amenu->data);
8354 can_encrypt = FALSE;
8359 amenu = amenu->next;
8361 g_list_free(children);
8362 if (menuitem != NULL)
8363 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8365 if (warn && !found && strlen(compose->privacy_system)) {
8366 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8367 "will not be able to sign or encrypt this message."),
8368 compose->privacy_system);
8372 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8373 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8376 static void compose_set_out_encoding(Compose *compose)
8378 CharSet out_encoding;
8379 const gchar *branch = NULL;
8380 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8382 switch(out_encoding) {
8383 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8384 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8385 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8386 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8387 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8388 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8389 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8390 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8391 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8392 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8393 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8394 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8395 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8396 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8397 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8398 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8399 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8400 case C_MACCYR: branch = "Menu/Options/Encoding/Cyrillic/" CS_MACCYR; break;
8401 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8402 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8403 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8404 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8405 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8406 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8407 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8408 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8409 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8410 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8411 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8412 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8413 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8414 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8415 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8416 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8418 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8421 static void compose_set_template_menu(Compose *compose)
8423 GSList *tmpl_list, *cur;
8427 tmpl_list = template_get_config();
8429 menu = gtk_menu_new();
8431 gtk_menu_set_accel_group (GTK_MENU (menu),
8432 gtk_ui_manager_get_accel_group(compose->ui_manager));
8433 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8434 Template *tmpl = (Template *)cur->data;
8435 gchar *accel_path = NULL;
8436 item = gtk_menu_item_new_with_label(tmpl->name);
8437 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8438 g_signal_connect(G_OBJECT(item), "activate",
8439 G_CALLBACK(compose_template_activate_cb),
8441 g_object_set_data(G_OBJECT(item), "template", tmpl);
8442 gtk_widget_show(item);
8443 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8444 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8448 gtk_widget_show(menu);
8449 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8452 void compose_update_actions_menu(Compose *compose)
8454 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8457 static void compose_update_privacy_systems_menu(Compose *compose)
8459 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8460 GSList *systems, *cur;
8462 GtkWidget *system_none;
8464 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8465 GtkWidget *privacy_menu = gtk_menu_new();
8467 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8468 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8470 g_signal_connect(G_OBJECT(system_none), "activate",
8471 G_CALLBACK(compose_set_privacy_system_cb), compose);
8473 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8474 gtk_widget_show(system_none);
8476 systems = privacy_get_system_ids();
8477 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8478 gchar *systemid = cur->data;
8480 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8481 widget = gtk_radio_menu_item_new_with_label(group,
8482 privacy_system_get_name(systemid));
8483 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8484 g_strdup(systemid), g_free);
8485 g_signal_connect(G_OBJECT(widget), "activate",
8486 G_CALLBACK(compose_set_privacy_system_cb), compose);
8488 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8489 gtk_widget_show(widget);
8492 g_slist_free(systems);
8493 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8494 gtk_widget_show_all(privacy_menu);
8495 gtk_widget_show_all(privacy_menuitem);
8498 void compose_reflect_prefs_all(void)
8503 for (cur = compose_list; cur != NULL; cur = cur->next) {
8504 compose = (Compose *)cur->data;
8505 compose_set_template_menu(compose);
8509 void compose_reflect_prefs_pixmap_theme(void)
8514 for (cur = compose_list; cur != NULL; cur = cur->next) {
8515 compose = (Compose *)cur->data;
8516 toolbar_update(TOOLBAR_COMPOSE, compose);
8520 static const gchar *compose_quote_char_from_context(Compose *compose)
8522 const gchar *qmark = NULL;
8524 cm_return_val_if_fail(compose != NULL, NULL);
8526 switch (compose->mode) {
8527 /* use forward-specific quote char */
8528 case COMPOSE_FORWARD:
8529 case COMPOSE_FORWARD_AS_ATTACH:
8530 case COMPOSE_FORWARD_INLINE:
8531 if (compose->folder && compose->folder->prefs &&
8532 compose->folder->prefs->forward_with_format)
8533 qmark = compose->folder->prefs->forward_quotemark;
8534 else if (compose->account->forward_with_format)
8535 qmark = compose->account->forward_quotemark;
8537 qmark = prefs_common.fw_quotemark;
8540 /* use reply-specific quote char in all other modes */
8542 if (compose->folder && compose->folder->prefs &&
8543 compose->folder->prefs->reply_with_format)
8544 qmark = compose->folder->prefs->reply_quotemark;
8545 else if (compose->account->reply_with_format)
8546 qmark = compose->account->reply_quotemark;
8548 qmark = prefs_common.quotemark;
8552 if (qmark == NULL || *qmark == '\0')
8558 static void compose_template_apply(Compose *compose, Template *tmpl,
8562 GtkTextBuffer *buffer;
8566 gchar *parsed_str = NULL;
8567 gint cursor_pos = 0;
8568 const gchar *err_msg = _("The body of the template has an error at line %d.");
8571 /* process the body */
8573 text = GTK_TEXT_VIEW(compose->text);
8574 buffer = gtk_text_view_get_buffer(text);
8577 qmark = compose_quote_char_from_context(compose);
8579 if (compose->replyinfo != NULL) {
8582 gtk_text_buffer_set_text(buffer, "", -1);
8583 mark = gtk_text_buffer_get_insert(buffer);
8584 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8586 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8587 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8589 } else if (compose->fwdinfo != NULL) {
8592 gtk_text_buffer_set_text(buffer, "", -1);
8593 mark = gtk_text_buffer_get_insert(buffer);
8594 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8596 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8597 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8600 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8602 GtkTextIter start, end;
8605 gtk_text_buffer_get_start_iter(buffer, &start);
8606 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8607 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8609 /* clear the buffer now */
8611 gtk_text_buffer_set_text(buffer, "", -1);
8613 parsed_str = compose_quote_fmt(compose, dummyinfo,
8614 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8615 procmsg_msginfo_free( dummyinfo );
8621 gtk_text_buffer_set_text(buffer, "", -1);
8622 mark = gtk_text_buffer_get_insert(buffer);
8623 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8626 if (replace && parsed_str && compose->account->auto_sig)
8627 compose_insert_sig(compose, FALSE);
8629 if (replace && parsed_str) {
8630 gtk_text_buffer_get_start_iter(buffer, &iter);
8631 gtk_text_buffer_place_cursor(buffer, &iter);
8635 cursor_pos = quote_fmt_get_cursor_pos();
8636 compose->set_cursor_pos = cursor_pos;
8637 if (cursor_pos == -1)
8639 gtk_text_buffer_get_start_iter(buffer, &iter);
8640 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8641 gtk_text_buffer_place_cursor(buffer, &iter);
8644 /* process the other fields */
8646 compose_template_apply_fields(compose, tmpl);
8647 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8648 quote_fmt_reset_vartable();
8649 compose_changed_cb(NULL, compose);
8652 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8653 gtkaspell_highlight_all(compose->gtkaspell);
8657 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8659 MsgInfo* dummyinfo = NULL;
8660 MsgInfo *msginfo = NULL;
8663 if (compose->replyinfo != NULL)
8664 msginfo = compose->replyinfo;
8665 else if (compose->fwdinfo != NULL)
8666 msginfo = compose->fwdinfo;
8668 dummyinfo = compose_msginfo_new_from_compose(compose);
8669 msginfo = dummyinfo;
8672 if (tmpl->from && *tmpl->from != '\0') {
8674 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8675 compose->gtkaspell);
8677 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8679 quote_fmt_scan_string(tmpl->from);
8682 buf = quote_fmt_get_buffer();
8684 alertpanel_error(_("Template From format error."));
8686 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8690 if (tmpl->to && *tmpl->to != '\0') {
8692 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8693 compose->gtkaspell);
8695 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8697 quote_fmt_scan_string(tmpl->to);
8700 buf = quote_fmt_get_buffer();
8702 alertpanel_error(_("Template To format error."));
8704 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8708 if (tmpl->cc && *tmpl->cc != '\0') {
8710 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8711 compose->gtkaspell);
8713 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8715 quote_fmt_scan_string(tmpl->cc);
8718 buf = quote_fmt_get_buffer();
8720 alertpanel_error(_("Template Cc format error."));
8722 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8726 if (tmpl->bcc && *tmpl->bcc != '\0') {
8728 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8729 compose->gtkaspell);
8731 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8733 quote_fmt_scan_string(tmpl->bcc);
8736 buf = quote_fmt_get_buffer();
8738 alertpanel_error(_("Template Bcc format error."));
8740 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8744 if (tmpl->replyto && *tmpl->replyto != '\0') {
8746 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8747 compose->gtkaspell);
8749 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8751 quote_fmt_scan_string(tmpl->replyto);
8754 buf = quote_fmt_get_buffer();
8756 alertpanel_error(_("Template Reply-To format error."));
8758 compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
8762 /* process the subject */
8763 if (tmpl->subject && *tmpl->subject != '\0') {
8765 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8766 compose->gtkaspell);
8768 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8770 quote_fmt_scan_string(tmpl->subject);
8773 buf = quote_fmt_get_buffer();
8775 alertpanel_error(_("Template subject format error."));
8777 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8781 procmsg_msginfo_free( dummyinfo );
8784 static void compose_destroy(Compose *compose)
8786 GtkAllocation allocation;
8787 GtkTextBuffer *buffer;
8788 GtkClipboard *clipboard;
8790 compose_list = g_list_remove(compose_list, compose);
8792 if (compose->updating) {
8793 debug_print("danger, not destroying anything now\n");
8794 compose->deferred_destroy = TRUE;
8798 /* NOTE: address_completion_end() does nothing with the window
8799 * however this may change. */
8800 address_completion_end(compose->window);
8802 slist_free_strings_full(compose->to_list);
8803 slist_free_strings_full(compose->newsgroup_list);
8804 slist_free_strings_full(compose->header_list);
8806 slist_free_strings_full(extra_headers);
8807 extra_headers = NULL;
8809 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8811 g_hash_table_destroy(compose->email_hashtable);
8813 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8814 compose->folder_update_callback_id);
8816 procmsg_msginfo_free(compose->targetinfo);
8817 procmsg_msginfo_free(compose->replyinfo);
8818 procmsg_msginfo_free(compose->fwdinfo);
8820 g_free(compose->replyto);
8821 g_free(compose->cc);
8822 g_free(compose->bcc);
8823 g_free(compose->newsgroups);
8824 g_free(compose->followup_to);
8826 g_free(compose->ml_post);
8828 g_free(compose->inreplyto);
8829 g_free(compose->references);
8830 g_free(compose->msgid);
8831 g_free(compose->boundary);
8833 g_free(compose->redirect_filename);
8834 if (compose->undostruct)
8835 undo_destroy(compose->undostruct);
8837 g_free(compose->sig_str);
8839 g_free(compose->exteditor_file);
8841 g_free(compose->orig_charset);
8843 g_free(compose->privacy_system);
8844 g_free(compose->encdata);
8846 #ifndef USE_NEW_ADDRBOOK
8847 if (addressbook_get_target_compose() == compose)
8848 addressbook_set_target_compose(NULL);
8851 if (compose->gtkaspell) {
8852 gtkaspell_delete(compose->gtkaspell);
8853 compose->gtkaspell = NULL;
8857 if (!compose->batch) {
8858 gtk_widget_get_allocation(compose->window, &allocation);
8859 prefs_common.compose_width = allocation.width;
8860 prefs_common.compose_height = allocation.height;
8863 if (!gtk_widget_get_parent(compose->paned))
8864 gtk_widget_destroy(compose->paned);
8865 gtk_widget_destroy(compose->popupmenu);
8867 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8868 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8869 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8871 gtk_widget_destroy(compose->window);
8872 toolbar_destroy(compose->toolbar);
8873 g_free(compose->toolbar);
8874 cm_mutex_free(compose->mutex);
8878 static void compose_attach_info_free(AttachInfo *ainfo)
8880 g_free(ainfo->file);
8881 g_free(ainfo->content_type);
8882 g_free(ainfo->name);
8883 g_free(ainfo->charset);
8887 static void compose_attach_update_label(Compose *compose)
8892 GtkTreeModel *model;
8897 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8898 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8899 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8903 while(gtk_tree_model_iter_next(model, &iter))
8906 text = g_strdup_printf("(%d)", i);
8907 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8911 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8913 Compose *compose = (Compose *)data;
8914 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8915 GtkTreeSelection *selection;
8917 GtkTreeModel *model;
8919 selection = gtk_tree_view_get_selection(tree_view);
8920 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8925 for (cur = sel; cur != NULL; cur = cur->next) {
8926 GtkTreePath *path = cur->data;
8927 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8930 gtk_tree_path_free(path);
8933 for (cur = sel; cur != NULL; cur = cur->next) {
8934 GtkTreeRowReference *ref = cur->data;
8935 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8938 if (gtk_tree_model_get_iter(model, &iter, path))
8939 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8941 gtk_tree_path_free(path);
8942 gtk_tree_row_reference_free(ref);
8946 compose_attach_update_label(compose);
8949 static struct _AttachProperty
8952 GtkWidget *mimetype_entry;
8953 GtkWidget *encoding_optmenu;
8954 GtkWidget *path_entry;
8955 GtkWidget *filename_entry;
8957 GtkWidget *cancel_btn;
8960 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8962 gtk_tree_path_free((GtkTreePath *)ptr);
8965 static void compose_attach_property(GtkAction *action, gpointer data)
8967 Compose *compose = (Compose *)data;
8968 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8970 GtkComboBox *optmenu;
8971 GtkTreeSelection *selection;
8973 GtkTreeModel *model;
8976 static gboolean cancelled;
8978 /* only if one selected */
8979 selection = gtk_tree_view_get_selection(tree_view);
8980 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8983 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8987 path = (GtkTreePath *) sel->data;
8988 gtk_tree_model_get_iter(model, &iter, path);
8989 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8992 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8998 if (!attach_prop.window)
8999 compose_attach_property_create(&cancelled);
9000 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
9001 gtk_widget_grab_focus(attach_prop.ok_btn);
9002 gtk_widget_show(attach_prop.window);
9003 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
9004 GTK_WINDOW(compose->window));
9006 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
9007 if (ainfo->encoding == ENC_UNKNOWN)
9008 combobox_select_by_data(optmenu, ENC_BASE64);
9010 combobox_select_by_data(optmenu, ainfo->encoding);
9012 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
9013 ainfo->content_type ? ainfo->content_type : "");
9014 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
9015 ainfo->file ? ainfo->file : "");
9016 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
9017 ainfo->name ? ainfo->name : "");
9020 const gchar *entry_text;
9022 gchar *cnttype = NULL;
9029 gtk_widget_hide(attach_prop.window);
9030 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
9035 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
9036 if (*entry_text != '\0') {
9039 text = g_strstrip(g_strdup(entry_text));
9040 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
9041 cnttype = g_strdup(text);
9044 alertpanel_error(_("Invalid MIME type."));
9050 ainfo->encoding = combobox_get_active_data(optmenu);
9052 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
9053 if (*entry_text != '\0') {
9054 if (is_file_exist(entry_text) &&
9055 (size = get_file_size(entry_text)) > 0)
9056 file = g_strdup(entry_text);
9059 (_("File doesn't exist or is empty."));
9065 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
9066 if (*entry_text != '\0') {
9067 g_free(ainfo->name);
9068 ainfo->name = g_strdup(entry_text);
9072 g_free(ainfo->content_type);
9073 ainfo->content_type = cnttype;
9076 g_free(ainfo->file);
9080 ainfo->size = (goffset)size;
9082 /* update tree store */
9083 text = to_human_readable(ainfo->size);
9084 gtk_tree_model_get_iter(model, &iter, path);
9085 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
9086 COL_MIMETYPE, ainfo->content_type,
9088 COL_NAME, ainfo->name,
9089 COL_CHARSET, ainfo->charset,
9095 gtk_tree_path_free(path);
9098 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9100 label = gtk_label_new(str); \
9101 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9102 GTK_FILL, 0, 0, 0); \
9103 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9105 entry = gtk_entry_new(); \
9106 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9107 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9110 static void compose_attach_property_create(gboolean *cancelled)
9116 GtkWidget *mimetype_entry;
9119 GtkListStore *optmenu_menu;
9120 GtkWidget *path_entry;
9121 GtkWidget *filename_entry;
9124 GtkWidget *cancel_btn;
9125 GList *mime_type_list, *strlist;
9128 debug_print("Creating attach_property window...\n");
9130 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9131 gtk_widget_set_size_request(window, 480, -1);
9132 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9133 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9134 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9135 g_signal_connect(G_OBJECT(window), "delete_event",
9136 G_CALLBACK(attach_property_delete_event),
9138 g_signal_connect(G_OBJECT(window), "key_press_event",
9139 G_CALLBACK(attach_property_key_pressed),
9142 vbox = gtk_vbox_new(FALSE, 8);
9143 gtk_container_add(GTK_CONTAINER(window), vbox);
9145 table = gtk_table_new(4, 2, FALSE);
9146 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9147 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9148 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9150 label = gtk_label_new(_("MIME type"));
9151 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
9153 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9154 #if !GTK_CHECK_VERSION(2, 24, 0)
9155 mimetype_entry = gtk_combo_box_entry_new_text();
9157 mimetype_entry = gtk_combo_box_text_new_with_entry();
9159 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
9160 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9162 /* stuff with list */
9163 mime_type_list = procmime_get_mime_type_list();
9165 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9166 MimeType *type = (MimeType *) mime_type_list->data;
9169 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9171 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9174 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9175 (GCompareFunc)strcmp2);
9178 for (mime_type_list = strlist; mime_type_list != NULL;
9179 mime_type_list = mime_type_list->next) {
9180 #if !GTK_CHECK_VERSION(2, 24, 0)
9181 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9183 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9185 g_free(mime_type_list->data);
9187 g_list_free(strlist);
9188 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
9189 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
9191 label = gtk_label_new(_("Encoding"));
9192 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9194 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9196 hbox = gtk_hbox_new(FALSE, 0);
9197 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9198 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9200 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9201 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9203 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9204 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9205 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
9206 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9207 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9209 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9211 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
9212 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9214 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9215 &ok_btn, GTK_STOCK_OK,
9217 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9218 gtk_widget_grab_default(ok_btn);
9220 g_signal_connect(G_OBJECT(ok_btn), "clicked",
9221 G_CALLBACK(attach_property_ok),
9223 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9224 G_CALLBACK(attach_property_cancel),
9227 gtk_widget_show_all(vbox);
9229 attach_prop.window = window;
9230 attach_prop.mimetype_entry = mimetype_entry;
9231 attach_prop.encoding_optmenu = optmenu;
9232 attach_prop.path_entry = path_entry;
9233 attach_prop.filename_entry = filename_entry;
9234 attach_prop.ok_btn = ok_btn;
9235 attach_prop.cancel_btn = cancel_btn;
9238 #undef SET_LABEL_AND_ENTRY
9240 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9246 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9252 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9253 gboolean *cancelled)
9261 static gboolean attach_property_key_pressed(GtkWidget *widget,
9263 gboolean *cancelled)
9265 if (event && event->keyval == GDK_KEY_Escape) {
9269 if (event && event->keyval == GDK_KEY_Return) {
9277 static void compose_exec_ext_editor(Compose *compose)
9284 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9285 G_DIR_SEPARATOR, compose);
9287 if (pipe(pipe_fds) < 0) {
9293 if ((pid = fork()) < 0) {
9300 /* close the write side of the pipe */
9303 compose->exteditor_file = g_strdup(tmp);
9304 compose->exteditor_pid = pid;
9306 compose_set_ext_editor_sensitive(compose, FALSE);
9309 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9311 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9313 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9317 } else { /* process-monitoring process */
9323 /* close the read side of the pipe */
9326 if (compose_write_body_to_file(compose, tmp) < 0) {
9327 fd_write_all(pipe_fds[1], "2\n", 2);
9331 pid_ed = compose_exec_ext_editor_real(tmp);
9333 fd_write_all(pipe_fds[1], "1\n", 2);
9337 /* wait until editor is terminated */
9338 waitpid(pid_ed, NULL, 0);
9340 fd_write_all(pipe_fds[1], "0\n", 2);
9347 #endif /* G_OS_UNIX */
9351 static gint compose_exec_ext_editor_real(const gchar *file)
9358 cm_return_val_if_fail(file != NULL, -1);
9360 if ((pid = fork()) < 0) {
9365 if (pid != 0) return pid;
9367 /* grandchild process */
9369 if (setpgid(0, getppid()))
9372 if (prefs_common_get_ext_editor_cmd() &&
9373 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9374 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9375 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9377 if (prefs_common_get_ext_editor_cmd())
9378 g_warning("External editor command-line is invalid: '%s'",
9379 prefs_common_get_ext_editor_cmd());
9380 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9383 cmdline = strsplit_with_quote(buf, " ", 1024);
9384 execvp(cmdline[0], cmdline);
9387 g_strfreev(cmdline);
9392 static gboolean compose_ext_editor_kill(Compose *compose)
9394 pid_t pgid = compose->exteditor_pid * -1;
9397 ret = kill(pgid, 0);
9399 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9403 msg = g_strdup_printf
9404 (_("The external editor is still working.\n"
9405 "Force terminating the process?\n"
9406 "process group id: %d"), -pgid);
9407 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9408 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9412 if (val == G_ALERTALTERNATE) {
9413 g_source_remove(compose->exteditor_tag);
9414 g_io_channel_shutdown(compose->exteditor_ch,
9416 g_io_channel_unref(compose->exteditor_ch);
9418 if (kill(pgid, SIGTERM) < 0) perror("kill");
9419 waitpid(compose->exteditor_pid, NULL, 0);
9421 g_warning("Terminated process group id: %d. "
9422 "Temporary file: %s", -pgid, compose->exteditor_file);
9424 compose_set_ext_editor_sensitive(compose, TRUE);
9426 g_free(compose->exteditor_file);
9427 compose->exteditor_file = NULL;
9428 compose->exteditor_pid = -1;
9429 compose->exteditor_ch = NULL;
9430 compose->exteditor_tag = -1;
9438 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9442 Compose *compose = (Compose *)data;
9445 debug_print("Compose: input from monitoring process\n");
9447 if (g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL) != G_IO_STATUS_NORMAL) {
9452 g_io_channel_shutdown(source, FALSE, NULL);
9453 g_io_channel_unref(source);
9455 waitpid(compose->exteditor_pid, NULL, 0);
9457 if (buf[0] == '0') { /* success */
9458 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9459 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9460 GtkTextIter start, end;
9463 gtk_text_buffer_set_text(buffer, "", -1);
9464 compose_insert_file(compose, compose->exteditor_file);
9465 compose_changed_cb(NULL, compose);
9466 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9468 if (claws_unlink(compose->exteditor_file) < 0)
9469 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9471 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9472 gtk_text_buffer_get_start_iter(buffer, &start);
9473 gtk_text_buffer_get_end_iter(buffer, &end);
9474 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
9475 if (chars && strlen(chars) > 0)
9476 compose->modified = TRUE;
9478 } else if (buf[0] == '1') { /* failed */
9479 g_warning("Couldn't exec external editor");
9480 if (claws_unlink(compose->exteditor_file) < 0)
9481 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9482 } else if (buf[0] == '2') {
9483 g_warning("Couldn't write to file");
9484 } else if (buf[0] == '3') {
9485 g_warning("Pipe read failed");
9488 compose_set_ext_editor_sensitive(compose, TRUE);
9490 g_free(compose->exteditor_file);
9491 compose->exteditor_file = NULL;
9492 compose->exteditor_pid = -1;
9493 compose->exteditor_ch = NULL;
9494 compose->exteditor_tag = -1;
9499 static void compose_set_ext_editor_sensitive(Compose *compose,
9502 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9504 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9505 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9506 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", sensitive);
9507 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9508 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9509 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9511 gtk_widget_set_sensitive(compose->text, sensitive);
9512 if (compose->toolbar->send_btn)
9513 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9514 if (compose->toolbar->sendl_btn)
9515 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9516 if (compose->toolbar->draft_btn)
9517 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9518 if (compose->toolbar->insert_btn)
9519 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9520 if (compose->toolbar->sig_btn)
9521 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9522 if (compose->toolbar->exteditor_btn)
9523 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9524 if (compose->toolbar->linewrap_current_btn)
9525 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9526 if (compose->toolbar->linewrap_all_btn)
9527 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9529 #endif /* G_OS_UNIX */
9532 * compose_undo_state_changed:
9534 * Change the sensivity of the menuentries undo and redo
9536 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9537 gint redo_state, gpointer data)
9539 Compose *compose = (Compose *)data;
9541 switch (undo_state) {
9542 case UNDO_STATE_TRUE:
9543 if (!undostruct->undo_state) {
9544 undostruct->undo_state = TRUE;
9545 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9548 case UNDO_STATE_FALSE:
9549 if (undostruct->undo_state) {
9550 undostruct->undo_state = FALSE;
9551 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9554 case UNDO_STATE_UNCHANGED:
9556 case UNDO_STATE_REFRESH:
9557 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9560 g_warning("Undo state not recognized");
9564 switch (redo_state) {
9565 case UNDO_STATE_TRUE:
9566 if (!undostruct->redo_state) {
9567 undostruct->redo_state = TRUE;
9568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9571 case UNDO_STATE_FALSE:
9572 if (undostruct->redo_state) {
9573 undostruct->redo_state = FALSE;
9574 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9577 case UNDO_STATE_UNCHANGED:
9579 case UNDO_STATE_REFRESH:
9580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9583 g_warning("Redo state not recognized");
9588 /* callback functions */
9590 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9591 GtkAllocation *allocation,
9594 prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9597 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9598 * includes "non-client" (windows-izm) in calculation, so this calculation
9599 * may not be accurate.
9601 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9602 GtkAllocation *allocation,
9603 GtkSHRuler *shruler)
9605 if (prefs_common.show_ruler) {
9606 gint char_width = 0, char_height = 0;
9607 gint line_width_in_chars;
9609 gtkut_get_font_size(GTK_WIDGET(widget),
9610 &char_width, &char_height);
9611 line_width_in_chars =
9612 (allocation->width - allocation->x) / char_width;
9614 /* got the maximum */
9615 gtk_shruler_set_range(GTK_SHRULER(shruler),
9616 0.0, line_width_in_chars, 0);
9625 ComposePrefType type;
9626 gboolean entry_marked;
9629 static void account_activated(GtkComboBox *optmenu, gpointer data)
9631 Compose *compose = (Compose *)data;
9634 gchar *folderidentifier;
9635 gint account_id = 0;
9638 GSList *list, *saved_list = NULL;
9639 HeaderEntryState *state;
9640 GtkRcStyle *style = NULL;
9641 #if !GTK_CHECK_VERSION(3, 0, 0)
9642 static GdkColor yellow;
9643 static gboolean color_set = FALSE;
9645 static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9648 /* Get ID of active account in the combo box */
9649 menu = gtk_combo_box_get_model(optmenu);
9650 cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9651 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9653 ac = account_find_from_id(account_id);
9654 cm_return_if_fail(ac != NULL);
9656 if (ac != compose->account) {
9657 compose_select_account(compose, ac, FALSE);
9659 for (list = compose->header_list; list; list = list->next) {
9660 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9662 if (hentry->type == PREF_ACCOUNT || !list->next) {
9663 compose_destroy_headerentry(compose, hentry);
9667 state = g_malloc0(sizeof(HeaderEntryState));
9668 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9669 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9670 state->entry = gtk_editable_get_chars(
9671 GTK_EDITABLE(hentry->entry), 0, -1);
9672 state->type = hentry->type;
9674 #if !GTK_CHECK_VERSION(3, 0, 0)
9676 gdk_color_parse("#f5f6be", &yellow);
9677 color_set = gdk_colormap_alloc_color(
9678 gdk_colormap_get_system(),
9679 &yellow, FALSE, TRUE);
9683 style = gtk_widget_get_modifier_style(hentry->entry);
9684 state->entry_marked = gdk_color_equal(&yellow,
9685 &style->base[GTK_STATE_NORMAL]);
9687 saved_list = g_slist_append(saved_list, state);
9688 compose_destroy_headerentry(compose, hentry);
9691 compose->header_last = NULL;
9692 g_slist_free(compose->header_list);
9693 compose->header_list = NULL;
9694 compose->header_nextrow = 1;
9695 compose_create_header_entry(compose);
9697 if (ac->set_autocc && ac->auto_cc)
9698 compose_entry_append(compose, ac->auto_cc,
9699 COMPOSE_CC, PREF_ACCOUNT);
9701 if (ac->set_autobcc && ac->auto_bcc)
9702 compose_entry_append(compose, ac->auto_bcc,
9703 COMPOSE_BCC, PREF_ACCOUNT);
9705 if (ac->set_autoreplyto && ac->auto_replyto)
9706 compose_entry_append(compose, ac->auto_replyto,
9707 COMPOSE_REPLYTO, PREF_ACCOUNT);
9709 for (list = saved_list; list; list = list->next) {
9710 state = (HeaderEntryState *) list->data;
9712 compose_add_header_entry(compose, state->header,
9713 state->entry, state->type);
9714 if (state->entry_marked)
9715 compose_entry_mark_default_to(compose, state->entry);
9717 g_free(state->header);
9718 g_free(state->entry);
9721 g_slist_free(saved_list);
9723 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9724 (ac->protocol == A_NNTP) ?
9725 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9728 /* Set message save folder */
9729 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9730 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9732 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9733 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9735 compose_set_save_to(compose, NULL);
9736 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9737 folderidentifier = folder_item_get_identifier(account_get_special_folder
9738 (compose->account, F_OUTBOX));
9739 compose_set_save_to(compose, folderidentifier);
9740 g_free(folderidentifier);
9744 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9745 GtkTreeViewColumn *column, Compose *compose)
9747 compose_attach_property(NULL, compose);
9750 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9753 Compose *compose = (Compose *)data;
9754 GtkTreeSelection *attach_selection;
9755 gint attach_nr_selected;
9758 if (!event) return FALSE;
9760 if (event->button == 3) {
9761 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9762 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9764 /* If no rows, or just one row is selected, right-click should
9765 * open menu relevant to the row being right-clicked on. We
9766 * achieve that by selecting the clicked row first. If more
9767 * than one row is selected, we shouldn't modify the selection,
9768 * as user may want to remove selected rows (attachments). */
9769 if (attach_nr_selected < 2) {
9770 gtk_tree_selection_unselect_all(attach_selection);
9771 attach_nr_selected = 0;
9772 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
9773 event->x, event->y, &path, NULL, NULL, NULL);
9775 gtk_tree_selection_select_path(attach_selection, path);
9776 gtk_tree_path_free(path);
9777 attach_nr_selected++;
9781 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9782 /* Properties menu item makes no sense with more than one row
9783 * selected, the properties dialog can only edit one attachment. */
9784 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected == 1));
9786 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9787 NULL, NULL, event->button, event->time);
9794 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9797 Compose *compose = (Compose *)data;
9799 if (!event) return FALSE;
9801 switch (event->keyval) {
9802 case GDK_KEY_Delete:
9803 compose_attach_remove_selected(NULL, compose);
9809 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9811 toolbar_comp_set_sensitive(compose, allow);
9812 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9813 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9815 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9817 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9818 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9819 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9821 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9825 static void compose_send_cb(GtkAction *action, gpointer data)
9827 Compose *compose = (Compose *)data;
9829 if (prefs_common.work_offline &&
9830 !inc_offline_should_override(TRUE,
9831 _("Claws Mail needs network access in order "
9832 "to send this email.")))
9835 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9836 g_source_remove(compose->draft_timeout_tag);
9837 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
9840 compose_send(compose);
9843 static void compose_send_later_cb(GtkAction *action, gpointer data)
9845 Compose *compose = (Compose *)data;
9849 compose_allow_user_actions(compose, FALSE);
9850 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9851 compose_allow_user_actions(compose, TRUE);
9855 compose_close(compose);
9856 } else if (val == -1) {
9857 alertpanel_error(_("Could not queue message."));
9858 } else if (val == -2) {
9859 alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno));
9860 } else if (val == -3) {
9861 if (privacy_peek_error())
9862 alertpanel_error(_("Could not queue message for sending:\n\n"
9863 "Signature failed: %s"), privacy_get_error());
9864 } else if (val == -4) {
9865 alertpanel_error(_("Could not queue message for sending:\n\n"
9866 "Charset conversion failed."));
9867 } else if (val == -5) {
9868 alertpanel_error(_("Could not queue message for sending:\n\n"
9869 "Couldn't get recipient encryption key."));
9870 } else if (val == -6) {
9873 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9876 #define DRAFTED_AT_EXIT "drafted_at_exit"
9877 static void compose_register_draft(MsgInfo *info)
9879 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9880 DRAFTED_AT_EXIT, NULL);
9881 FILE *fp = g_fopen(filepath, "ab");
9884 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9892 gboolean compose_draft (gpointer data, guint action)
9894 Compose *compose = (Compose *)data;
9899 MsgFlags flag = {0, 0};
9900 static gboolean lock = FALSE;
9901 MsgInfo *newmsginfo;
9903 gboolean target_locked = FALSE;
9904 gboolean err = FALSE;
9906 if (lock) return FALSE;
9908 if (compose->sending)
9911 draft = account_get_special_folder(compose->account, F_DRAFT);
9912 cm_return_val_if_fail(draft != NULL, FALSE);
9914 if (!g_mutex_trylock(compose->mutex)) {
9915 /* we don't want to lock the mutex once it's available,
9916 * because as the only other part of compose.c locking
9917 * it is compose_close - which means once unlocked,
9918 * the compose struct will be freed */
9919 debug_print("couldn't lock mutex, probably sending\n");
9925 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9926 G_DIR_SEPARATOR, compose);
9927 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9928 FILE_OP_ERROR(tmp, "fopen");
9932 /* chmod for security */
9933 if (change_file_mode_rw(fp, tmp) < 0) {
9934 FILE_OP_ERROR(tmp, "chmod");
9935 g_warning("can't change file mode");
9938 /* Save draft infos */
9939 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9940 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9942 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9943 gchar *savefolderid;
9945 savefolderid = compose_get_save_to(compose);
9946 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9947 g_free(savefolderid);
9949 if (compose->return_receipt) {
9950 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9952 if (compose->privacy_system) {
9953 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9954 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9955 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9958 /* Message-ID of message replying to */
9959 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9960 gchar *folderid = NULL;
9962 if (compose->replyinfo->folder)
9963 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9964 if (folderid == NULL)
9965 folderid = g_strdup("NULL");
9967 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9970 /* Message-ID of message forwarding to */
9971 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9972 gchar *folderid = NULL;
9974 if (compose->fwdinfo->folder)
9975 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9976 if (folderid == NULL)
9977 folderid = g_strdup("NULL");
9979 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9983 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9984 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9986 sheaders = compose_get_manual_headers_info(compose);
9987 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9990 /* end of headers */
9991 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9998 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
10002 if (fclose(fp) == EOF) {
10006 flag.perm_flags = MSG_NEW|MSG_UNREAD;
10007 if (compose->targetinfo) {
10008 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
10010 flag.perm_flags |= MSG_LOCKED;
10012 flag.tmp_flags = MSG_DRAFT;
10014 folder_item_scan(draft);
10015 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
10016 MsgInfo *tmpinfo = NULL;
10017 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
10018 if (compose->msgid) {
10019 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
10022 msgnum = tmpinfo->msgnum;
10023 procmsg_msginfo_free(tmpinfo);
10024 debug_print("got draft msgnum %d from scanning\n", msgnum);
10026 debug_print("didn't get draft msgnum after scanning\n");
10029 debug_print("got draft msgnum %d from adding\n", msgnum);
10035 if (action != COMPOSE_AUTO_SAVE) {
10036 if (action != COMPOSE_DRAFT_FOR_EXIT)
10037 alertpanel_error(_("Could not save draft."));
10040 gtkut_window_popup(compose->window);
10041 val = alertpanel_full(_("Could not save draft"),
10042 _("Could not save draft.\n"
10043 "Do you want to cancel exit or discard this email?"),
10044 _("_Cancel exit"), _("_Discard email"), NULL,
10045 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
10046 if (val == G_ALERTALTERNATE) {
10048 g_mutex_unlock(compose->mutex); /* must be done before closing */
10049 compose_close(compose);
10053 g_mutex_unlock(compose->mutex); /* must be done before closing */
10062 if (compose->mode == COMPOSE_REEDIT) {
10063 compose_remove_reedit_target(compose, TRUE);
10066 newmsginfo = folder_item_get_msginfo(draft, msgnum);
10069 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
10071 procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
10073 procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
10074 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
10075 procmsg_msginfo_set_flags(newmsginfo, 0,
10076 MSG_HAS_ATTACHMENT);
10078 if (action == COMPOSE_DRAFT_FOR_EXIT) {
10079 compose_register_draft(newmsginfo);
10081 procmsg_msginfo_free(newmsginfo);
10084 folder_item_scan(draft);
10086 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
10088 g_mutex_unlock(compose->mutex); /* must be done before closing */
10089 compose_close(compose);
10095 path = folder_item_fetch_msg(draft, msgnum);
10096 if (path == NULL) {
10097 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
10100 if (g_stat(path, &s) < 0) {
10101 FILE_OP_ERROR(path, "stat");
10107 procmsg_msginfo_free(compose->targetinfo);
10108 compose->targetinfo = procmsg_msginfo_new();
10109 compose->targetinfo->msgnum = msgnum;
10110 compose->targetinfo->size = (goffset)s.st_size;
10111 compose->targetinfo->mtime = s.st_mtime;
10112 compose->targetinfo->folder = draft;
10114 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
10115 compose->mode = COMPOSE_REEDIT;
10117 if (action == COMPOSE_AUTO_SAVE) {
10118 compose->autosaved_draft = compose->targetinfo;
10120 compose->modified = FALSE;
10121 compose_set_title(compose);
10125 g_mutex_unlock(compose->mutex);
10129 void compose_clear_exit_drafts(void)
10131 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10132 DRAFTED_AT_EXIT, NULL);
10133 if (is_file_exist(filepath))
10134 claws_unlink(filepath);
10139 void compose_reopen_exit_drafts(void)
10141 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10142 DRAFTED_AT_EXIT, NULL);
10143 FILE *fp = g_fopen(filepath, "rb");
10147 while (fgets(buf, sizeof(buf), fp)) {
10148 gchar **parts = g_strsplit(buf, "\t", 2);
10149 const gchar *folder = parts[0];
10150 int msgnum = parts[1] ? atoi(parts[1]):-1;
10152 if (folder && *folder && msgnum > -1) {
10153 FolderItem *item = folder_find_item_from_identifier(folder);
10154 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10156 compose_reedit(info, FALSE);
10163 compose_clear_exit_drafts();
10166 static void compose_save_cb(GtkAction *action, gpointer data)
10168 Compose *compose = (Compose *)data;
10169 compose_draft(compose, COMPOSE_KEEP_EDITING);
10170 compose->rmode = COMPOSE_REEDIT;
10173 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10175 if (compose && file_list) {
10178 for ( tmp = file_list; tmp; tmp = tmp->next) {
10179 gchar *file = (gchar *) tmp->data;
10180 gchar *utf8_filename = conv_filename_to_utf8(file);
10181 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10182 compose_changed_cb(NULL, compose);
10187 g_free(utf8_filename);
10192 static void compose_attach_cb(GtkAction *action, gpointer data)
10194 Compose *compose = (Compose *)data;
10197 if (compose->redirect_filename != NULL)
10200 /* Set focus_window properly, in case we were called via popup menu,
10201 * which unsets it (via focus_out_event callback on compose window). */
10202 manage_window_focus_in(compose->window, NULL, NULL);
10204 file_list = filesel_select_multiple_files_open(_("Select file"));
10207 compose_attach_from_list(compose, file_list, TRUE);
10208 g_list_free(file_list);
10212 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10214 Compose *compose = (Compose *)data;
10216 gint files_inserted = 0;
10218 file_list = filesel_select_multiple_files_open(_("Select file"));
10223 for ( tmp = file_list; tmp; tmp = tmp->next) {
10224 gchar *file = (gchar *) tmp->data;
10225 gchar *filedup = g_strdup(file);
10226 gchar *shortfile = g_path_get_basename(filedup);
10227 ComposeInsertResult res;
10228 /* insert the file if the file is short or if the user confirmed that
10229 he/she wants to insert the large file */
10230 res = compose_insert_file(compose, file);
10231 if (res == COMPOSE_INSERT_READ_ERROR) {
10232 alertpanel_error(_("File '%s' could not be read."), shortfile);
10233 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10234 alertpanel_error(_("File '%s' contained invalid characters\n"
10235 "for the current encoding, insertion may be incorrect."),
10237 } else if (res == COMPOSE_INSERT_SUCCESS)
10244 g_list_free(file_list);
10248 if (files_inserted > 0 && compose->gtkaspell &&
10249 compose->gtkaspell->check_while_typing)
10250 gtkaspell_highlight_all(compose->gtkaspell);
10254 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10256 Compose *compose = (Compose *)data;
10258 compose_insert_sig(compose, FALSE);
10261 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10263 Compose *compose = (Compose *)data;
10265 compose_insert_sig(compose, TRUE);
10268 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10272 Compose *compose = (Compose *)data;
10274 gtkut_widget_get_uposition(widget, &x, &y);
10275 if (!compose->batch) {
10276 prefs_common.compose_x = x;
10277 prefs_common.compose_y = y;
10279 if (compose->sending || compose->updating)
10281 compose_close_cb(NULL, compose);
10285 void compose_close_toolbar(Compose *compose)
10287 compose_close_cb(NULL, compose);
10290 static gboolean compose_can_autosave(Compose *compose)
10292 if (compose->privacy_system && compose->use_encryption)
10293 return prefs_common.autosave && prefs_common.autosave_encrypted;
10295 return prefs_common.autosave;
10298 static void compose_close_cb(GtkAction *action, gpointer data)
10300 Compose *compose = (Compose *)data;
10304 if (compose->exteditor_tag != -1) {
10305 if (!compose_ext_editor_kill(compose))
10310 if (compose->modified) {
10311 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10312 if (!g_mutex_trylock(compose->mutex)) {
10313 /* we don't want to lock the mutex once it's available,
10314 * because as the only other part of compose.c locking
10315 * it is compose_close - which means once unlocked,
10316 * the compose struct will be freed */
10317 debug_print("couldn't lock mutex, probably sending\n");
10321 val = alertpanel(_("Discard message"),
10322 _("This message has been modified. Discard it?"),
10323 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10325 val = alertpanel(_("Save changes"),
10326 _("This message has been modified. Save the latest changes?"),
10327 _("_Don't save"), g_strconcat("+", _("_Save to Drafts"), NULL),
10330 g_mutex_unlock(compose->mutex);
10332 case G_ALERTDEFAULT:
10333 if (compose_can_autosave(compose) && !reedit)
10334 compose_remove_draft(compose);
10336 case G_ALERTALTERNATE:
10337 compose_draft(data, COMPOSE_QUIT_EDITING);
10344 compose_close(compose);
10347 static void compose_print_cb(GtkAction *action, gpointer data)
10349 Compose *compose = (Compose *) data;
10351 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10352 if (compose->targetinfo)
10353 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10356 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10358 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10359 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10360 Compose *compose = (Compose *) data;
10363 compose->out_encoding = (CharSet)value;
10366 static void compose_address_cb(GtkAction *action, gpointer data)
10368 Compose *compose = (Compose *)data;
10370 #ifndef USE_NEW_ADDRBOOK
10371 addressbook_open(compose);
10373 GError* error = NULL;
10374 addressbook_connect_signals(compose);
10375 addressbook_dbus_open(TRUE, &error);
10377 g_warning("%s", error->message);
10378 g_error_free(error);
10383 static void about_show_cb(GtkAction *action, gpointer data)
10388 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10390 Compose *compose = (Compose *)data;
10395 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10396 cm_return_if_fail(tmpl != NULL);
10398 msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10400 val = alertpanel(_("Apply template"), msg,
10401 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10404 if (val == G_ALERTDEFAULT)
10405 compose_template_apply(compose, tmpl, TRUE);
10406 else if (val == G_ALERTALTERNATE)
10407 compose_template_apply(compose, tmpl, FALSE);
10410 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10412 Compose *compose = (Compose *)data;
10414 compose_exec_ext_editor(compose);
10417 static void compose_undo_cb(GtkAction *action, gpointer data)
10419 Compose *compose = (Compose *)data;
10420 gboolean prev_autowrap = compose->autowrap;
10422 compose->autowrap = FALSE;
10423 undo_undo(compose->undostruct);
10424 compose->autowrap = prev_autowrap;
10427 static void compose_redo_cb(GtkAction *action, gpointer data)
10429 Compose *compose = (Compose *)data;
10430 gboolean prev_autowrap = compose->autowrap;
10432 compose->autowrap = FALSE;
10433 undo_redo(compose->undostruct);
10434 compose->autowrap = prev_autowrap;
10437 static void entry_cut_clipboard(GtkWidget *entry)
10439 if (GTK_IS_EDITABLE(entry))
10440 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10441 else if (GTK_IS_TEXT_VIEW(entry))
10442 gtk_text_buffer_cut_clipboard(
10443 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10444 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10448 static void entry_copy_clipboard(GtkWidget *entry)
10450 if (GTK_IS_EDITABLE(entry))
10451 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10452 else if (GTK_IS_TEXT_VIEW(entry))
10453 gtk_text_buffer_copy_clipboard(
10454 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10455 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10458 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10459 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10461 if (GTK_IS_TEXT_VIEW(entry)) {
10462 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10463 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10464 GtkTextIter start_iter, end_iter;
10466 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10468 if (contents == NULL)
10471 /* we shouldn't delete the selection when middle-click-pasting, or we
10472 * can't mid-click-paste our own selection */
10473 if (clip != GDK_SELECTION_PRIMARY) {
10474 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10475 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10478 if (insert_place == NULL) {
10479 /* if insert_place isn't specified, insert at the cursor.
10480 * used for Ctrl-V pasting */
10481 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10482 start = gtk_text_iter_get_offset(&start_iter);
10483 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10485 /* if insert_place is specified, paste here.
10486 * used for mid-click-pasting */
10487 start = gtk_text_iter_get_offset(insert_place);
10488 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10489 if (prefs_common.primary_paste_unselects)
10490 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10494 /* paste unwrapped: mark the paste so it's not wrapped later */
10495 end = start + strlen(contents);
10496 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10497 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10498 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10499 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10500 /* rewrap paragraph now (after a mid-click-paste) */
10501 mark_start = gtk_text_buffer_get_insert(buffer);
10502 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10503 gtk_text_iter_backward_char(&start_iter);
10504 compose_beautify_paragraph(compose, &start_iter, TRUE);
10506 } else if (GTK_IS_EDITABLE(entry))
10507 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10509 compose->modified = TRUE;
10512 static void entry_allsel(GtkWidget *entry)
10514 if (GTK_IS_EDITABLE(entry))
10515 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10516 else if (GTK_IS_TEXT_VIEW(entry)) {
10517 GtkTextIter startiter, enditer;
10518 GtkTextBuffer *textbuf;
10520 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10521 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10522 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10524 gtk_text_buffer_move_mark_by_name(textbuf,
10525 "selection_bound", &startiter);
10526 gtk_text_buffer_move_mark_by_name(textbuf,
10527 "insert", &enditer);
10531 static void compose_cut_cb(GtkAction *action, gpointer data)
10533 Compose *compose = (Compose *)data;
10534 if (compose->focused_editable
10535 #ifndef GENERIC_UMPC
10536 && gtk_widget_has_focus(compose->focused_editable)
10539 entry_cut_clipboard(compose->focused_editable);
10542 static void compose_copy_cb(GtkAction *action, gpointer data)
10544 Compose *compose = (Compose *)data;
10545 if (compose->focused_editable
10546 #ifndef GENERIC_UMPC
10547 && gtk_widget_has_focus(compose->focused_editable)
10550 entry_copy_clipboard(compose->focused_editable);
10553 static void compose_paste_cb(GtkAction *action, gpointer data)
10555 Compose *compose = (Compose *)data;
10556 gint prev_autowrap;
10557 GtkTextBuffer *buffer;
10559 if (compose->focused_editable &&
10560 #ifndef GENERIC_UMPC
10561 gtk_widget_has_focus(compose->focused_editable)
10564 entry_paste_clipboard(compose, compose->focused_editable,
10565 prefs_common.linewrap_pastes,
10566 GDK_SELECTION_CLIPBOARD, NULL);
10571 #ifndef GENERIC_UMPC
10572 gtk_widget_has_focus(compose->text) &&
10574 compose->gtkaspell &&
10575 compose->gtkaspell->check_while_typing)
10576 gtkaspell_highlight_all(compose->gtkaspell);
10580 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10582 Compose *compose = (Compose *)data;
10583 gint wrap_quote = prefs_common.linewrap_quote;
10584 if (compose->focused_editable
10585 #ifndef GENERIC_UMPC
10586 && gtk_widget_has_focus(compose->focused_editable)
10589 /* let text_insert() (called directly or at a later time
10590 * after the gtk_editable_paste_clipboard) know that
10591 * text is to be inserted as a quotation. implemented
10592 * by using a simple refcount... */
10593 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10594 G_OBJECT(compose->focused_editable),
10595 "paste_as_quotation"));
10596 g_object_set_data(G_OBJECT(compose->focused_editable),
10597 "paste_as_quotation",
10598 GINT_TO_POINTER(paste_as_quotation + 1));
10599 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10600 entry_paste_clipboard(compose, compose->focused_editable,
10601 prefs_common.linewrap_pastes,
10602 GDK_SELECTION_CLIPBOARD, NULL);
10603 prefs_common.linewrap_quote = wrap_quote;
10607 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10609 Compose *compose = (Compose *)data;
10610 gint prev_autowrap;
10611 GtkTextBuffer *buffer;
10613 if (compose->focused_editable
10614 #ifndef GENERIC_UMPC
10615 && gtk_widget_has_focus(compose->focused_editable)
10618 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10619 GDK_SELECTION_CLIPBOARD, NULL);
10624 #ifndef GENERIC_UMPC
10625 gtk_widget_has_focus(compose->text) &&
10627 compose->gtkaspell &&
10628 compose->gtkaspell->check_while_typing)
10629 gtkaspell_highlight_all(compose->gtkaspell);
10633 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10635 Compose *compose = (Compose *)data;
10636 gint prev_autowrap;
10637 GtkTextBuffer *buffer;
10639 if (compose->focused_editable
10640 #ifndef GENERIC_UMPC
10641 && gtk_widget_has_focus(compose->focused_editable)
10644 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10645 GDK_SELECTION_CLIPBOARD, NULL);
10650 #ifndef GENERIC_UMPC
10651 gtk_widget_has_focus(compose->text) &&
10653 compose->gtkaspell &&
10654 compose->gtkaspell->check_while_typing)
10655 gtkaspell_highlight_all(compose->gtkaspell);
10659 static void compose_allsel_cb(GtkAction *action, gpointer data)
10661 Compose *compose = (Compose *)data;
10662 if (compose->focused_editable
10663 #ifndef GENERIC_UMPC
10664 && gtk_widget_has_focus(compose->focused_editable)
10667 entry_allsel(compose->focused_editable);
10670 static void textview_move_beginning_of_line (GtkTextView *text)
10672 GtkTextBuffer *buffer;
10676 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10678 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10679 mark = gtk_text_buffer_get_insert(buffer);
10680 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10681 gtk_text_iter_set_line_offset(&ins, 0);
10682 gtk_text_buffer_place_cursor(buffer, &ins);
10685 static void textview_move_forward_character (GtkTextView *text)
10687 GtkTextBuffer *buffer;
10691 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10693 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10694 mark = gtk_text_buffer_get_insert(buffer);
10695 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10696 if (gtk_text_iter_forward_cursor_position(&ins))
10697 gtk_text_buffer_place_cursor(buffer, &ins);
10700 static void textview_move_backward_character (GtkTextView *text)
10702 GtkTextBuffer *buffer;
10706 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10708 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10709 mark = gtk_text_buffer_get_insert(buffer);
10710 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10711 if (gtk_text_iter_backward_cursor_position(&ins))
10712 gtk_text_buffer_place_cursor(buffer, &ins);
10715 static void textview_move_forward_word (GtkTextView *text)
10717 GtkTextBuffer *buffer;
10722 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10724 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10725 mark = gtk_text_buffer_get_insert(buffer);
10726 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10727 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10728 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10729 gtk_text_iter_backward_word_start(&ins);
10730 gtk_text_buffer_place_cursor(buffer, &ins);
10734 static void textview_move_backward_word (GtkTextView *text)
10736 GtkTextBuffer *buffer;
10740 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10742 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10743 mark = gtk_text_buffer_get_insert(buffer);
10744 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10745 if (gtk_text_iter_backward_word_starts(&ins, 1))
10746 gtk_text_buffer_place_cursor(buffer, &ins);
10749 static void textview_move_end_of_line (GtkTextView *text)
10751 GtkTextBuffer *buffer;
10755 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10757 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10758 mark = gtk_text_buffer_get_insert(buffer);
10759 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10760 if (gtk_text_iter_forward_to_line_end(&ins))
10761 gtk_text_buffer_place_cursor(buffer, &ins);
10764 static void textview_move_next_line (GtkTextView *text)
10766 GtkTextBuffer *buffer;
10771 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10773 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10774 mark = gtk_text_buffer_get_insert(buffer);
10775 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10776 offset = gtk_text_iter_get_line_offset(&ins);
10777 if (gtk_text_iter_forward_line(&ins)) {
10778 gtk_text_iter_set_line_offset(&ins, offset);
10779 gtk_text_buffer_place_cursor(buffer, &ins);
10783 static void textview_move_previous_line (GtkTextView *text)
10785 GtkTextBuffer *buffer;
10790 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10792 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10793 mark = gtk_text_buffer_get_insert(buffer);
10794 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10795 offset = gtk_text_iter_get_line_offset(&ins);
10796 if (gtk_text_iter_backward_line(&ins)) {
10797 gtk_text_iter_set_line_offset(&ins, offset);
10798 gtk_text_buffer_place_cursor(buffer, &ins);
10802 static void textview_delete_forward_character (GtkTextView *text)
10804 GtkTextBuffer *buffer;
10806 GtkTextIter ins, end_iter;
10808 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10810 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10811 mark = gtk_text_buffer_get_insert(buffer);
10812 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10814 if (gtk_text_iter_forward_char(&end_iter)) {
10815 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10819 static void textview_delete_backward_character (GtkTextView *text)
10821 GtkTextBuffer *buffer;
10823 GtkTextIter ins, end_iter;
10825 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10827 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10828 mark = gtk_text_buffer_get_insert(buffer);
10829 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10831 if (gtk_text_iter_backward_char(&end_iter)) {
10832 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10836 static void textview_delete_forward_word (GtkTextView *text)
10838 GtkTextBuffer *buffer;
10840 GtkTextIter ins, end_iter;
10842 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10844 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10845 mark = gtk_text_buffer_get_insert(buffer);
10846 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10848 if (gtk_text_iter_forward_word_end(&end_iter)) {
10849 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10853 static void textview_delete_backward_word (GtkTextView *text)
10855 GtkTextBuffer *buffer;
10857 GtkTextIter ins, end_iter;
10859 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10861 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10862 mark = gtk_text_buffer_get_insert(buffer);
10863 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10865 if (gtk_text_iter_backward_word_start(&end_iter)) {
10866 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10870 static void textview_delete_line (GtkTextView *text)
10872 GtkTextBuffer *buffer;
10874 GtkTextIter ins, start_iter, end_iter;
10876 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10878 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10879 mark = gtk_text_buffer_get_insert(buffer);
10880 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10883 gtk_text_iter_set_line_offset(&start_iter, 0);
10886 if (gtk_text_iter_ends_line(&end_iter)){
10887 if (!gtk_text_iter_forward_char(&end_iter))
10888 gtk_text_iter_backward_char(&start_iter);
10891 gtk_text_iter_forward_to_line_end(&end_iter);
10892 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10895 static void textview_delete_to_line_end (GtkTextView *text)
10897 GtkTextBuffer *buffer;
10899 GtkTextIter ins, end_iter;
10901 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10903 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10904 mark = gtk_text_buffer_get_insert(buffer);
10905 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10907 if (gtk_text_iter_ends_line(&end_iter))
10908 gtk_text_iter_forward_char(&end_iter);
10910 gtk_text_iter_forward_to_line_end(&end_iter);
10911 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10914 #define DO_ACTION(name, act) { \
10915 if(!strcmp(name, a_name)) { \
10919 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10921 const gchar *a_name = gtk_action_get_name(action);
10922 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10923 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10924 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10925 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10926 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10927 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10928 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10929 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10930 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10931 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10932 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10933 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10934 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10935 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10939 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10941 Compose *compose = (Compose *)data;
10942 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10943 ComposeCallAdvancedAction action = -1;
10945 action = compose_call_advanced_action_from_path(gaction);
10948 void (*do_action) (GtkTextView *text);
10949 } action_table[] = {
10950 {textview_move_beginning_of_line},
10951 {textview_move_forward_character},
10952 {textview_move_backward_character},
10953 {textview_move_forward_word},
10954 {textview_move_backward_word},
10955 {textview_move_end_of_line},
10956 {textview_move_next_line},
10957 {textview_move_previous_line},
10958 {textview_delete_forward_character},
10959 {textview_delete_backward_character},
10960 {textview_delete_forward_word},
10961 {textview_delete_backward_word},
10962 {textview_delete_line},
10963 {textview_delete_to_line_end}
10966 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10968 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10969 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10970 if (action_table[action].do_action)
10971 action_table[action].do_action(text);
10973 g_warning("Not implemented yet.");
10977 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10979 GtkAllocation allocation;
10983 if (GTK_IS_EDITABLE(widget)) {
10984 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10985 gtk_editable_set_position(GTK_EDITABLE(widget),
10988 if ((parent = gtk_widget_get_parent(widget))
10989 && (parent = gtk_widget_get_parent(parent))
10990 && (parent = gtk_widget_get_parent(parent))) {
10991 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10992 gtk_widget_get_allocation(widget, &allocation);
10993 gint y = allocation.y;
10994 gint height = allocation.height;
10995 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10996 (GTK_SCROLLED_WINDOW(parent));
10998 gfloat value = gtk_adjustment_get_value(shown);
10999 gfloat upper = gtk_adjustment_get_upper(shown);
11000 gfloat page_size = gtk_adjustment_get_page_size(shown);
11001 if (y < (int)value) {
11002 gtk_adjustment_set_value(shown, y - 1);
11004 if ((y + height) > ((int)value + (int)page_size)) {
11005 if ((y - height - 1) < ((int)upper - (int)page_size)) {
11006 gtk_adjustment_set_value(shown,
11007 y + height - (int)page_size - 1);
11009 gtk_adjustment_set_value(shown,
11010 (int)upper - (int)page_size - 1);
11017 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
11018 compose->focused_editable = widget;
11020 #ifdef GENERIC_UMPC
11021 if (GTK_IS_TEXT_VIEW(widget)
11022 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
11023 g_object_ref(compose->notebook);
11024 g_object_ref(compose->edit_vbox);
11025 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11026 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11027 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
11028 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
11029 g_object_unref(compose->notebook);
11030 g_object_unref(compose->edit_vbox);
11031 g_signal_handlers_block_by_func(G_OBJECT(widget),
11032 G_CALLBACK(compose_grab_focus_cb),
11034 gtk_widget_grab_focus(widget);
11035 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11036 G_CALLBACK(compose_grab_focus_cb),
11038 } else if (!GTK_IS_TEXT_VIEW(widget)
11039 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
11040 g_object_ref(compose->notebook);
11041 g_object_ref(compose->edit_vbox);
11042 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11043 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11044 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
11045 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
11046 g_object_unref(compose->notebook);
11047 g_object_unref(compose->edit_vbox);
11048 g_signal_handlers_block_by_func(G_OBJECT(widget),
11049 G_CALLBACK(compose_grab_focus_cb),
11051 gtk_widget_grab_focus(widget);
11052 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11053 G_CALLBACK(compose_grab_focus_cb),
11059 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
11061 compose->modified = TRUE;
11062 // compose_beautify_paragraph(compose, NULL, TRUE);
11063 #ifndef GENERIC_UMPC
11064 compose_set_title(compose);
11068 static void compose_wrap_cb(GtkAction *action, gpointer data)
11070 Compose *compose = (Compose *)data;
11071 compose_beautify_paragraph(compose, NULL, TRUE);
11074 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
11076 Compose *compose = (Compose *)data;
11077 compose_wrap_all_full(compose, TRUE);
11080 static void compose_find_cb(GtkAction *action, gpointer data)
11082 Compose *compose = (Compose *)data;
11084 message_search_compose(compose);
11087 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
11090 Compose *compose = (Compose *)data;
11091 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11092 if (compose->autowrap)
11093 compose_wrap_all_full(compose, TRUE);
11094 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11097 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
11100 Compose *compose = (Compose *)data;
11101 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11104 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
11106 Compose *compose = (Compose *)data;
11108 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11111 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
11113 Compose *compose = (Compose *)data;
11115 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11118 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
11120 g_free(compose->privacy_system);
11121 g_free(compose->encdata);
11123 compose->privacy_system = g_strdup(account->default_privacy_system);
11124 compose_update_privacy_system_menu_item(compose, warn);
11127 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
11129 Compose *compose = (Compose *)data;
11131 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
11132 gtk_widget_show(compose->ruler_hbox);
11133 prefs_common.show_ruler = TRUE;
11135 gtk_widget_hide(compose->ruler_hbox);
11136 gtk_widget_queue_resize(compose->edit_vbox);
11137 prefs_common.show_ruler = FALSE;
11141 static void compose_attach_drag_received_cb (GtkWidget *widget,
11142 GdkDragContext *context,
11145 GtkSelectionData *data,
11148 gpointer user_data)
11150 Compose *compose = (Compose *)user_data;
11154 type = gtk_selection_data_get_data_type(data);
11155 if ((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11156 && gtk_drag_get_source_widget(context) !=
11157 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11158 list = uri_list_extract_filenames(
11159 (const gchar *)gtk_selection_data_get_data(data));
11160 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11161 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11162 compose_attach_append
11163 (compose, (const gchar *)tmp->data,
11164 utf8_filename, NULL, NULL);
11165 g_free(utf8_filename);
11167 if (list) compose_changed_cb(NULL, compose);
11168 list_free_strings(list);
11170 } else if (gtk_drag_get_source_widget(context)
11171 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11172 /* comes from our summaryview */
11173 SummaryView * summaryview = NULL;
11174 GSList * list = NULL, *cur = NULL;
11176 if (mainwindow_get_mainwindow())
11177 summaryview = mainwindow_get_mainwindow()->summaryview;
11180 list = summary_get_selected_msg_list(summaryview);
11182 for (cur = list; cur; cur = cur->next) {
11183 MsgInfo *msginfo = (MsgInfo *)cur->data;
11184 gchar *file = NULL;
11186 file = procmsg_get_message_file_full(msginfo,
11189 compose_attach_append(compose, (const gchar *)file,
11190 (const gchar *)file, "message/rfc822", NULL);
11194 g_slist_free(list);
11198 static gboolean compose_drag_drop(GtkWidget *widget,
11199 GdkDragContext *drag_context,
11201 guint time, gpointer user_data)
11203 /* not handling this signal makes compose_insert_drag_received_cb
11208 static gboolean completion_set_focus_to_subject
11209 (GtkWidget *widget,
11210 GdkEventKey *event,
11213 cm_return_val_if_fail(compose != NULL, FALSE);
11215 /* make backtab move to subject field */
11216 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11217 gtk_widget_grab_focus(compose->subject_entry);
11223 static void compose_insert_drag_received_cb (GtkWidget *widget,
11224 GdkDragContext *drag_context,
11227 GtkSelectionData *data,
11230 gpointer user_data)
11232 Compose *compose = (Compose *)user_data;
11236 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11238 type = gtk_selection_data_get_data_type(data);
11239 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11240 AlertValue val = G_ALERTDEFAULT;
11241 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11243 list = uri_list_extract_filenames(ddata);
11244 if (list == NULL && strstr(ddata, "://")) {
11245 /* Assume a list of no files, and data has ://, is a remote link */
11246 gchar *tmpdata = g_strstrip(g_strdup(ddata));
11247 gchar *tmpfile = get_tmp_file();
11248 str_write_to_file(tmpdata, tmpfile);
11250 compose_insert_file(compose, tmpfile);
11251 claws_unlink(tmpfile);
11253 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11254 compose_beautify_paragraph(compose, NULL, TRUE);
11257 switch (prefs_common.compose_dnd_mode) {
11258 case COMPOSE_DND_ASK:
11259 val = alertpanel_full(_("Insert or attach?"),
11260 _("Do you want to insert the contents of the file(s) "
11261 "into the message body, or attach it to the email?"),
11262 GTK_STOCK_CANCEL, g_strconcat("+", _("_Insert"), NULL), _("_Attach"),
11263 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11265 case COMPOSE_DND_INSERT:
11266 val = G_ALERTALTERNATE;
11268 case COMPOSE_DND_ATTACH:
11269 val = G_ALERTOTHER;
11272 /* unexpected case */
11273 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11276 if (val & G_ALERTDISABLE) {
11277 val &= ~G_ALERTDISABLE;
11278 /* remember what action to perform by default, only if we don't click Cancel */
11279 if (val == G_ALERTALTERNATE)
11280 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11281 else if (val == G_ALERTOTHER)
11282 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11285 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11286 gtk_drag_finish(drag_context, FALSE, FALSE, time);
11287 list_free_strings(list);
11290 } else if (val == G_ALERTOTHER) {
11291 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11292 list_free_strings(list);
11297 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11298 compose_insert_file(compose, (const gchar *)tmp->data);
11300 list_free_strings(list);
11302 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11307 static void compose_header_drag_received_cb (GtkWidget *widget,
11308 GdkDragContext *drag_context,
11311 GtkSelectionData *data,
11314 gpointer user_data)
11316 GtkEditable *entry = (GtkEditable *)user_data;
11317 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11319 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11322 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11323 gchar *decoded=g_new(gchar, strlen(email));
11326 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11327 gtk_editable_delete_text(entry, 0, -1);
11328 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11329 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11333 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11336 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11338 Compose *compose = (Compose *)data;
11340 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11341 compose->return_receipt = TRUE;
11343 compose->return_receipt = FALSE;
11346 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11348 Compose *compose = (Compose *)data;
11350 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11351 compose->remove_references = TRUE;
11353 compose->remove_references = FALSE;
11356 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11357 ComposeHeaderEntry *headerentry)
11359 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11363 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11364 GdkEventKey *event,
11365 ComposeHeaderEntry *headerentry)
11367 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11368 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11369 !(event->state & GDK_MODIFIER_MASK) &&
11370 (event->keyval == GDK_KEY_BackSpace) &&
11371 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11372 gtk_container_remove
11373 (GTK_CONTAINER(headerentry->compose->header_table),
11374 headerentry->combo);
11375 gtk_container_remove
11376 (GTK_CONTAINER(headerentry->compose->header_table),
11377 headerentry->entry);
11378 headerentry->compose->header_list =
11379 g_slist_remove(headerentry->compose->header_list,
11381 g_free(headerentry);
11382 } else if (event->keyval == GDK_KEY_Tab) {
11383 if (headerentry->compose->header_last == headerentry) {
11384 /* Override default next focus, and give it to subject_entry
11385 * instead of notebook tabs
11387 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11388 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11395 static gboolean scroll_postpone(gpointer data)
11397 Compose *compose = (Compose *)data;
11399 if (compose->batch)
11402 GTK_EVENTS_FLUSH();
11403 compose_show_first_last_header(compose, FALSE);
11407 static void compose_headerentry_changed_cb(GtkWidget *entry,
11408 ComposeHeaderEntry *headerentry)
11410 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11411 compose_create_header_entry(headerentry->compose);
11412 g_signal_handlers_disconnect_matched
11413 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11414 0, 0, NULL, NULL, headerentry);
11416 if (!headerentry->compose->batch)
11417 g_timeout_add(0, scroll_postpone, headerentry->compose);
11421 static gboolean compose_defer_auto_save_draft(Compose *compose)
11423 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11424 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11428 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11430 GtkAdjustment *vadj;
11432 cm_return_if_fail(compose);
11437 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11438 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11439 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11440 gtk_widget_get_parent(compose->header_table)));
11441 gtk_adjustment_set_value(vadj, (show_first ?
11442 gtk_adjustment_get_lower(vadj) :
11443 (gtk_adjustment_get_upper(vadj) -
11444 gtk_adjustment_get_page_size(vadj))));
11445 gtk_adjustment_changed(vadj);
11448 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11449 const gchar *text, gint len, Compose *compose)
11451 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11452 (G_OBJECT(compose->text), "paste_as_quotation"));
11455 cm_return_if_fail(text != NULL);
11457 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11458 G_CALLBACK(text_inserted),
11460 if (paste_as_quotation) {
11462 const gchar *qmark;
11464 GtkTextIter start_iter;
11467 len = strlen(text);
11469 new_text = g_strndup(text, len);
11471 qmark = compose_quote_char_from_context(compose);
11473 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11474 gtk_text_buffer_place_cursor(buffer, iter);
11476 pos = gtk_text_iter_get_offset(iter);
11478 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11479 _("Quote format error at line %d."));
11480 quote_fmt_reset_vartable();
11482 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11483 GINT_TO_POINTER(paste_as_quotation - 1));
11485 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11486 gtk_text_buffer_place_cursor(buffer, iter);
11487 gtk_text_buffer_delete_mark(buffer, mark);
11489 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11490 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11491 compose_beautify_paragraph(compose, &start_iter, FALSE);
11492 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11493 gtk_text_buffer_delete_mark(buffer, mark);
11495 if (strcmp(text, "\n") || compose->automatic_break
11496 || gtk_text_iter_starts_line(iter)) {
11497 GtkTextIter before_ins;
11498 gtk_text_buffer_insert(buffer, iter, text, len);
11499 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11500 before_ins = *iter;
11501 gtk_text_iter_backward_chars(&before_ins, len);
11502 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11505 /* check if the preceding is just whitespace or quote */
11506 GtkTextIter start_line;
11507 gchar *tmp = NULL, *quote = NULL;
11508 gint quote_len = 0, is_normal = 0;
11509 start_line = *iter;
11510 gtk_text_iter_set_line_offset(&start_line, 0);
11511 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11514 if (*tmp == '\0') {
11517 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11525 gtk_text_buffer_insert(buffer, iter, text, len);
11527 gtk_text_buffer_insert_with_tags_by_name(buffer,
11528 iter, text, len, "no_join", NULL);
11533 if (!paste_as_quotation) {
11534 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11535 compose_beautify_paragraph(compose, iter, FALSE);
11536 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11537 gtk_text_buffer_delete_mark(buffer, mark);
11540 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11541 G_CALLBACK(text_inserted),
11543 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11545 if (compose_can_autosave(compose) &&
11546 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11547 compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11548 compose->draft_timeout_tag = g_timeout_add
11549 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11553 static void compose_check_all(GtkAction *action, gpointer data)
11555 Compose *compose = (Compose *)data;
11556 if (!compose->gtkaspell)
11559 if (gtk_widget_has_focus(compose->subject_entry))
11560 claws_spell_entry_check_all(
11561 CLAWS_SPELL_ENTRY(compose->subject_entry));
11563 gtkaspell_check_all(compose->gtkaspell);
11566 static void compose_highlight_all(GtkAction *action, gpointer data)
11568 Compose *compose = (Compose *)data;
11569 if (compose->gtkaspell) {
11570 claws_spell_entry_recheck_all(
11571 CLAWS_SPELL_ENTRY(compose->subject_entry));
11572 gtkaspell_highlight_all(compose->gtkaspell);
11576 static void compose_check_backwards(GtkAction *action, gpointer data)
11578 Compose *compose = (Compose *)data;
11579 if (!compose->gtkaspell) {
11580 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11584 if (gtk_widget_has_focus(compose->subject_entry))
11585 claws_spell_entry_check_backwards(
11586 CLAWS_SPELL_ENTRY(compose->subject_entry));
11588 gtkaspell_check_backwards(compose->gtkaspell);
11591 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11593 Compose *compose = (Compose *)data;
11594 if (!compose->gtkaspell) {
11595 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11599 if (gtk_widget_has_focus(compose->subject_entry))
11600 claws_spell_entry_check_forwards_go(
11601 CLAWS_SPELL_ENTRY(compose->subject_entry));
11603 gtkaspell_check_forwards_go(compose->gtkaspell);
11608 *\brief Guess originating forward account from MsgInfo and several
11609 * "common preference" settings. Return NULL if no guess.
11611 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11613 PrefsAccount *account = NULL;
11615 cm_return_val_if_fail(msginfo, NULL);
11616 cm_return_val_if_fail(msginfo->folder, NULL);
11617 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11619 if (msginfo->folder->prefs->enable_default_account)
11620 account = account_find_from_id(msginfo->folder->prefs->default_account);
11623 account = msginfo->folder->folder->account;
11625 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11627 Xstrdup_a(to, msginfo->to, return NULL);
11628 extract_address(to);
11629 account = account_find_from_address(to, FALSE);
11632 if (!account && prefs_common.forward_account_autosel) {
11633 gchar cc[BUFFSIZE];
11634 if (!procheader_get_header_from_msginfo
11635 (msginfo, cc,sizeof cc , "Cc:")) {
11636 gchar *buf = cc + strlen("Cc:");
11637 extract_address(buf);
11638 account = account_find_from_address(buf, FALSE);
11642 if (!account && prefs_common.forward_account_autosel) {
11643 gchar deliveredto[BUFFSIZE];
11644 if (!procheader_get_header_from_msginfo
11645 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11646 gchar *buf = deliveredto + strlen("Delivered-To:");
11647 extract_address(buf);
11648 account = account_find_from_address(buf, FALSE);
11655 gboolean compose_close(Compose *compose)
11659 cm_return_val_if_fail(compose, FALSE);
11661 if (!g_mutex_trylock(compose->mutex)) {
11662 /* we have to wait for the (possibly deferred by auto-save)
11663 * drafting to be done, before destroying the compose under
11665 debug_print("waiting for drafting to finish...\n");
11666 compose_allow_user_actions(compose, FALSE);
11667 if (compose->close_timeout_tag == 0) {
11668 compose->close_timeout_tag =
11669 g_timeout_add (500, (GSourceFunc) compose_close,
11675 if (compose->draft_timeout_tag >= 0) {
11676 g_source_remove(compose->draft_timeout_tag);
11677 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11680 gtkut_widget_get_uposition(compose->window, &x, &y);
11681 if (!compose->batch) {
11682 prefs_common.compose_x = x;
11683 prefs_common.compose_y = y;
11685 g_mutex_unlock(compose->mutex);
11686 compose_destroy(compose);
11691 * Add entry field for each address in list.
11692 * \param compose E-Mail composition object.
11693 * \param listAddress List of (formatted) E-Mail addresses.
11695 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11698 node = listAddress;
11700 addr = ( gchar * ) node->data;
11701 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11702 node = g_list_next( node );
11706 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11707 guint action, gboolean opening_multiple)
11709 gchar *body = NULL;
11710 GSList *new_msglist = NULL;
11711 MsgInfo *tmp_msginfo = NULL;
11712 gboolean originally_enc = FALSE;
11713 gboolean originally_sig = FALSE;
11714 Compose *compose = NULL;
11715 gchar *s_system = NULL;
11717 cm_return_if_fail(msgview != NULL);
11719 cm_return_if_fail(msginfo_list != NULL);
11721 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11722 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11723 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11725 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11726 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11727 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11728 orig_msginfo, mimeinfo);
11729 if (tmp_msginfo != NULL) {
11730 new_msglist = g_slist_append(NULL, tmp_msginfo);
11732 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11733 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11734 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11736 tmp_msginfo->folder = orig_msginfo->folder;
11737 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11738 if (orig_msginfo->tags) {
11739 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11740 tmp_msginfo->folder->tags_dirty = TRUE;
11746 if (!opening_multiple)
11747 body = messageview_get_selection(msgview);
11750 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11751 procmsg_msginfo_free(tmp_msginfo);
11752 g_slist_free(new_msglist);
11754 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11756 if (compose && originally_enc) {
11757 compose_force_encryption(compose, compose->account, FALSE, s_system);
11760 if (compose && originally_sig && compose->account->default_sign_reply) {
11761 compose_force_signing(compose, compose->account, s_system);
11765 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11768 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11771 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11772 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11773 GSList *cur = msginfo_list;
11774 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11775 "messages. Opening the windows "
11776 "could take some time. Do you "
11777 "want to continue?"),
11778 g_slist_length(msginfo_list));
11779 if (g_slist_length(msginfo_list) > 9
11780 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11781 != G_ALERTALTERNATE) {
11786 /* We'll open multiple compose windows */
11787 /* let the WM place the next windows */
11788 compose_force_window_origin = FALSE;
11789 for (; cur; cur = cur->next) {
11791 tmplist.data = cur->data;
11792 tmplist.next = NULL;
11793 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11795 compose_force_window_origin = TRUE;
11797 /* forwarding multiple mails as attachments is done via a
11798 * single compose window */
11799 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11803 void compose_check_for_email_account(Compose *compose)
11805 PrefsAccount *ac = NULL, *curr = NULL;
11811 if (compose->account && compose->account->protocol == A_NNTP) {
11812 ac = account_get_cur_account();
11813 if (ac->protocol == A_NNTP) {
11814 list = account_get_list();
11816 for( ; list != NULL ; list = g_list_next(list)) {
11817 curr = (PrefsAccount *) list->data;
11818 if (curr->protocol != A_NNTP) {
11824 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11829 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11830 const gchar *address)
11832 GSList *msginfo_list = NULL;
11833 gchar *body = messageview_get_selection(msgview);
11836 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11838 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11839 compose_check_for_email_account(compose);
11840 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11841 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11842 compose_reply_set_subject(compose, msginfo);
11845 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11848 void compose_set_position(Compose *compose, gint pos)
11850 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11852 gtkut_text_view_set_position(text, pos);
11855 gboolean compose_search_string(Compose *compose,
11856 const gchar *str, gboolean case_sens)
11858 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11860 return gtkut_text_view_search_string(text, str, case_sens);
11863 gboolean compose_search_string_backward(Compose *compose,
11864 const gchar *str, gboolean case_sens)
11866 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11868 return gtkut_text_view_search_string_backward(text, str, case_sens);
11871 /* allocate a msginfo structure and populate its data from a compose data structure */
11872 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11874 MsgInfo *newmsginfo;
11876 gchar buf[BUFFSIZE];
11878 cm_return_val_if_fail( compose != NULL, NULL );
11880 newmsginfo = procmsg_msginfo_new();
11883 get_rfc822_date(buf, sizeof(buf));
11884 newmsginfo->date = g_strdup(buf);
11887 if (compose->from_name) {
11888 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11889 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11893 if (compose->subject_entry)
11894 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11896 /* to, cc, reply-to, newsgroups */
11897 for (list = compose->header_list; list; list = list->next) {
11898 gchar *header = gtk_editable_get_chars(
11900 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11901 gchar *entry = gtk_editable_get_chars(
11902 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11904 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11905 if ( newmsginfo->to == NULL ) {
11906 newmsginfo->to = g_strdup(entry);
11907 } else if (entry && *entry) {
11908 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11909 g_free(newmsginfo->to);
11910 newmsginfo->to = tmp;
11913 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11914 if ( newmsginfo->cc == NULL ) {
11915 newmsginfo->cc = g_strdup(entry);
11916 } else if (entry && *entry) {
11917 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11918 g_free(newmsginfo->cc);
11919 newmsginfo->cc = tmp;
11922 if ( strcasecmp(header,
11923 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11924 if ( newmsginfo->newsgroups == NULL ) {
11925 newmsginfo->newsgroups = g_strdup(entry);
11926 } else if (entry && *entry) {
11927 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11928 g_free(newmsginfo->newsgroups);
11929 newmsginfo->newsgroups = tmp;
11937 /* other data is unset */
11943 /* update compose's dictionaries from folder dict settings */
11944 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11945 FolderItem *folder_item)
11947 cm_return_if_fail(compose != NULL);
11949 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11950 FolderItemPrefs *prefs = folder_item->prefs;
11952 if (prefs->enable_default_dictionary)
11953 gtkaspell_change_dict(compose->gtkaspell,
11954 prefs->default_dictionary, FALSE);
11955 if (folder_item->prefs->enable_default_alt_dictionary)
11956 gtkaspell_change_alt_dict(compose->gtkaspell,
11957 prefs->default_alt_dictionary);
11958 if (prefs->enable_default_dictionary
11959 || prefs->enable_default_alt_dictionary)
11960 compose_spell_menu_changed(compose);
11965 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11967 Compose *compose = (Compose *)data;
11969 cm_return_if_fail(compose != NULL);
11971 gtk_widget_grab_focus(compose->text);
11974 static void from_name_activate_cb(GtkWidget *widget, gpointer data)
11976 gtk_combo_box_popup(GTK_COMBO_BOX(data));