2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void account_activated (GtkComboBox *optmenu,
359 static void attach_selected (GtkTreeView *tree_view,
360 GtkTreePath *tree_path,
361 GtkTreeViewColumn *column,
363 static gboolean attach_button_pressed (GtkWidget *widget,
364 GdkEventButton *event,
366 static gboolean attach_key_pressed (GtkWidget *widget,
369 static void compose_send_cb (GtkAction *action, gpointer data);
370 static void compose_send_later_cb (GtkAction *action, gpointer data);
372 static void compose_save_cb (GtkAction *action,
375 static void compose_attach_cb (GtkAction *action,
377 static void compose_insert_file_cb (GtkAction *action,
379 static void compose_insert_sig_cb (GtkAction *action,
382 static void compose_close_cb (GtkAction *action,
385 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
387 static void compose_address_cb (GtkAction *action,
389 static void about_show_cb (GtkAction *action,
391 static void compose_template_activate_cb(GtkWidget *widget,
394 static void compose_ext_editor_cb (GtkAction *action,
397 static gint compose_delete_cb (GtkWidget *widget,
401 static void compose_undo_cb (GtkAction *action,
403 static void compose_redo_cb (GtkAction *action,
405 static void compose_cut_cb (GtkAction *action,
407 static void compose_copy_cb (GtkAction *action,
409 static void compose_paste_cb (GtkAction *action,
411 static void compose_paste_as_quote_cb (GtkAction *action,
413 static void compose_paste_no_wrap_cb (GtkAction *action,
415 static void compose_paste_wrap_cb (GtkAction *action,
417 static void compose_allsel_cb (GtkAction *action,
420 static void compose_advanced_action_cb (GtkAction *action,
423 static void compose_grab_focus_cb (GtkWidget *widget,
426 static void compose_changed_cb (GtkTextBuffer *textbuf,
429 static void compose_wrap_cb (GtkAction *action,
431 static void compose_wrap_all_cb (GtkAction *action,
433 static void compose_find_cb (GtkAction *action,
435 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
437 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
440 static void compose_toggle_ruler_cb (GtkToggleAction *action,
442 static void compose_toggle_sign_cb (GtkToggleAction *action,
444 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
446 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
447 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
448 static void activate_privacy_system (Compose *compose,
449 PrefsAccount *account,
451 static void compose_use_signing(Compose *compose, gboolean use_signing);
452 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
453 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
455 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
457 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
458 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
459 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
461 static void compose_attach_drag_received_cb (GtkWidget *widget,
462 GdkDragContext *drag_context,
465 GtkSelectionData *data,
469 static void compose_insert_drag_received_cb (GtkWidget *widget,
470 GdkDragContext *drag_context,
473 GtkSelectionData *data,
477 static void compose_header_drag_received_cb (GtkWidget *widget,
478 GdkDragContext *drag_context,
481 GtkSelectionData *data,
486 static gboolean compose_drag_drop (GtkWidget *widget,
487 GdkDragContext *drag_context,
489 guint time, gpointer user_data);
491 static void text_inserted (GtkTextBuffer *buffer,
496 static Compose *compose_generic_reply(MsgInfo *msginfo,
497 ComposeQuoteMode quote_mode,
501 gboolean followup_and_reply_to,
504 static void compose_headerentry_changed_cb (GtkWidget *entry,
505 ComposeHeaderEntry *headerentry);
506 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
508 ComposeHeaderEntry *headerentry);
509 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
510 ComposeHeaderEntry *headerentry);
512 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
514 static void compose_allow_user_actions (Compose *compose, gboolean allow);
516 static void compose_nothing_cb (GtkAction *action, gpointer data)
522 static void compose_check_all (GtkAction *action, gpointer data);
523 static void compose_highlight_all (GtkAction *action, gpointer data);
524 static void compose_check_backwards (GtkAction *action, gpointer data);
525 static void compose_check_forwards_go (GtkAction *action, gpointer data);
528 static gint compose_defer_auto_save_draft (Compose *compose);
529 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
531 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
534 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
535 FolderItem *folder_item);
537 static void compose_attach_update_label(Compose *compose);
538 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
539 gboolean respect_default_to);
541 static GtkActionEntry compose_popup_entries[] =
543 {"Compose", NULL, "Compose" },
544 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
545 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
546 {"Compose/---", NULL, "---", NULL, NULL, NULL },
547 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
550 static GtkActionEntry compose_entries[] =
552 {"Menu", NULL, "Menu" },
554 {"Message", NULL, N_("_Message") },
555 {"Edit", NULL, N_("_Edit") },
557 {"Spelling", NULL, N_("_Spelling") },
559 {"Options", NULL, N_("_Options") },
560 {"Tools", NULL, N_("_Tools") },
561 {"Help", NULL, N_("_Help") },
563 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
564 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
565 {"Message/---", NULL, "---" },
567 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
568 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
569 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
570 /* {"Message/---", NULL, "---" }, */
571 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
572 /* {"Message/---", NULL, "---" }, */
573 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
576 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
577 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
578 {"Edit/---", NULL, "---" },
580 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
581 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
582 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
584 {"Edit/SpecialPaste", NULL, N_("Special paste") },
585 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
586 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
587 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
589 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
591 {"Edit/Advanced", NULL, N_("A_dvanced") },
592 {"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*/
593 {"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*/
594 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
595 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
596 {"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*/
597 {"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*/
598 {"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*/
599 {"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*/
600 {"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*/
601 {"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*/
602 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
603 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
604 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
605 {"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*/
607 /* {"Edit/---", NULL, "---" }, */
608 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
610 /* {"Edit/---", NULL, "---" }, */
611 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
612 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
613 /* {"Edit/---", NULL, "---" }, */
614 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
617 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
618 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
619 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
620 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
622 {"Spelling/---", NULL, "---" },
623 {"Spelling/Options", NULL, N_("_Options") },
628 {"Options/ReplyMode", NULL, N_("Reply _mode") },
629 {"Options/---", NULL, "---" },
630 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
631 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
633 /* {"Options/---", NULL, "---" }, */
635 {"Options/Priority", NULL, N_("_Priority") },
637 {"Options/Encoding", NULL, N_("Character _encoding") },
638 {"Options/Encoding/---", NULL, "---" },
639 #define ENC_ACTION(cs_char,c_char,string) \
640 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
642 {"Options/Encoding/Western", NULL, N_("Western European") },
643 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
644 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
645 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
646 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
647 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
648 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
649 {"Options/Encoding/Korean", NULL, N_("Korean") },
650 {"Options/Encoding/Thai", NULL, N_("Thai") },
653 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
655 {"Tools/Template", NULL, N_("_Template") },
656 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Tools/Actions", NULL, N_("Actio_ns") },
658 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
661 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
664 static GtkToggleActionEntry compose_toggle_entries[] =
666 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
667 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
668 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
669 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
670 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
671 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
672 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
675 static GtkRadioActionEntry compose_radio_rm_entries[] =
677 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
680 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
683 static GtkRadioActionEntry compose_radio_prio_entries[] =
685 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
689 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
692 static GtkRadioActionEntry compose_radio_enc_entries[] =
694 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
728 static GtkTargetEntry compose_mime_types[] =
730 {"text/uri-list", 0, 0},
731 {"UTF8_STRING", 0, 0},
735 static gboolean compose_put_existing_to_front(MsgInfo *info)
737 GList *compose_list = compose_get_compose_list();
741 for (elem = compose_list; elem != NULL && elem->data != NULL;
743 Compose *c = (Compose*)elem->data;
745 if (!c->targetinfo || !c->targetinfo->msgid ||
749 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
750 gtkut_window_popup(c->window);
758 static GdkColor quote_color1 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_color2 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762 static GdkColor quote_color3 =
763 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor1 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor2 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
769 static GdkColor quote_bgcolor3 =
770 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor signature_color = {
779 static GdkColor uri_color = {
786 static void compose_create_tags(GtkTextView *text, Compose *compose)
788 GtkTextBuffer *buffer;
789 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 buffer = gtk_text_view_get_buffer(text);
797 if (prefs_common.enable_color) {
798 /* grab the quote colors, converting from an int to a GdkColor */
799 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
807 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
809 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
811 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
813 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
816 signature_color = quote_color1 = quote_color2 = quote_color3 =
817 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
820 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
821 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
822 "foreground-gdk", "e_color1,
823 "paragraph-background-gdk", "e_bgcolor1,
825 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
826 "foreground-gdk", "e_color2,
827 "paragraph-background-gdk", "e_bgcolor2,
829 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
830 "foreground-gdk", "e_color3,
831 "paragraph-background-gdk", "e_bgcolor3,
834 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
835 "foreground-gdk", "e_color1,
837 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
838 "foreground-gdk", "e_color2,
840 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
841 "foreground-gdk", "e_color3,
845 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
846 "foreground-gdk", &signature_color,
849 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
850 "foreground-gdk", &uri_color,
852 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
853 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
855 color[0] = quote_color1;
856 color[1] = quote_color2;
857 color[2] = quote_color3;
858 color[3] = quote_bgcolor1;
859 color[4] = quote_bgcolor2;
860 color[5] = quote_bgcolor3;
861 color[6] = signature_color;
862 color[7] = uri_color;
863 cmap = gdk_drawable_get_colormap(compose->window->window);
864 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
866 for (i = 0; i < 8; i++) {
867 if (success[i] == FALSE) {
870 g_warning("Compose: color allocation failed.\n");
871 style = gtk_widget_get_style(GTK_WIDGET(text));
872 quote_color1 = quote_color2 = quote_color3 =
873 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
874 signature_color = uri_color = black;
879 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
880 GPtrArray *attach_files)
882 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
885 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
887 return compose_generic_new(account, mailto, item, NULL, NULL);
890 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
892 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
895 #define SCROLL_TO_CURSOR(compose) { \
896 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
897 gtk_text_view_get_buffer( \
898 GTK_TEXT_VIEW(compose->text))); \
899 gtk_text_view_scroll_mark_onscreen( \
900 GTK_TEXT_VIEW(compose->text), \
904 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
907 if (folderidentifier) {
908 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
909 prefs_common.compose_save_to_history = add_history(
910 prefs_common.compose_save_to_history, folderidentifier);
911 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
912 prefs_common.compose_save_to_history);
915 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
916 if (folderidentifier)
917 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
919 gtk_entry_set_text(GTK_ENTRY(entry), "");
922 static gchar *compose_get_save_to(Compose *compose)
925 gchar *result = NULL;
926 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
927 result = gtk_editable_get_chars(entry, 0, -1);
930 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
931 prefs_common.compose_save_to_history = add_history(
932 prefs_common.compose_save_to_history, result);
933 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
934 prefs_common.compose_save_to_history);
939 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
940 GPtrArray *attach_files, GList *listAddress )
943 GtkTextView *textview;
944 GtkTextBuffer *textbuf;
946 const gchar *subject_format = NULL;
947 const gchar *body_format = NULL;
948 gchar *mailto_from = NULL;
949 PrefsAccount *mailto_account = NULL;
950 MsgInfo* dummyinfo = NULL;
951 MailField mfield = NO_FIELD_PRESENT;
955 /* check if mailto defines a from */
956 if (mailto && *mailto != '\0') {
957 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
958 /* mailto defines a from, check if we can get account prefs from it,
959 if not, the account prefs will be guessed using other ways, but we'll keep
962 mailto_account = account_find_from_address(mailto_from, TRUE);
964 account = mailto_account;
967 /* if no account prefs set from mailto, set if from folder prefs (if any) */
968 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
969 account = account_find_from_id(item->prefs->default_account);
971 /* if no account prefs set, fallback to the current one */
972 if (!account) account = cur_account;
973 cm_return_val_if_fail(account != NULL, NULL);
975 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
977 /* override from name if mailto asked for it */
979 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
982 /* override from name according to folder properties */
983 if (item && item->prefs &&
984 item->prefs->compose_with_format &&
985 item->prefs->compose_override_from_format &&
986 *item->prefs->compose_override_from_format != '\0') {
991 dummyinfo = compose_msginfo_new_from_compose(compose);
993 /* decode \-escape sequences in the internal representation of the quote format */
994 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
995 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
998 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1001 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1003 quote_fmt_scan_string(tmp);
1006 buf = quote_fmt_get_buffer();
1008 alertpanel_error(_("New message From format error."));
1010 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1011 quote_fmt_reset_vartable();
1016 compose->replyinfo = NULL;
1017 compose->fwdinfo = NULL;
1019 textview = GTK_TEXT_VIEW(compose->text);
1020 textbuf = gtk_text_view_get_buffer(textview);
1021 compose_create_tags(textview, compose);
1023 undo_block(compose->undostruct);
1025 compose_set_dictionaries_from_folder_prefs(compose, item);
1028 if (account->auto_sig)
1029 compose_insert_sig(compose, FALSE);
1030 gtk_text_buffer_get_start_iter(textbuf, &iter);
1031 gtk_text_buffer_place_cursor(textbuf, &iter);
1033 if (account->protocol != A_NNTP) {
1034 if (mailto && *mailto != '\0') {
1035 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1038 compose_set_folder_prefs(compose, item, TRUE);
1040 if (item && item->ret_rcpt) {
1041 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1044 if (mailto && *mailto != '\0') {
1045 if (!strchr(mailto, '@'))
1046 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1048 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1049 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1050 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1051 mfield = TO_FIELD_PRESENT;
1054 * CLAWS: just don't allow return receipt request, even if the user
1055 * may want to send an email. simple but foolproof.
1057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1059 compose_add_field_list( compose, listAddress );
1061 if (item && item->prefs && item->prefs->compose_with_format) {
1062 subject_format = item->prefs->compose_subject_format;
1063 body_format = item->prefs->compose_body_format;
1064 } else if (account->compose_with_format) {
1065 subject_format = account->compose_subject_format;
1066 body_format = account->compose_body_format;
1067 } else if (prefs_common.compose_with_format) {
1068 subject_format = prefs_common.compose_subject_format;
1069 body_format = prefs_common.compose_body_format;
1072 if (subject_format || body_format) {
1075 && *subject_format != '\0' )
1077 gchar *subject = NULL;
1082 dummyinfo = compose_msginfo_new_from_compose(compose);
1084 /* decode \-escape sequences in the internal representation of the quote format */
1085 tmp = malloc(strlen(subject_format)+1);
1086 pref_get_unescaped_pref(tmp, subject_format);
1088 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1090 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1091 compose->gtkaspell);
1093 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1095 quote_fmt_scan_string(tmp);
1098 buf = quote_fmt_get_buffer();
1100 alertpanel_error(_("New message subject format error."));
1102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1103 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1104 quote_fmt_reset_vartable();
1108 mfield = SUBJECT_FIELD_PRESENT;
1112 && *body_format != '\0' )
1115 GtkTextBuffer *buffer;
1116 GtkTextIter start, end;
1120 dummyinfo = compose_msginfo_new_from_compose(compose);
1122 text = GTK_TEXT_VIEW(compose->text);
1123 buffer = gtk_text_view_get_buffer(text);
1124 gtk_text_buffer_get_start_iter(buffer, &start);
1125 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1126 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1128 compose_quote_fmt(compose, dummyinfo,
1130 NULL, tmp, FALSE, TRUE,
1131 _("The body of the \"New message\" template has an error at line %d."));
1132 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1133 quote_fmt_reset_vartable();
1137 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1138 gtkaspell_highlight_all(compose->gtkaspell);
1140 mfield = BODY_FIELD_PRESENT;
1144 procmsg_msginfo_free( dummyinfo );
1150 for (i = 0; i < attach_files->len; i++) {
1151 file = g_ptr_array_index(attach_files, i);
1152 compose_attach_append(compose, file, file, NULL, NULL);
1156 compose_show_first_last_header(compose, TRUE);
1158 /* Set save folder */
1159 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1160 gchar *folderidentifier;
1162 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1163 folderidentifier = folder_item_get_identifier(item);
1164 compose_set_save_to(compose, folderidentifier);
1165 g_free(folderidentifier);
1168 /* Place cursor according to provided input (mfield) */
1170 case NO_FIELD_PRESENT:
1171 if (compose->header_last)
1172 gtk_widget_grab_focus(compose->header_last->entry);
1174 case TO_FIELD_PRESENT:
1175 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1177 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 gtk_widget_grab_focus(compose->subject_entry);
1182 case SUBJECT_FIELD_PRESENT:
1183 textview = GTK_TEXT_VIEW(compose->text);
1186 textbuf = gtk_text_view_get_buffer(textview);
1189 mark = gtk_text_buffer_get_insert(textbuf);
1190 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1191 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1193 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1194 * only defers where it comes to the variable body
1195 * is not null. If no body is present compose->text
1196 * will be null in which case you cannot place the
1197 * cursor inside the component so. An empty component
1198 * is therefore created before placing the cursor
1200 case BODY_FIELD_PRESENT:
1201 gtk_widget_grab_focus(compose->text);
1205 undo_unblock(compose->undostruct);
1207 if (prefs_common.auto_exteditor)
1208 compose_exec_ext_editor(compose);
1210 compose->draft_timeout_tag = -1;
1211 SCROLL_TO_CURSOR(compose);
1213 compose->modified = FALSE;
1214 compose_set_title(compose);
1216 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1221 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1222 gboolean override_pref, const gchar *system)
1224 const gchar *privacy = NULL;
1226 cm_return_if_fail(compose != NULL);
1227 cm_return_if_fail(account != NULL);
1229 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1234 else if (account->default_privacy_system
1235 && strlen(account->default_privacy_system)) {
1236 privacy = account->default_privacy_system;
1238 GSList *privacy_avail = privacy_get_system_ids();
1239 if (privacy_avail && g_slist_length(privacy_avail)) {
1240 privacy = (gchar *)(privacy_avail->data);
1243 if (privacy != NULL) {
1245 g_free(compose->privacy_system);
1246 compose->privacy_system = NULL;
1248 if (compose->privacy_system == NULL)
1249 compose->privacy_system = g_strdup(privacy);
1250 else if (*(compose->privacy_system) == '\0') {
1251 g_free(compose->privacy_system);
1252 compose->privacy_system = g_strdup(privacy);
1254 compose_update_privacy_system_menu_item(compose, FALSE);
1255 compose_use_encryption(compose, TRUE);
1259 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1261 const gchar *privacy = NULL;
1265 else if (account->default_privacy_system
1266 && strlen(account->default_privacy_system)) {
1267 privacy = account->default_privacy_system;
1269 GSList *privacy_avail = privacy_get_system_ids();
1270 if (privacy_avail && g_slist_length(privacy_avail)) {
1271 privacy = (gchar *)(privacy_avail->data);
1275 if (privacy != NULL) {
1277 g_free(compose->privacy_system);
1278 compose->privacy_system = NULL;
1280 if (compose->privacy_system == NULL)
1281 compose->privacy_system = g_strdup(privacy);
1282 compose_update_privacy_system_menu_item(compose, FALSE);
1283 compose_use_signing(compose, TRUE);
1287 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1291 Compose *compose = NULL;
1293 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1295 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1296 cm_return_val_if_fail(msginfo != NULL, NULL);
1298 list_len = g_slist_length(msginfo_list);
1302 case COMPOSE_REPLY_TO_ADDRESS:
1303 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1304 FALSE, prefs_common.default_reply_list, FALSE, body);
1306 case COMPOSE_REPLY_WITH_QUOTE:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1308 FALSE, prefs_common.default_reply_list, FALSE, body);
1310 case COMPOSE_REPLY_WITHOUT_QUOTE:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1312 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1314 case COMPOSE_REPLY_TO_SENDER:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1316 FALSE, FALSE, TRUE, body);
1318 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1319 compose = compose_followup_and_reply_to(msginfo,
1320 COMPOSE_QUOTE_CHECK,
1321 FALSE, FALSE, body);
1323 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1324 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1325 FALSE, FALSE, TRUE, body);
1327 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1329 FALSE, FALSE, TRUE, NULL);
1331 case COMPOSE_REPLY_TO_ALL:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1333 TRUE, FALSE, FALSE, body);
1335 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1337 TRUE, FALSE, FALSE, body);
1339 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1341 TRUE, FALSE, FALSE, NULL);
1343 case COMPOSE_REPLY_TO_LIST:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345 FALSE, TRUE, FALSE, body);
1347 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1349 FALSE, TRUE, FALSE, body);
1351 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1353 FALSE, TRUE, FALSE, NULL);
1355 case COMPOSE_FORWARD:
1356 if (prefs_common.forward_as_attachment) {
1357 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1360 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1364 case COMPOSE_FORWARD_INLINE:
1365 /* check if we reply to more than one Message */
1366 if (list_len == 1) {
1367 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1370 /* more messages FALL THROUGH */
1371 case COMPOSE_FORWARD_AS_ATTACH:
1372 compose = compose_forward_multiple(NULL, msginfo_list);
1374 case COMPOSE_REDIRECT:
1375 compose = compose_redirect(NULL, msginfo, FALSE);
1378 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1381 if (compose == NULL) {
1382 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1386 compose->rmode = mode;
1387 switch (compose->rmode) {
1389 case COMPOSE_REPLY_WITH_QUOTE:
1390 case COMPOSE_REPLY_WITHOUT_QUOTE:
1391 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1392 debug_print("reply mode Normal\n");
1393 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1394 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1396 case COMPOSE_REPLY_TO_SENDER:
1397 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1398 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1399 debug_print("reply mode Sender\n");
1400 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1402 case COMPOSE_REPLY_TO_ALL:
1403 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1404 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1405 debug_print("reply mode All\n");
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1408 case COMPOSE_REPLY_TO_LIST:
1409 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1410 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1411 debug_print("reply mode List\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1414 case COMPOSE_REPLY_TO_ADDRESS:
1415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1423 static Compose *compose_reply(MsgInfo *msginfo,
1424 ComposeQuoteMode quote_mode,
1430 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1431 to_sender, FALSE, body);
1434 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1435 ComposeQuoteMode quote_mode,
1440 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1441 to_sender, TRUE, body);
1444 static void compose_extract_original_charset(Compose *compose)
1446 MsgInfo *info = NULL;
1447 if (compose->replyinfo) {
1448 info = compose->replyinfo;
1449 } else if (compose->fwdinfo) {
1450 info = compose->fwdinfo;
1451 } else if (compose->targetinfo) {
1452 info = compose->targetinfo;
1455 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1456 MimeInfo *partinfo = mimeinfo;
1457 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1458 partinfo = procmime_mimeinfo_next(partinfo);
1460 compose->orig_charset =
1461 g_strdup(procmime_mimeinfo_get_parameter(
1462 partinfo, "charset"));
1464 procmime_mimeinfo_free_all(mimeinfo);
1468 #define SIGNAL_BLOCK(buffer) { \
1469 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1470 G_CALLBACK(compose_changed_cb), \
1472 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1473 G_CALLBACK(text_inserted), \
1477 #define SIGNAL_UNBLOCK(buffer) { \
1478 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1479 G_CALLBACK(compose_changed_cb), \
1481 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1482 G_CALLBACK(text_inserted), \
1486 static Compose *compose_generic_reply(MsgInfo *msginfo,
1487 ComposeQuoteMode quote_mode,
1488 gboolean to_all, gboolean to_ml,
1490 gboolean followup_and_reply_to,
1494 PrefsAccount *account = NULL;
1495 GtkTextView *textview;
1496 GtkTextBuffer *textbuf;
1497 gboolean quote = FALSE;
1498 const gchar *qmark = NULL;
1499 const gchar *body_fmt = NULL;
1500 gchar *s_system = NULL;
1502 cm_return_val_if_fail(msginfo != NULL, NULL);
1503 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1505 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1507 cm_return_val_if_fail(account != NULL, NULL);
1509 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1511 compose->updating = TRUE;
1513 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1514 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1516 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1517 if (!compose->replyinfo)
1518 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1520 compose_extract_original_charset(compose);
1522 if (msginfo->folder && msginfo->folder->ret_rcpt)
1523 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1525 /* Set save folder */
1526 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1527 gchar *folderidentifier;
1529 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1530 folderidentifier = folder_item_get_identifier(msginfo->folder);
1531 compose_set_save_to(compose, folderidentifier);
1532 g_free(folderidentifier);
1535 if (compose_parse_header(compose, msginfo) < 0) {
1536 compose->updating = FALSE;
1537 compose_destroy(compose);
1541 /* override from name according to folder properties */
1542 if (msginfo->folder && msginfo->folder->prefs &&
1543 msginfo->folder->prefs->reply_with_format &&
1544 msginfo->folder->prefs->reply_override_from_format &&
1545 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1550 /* decode \-escape sequences in the internal representation of the quote format */
1551 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1552 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1555 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1556 compose->gtkaspell);
1558 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1560 quote_fmt_scan_string(tmp);
1563 buf = quote_fmt_get_buffer();
1565 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1567 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1568 quote_fmt_reset_vartable();
1573 textview = (GTK_TEXT_VIEW(compose->text));
1574 textbuf = gtk_text_view_get_buffer(textview);
1575 compose_create_tags(textview, compose);
1577 undo_block(compose->undostruct);
1579 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1582 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1583 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1584 /* use the reply format of folder (if enabled), or the account's one
1585 (if enabled) or fallback to the global reply format, which is always
1586 enabled (even if empty), and use the relevant quotemark */
1588 if (msginfo->folder && msginfo->folder->prefs &&
1589 msginfo->folder->prefs->reply_with_format) {
1590 qmark = msginfo->folder->prefs->reply_quotemark;
1591 body_fmt = msginfo->folder->prefs->reply_body_format;
1593 } else if (account->reply_with_format) {
1594 qmark = account->reply_quotemark;
1595 body_fmt = account->reply_body_format;
1598 qmark = prefs_common.quotemark;
1599 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1600 body_fmt = gettext(prefs_common.quotefmt);
1607 /* empty quotemark is not allowed */
1608 if (qmark == NULL || *qmark == '\0')
1610 compose_quote_fmt(compose, compose->replyinfo,
1611 body_fmt, qmark, body, FALSE, TRUE,
1612 _("The body of the \"Reply\" template has an error at line %d."));
1613 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1614 quote_fmt_reset_vartable();
1616 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1617 gtkaspell_highlight_all(compose->gtkaspell);
1621 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1622 compose_force_encryption(compose, account, FALSE, s_system);
1625 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1626 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1627 compose_force_signing(compose, account, s_system);
1631 SIGNAL_BLOCK(textbuf);
1633 if (account->auto_sig)
1634 compose_insert_sig(compose, FALSE);
1636 compose_wrap_all(compose);
1638 SIGNAL_UNBLOCK(textbuf);
1640 gtk_widget_grab_focus(compose->text);
1642 undo_unblock(compose->undostruct);
1644 if (prefs_common.auto_exteditor)
1645 compose_exec_ext_editor(compose);
1647 compose->modified = FALSE;
1648 compose_set_title(compose);
1650 compose->updating = FALSE;
1651 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1652 SCROLL_TO_CURSOR(compose);
1654 if (compose->deferred_destroy) {
1655 compose_destroy(compose);
1663 #define INSERT_FW_HEADER(var, hdr) \
1664 if (msginfo->var && *msginfo->var) { \
1665 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1666 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1667 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1670 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1671 gboolean as_attach, const gchar *body,
1672 gboolean no_extedit,
1676 GtkTextView *textview;
1677 GtkTextBuffer *textbuf;
1680 cm_return_val_if_fail(msginfo != NULL, NULL);
1681 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1684 !(account = compose_guess_forward_account_from_msginfo
1686 account = cur_account;
1688 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1690 compose->updating = TRUE;
1691 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1692 if (!compose->fwdinfo)
1693 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1695 compose_extract_original_charset(compose);
1697 if (msginfo->subject && *msginfo->subject) {
1698 gchar *buf, *buf2, *p;
1700 buf = p = g_strdup(msginfo->subject);
1701 p += subject_get_prefix_length(p);
1702 memmove(buf, p, strlen(p) + 1);
1704 buf2 = g_strdup_printf("Fw: %s", buf);
1705 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1711 /* override from name according to folder properties */
1712 if (msginfo->folder && msginfo->folder->prefs &&
1713 msginfo->folder->prefs->forward_with_format &&
1714 msginfo->folder->prefs->forward_override_from_format &&
1715 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1719 MsgInfo *full_msginfo = NULL;
1722 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1724 full_msginfo = procmsg_msginfo_copy(msginfo);
1726 /* decode \-escape sequences in the internal representation of the quote format */
1727 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1728 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1731 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1732 compose->gtkaspell);
1734 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1736 quote_fmt_scan_string(tmp);
1739 buf = quote_fmt_get_buffer();
1741 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1743 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1744 quote_fmt_reset_vartable();
1747 procmsg_msginfo_free(full_msginfo);
1750 textview = GTK_TEXT_VIEW(compose->text);
1751 textbuf = gtk_text_view_get_buffer(textview);
1752 compose_create_tags(textview, compose);
1754 undo_block(compose->undostruct);
1758 msgfile = procmsg_get_message_file(msginfo);
1759 if (!is_file_exist(msgfile))
1760 g_warning("%s: file not exist\n", msgfile);
1762 compose_attach_append(compose, msgfile, msgfile,
1763 "message/rfc822", NULL);
1767 const gchar *qmark = NULL;
1768 const gchar *body_fmt = NULL;
1769 MsgInfo *full_msginfo;
1771 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1772 body_fmt = gettext(prefs_common.fw_quotefmt);
1776 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1778 full_msginfo = procmsg_msginfo_copy(msginfo);
1780 /* use the forward format of folder (if enabled), or the account's one
1781 (if enabled) or fallback to the global forward format, which is always
1782 enabled (even if empty), and use the relevant quotemark */
1783 if (msginfo->folder && msginfo->folder->prefs &&
1784 msginfo->folder->prefs->forward_with_format) {
1785 qmark = msginfo->folder->prefs->forward_quotemark;
1786 body_fmt = msginfo->folder->prefs->forward_body_format;
1788 } else if (account->forward_with_format) {
1789 qmark = account->forward_quotemark;
1790 body_fmt = account->forward_body_format;
1793 qmark = prefs_common.fw_quotemark;
1794 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1795 body_fmt = gettext(prefs_common.fw_quotefmt);
1800 /* empty quotemark is not allowed */
1801 if (qmark == NULL || *qmark == '\0')
1804 compose_quote_fmt(compose, full_msginfo,
1805 body_fmt, qmark, body, FALSE, TRUE,
1806 _("The body of the \"Forward\" template has an error at line %d."));
1807 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1808 quote_fmt_reset_vartable();
1809 compose_attach_parts(compose, msginfo);
1811 procmsg_msginfo_free(full_msginfo);
1813 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1814 gtkaspell_highlight_all(compose->gtkaspell);
1818 SIGNAL_BLOCK(textbuf);
1820 if (account->auto_sig)
1821 compose_insert_sig(compose, FALSE);
1823 compose_wrap_all(compose);
1825 SIGNAL_UNBLOCK(textbuf);
1827 gtk_text_buffer_get_start_iter(textbuf, &iter);
1828 gtk_text_buffer_place_cursor(textbuf, &iter);
1830 gtk_widget_grab_focus(compose->header_last->entry);
1832 if (!no_extedit && prefs_common.auto_exteditor)
1833 compose_exec_ext_editor(compose);
1836 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1837 gchar *folderidentifier;
1839 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1840 folderidentifier = folder_item_get_identifier(msginfo->folder);
1841 compose_set_save_to(compose, folderidentifier);
1842 g_free(folderidentifier);
1845 undo_unblock(compose->undostruct);
1847 compose->modified = FALSE;
1848 compose_set_title(compose);
1850 compose->updating = FALSE;
1851 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1852 SCROLL_TO_CURSOR(compose);
1854 if (compose->deferred_destroy) {
1855 compose_destroy(compose);
1859 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1864 #undef INSERT_FW_HEADER
1866 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1869 GtkTextView *textview;
1870 GtkTextBuffer *textbuf;
1874 gboolean single_mail = TRUE;
1876 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1878 if (g_slist_length(msginfo_list) > 1)
1879 single_mail = FALSE;
1881 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1882 if (((MsgInfo *)msginfo->data)->folder == NULL)
1885 /* guess account from first selected message */
1887 !(account = compose_guess_forward_account_from_msginfo
1888 (msginfo_list->data)))
1889 account = cur_account;
1891 cm_return_val_if_fail(account != NULL, NULL);
1893 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1894 if (msginfo->data) {
1895 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1896 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1900 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1901 g_warning("no msginfo_list");
1905 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1907 compose->updating = TRUE;
1909 /* override from name according to folder properties */
1910 if (msginfo_list->data) {
1911 MsgInfo *msginfo = msginfo_list->data;
1913 if (msginfo->folder && msginfo->folder->prefs &&
1914 msginfo->folder->prefs->forward_with_format &&
1915 msginfo->folder->prefs->forward_override_from_format &&
1916 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1921 /* decode \-escape sequences in the internal representation of the quote format */
1922 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1923 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1926 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1927 compose->gtkaspell);
1929 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1931 quote_fmt_scan_string(tmp);
1934 buf = quote_fmt_get_buffer();
1936 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1938 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1939 quote_fmt_reset_vartable();
1945 textview = GTK_TEXT_VIEW(compose->text);
1946 textbuf = gtk_text_view_get_buffer(textview);
1947 compose_create_tags(textview, compose);
1949 undo_block(compose->undostruct);
1950 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1951 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1953 if (!is_file_exist(msgfile))
1954 g_warning("%s: file not exist\n", msgfile);
1956 compose_attach_append(compose, msgfile, msgfile,
1957 "message/rfc822", NULL);
1962 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1963 if (info->subject && *info->subject) {
1964 gchar *buf, *buf2, *p;
1966 buf = p = g_strdup(info->subject);
1967 p += subject_get_prefix_length(p);
1968 memmove(buf, p, strlen(p) + 1);
1970 buf2 = g_strdup_printf("Fw: %s", buf);
1971 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1977 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1978 _("Fw: multiple emails"));
1981 SIGNAL_BLOCK(textbuf);
1983 if (account->auto_sig)
1984 compose_insert_sig(compose, FALSE);
1986 compose_wrap_all(compose);
1988 SIGNAL_UNBLOCK(textbuf);
1990 gtk_text_buffer_get_start_iter(textbuf, &iter);
1991 gtk_text_buffer_place_cursor(textbuf, &iter);
1993 gtk_widget_grab_focus(compose->header_last->entry);
1994 undo_unblock(compose->undostruct);
1995 compose->modified = FALSE;
1996 compose_set_title(compose);
1998 compose->updating = FALSE;
1999 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2000 SCROLL_TO_CURSOR(compose);
2002 if (compose->deferred_destroy) {
2003 compose_destroy(compose);
2007 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2012 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2014 GtkTextIter start = *iter;
2015 GtkTextIter end_iter;
2016 int start_pos = gtk_text_iter_get_offset(&start);
2018 if (!compose->account->sig_sep)
2021 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2022 start_pos+strlen(compose->account->sig_sep));
2024 /* check sig separator */
2025 str = gtk_text_iter_get_text(&start, &end_iter);
2026 if (!strcmp(str, compose->account->sig_sep)) {
2028 /* check end of line (\n) */
2029 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2030 start_pos+strlen(compose->account->sig_sep));
2031 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2032 start_pos+strlen(compose->account->sig_sep)+1);
2033 tmp = gtk_text_iter_get_text(&start, &end_iter);
2034 if (!strcmp(tmp,"\n")) {
2046 static void compose_colorize_signature(Compose *compose)
2048 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2050 GtkTextIter end_iter;
2051 gtk_text_buffer_get_start_iter(buffer, &iter);
2052 while (gtk_text_iter_forward_line(&iter))
2053 if (compose_is_sig_separator(compose, buffer, &iter)) {
2054 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2055 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2059 #define BLOCK_WRAP() { \
2060 prev_autowrap = compose->autowrap; \
2061 buffer = gtk_text_view_get_buffer( \
2062 GTK_TEXT_VIEW(compose->text)); \
2063 compose->autowrap = FALSE; \
2065 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2066 G_CALLBACK(compose_changed_cb), \
2068 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2069 G_CALLBACK(text_inserted), \
2072 #define UNBLOCK_WRAP() { \
2073 compose->autowrap = prev_autowrap; \
2074 if (compose->autowrap) { \
2075 gint old = compose->draft_timeout_tag; \
2076 compose->draft_timeout_tag = -2; \
2077 compose_wrap_all(compose); \
2078 compose->draft_timeout_tag = old; \
2081 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2082 G_CALLBACK(compose_changed_cb), \
2084 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2085 G_CALLBACK(text_inserted), \
2089 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2091 Compose *compose = NULL;
2092 PrefsAccount *account = NULL;
2093 GtkTextView *textview;
2094 GtkTextBuffer *textbuf;
2098 gchar buf[BUFFSIZE];
2099 gboolean use_signing = FALSE;
2100 gboolean use_encryption = FALSE;
2101 gchar *privacy_system = NULL;
2102 int priority = PRIORITY_NORMAL;
2103 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2104 gboolean autowrap = prefs_common.autowrap;
2105 gboolean autoindent = prefs_common.auto_indent;
2107 cm_return_val_if_fail(msginfo != NULL, NULL);
2108 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2110 if (compose_put_existing_to_front(msginfo)) {
2114 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2115 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2116 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2117 gchar queueheader_buf[BUFFSIZE];
2120 /* Select Account from queue headers */
2121 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2122 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2123 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2124 account = account_find_from_id(id);
2126 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2127 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2128 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2129 account = account_find_from_id(id);
2131 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2132 sizeof(queueheader_buf), "NAID:")) {
2133 id = atoi(&queueheader_buf[strlen("NAID:")]);
2134 account = account_find_from_id(id);
2136 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "MAID:")) {
2138 id = atoi(&queueheader_buf[strlen("MAID:")]);
2139 account = account_find_from_id(id);
2141 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2142 sizeof(queueheader_buf), "S:")) {
2143 account = account_find_from_address(queueheader_buf, FALSE);
2145 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2146 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2147 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2148 use_signing = param;
2151 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2152 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2153 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2154 use_signing = param;
2157 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2158 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2159 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2160 use_encryption = param;
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2164 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2165 use_encryption = param;
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2169 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2174 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2177 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2179 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2183 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2185 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "X-Priority: ")) {
2187 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2190 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2191 sizeof(queueheader_buf), "RMID:")) {
2192 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2193 if (tokens[0] && tokens[1] && tokens[2]) {
2194 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2195 if (orig_item != NULL) {
2196 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2201 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2202 sizeof(queueheader_buf), "FMID:")) {
2203 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2204 if (tokens[0] && tokens[1] && tokens[2]) {
2205 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2206 if (orig_item != NULL) {
2207 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2213 account = msginfo->folder->folder->account;
2216 if (!account && prefs_common.reedit_account_autosel) {
2217 gchar from[BUFFSIZE];
2218 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2219 extract_address(from);
2220 account = account_find_from_address(from, FALSE);
2224 account = cur_account;
2226 cm_return_val_if_fail(account != NULL, NULL);
2228 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2230 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2231 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2232 compose->autowrap = autowrap;
2233 compose->replyinfo = replyinfo;
2234 compose->fwdinfo = fwdinfo;
2236 compose->updating = TRUE;
2237 compose->priority = priority;
2239 if (privacy_system != NULL) {
2240 compose->privacy_system = privacy_system;
2241 compose_use_signing(compose, use_signing);
2242 compose_use_encryption(compose, use_encryption);
2243 compose_update_privacy_system_menu_item(compose, FALSE);
2245 activate_privacy_system(compose, account, FALSE);
2248 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2250 compose_extract_original_charset(compose);
2252 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2253 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2254 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2255 gchar queueheader_buf[BUFFSIZE];
2257 /* Set message save folder */
2258 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2259 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2260 compose_set_save_to(compose, &queueheader_buf[4]);
2262 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2263 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2265 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2270 if (compose_parse_header(compose, msginfo) < 0) {
2271 compose->updating = FALSE;
2272 compose_destroy(compose);
2275 compose_reedit_set_entry(compose, msginfo);
2277 textview = GTK_TEXT_VIEW(compose->text);
2278 textbuf = gtk_text_view_get_buffer(textview);
2279 compose_create_tags(textview, compose);
2281 mark = gtk_text_buffer_get_insert(textbuf);
2282 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2284 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2285 G_CALLBACK(compose_changed_cb),
2288 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2289 fp = procmime_get_first_encrypted_text_content(msginfo);
2291 compose_force_encryption(compose, account, TRUE, NULL);
2294 fp = procmime_get_first_text_content(msginfo);
2297 g_warning("Can't get text part\n");
2301 gboolean prev_autowrap = compose->autowrap;
2302 GtkTextBuffer *buffer = textbuf;
2304 while (fgets(buf, sizeof(buf), fp) != NULL) {
2306 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2312 compose_attach_parts(compose, msginfo);
2314 compose_colorize_signature(compose);
2316 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2317 G_CALLBACK(compose_changed_cb),
2320 gtk_widget_grab_focus(compose->text);
2322 if (prefs_common.auto_exteditor) {
2323 compose_exec_ext_editor(compose);
2325 compose->modified = FALSE;
2326 compose_set_title(compose);
2328 compose->updating = FALSE;
2329 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2330 SCROLL_TO_CURSOR(compose);
2332 if (compose->deferred_destroy) {
2333 compose_destroy(compose);
2337 compose->sig_str = account_get_signature_str(compose->account);
2339 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2344 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2351 cm_return_val_if_fail(msginfo != NULL, NULL);
2354 account = account_get_reply_account(msginfo,
2355 prefs_common.reply_account_autosel);
2356 cm_return_val_if_fail(account != NULL, NULL);
2358 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2360 compose->updating = TRUE;
2362 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2363 compose->replyinfo = NULL;
2364 compose->fwdinfo = NULL;
2366 compose_show_first_last_header(compose, TRUE);
2368 gtk_widget_grab_focus(compose->header_last->entry);
2370 filename = procmsg_get_message_file(msginfo);
2372 if (filename == NULL) {
2373 compose->updating = FALSE;
2374 compose_destroy(compose);
2379 compose->redirect_filename = filename;
2381 /* Set save folder */
2382 item = msginfo->folder;
2383 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2384 gchar *folderidentifier;
2386 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2387 folderidentifier = folder_item_get_identifier(item);
2388 compose_set_save_to(compose, folderidentifier);
2389 g_free(folderidentifier);
2392 compose_attach_parts(compose, msginfo);
2394 if (msginfo->subject)
2395 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2397 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2399 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2400 _("The body of the \"Redirect\" template has an error at line %d."));
2401 quote_fmt_reset_vartable();
2402 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2404 compose_colorize_signature(compose);
2407 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2408 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2409 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2413 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2417 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2420 if (compose->toolbar->draft_btn)
2421 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2422 if (compose->toolbar->insert_btn)
2423 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2424 if (compose->toolbar->attach_btn)
2425 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2426 if (compose->toolbar->sig_btn)
2427 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2428 if (compose->toolbar->exteditor_btn)
2429 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2430 if (compose->toolbar->linewrap_current_btn)
2431 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2432 if (compose->toolbar->linewrap_all_btn)
2433 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2435 compose->modified = FALSE;
2436 compose_set_title(compose);
2437 compose->updating = FALSE;
2438 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2439 SCROLL_TO_CURSOR(compose);
2441 if (compose->deferred_destroy) {
2442 compose_destroy(compose);
2446 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2451 GList *compose_get_compose_list(void)
2453 return compose_list;
2456 void compose_entry_append(Compose *compose, const gchar *address,
2457 ComposeEntryType type, ComposePrefType pref_type)
2459 const gchar *header;
2461 gboolean in_quote = FALSE;
2462 if (!address || *address == '\0') return;
2469 header = N_("Bcc:");
2471 case COMPOSE_REPLYTO:
2472 header = N_("Reply-To:");
2474 case COMPOSE_NEWSGROUPS:
2475 header = N_("Newsgroups:");
2477 case COMPOSE_FOLLOWUPTO:
2478 header = N_( "Followup-To:");
2480 case COMPOSE_INREPLYTO:
2481 header = N_( "In-Reply-To:");
2488 header = prefs_common_translated_header_name(header);
2490 cur = begin = (gchar *)address;
2492 /* we separate the line by commas, but not if we're inside a quoted
2494 while (*cur != '\0') {
2496 in_quote = !in_quote;
2497 if (*cur == ',' && !in_quote) {
2498 gchar *tmp = g_strdup(begin);
2500 tmp[cur-begin]='\0';
2503 while (*tmp == ' ' || *tmp == '\t')
2505 compose_add_header_entry(compose, header, tmp, pref_type);
2512 gchar *tmp = g_strdup(begin);
2514 tmp[cur-begin]='\0';
2517 while (*tmp == ' ' || *tmp == '\t')
2519 compose_add_header_entry(compose, header, tmp, pref_type);
2524 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2526 static GdkColor yellow;
2527 static GdkColor black;
2528 static gboolean yellow_initialised = FALSE;
2532 if (!yellow_initialised) {
2533 gdk_color_parse("#f5f6be", &yellow);
2534 gdk_color_parse("#000000", &black);
2535 yellow_initialised = gdk_colormap_alloc_color(
2536 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2537 yellow_initialised &= gdk_colormap_alloc_color(
2538 gdk_colormap_get_system(), &black, FALSE, TRUE);
2541 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2542 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2543 if (gtk_entry_get_text(entry) &&
2544 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2545 if (yellow_initialised) {
2546 gtk_widget_modify_base(
2547 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2548 GTK_STATE_NORMAL, &yellow);
2549 gtk_widget_modify_text(
2550 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2551 GTK_STATE_NORMAL, &black);
2557 void compose_toolbar_cb(gint action, gpointer data)
2559 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2560 Compose *compose = (Compose*)toolbar_item->parent;
2562 cm_return_if_fail(compose != NULL);
2566 compose_send_cb(NULL, compose);
2569 compose_send_later_cb(NULL, compose);
2572 compose_draft(compose, COMPOSE_QUIT_EDITING);
2575 compose_insert_file_cb(NULL, compose);
2578 compose_attach_cb(NULL, compose);
2581 compose_insert_sig(compose, FALSE);
2584 compose_ext_editor_cb(NULL, compose);
2586 case A_LINEWRAP_CURRENT:
2587 compose_beautify_paragraph(compose, NULL, TRUE);
2589 case A_LINEWRAP_ALL:
2590 compose_wrap_all_full(compose, TRUE);
2593 compose_address_cb(NULL, compose);
2596 case A_CHECK_SPELLING:
2597 compose_check_all(NULL, compose);
2605 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2610 gchar *subject = NULL;
2614 gchar **attach = NULL;
2615 gchar *inreplyto = NULL;
2616 MailField mfield = NO_FIELD_PRESENT;
2618 /* get mailto parts but skip from */
2619 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2622 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2623 mfield = TO_FIELD_PRESENT;
2626 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2628 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2630 if (!g_utf8_validate (subject, -1, NULL)) {
2631 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2632 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2635 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2637 mfield = SUBJECT_FIELD_PRESENT;
2640 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2641 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2644 gboolean prev_autowrap = compose->autowrap;
2646 compose->autowrap = FALSE;
2648 mark = gtk_text_buffer_get_insert(buffer);
2649 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2651 if (!g_utf8_validate (body, -1, NULL)) {
2652 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2653 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2656 gtk_text_buffer_insert(buffer, &iter, body, -1);
2658 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2660 compose->autowrap = prev_autowrap;
2661 if (compose->autowrap)
2662 compose_wrap_all(compose);
2663 mfield = BODY_FIELD_PRESENT;
2667 gint i = 0, att = 0;
2668 gchar *warn_files = NULL;
2669 while (attach[i] != NULL) {
2670 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2671 if (utf8_filename) {
2672 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2673 gchar *tmp = g_strdup_printf("%s%s\n",
2674 warn_files?warn_files:"",
2680 g_free(utf8_filename);
2682 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2687 alertpanel_notice(ngettext(
2688 "The following file has been attached: \n%s",
2689 "The following files have been attached: \n%s", att), warn_files);
2694 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2707 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2709 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2710 {"Cc:", NULL, TRUE},
2711 {"References:", NULL, FALSE},
2712 {"Bcc:", NULL, TRUE},
2713 {"Newsgroups:", NULL, TRUE},
2714 {"Followup-To:", NULL, TRUE},
2715 {"List-Post:", NULL, FALSE},
2716 {"X-Priority:", NULL, FALSE},
2717 {NULL, NULL, FALSE}};
2733 cm_return_val_if_fail(msginfo != NULL, -1);
2735 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2736 procheader_get_header_fields(fp, hentry);
2739 if (hentry[H_REPLY_TO].body != NULL) {
2740 if (hentry[H_REPLY_TO].body[0] != '\0') {
2742 conv_unmime_header(hentry[H_REPLY_TO].body,
2745 g_free(hentry[H_REPLY_TO].body);
2746 hentry[H_REPLY_TO].body = NULL;
2748 if (hentry[H_CC].body != NULL) {
2749 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2750 g_free(hentry[H_CC].body);
2751 hentry[H_CC].body = NULL;
2753 if (hentry[H_REFERENCES].body != NULL) {
2754 if (compose->mode == COMPOSE_REEDIT)
2755 compose->references = hentry[H_REFERENCES].body;
2757 compose->references = compose_parse_references
2758 (hentry[H_REFERENCES].body, msginfo->msgid);
2759 g_free(hentry[H_REFERENCES].body);
2761 hentry[H_REFERENCES].body = NULL;
2763 if (hentry[H_BCC].body != NULL) {
2764 if (compose->mode == COMPOSE_REEDIT)
2766 conv_unmime_header(hentry[H_BCC].body, NULL);
2767 g_free(hentry[H_BCC].body);
2768 hentry[H_BCC].body = NULL;
2770 if (hentry[H_NEWSGROUPS].body != NULL) {
2771 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2772 hentry[H_NEWSGROUPS].body = NULL;
2774 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2775 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2776 compose->followup_to =
2777 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2780 g_free(hentry[H_FOLLOWUP_TO].body);
2781 hentry[H_FOLLOWUP_TO].body = NULL;
2783 if (hentry[H_LIST_POST].body != NULL) {
2784 gchar *to = NULL, *start = NULL;
2786 extract_address(hentry[H_LIST_POST].body);
2787 if (hentry[H_LIST_POST].body[0] != '\0') {
2788 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2790 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2791 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2794 g_free(compose->ml_post);
2795 compose->ml_post = to;
2798 g_free(hentry[H_LIST_POST].body);
2799 hentry[H_LIST_POST].body = NULL;
2802 /* CLAWS - X-Priority */
2803 if (compose->mode == COMPOSE_REEDIT)
2804 if (hentry[H_X_PRIORITY].body != NULL) {
2807 priority = atoi(hentry[H_X_PRIORITY].body);
2808 g_free(hentry[H_X_PRIORITY].body);
2810 hentry[H_X_PRIORITY].body = NULL;
2812 if (priority < PRIORITY_HIGHEST ||
2813 priority > PRIORITY_LOWEST)
2814 priority = PRIORITY_NORMAL;
2816 compose->priority = priority;
2819 if (compose->mode == COMPOSE_REEDIT) {
2820 if (msginfo->inreplyto && *msginfo->inreplyto)
2821 compose->inreplyto = g_strdup(msginfo->inreplyto);
2825 if (msginfo->msgid && *msginfo->msgid)
2826 compose->inreplyto = g_strdup(msginfo->msgid);
2828 if (!compose->references) {
2829 if (msginfo->msgid && *msginfo->msgid) {
2830 if (msginfo->inreplyto && *msginfo->inreplyto)
2831 compose->references =
2832 g_strdup_printf("<%s>\n\t<%s>",
2836 compose->references =
2837 g_strconcat("<", msginfo->msgid, ">",
2839 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2840 compose->references =
2841 g_strconcat("<", msginfo->inreplyto, ">",
2849 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2851 GSList *ref_id_list, *cur;
2855 ref_id_list = references_list_append(NULL, ref);
2856 if (!ref_id_list) return NULL;
2857 if (msgid && *msgid)
2858 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2863 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2864 /* "<" + Message-ID + ">" + CR+LF+TAB */
2865 len += strlen((gchar *)cur->data) + 5;
2867 if (len > MAX_REFERENCES_LEN) {
2868 /* remove second message-ID */
2869 if (ref_id_list && ref_id_list->next &&
2870 ref_id_list->next->next) {
2871 g_free(ref_id_list->next->data);
2872 ref_id_list = g_slist_remove
2873 (ref_id_list, ref_id_list->next->data);
2875 slist_free_strings(ref_id_list);
2876 g_slist_free(ref_id_list);
2883 new_ref = g_string_new("");
2884 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2885 if (new_ref->len > 0)
2886 g_string_append(new_ref, "\n\t");
2887 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2890 slist_free_strings(ref_id_list);
2891 g_slist_free(ref_id_list);
2893 new_ref_str = new_ref->str;
2894 g_string_free(new_ref, FALSE);
2899 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2900 const gchar *fmt, const gchar *qmark,
2901 const gchar *body, gboolean rewrap,
2902 gboolean need_unescape,
2903 const gchar *err_msg)
2905 MsgInfo* dummyinfo = NULL;
2906 gchar *quote_str = NULL;
2908 gboolean prev_autowrap;
2909 const gchar *trimmed_body = body;
2910 gint cursor_pos = -1;
2911 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2912 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2917 SIGNAL_BLOCK(buffer);
2920 dummyinfo = compose_msginfo_new_from_compose(compose);
2921 msginfo = dummyinfo;
2924 if (qmark != NULL) {
2926 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2927 compose->gtkaspell);
2929 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2931 quote_fmt_scan_string(qmark);
2934 buf = quote_fmt_get_buffer();
2936 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2938 Xstrdup_a(quote_str, buf, goto error)
2941 if (fmt && *fmt != '\0') {
2944 while (*trimmed_body == '\n')
2948 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2949 compose->gtkaspell);
2951 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2953 if (need_unescape) {
2956 /* decode \-escape sequences in the internal representation of the quote format */
2957 tmp = malloc(strlen(fmt)+1);
2958 pref_get_unescaped_pref(tmp, fmt);
2959 quote_fmt_scan_string(tmp);
2963 quote_fmt_scan_string(fmt);
2967 buf = quote_fmt_get_buffer();
2969 gint line = quote_fmt_get_line();
2970 alertpanel_error(err_msg, line);
2976 prev_autowrap = compose->autowrap;
2977 compose->autowrap = FALSE;
2979 mark = gtk_text_buffer_get_insert(buffer);
2980 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2981 if (g_utf8_validate(buf, -1, NULL)) {
2982 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2984 gchar *tmpout = NULL;
2985 tmpout = conv_codeset_strdup
2986 (buf, conv_get_locale_charset_str_no_utf8(),
2988 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2990 tmpout = g_malloc(strlen(buf)*2+1);
2991 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2993 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2997 cursor_pos = quote_fmt_get_cursor_pos();
2998 if (cursor_pos == -1)
2999 cursor_pos = gtk_text_iter_get_offset(&iter);
3000 compose->set_cursor_pos = cursor_pos;
3002 gtk_text_buffer_get_start_iter(buffer, &iter);
3003 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3004 gtk_text_buffer_place_cursor(buffer, &iter);
3006 compose->autowrap = prev_autowrap;
3007 if (compose->autowrap && rewrap)
3008 compose_wrap_all(compose);
3015 SIGNAL_UNBLOCK(buffer);
3017 procmsg_msginfo_free( dummyinfo );
3022 /* if ml_post is of type addr@host and from is of type
3023 * addr-anything@host, return TRUE
3025 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3027 gchar *left_ml = NULL;
3028 gchar *right_ml = NULL;
3029 gchar *left_from = NULL;
3030 gchar *right_from = NULL;
3031 gboolean result = FALSE;
3033 if (!ml_post || !from)
3036 left_ml = g_strdup(ml_post);
3037 if (strstr(left_ml, "@")) {
3038 right_ml = strstr(left_ml, "@")+1;
3039 *(strstr(left_ml, "@")) = '\0';
3042 left_from = g_strdup(from);
3043 if (strstr(left_from, "@")) {
3044 right_from = strstr(left_from, "@")+1;
3045 *(strstr(left_from, "@")) = '\0';
3048 if (left_ml && left_from && right_ml && right_from
3049 && !strncmp(left_from, left_ml, strlen(left_ml))
3050 && !strcmp(right_from, right_ml)) {
3059 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3060 gboolean respect_default_to)
3064 if (!folder || !folder->prefs)
3067 if (respect_default_to && folder->prefs->enable_default_to) {
3068 compose_entry_append(compose, folder->prefs->default_to,
3069 COMPOSE_TO, PREF_FOLDER);
3070 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3072 if (folder->prefs->enable_default_cc)
3073 compose_entry_append(compose, folder->prefs->default_cc,
3074 COMPOSE_CC, PREF_FOLDER);
3075 if (folder->prefs->enable_default_bcc)
3076 compose_entry_append(compose, folder->prefs->default_bcc,
3077 COMPOSE_BCC, PREF_FOLDER);
3078 if (folder->prefs->enable_default_replyto)
3079 compose_entry_append(compose, folder->prefs->default_replyto,
3080 COMPOSE_REPLYTO, PREF_FOLDER);
3083 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3088 if (!compose || !msginfo)
3091 if (msginfo->subject && *msginfo->subject) {
3092 buf = p = g_strdup(msginfo->subject);
3093 p += subject_get_prefix_length(p);
3094 memmove(buf, p, strlen(p) + 1);
3096 buf2 = g_strdup_printf("Re: %s", buf);
3097 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3105 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3106 gboolean to_all, gboolean to_ml,
3108 gboolean followup_and_reply_to)
3110 GSList *cc_list = NULL;
3113 gchar *replyto = NULL;
3114 gchar *ac_email = NULL;
3116 gboolean reply_to_ml = FALSE;
3117 gboolean default_reply_to = FALSE;
3119 cm_return_if_fail(compose->account != NULL);
3120 cm_return_if_fail(msginfo != NULL);
3122 reply_to_ml = to_ml && compose->ml_post;
3124 default_reply_to = msginfo->folder &&
3125 msginfo->folder->prefs->enable_default_reply_to;
3127 if (compose->account->protocol != A_NNTP) {
3128 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3130 if (reply_to_ml && !default_reply_to) {
3132 gboolean is_subscr = is_subscription(compose->ml_post,
3135 /* normal answer to ml post with a reply-to */
3136 compose_entry_append(compose,
3138 COMPOSE_TO, PREF_ML);
3139 if (compose->replyto)
3140 compose_entry_append(compose,
3142 COMPOSE_CC, PREF_ML);
3144 /* answer to subscription confirmation */
3145 if (compose->replyto)
3146 compose_entry_append(compose,
3148 COMPOSE_TO, PREF_ML);
3149 else if (msginfo->from)
3150 compose_entry_append(compose,
3152 COMPOSE_TO, PREF_ML);
3155 else if (!(to_all || to_sender) && default_reply_to) {
3156 compose_entry_append(compose,
3157 msginfo->folder->prefs->default_reply_to,
3158 COMPOSE_TO, PREF_FOLDER);
3159 compose_entry_mark_default_to(compose,
3160 msginfo->folder->prefs->default_reply_to);
3165 Xstrdup_a(tmp1, msginfo->from, return);
3166 extract_address(tmp1);
3167 if (to_all || to_sender ||
3168 !account_find_from_address(tmp1, FALSE))
3169 compose_entry_append(compose,
3170 (compose->replyto && !to_sender)
3171 ? compose->replyto :
3172 msginfo->from ? msginfo->from : "",
3173 COMPOSE_TO, PREF_NONE);
3174 else if (!to_all && !to_sender) {
3175 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3176 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3177 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3178 if (compose->replyto) {
3179 compose_entry_append(compose,
3181 COMPOSE_TO, PREF_NONE);
3183 compose_entry_append(compose,
3184 msginfo->from ? msginfo->from : "",
3185 COMPOSE_TO, PREF_NONE);
3188 /* replying to own mail, use original recp */
3189 compose_entry_append(compose,
3190 msginfo->to ? msginfo->to : "",
3191 COMPOSE_TO, PREF_NONE);
3192 compose_entry_append(compose,
3193 msginfo->cc ? msginfo->cc : "",
3194 COMPOSE_CC, PREF_NONE);
3199 if (to_sender || (compose->followup_to &&
3200 !strncmp(compose->followup_to, "poster", 6)))
3201 compose_entry_append
3203 (compose->replyto ? compose->replyto :
3204 msginfo->from ? msginfo->from : ""),
3205 COMPOSE_TO, PREF_NONE);
3207 else if (followup_and_reply_to || to_all) {
3208 compose_entry_append
3210 (compose->replyto ? compose->replyto :
3211 msginfo->from ? msginfo->from : ""),
3212 COMPOSE_TO, PREF_NONE);
3214 compose_entry_append
3216 compose->followup_to ? compose->followup_to :
3217 compose->newsgroups ? compose->newsgroups : "",
3218 COMPOSE_NEWSGROUPS, PREF_NONE);
3221 compose_entry_append
3223 compose->followup_to ? compose->followup_to :
3224 compose->newsgroups ? compose->newsgroups : "",
3225 COMPOSE_NEWSGROUPS, PREF_NONE);
3227 compose_reply_set_subject(compose, msginfo);
3229 if (to_ml && compose->ml_post) return;
3230 if (!to_all || compose->account->protocol == A_NNTP) return;
3232 if (compose->replyto) {
3233 Xstrdup_a(replyto, compose->replyto, return);
3234 extract_address(replyto);
3236 if (msginfo->from) {
3237 Xstrdup_a(from, msginfo->from, return);
3238 extract_address(from);
3241 if (replyto && from)
3242 cc_list = address_list_append_with_comments(cc_list, from);
3243 if (to_all && msginfo->folder &&
3244 msginfo->folder->prefs->enable_default_reply_to)
3245 cc_list = address_list_append_with_comments(cc_list,
3246 msginfo->folder->prefs->default_reply_to);
3247 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3248 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3250 ac_email = g_utf8_strdown(compose->account->address, -1);
3253 for (cur = cc_list; cur != NULL; cur = cur->next) {
3254 gchar *addr = g_utf8_strdown(cur->data, -1);
3255 extract_address(addr);
3257 if (strcmp(ac_email, addr))
3258 compose_entry_append(compose, (gchar *)cur->data,
3259 COMPOSE_CC, PREF_NONE);
3261 debug_print("Cc address same as compose account's, ignoring\n");
3266 slist_free_strings(cc_list);
3267 g_slist_free(cc_list);
3273 #define SET_ENTRY(entry, str) \
3276 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3279 #define SET_ADDRESS(type, str) \
3282 compose_entry_append(compose, str, type, PREF_NONE); \
3285 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3287 cm_return_if_fail(msginfo != NULL);
3289 SET_ENTRY(subject_entry, msginfo->subject);
3290 SET_ENTRY(from_name, msginfo->from);
3291 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3292 SET_ADDRESS(COMPOSE_CC, compose->cc);
3293 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3294 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3295 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3296 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3298 compose_update_priority_menu_item(compose);
3299 compose_update_privacy_system_menu_item(compose, FALSE);
3300 compose_show_first_last_header(compose, TRUE);
3306 static void compose_insert_sig(Compose *compose, gboolean replace)
3308 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3309 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3311 GtkTextIter iter, iter_end;
3312 gint cur_pos, ins_pos;
3313 gboolean prev_autowrap;
3314 gboolean found = FALSE;
3315 gboolean exists = FALSE;
3317 cm_return_if_fail(compose->account != NULL);
3321 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3322 G_CALLBACK(compose_changed_cb),
3325 mark = gtk_text_buffer_get_insert(buffer);
3326 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3327 cur_pos = gtk_text_iter_get_offset (&iter);
3330 gtk_text_buffer_get_end_iter(buffer, &iter);
3332 exists = (compose->sig_str != NULL);
3335 GtkTextIter first_iter, start_iter, end_iter;
3337 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3339 if (!exists || compose->sig_str[0] == '\0')
3342 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3343 compose->signature_tag);
3346 /* include previous \n\n */
3347 gtk_text_iter_backward_chars(&first_iter, 1);
3348 start_iter = first_iter;
3349 end_iter = first_iter;
3351 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3352 compose->signature_tag);
3353 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3354 compose->signature_tag);
3356 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3362 g_free(compose->sig_str);
3363 compose->sig_str = account_get_signature_str(compose->account);
3365 cur_pos = gtk_text_iter_get_offset(&iter);
3367 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3368 g_free(compose->sig_str);
3369 compose->sig_str = NULL;
3371 if (compose->sig_inserted == FALSE)
3372 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3373 compose->sig_inserted = TRUE;
3375 cur_pos = gtk_text_iter_get_offset(&iter);
3376 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3378 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3379 gtk_text_iter_forward_chars(&iter, 1);
3380 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3381 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3383 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3384 cur_pos = gtk_text_buffer_get_char_count (buffer);
3387 /* put the cursor where it should be
3388 * either where the quote_fmt says, either where it was */
3389 if (compose->set_cursor_pos < 0)
3390 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3392 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3393 compose->set_cursor_pos);
3395 compose->set_cursor_pos = -1;
3396 gtk_text_buffer_place_cursor(buffer, &iter);
3397 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3398 G_CALLBACK(compose_changed_cb),
3404 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3407 GtkTextBuffer *buffer;
3410 const gchar *cur_encoding;
3411 gchar buf[BUFFSIZE];
3414 gboolean prev_autowrap;
3415 gboolean badtxt = FALSE;
3416 struct stat file_stat;
3419 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3421 /* get the size of the file we are about to insert */
3422 ret = g_stat(file, &file_stat);
3424 gchar *shortfile = g_path_get_basename(file);
3425 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3427 return COMPOSE_INSERT_NO_FILE;
3428 } else if (prefs_common.warn_large_insert == TRUE) {
3430 /* ask user for confirmation if the file is large */
3431 if (prefs_common.warn_large_insert_size < 0 ||
3432 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3436 msg = g_strdup_printf(_("You are about to insert a file of %s "
3437 "in the message body. Are you sure you want to do that?"),
3438 to_human_readable(file_stat.st_size));
3439 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3440 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3443 /* do we ask for confirmation next time? */
3444 if (aval & G_ALERTDISABLE) {
3445 /* no confirmation next time, disable feature in preferences */
3446 aval &= ~G_ALERTDISABLE;
3447 prefs_common.warn_large_insert = FALSE;
3450 /* abort file insertion if user canceled action */
3451 if (aval != G_ALERTALTERNATE) {
3452 return COMPOSE_INSERT_NO_FILE;
3458 if ((fp = g_fopen(file, "rb")) == NULL) {
3459 FILE_OP_ERROR(file, "fopen");
3460 return COMPOSE_INSERT_READ_ERROR;
3463 prev_autowrap = compose->autowrap;
3464 compose->autowrap = FALSE;
3466 text = GTK_TEXT_VIEW(compose->text);
3467 buffer = gtk_text_view_get_buffer(text);
3468 mark = gtk_text_buffer_get_insert(buffer);
3469 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3471 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3472 G_CALLBACK(text_inserted),
3475 cur_encoding = conv_get_locale_charset_str_no_utf8();
3477 while (fgets(buf, sizeof(buf), fp) != NULL) {
3480 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3481 str = g_strdup(buf);
3483 str = conv_codeset_strdup
3484 (buf, cur_encoding, CS_INTERNAL);
3487 /* strip <CR> if DOS/Windows file,
3488 replace <CR> with <LF> if Macintosh file. */
3491 if (len > 0 && str[len - 1] != '\n') {
3493 if (str[len] == '\r') str[len] = '\n';
3496 gtk_text_buffer_insert(buffer, &iter, str, -1);
3500 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3501 G_CALLBACK(text_inserted),
3503 compose->autowrap = prev_autowrap;
3504 if (compose->autowrap)
3505 compose_wrap_all(compose);
3510 return COMPOSE_INSERT_INVALID_CHARACTER;
3512 return COMPOSE_INSERT_SUCCESS;
3515 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3516 const gchar *filename,
3517 const gchar *content_type,
3518 const gchar *charset)
3526 GtkListStore *store;
3528 gboolean has_binary = FALSE;
3530 if (!is_file_exist(file)) {
3531 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3532 gboolean result = FALSE;
3533 if (file_from_uri && is_file_exist(file_from_uri)) {
3534 result = compose_attach_append(
3535 compose, file_from_uri,
3536 filename, content_type,
3539 g_free(file_from_uri);
3542 alertpanel_error("File %s doesn't exist\n", filename);
3545 if ((size = get_file_size(file)) < 0) {
3546 alertpanel_error("Can't get file size of %s\n", filename);
3550 alertpanel_error(_("File %s is empty."), filename);
3553 if ((fp = g_fopen(file, "rb")) == NULL) {
3554 alertpanel_error(_("Can't read %s."), filename);
3559 ainfo = g_new0(AttachInfo, 1);
3560 auto_ainfo = g_auto_pointer_new_with_free
3561 (ainfo, (GFreeFunc) compose_attach_info_free);
3562 ainfo->file = g_strdup(file);
3565 ainfo->content_type = g_strdup(content_type);
3566 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3568 MsgFlags flags = {0, 0};
3570 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3571 ainfo->encoding = ENC_7BIT;
3573 ainfo->encoding = ENC_8BIT;
3575 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3576 if (msginfo && msginfo->subject)
3577 name = g_strdup(msginfo->subject);
3579 name = g_path_get_basename(filename ? filename : file);
3581 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3583 procmsg_msginfo_free(msginfo);
3585 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3586 ainfo->charset = g_strdup(charset);
3587 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3589 ainfo->encoding = ENC_BASE64;
3591 name = g_path_get_basename(filename ? filename : file);
3592 ainfo->name = g_strdup(name);
3596 ainfo->content_type = procmime_get_mime_type(file);
3597 if (!ainfo->content_type) {
3598 ainfo->content_type =
3599 g_strdup("application/octet-stream");
3600 ainfo->encoding = ENC_BASE64;
3601 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3603 procmime_get_encoding_for_text_file(file, &has_binary);
3605 ainfo->encoding = ENC_BASE64;
3606 name = g_path_get_basename(filename ? filename : file);
3607 ainfo->name = g_strdup(name);
3611 if (ainfo->name != NULL
3612 && !strcmp(ainfo->name, ".")) {
3613 g_free(ainfo->name);
3617 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3618 g_free(ainfo->content_type);
3619 ainfo->content_type = g_strdup("application/octet-stream");
3620 g_free(ainfo->charset);
3621 ainfo->charset = NULL;
3624 ainfo->size = (goffset)size;
3625 size_text = to_human_readable((goffset)size);
3627 store = GTK_LIST_STORE(gtk_tree_view_get_model
3628 (GTK_TREE_VIEW(compose->attach_clist)));
3630 gtk_list_store_append(store, &iter);
3631 gtk_list_store_set(store, &iter,
3632 COL_MIMETYPE, ainfo->content_type,
3633 COL_SIZE, size_text,
3634 COL_NAME, ainfo->name,
3635 COL_CHARSET, ainfo->charset,
3637 COL_AUTODATA, auto_ainfo,
3640 g_auto_pointer_free(auto_ainfo);
3641 compose_attach_update_label(compose);
3645 static void compose_use_signing(Compose *compose, gboolean use_signing)
3647 compose->use_signing = use_signing;
3648 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3651 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3653 compose->use_encryption = use_encryption;
3654 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3657 #define NEXT_PART_NOT_CHILD(info) \
3659 node = info->node; \
3660 while (node->children) \
3661 node = g_node_last_child(node); \
3662 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3665 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3669 MimeInfo *firsttext = NULL;
3670 MimeInfo *encrypted = NULL;
3673 const gchar *partname = NULL;
3675 mimeinfo = procmime_scan_message(msginfo);
3676 if (!mimeinfo) return;
3678 if (mimeinfo->node->children == NULL) {
3679 procmime_mimeinfo_free_all(mimeinfo);
3683 /* find first content part */
3684 child = (MimeInfo *) mimeinfo->node->children->data;
3685 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3686 child = (MimeInfo *)child->node->children->data;
3689 if (child->type == MIMETYPE_TEXT) {
3691 debug_print("First text part found\n");
3692 } else if (compose->mode == COMPOSE_REEDIT &&
3693 child->type == MIMETYPE_APPLICATION &&
3694 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3695 encrypted = (MimeInfo *)child->node->parent->data;
3698 child = (MimeInfo *) mimeinfo->node->children->data;
3699 while (child != NULL) {
3702 if (child == encrypted) {
3703 /* skip this part of tree */
3704 NEXT_PART_NOT_CHILD(child);
3708 if (child->type == MIMETYPE_MULTIPART) {
3709 /* get the actual content */
3710 child = procmime_mimeinfo_next(child);
3714 if (child == firsttext) {
3715 child = procmime_mimeinfo_next(child);
3719 outfile = procmime_get_tmp_file_name(child);
3720 if ((err = procmime_get_part(outfile, child)) < 0)
3721 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3723 gchar *content_type;
3725 content_type = procmime_get_content_type_str(child->type, child->subtype);
3727 /* if we meet a pgp signature, we don't attach it, but
3728 * we force signing. */
3729 if ((strcmp(content_type, "application/pgp-signature") &&
3730 strcmp(content_type, "application/pkcs7-signature") &&
3731 strcmp(content_type, "application/x-pkcs7-signature"))
3732 || compose->mode == COMPOSE_REDIRECT) {
3733 partname = procmime_mimeinfo_get_parameter(child, "filename");
3734 if (partname == NULL)
3735 partname = procmime_mimeinfo_get_parameter(child, "name");
3736 if (partname == NULL)
3738 compose_attach_append(compose, outfile,
3739 partname, content_type,
3740 procmime_mimeinfo_get_parameter(child, "charset"));
3742 compose_force_signing(compose, compose->account, NULL);
3744 g_free(content_type);
3747 NEXT_PART_NOT_CHILD(child);
3749 procmime_mimeinfo_free_all(mimeinfo);
3752 #undef NEXT_PART_NOT_CHILD
3757 WAIT_FOR_INDENT_CHAR,
3758 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3761 /* return indent length, we allow:
3762 indent characters followed by indent characters or spaces/tabs,
3763 alphabets and numbers immediately followed by indent characters,
3764 and the repeating sequences of the above
3765 If quote ends with multiple spaces, only the first one is included. */
3766 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3767 const GtkTextIter *start, gint *len)
3769 GtkTextIter iter = *start;
3773 IndentState state = WAIT_FOR_INDENT_CHAR;
3776 gint alnum_count = 0;
3777 gint space_count = 0;
3780 if (prefs_common.quote_chars == NULL) {
3784 while (!gtk_text_iter_ends_line(&iter)) {
3785 wc = gtk_text_iter_get_char(&iter);
3786 if (g_unichar_iswide(wc))
3788 clen = g_unichar_to_utf8(wc, ch);
3792 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3793 is_space = g_unichar_isspace(wc);
3795 if (state == WAIT_FOR_INDENT_CHAR) {
3796 if (!is_indent && !g_unichar_isalnum(wc))
3799 quote_len += alnum_count + space_count + 1;
3800 alnum_count = space_count = 0;
3801 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3804 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3805 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3809 else if (is_indent) {
3810 quote_len += alnum_count + space_count + 1;
3811 alnum_count = space_count = 0;
3814 state = WAIT_FOR_INDENT_CHAR;
3818 gtk_text_iter_forward_char(&iter);
3821 if (quote_len > 0 && space_count > 0)
3827 if (quote_len > 0) {
3829 gtk_text_iter_forward_chars(&iter, quote_len);
3830 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3836 /* return >0 if the line is itemized */
3837 static int compose_itemized_length(GtkTextBuffer *buffer,
3838 const GtkTextIter *start)
3840 GtkTextIter iter = *start;
3845 if (gtk_text_iter_ends_line(&iter))
3850 wc = gtk_text_iter_get_char(&iter);
3851 if (!g_unichar_isspace(wc))
3853 gtk_text_iter_forward_char(&iter);
3854 if (gtk_text_iter_ends_line(&iter))
3858 clen = g_unichar_to_utf8(wc, ch);
3862 if (!strchr("*-+", ch[0]))
3865 gtk_text_iter_forward_char(&iter);
3866 if (gtk_text_iter_ends_line(&iter))
3868 wc = gtk_text_iter_get_char(&iter);
3869 if (g_unichar_isspace(wc)) {
3875 /* return the string at the start of the itemization */
3876 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3877 const GtkTextIter *start)
3879 GtkTextIter iter = *start;
3882 GString *item_chars = g_string_new("");
3885 if (gtk_text_iter_ends_line(&iter))
3890 wc = gtk_text_iter_get_char(&iter);
3891 if (!g_unichar_isspace(wc))
3893 gtk_text_iter_forward_char(&iter);
3894 if (gtk_text_iter_ends_line(&iter))
3896 g_string_append_unichar(item_chars, wc);
3899 str = item_chars->str;
3900 g_string_free(item_chars, FALSE);
3904 /* return the number of spaces at a line's start */
3905 static int compose_left_offset_length(GtkTextBuffer *buffer,
3906 const GtkTextIter *start)
3908 GtkTextIter iter = *start;
3911 if (gtk_text_iter_ends_line(&iter))
3915 wc = gtk_text_iter_get_char(&iter);
3916 if (!g_unichar_isspace(wc))
3919 gtk_text_iter_forward_char(&iter);
3920 if (gtk_text_iter_ends_line(&iter))
3924 gtk_text_iter_forward_char(&iter);
3925 if (gtk_text_iter_ends_line(&iter))
3930 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3931 const GtkTextIter *start,
3932 GtkTextIter *break_pos,
3936 GtkTextIter iter = *start, line_end = *start;
3937 PangoLogAttr *attrs;
3944 gboolean can_break = FALSE;
3945 gboolean do_break = FALSE;
3946 gboolean was_white = FALSE;
3947 gboolean prev_dont_break = FALSE;
3949 gtk_text_iter_forward_to_line_end(&line_end);
3950 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3951 len = g_utf8_strlen(str, -1);
3955 g_warning("compose_get_line_break_pos: len = 0!\n");
3959 /* g_print("breaking line: %d: %s (len = %d)\n",
3960 gtk_text_iter_get_line(&iter), str, len); */
3962 attrs = g_new(PangoLogAttr, len + 1);
3964 pango_default_break(str, -1, NULL, attrs, len + 1);
3968 /* skip quote and leading spaces */
3969 for (i = 0; *p != '\0' && i < len; i++) {
3972 wc = g_utf8_get_char(p);
3973 if (i >= quote_len && !g_unichar_isspace(wc))
3975 if (g_unichar_iswide(wc))
3977 else if (*p == '\t')
3981 p = g_utf8_next_char(p);
3984 for (; *p != '\0' && i < len; i++) {
3985 PangoLogAttr *attr = attrs + i;
3989 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3992 was_white = attr->is_white;
3994 /* don't wrap URI */
3995 if ((uri_len = get_uri_len(p)) > 0) {
3997 if (pos > 0 && col > max_col) {
4007 wc = g_utf8_get_char(p);
4008 if (g_unichar_iswide(wc)) {
4010 if (prev_dont_break && can_break && attr->is_line_break)
4012 } else if (*p == '\t')
4016 if (pos > 0 && col > max_col) {
4021 if (*p == '-' || *p == '/')
4022 prev_dont_break = TRUE;
4024 prev_dont_break = FALSE;
4026 p = g_utf8_next_char(p);
4030 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4035 *break_pos = *start;
4036 gtk_text_iter_set_line_offset(break_pos, pos);
4041 static gboolean compose_join_next_line(Compose *compose,
4042 GtkTextBuffer *buffer,
4044 const gchar *quote_str)
4046 GtkTextIter iter_ = *iter, cur, prev, next, end;
4047 PangoLogAttr attrs[3];
4049 gchar *next_quote_str;
4052 gboolean keep_cursor = FALSE;
4054 if (!gtk_text_iter_forward_line(&iter_) ||
4055 gtk_text_iter_ends_line(&iter_)) {
4058 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4060 if ((quote_str || next_quote_str) &&
4061 strcmp2(quote_str, next_quote_str) != 0) {
4062 g_free(next_quote_str);
4065 g_free(next_quote_str);
4068 if (quote_len > 0) {
4069 gtk_text_iter_forward_chars(&end, quote_len);
4070 if (gtk_text_iter_ends_line(&end)) {
4075 /* don't join itemized lines */
4076 if (compose_itemized_length(buffer, &end) > 0) {
4080 /* don't join signature separator */
4081 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4084 /* delete quote str */
4086 gtk_text_buffer_delete(buffer, &iter_, &end);
4088 /* don't join line breaks put by the user */
4090 gtk_text_iter_backward_char(&cur);
4091 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4092 gtk_text_iter_forward_char(&cur);
4096 gtk_text_iter_forward_char(&cur);
4097 /* delete linebreak and extra spaces */
4098 while (gtk_text_iter_backward_char(&cur)) {
4099 wc1 = gtk_text_iter_get_char(&cur);
4100 if (!g_unichar_isspace(wc1))
4105 while (!gtk_text_iter_ends_line(&cur)) {
4106 wc1 = gtk_text_iter_get_char(&cur);
4107 if (!g_unichar_isspace(wc1))
4109 gtk_text_iter_forward_char(&cur);
4112 if (!gtk_text_iter_equal(&prev, &next)) {
4115 mark = gtk_text_buffer_get_insert(buffer);
4116 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4117 if (gtk_text_iter_equal(&prev, &cur))
4119 gtk_text_buffer_delete(buffer, &prev, &next);
4123 /* insert space if required */
4124 gtk_text_iter_backward_char(&prev);
4125 wc1 = gtk_text_iter_get_char(&prev);
4126 wc2 = gtk_text_iter_get_char(&next);
4127 gtk_text_iter_forward_char(&next);
4128 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4129 pango_default_break(str, -1, NULL, attrs, 3);
4130 if (!attrs[1].is_line_break ||
4131 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4132 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4134 gtk_text_iter_backward_char(&iter_);
4135 gtk_text_buffer_place_cursor(buffer, &iter_);
4144 #define ADD_TXT_POS(bp_, ep_, pti_) \
4145 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4146 last = last->next; \
4147 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4148 last->next = NULL; \
4150 g_warning("alloc error scanning URIs\n"); \
4153 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4155 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4156 GtkTextBuffer *buffer;
4157 GtkTextIter iter, break_pos, end_of_line;
4158 gchar *quote_str = NULL;
4160 gboolean wrap_quote = prefs_common.linewrap_quote;
4161 gboolean prev_autowrap = compose->autowrap;
4162 gint startq_offset = -1, noq_offset = -1;
4163 gint uri_start = -1, uri_stop = -1;
4164 gint nouri_start = -1, nouri_stop = -1;
4165 gint num_blocks = 0;
4166 gint quotelevel = -1;
4167 gboolean modified = force;
4168 gboolean removed = FALSE;
4169 gboolean modified_before_remove = FALSE;
4171 gboolean start = TRUE;
4172 gint itemized_len = 0, rem_item_len = 0;
4173 gchar *itemized_chars = NULL;
4174 gboolean item_continuation = FALSE;
4179 if (compose->draft_timeout_tag == -2) {
4183 compose->autowrap = FALSE;
4185 buffer = gtk_text_view_get_buffer(text);
4186 undo_wrapping(compose->undostruct, TRUE);
4191 mark = gtk_text_buffer_get_insert(buffer);
4192 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4196 if (compose->draft_timeout_tag == -2) {
4197 if (gtk_text_iter_ends_line(&iter)) {
4198 while (gtk_text_iter_ends_line(&iter) &&
4199 gtk_text_iter_forward_line(&iter))
4202 while (gtk_text_iter_backward_line(&iter)) {
4203 if (gtk_text_iter_ends_line(&iter)) {
4204 gtk_text_iter_forward_line(&iter);
4210 /* move to line start */
4211 gtk_text_iter_set_line_offset(&iter, 0);
4214 itemized_len = compose_itemized_length(buffer, &iter);
4216 if (!itemized_len) {
4217 itemized_len = compose_left_offset_length(buffer, &iter);
4218 item_continuation = TRUE;
4222 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4224 /* go until paragraph end (empty line) */
4225 while (start || !gtk_text_iter_ends_line(&iter)) {
4226 gchar *scanpos = NULL;
4227 /* parse table - in order of priority */
4229 const gchar *needle; /* token */
4231 /* token search function */
4232 gchar *(*search) (const gchar *haystack,
4233 const gchar *needle);
4234 /* part parsing function */
4235 gboolean (*parse) (const gchar *start,
4236 const gchar *scanpos,
4240 /* part to URI function */
4241 gchar *(*build_uri) (const gchar *bp,
4245 static struct table parser[] = {
4246 {"http://", strcasestr, get_uri_part, make_uri_string},
4247 {"https://", strcasestr, get_uri_part, make_uri_string},
4248 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4249 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4250 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4251 {"www.", strcasestr, get_uri_part, make_http_string},
4252 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4253 {"@", strcasestr, get_email_part, make_email_string}
4255 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4256 gint last_index = PARSE_ELEMS;
4258 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4262 if (!prev_autowrap && num_blocks == 0) {
4264 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4265 G_CALLBACK(text_inserted),
4268 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4271 uri_start = uri_stop = -1;
4273 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4276 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4277 if (startq_offset == -1)
4278 startq_offset = gtk_text_iter_get_offset(&iter);
4279 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4280 if (quotelevel > 2) {
4281 /* recycle colors */
4282 if (prefs_common.recycle_quote_colors)
4291 if (startq_offset == -1)
4292 noq_offset = gtk_text_iter_get_offset(&iter);
4296 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4299 if (gtk_text_iter_ends_line(&iter)) {
4301 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4302 prefs_common.linewrap_len,
4304 GtkTextIter prev, next, cur;
4305 if (prev_autowrap != FALSE || force) {
4306 compose->automatic_break = TRUE;
4308 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4309 compose->automatic_break = FALSE;
4310 if (itemized_len && compose->autoindent) {
4311 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4312 if (!item_continuation)
4313 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4315 } else if (quote_str && wrap_quote) {
4316 compose->automatic_break = TRUE;
4318 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4319 compose->automatic_break = FALSE;
4320 if (itemized_len && compose->autoindent) {
4321 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4322 if (!item_continuation)
4323 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4327 /* remove trailing spaces */
4329 rem_item_len = itemized_len;
4330 while (compose->autoindent && rem_item_len-- > 0)
4331 gtk_text_iter_backward_char(&cur);
4332 gtk_text_iter_backward_char(&cur);
4335 while (!gtk_text_iter_starts_line(&cur)) {
4338 gtk_text_iter_backward_char(&cur);
4339 wc = gtk_text_iter_get_char(&cur);
4340 if (!g_unichar_isspace(wc))
4344 if (!gtk_text_iter_equal(&prev, &next)) {
4345 gtk_text_buffer_delete(buffer, &prev, &next);
4347 gtk_text_iter_forward_char(&break_pos);
4351 gtk_text_buffer_insert(buffer, &break_pos,
4355 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4357 /* move iter to current line start */
4358 gtk_text_iter_set_line_offset(&iter, 0);
4365 /* move iter to next line start */
4371 if (!prev_autowrap && num_blocks > 0) {
4373 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4374 G_CALLBACK(text_inserted),
4378 while (!gtk_text_iter_ends_line(&end_of_line)) {
4379 gtk_text_iter_forward_char(&end_of_line);
4381 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4383 nouri_start = gtk_text_iter_get_offset(&iter);
4384 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4386 walk_pos = gtk_text_iter_get_offset(&iter);
4387 /* FIXME: this looks phony. scanning for anything in the parse table */
4388 for (n = 0; n < PARSE_ELEMS; n++) {
4391 tmp = parser[n].search(walk, parser[n].needle);
4393 if (scanpos == NULL || tmp < scanpos) {
4402 /* check if URI can be parsed */
4403 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4404 (const gchar **)&ep, FALSE)
4405 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4409 strlen(parser[last_index].needle);
4412 uri_start = walk_pos + (bp - o_walk);
4413 uri_stop = walk_pos + (ep - o_walk);
4417 gtk_text_iter_forward_line(&iter);
4420 if (startq_offset != -1) {
4421 GtkTextIter startquote, endquote;
4422 gtk_text_buffer_get_iter_at_offset(
4423 buffer, &startquote, startq_offset);
4426 switch (quotelevel) {
4428 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4429 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4430 gtk_text_buffer_apply_tag_by_name(
4431 buffer, "quote0", &startquote, &endquote);
4432 gtk_text_buffer_remove_tag_by_name(
4433 buffer, "quote1", &startquote, &endquote);
4434 gtk_text_buffer_remove_tag_by_name(
4435 buffer, "quote2", &startquote, &endquote);
4440 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4441 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4442 gtk_text_buffer_apply_tag_by_name(
4443 buffer, "quote1", &startquote, &endquote);
4444 gtk_text_buffer_remove_tag_by_name(
4445 buffer, "quote0", &startquote, &endquote);
4446 gtk_text_buffer_remove_tag_by_name(
4447 buffer, "quote2", &startquote, &endquote);
4452 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4453 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4454 gtk_text_buffer_apply_tag_by_name(
4455 buffer, "quote2", &startquote, &endquote);
4456 gtk_text_buffer_remove_tag_by_name(
4457 buffer, "quote0", &startquote, &endquote);
4458 gtk_text_buffer_remove_tag_by_name(
4459 buffer, "quote1", &startquote, &endquote);
4465 } else if (noq_offset != -1) {
4466 GtkTextIter startnoquote, endnoquote;
4467 gtk_text_buffer_get_iter_at_offset(
4468 buffer, &startnoquote, noq_offset);
4471 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4472 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4473 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4474 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4475 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4476 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4477 gtk_text_buffer_remove_tag_by_name(
4478 buffer, "quote0", &startnoquote, &endnoquote);
4479 gtk_text_buffer_remove_tag_by_name(
4480 buffer, "quote1", &startnoquote, &endnoquote);
4481 gtk_text_buffer_remove_tag_by_name(
4482 buffer, "quote2", &startnoquote, &endnoquote);
4488 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4489 GtkTextIter nouri_start_iter, nouri_end_iter;
4490 gtk_text_buffer_get_iter_at_offset(
4491 buffer, &nouri_start_iter, nouri_start);
4492 gtk_text_buffer_get_iter_at_offset(
4493 buffer, &nouri_end_iter, nouri_stop);
4494 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4495 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4496 gtk_text_buffer_remove_tag_by_name(
4497 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4498 modified_before_remove = modified;
4503 if (uri_start >= 0 && uri_stop > 0) {
4504 GtkTextIter uri_start_iter, uri_end_iter, back;
4505 gtk_text_buffer_get_iter_at_offset(
4506 buffer, &uri_start_iter, uri_start);
4507 gtk_text_buffer_get_iter_at_offset(
4508 buffer, &uri_end_iter, uri_stop);
4509 back = uri_end_iter;
4510 gtk_text_iter_backward_char(&back);
4511 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4512 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4513 gtk_text_buffer_apply_tag_by_name(
4514 buffer, "link", &uri_start_iter, &uri_end_iter);
4516 if (removed && !modified_before_remove) {
4522 // debug_print("not modified, out after %d lines\n", lines);
4526 // debug_print("modified, out after %d lines\n", lines);
4528 g_free(itemized_chars);
4531 undo_wrapping(compose->undostruct, FALSE);
4532 compose->autowrap = prev_autowrap;
4537 void compose_action_cb(void *data)
4539 Compose *compose = (Compose *)data;
4540 compose_wrap_all(compose);
4543 static void compose_wrap_all(Compose *compose)
4545 compose_wrap_all_full(compose, FALSE);
4548 static void compose_wrap_all_full(Compose *compose, gboolean force)
4550 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4551 GtkTextBuffer *buffer;
4553 gboolean modified = TRUE;
4555 buffer = gtk_text_view_get_buffer(text);
4557 gtk_text_buffer_get_start_iter(buffer, &iter);
4558 while (!gtk_text_iter_is_end(&iter) && modified)
4559 modified = compose_beautify_paragraph(compose, &iter, force);
4563 static void compose_set_title(Compose *compose)
4569 edited = compose->modified ? _(" [Edited]") : "";
4571 subject = gtk_editable_get_chars(
4572 GTK_EDITABLE(compose->subject_entry), 0, -1);
4574 #ifndef GENERIC_UMPC
4575 if (subject && strlen(subject))
4576 str = g_strdup_printf(_("%s - Compose message%s"),
4579 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4581 str = g_strdup(_("Compose message"));
4584 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4590 * compose_current_mail_account:
4592 * Find a current mail account (the currently selected account, or the
4593 * default account, if a news account is currently selected). If a
4594 * mail account cannot be found, display an error message.
4596 * Return value: Mail account, or NULL if not found.
4598 static PrefsAccount *
4599 compose_current_mail_account(void)
4603 if (cur_account && cur_account->protocol != A_NNTP)
4606 ac = account_get_default();
4607 if (!ac || ac->protocol == A_NNTP) {
4608 alertpanel_error(_("Account for sending mail is not specified.\n"
4609 "Please select a mail account before sending."));
4616 #define QUOTE_IF_REQUIRED(out, str) \
4618 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4622 len = strlen(str) + 3; \
4623 if ((__tmp = alloca(len)) == NULL) { \
4624 g_warning("can't allocate memory\n"); \
4625 g_string_free(header, TRUE); \
4628 g_snprintf(__tmp, len, "\"%s\"", str); \
4633 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4634 g_warning("can't allocate memory\n"); \
4635 g_string_free(header, TRUE); \
4638 strcpy(__tmp, str); \
4644 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4646 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4650 len = strlen(str) + 3; \
4651 if ((__tmp = alloca(len)) == NULL) { \
4652 g_warning("can't allocate memory\n"); \
4655 g_snprintf(__tmp, len, "\"%s\"", str); \
4660 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4661 g_warning("can't allocate memory\n"); \
4664 strcpy(__tmp, str); \
4670 static void compose_select_account(Compose *compose, PrefsAccount *account,
4673 gchar *from = NULL, *header;
4674 ComposeHeaderEntry *header_entry;
4676 cm_return_if_fail(account != NULL);
4678 compose->account = account;
4679 if (account->name && *account->name) {
4681 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4682 from = g_strdup_printf("%s <%s>",
4683 buf, account->address);
4684 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4686 from = g_strdup_printf("<%s>",
4688 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4693 compose_set_title(compose);
4695 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4696 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4698 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4699 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4700 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4702 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4704 activate_privacy_system(compose, account, FALSE);
4706 if (!init && compose->mode != COMPOSE_REDIRECT) {
4707 undo_block(compose->undostruct);
4708 compose_insert_sig(compose, TRUE);
4709 undo_unblock(compose->undostruct);
4712 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4713 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4715 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4716 if (account->protocol == A_NNTP) {
4717 if (!strcmp(header, _("To:")))
4718 combobox_select_by_text(
4719 GTK_COMBO_BOX(header_entry->combo),
4722 if (!strcmp(header, _("Newsgroups:")))
4723 combobox_select_by_text(
4724 GTK_COMBO_BOX(header_entry->combo),
4732 /* use account's dict info if set */
4733 if (compose->gtkaspell) {
4734 if (account->enable_default_dictionary)
4735 gtkaspell_change_dict(compose->gtkaspell,
4736 account->default_dictionary, FALSE);
4737 if (account->enable_default_alt_dictionary)
4738 gtkaspell_change_alt_dict(compose->gtkaspell,
4739 account->default_alt_dictionary);
4740 if (account->enable_default_dictionary
4741 || account->enable_default_alt_dictionary)
4742 compose_spell_menu_changed(compose);
4747 gboolean compose_check_for_valid_recipient(Compose *compose) {
4748 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4749 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4750 gboolean recipient_found = FALSE;
4754 /* free to and newsgroup list */
4755 slist_free_strings(compose->to_list);
4756 g_slist_free(compose->to_list);
4757 compose->to_list = NULL;
4759 slist_free_strings(compose->newsgroup_list);
4760 g_slist_free(compose->newsgroup_list);
4761 compose->newsgroup_list = NULL;
4763 /* search header entries for to and newsgroup entries */
4764 for (list = compose->header_list; list; list = list->next) {
4767 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4768 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4771 if (entry[0] != '\0') {
4772 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4773 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4774 compose->to_list = address_list_append(compose->to_list, entry);
4775 recipient_found = TRUE;
4778 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4779 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4780 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4781 recipient_found = TRUE;
4788 return recipient_found;
4791 static gboolean compose_check_for_set_recipients(Compose *compose)
4793 if (compose->account->set_autocc && compose->account->auto_cc) {
4794 gboolean found_other = FALSE;
4796 /* search header entries for to and newsgroup entries */
4797 for (list = compose->header_list; list; list = list->next) {
4800 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4801 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4804 if (strcmp(entry, compose->account->auto_cc)
4805 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4815 if (compose->batch) {
4816 gtk_widget_show_all(compose->window);
4818 aval = alertpanel(_("Send"),
4819 _("The only recipient is the default CC address. Send anyway?"),
4820 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4821 if (aval != G_ALERTALTERNATE)
4825 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4826 gboolean found_other = FALSE;
4828 /* search header entries for to and newsgroup entries */
4829 for (list = compose->header_list; list; list = list->next) {
4832 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4833 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4836 if (strcmp(entry, compose->account->auto_bcc)
4837 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4847 if (compose->batch) {
4848 gtk_widget_show_all(compose->window);
4850 aval = alertpanel(_("Send"),
4851 _("The only recipient is the default BCC address. Send anyway?"),
4852 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4853 if (aval != G_ALERTALTERNATE)
4860 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4864 if (compose_check_for_valid_recipient(compose) == FALSE) {
4865 if (compose->batch) {
4866 gtk_widget_show_all(compose->window);
4868 alertpanel_error(_("Recipient is not specified."));
4872 if (compose_check_for_set_recipients(compose) == FALSE) {
4876 if (!compose->batch) {
4877 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4878 if (*str == '\0' && check_everything == TRUE &&
4879 compose->mode != COMPOSE_REDIRECT) {
4881 gchar *button_label;
4884 if (compose->sending)
4885 button_label = _("+_Send");
4887 button_label = _("+_Queue");
4888 message = g_strdup_printf(_("Subject is empty. %s"),
4889 compose->sending?_("Send it anyway?"):
4890 _("Queue it anyway?"));
4892 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4893 GTK_STOCK_CANCEL, button_label, NULL);
4895 if (aval != G_ALERTALTERNATE)
4900 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4906 gint compose_send(Compose *compose)
4909 FolderItem *folder = NULL;
4911 gchar *msgpath = NULL;
4912 gboolean discard_window = FALSE;
4913 gchar *errstr = NULL;
4914 gchar *tmsgid = NULL;
4915 MainWindow *mainwin = mainwindow_get_mainwindow();
4916 gboolean queued_removed = FALSE;
4918 if (prefs_common.send_dialog_invisible
4919 || compose->batch == TRUE)
4920 discard_window = TRUE;
4922 compose_allow_user_actions (compose, FALSE);
4923 compose->sending = TRUE;
4925 if (compose_check_entries(compose, TRUE) == FALSE) {
4926 if (compose->batch) {
4927 gtk_widget_show_all(compose->window);
4933 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4936 if (compose->batch) {
4937 gtk_widget_show_all(compose->window);
4940 alertpanel_error(_("Could not queue message for sending:\n\n"
4941 "Charset conversion failed."));
4942 } else if (val == -5) {
4943 alertpanel_error(_("Could not queue message for sending:\n\n"
4944 "Couldn't get recipient encryption key."));
4945 } else if (val == -6) {
4947 } else if (val == -3) {
4948 if (privacy_peek_error())
4949 alertpanel_error(_("Could not queue message for sending:\n\n"
4950 "Signature failed: %s"), privacy_get_error());
4951 } else if (val == -2 && errno != 0) {
4952 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4954 alertpanel_error(_("Could not queue message for sending."));
4959 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4960 if (discard_window) {
4961 compose->sending = FALSE;
4962 compose_close(compose);
4963 /* No more compose access in the normal codepath
4964 * after this point! */
4969 alertpanel_error(_("The message was queued but could not be "
4970 "sent.\nUse \"Send queued messages\" from "
4971 "the main window to retry."));
4972 if (!discard_window) {
4979 if (msgpath == NULL) {
4980 msgpath = folder_item_fetch_msg(folder, msgnum);
4981 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4984 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4985 claws_unlink(msgpath);
4988 if (!discard_window) {
4990 if (!queued_removed)
4991 folder_item_remove_msg(folder, msgnum);
4992 folder_item_scan(folder);
4994 /* make sure we delete that */
4995 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4997 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4998 folder_item_remove_msg(folder, tmp->msgnum);
4999 procmsg_msginfo_free(tmp);
5006 if (!queued_removed)
5007 folder_item_remove_msg(folder, msgnum);
5008 folder_item_scan(folder);
5010 /* make sure we delete that */
5011 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5013 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5014 folder_item_remove_msg(folder, tmp->msgnum);
5015 procmsg_msginfo_free(tmp);
5018 if (!discard_window) {
5019 compose->sending = FALSE;
5020 compose_allow_user_actions (compose, TRUE);
5021 compose_close(compose);
5025 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5026 "the main window to retry."), errstr);
5029 alertpanel_error_log(_("The message was queued but could not be "
5030 "sent.\nUse \"Send queued messages\" from "
5031 "the main window to retry."));
5033 if (!discard_window) {
5042 toolbar_main_set_sensitive(mainwin);
5043 main_window_set_menu_sensitive(mainwin);
5049 compose_allow_user_actions (compose, TRUE);
5050 compose->sending = FALSE;
5051 compose->modified = TRUE;
5052 toolbar_main_set_sensitive(mainwin);
5053 main_window_set_menu_sensitive(mainwin);
5058 static gboolean compose_use_attach(Compose *compose)
5060 GtkTreeModel *model = gtk_tree_view_get_model
5061 (GTK_TREE_VIEW(compose->attach_clist));
5062 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5065 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5068 gchar buf[BUFFSIZE];
5070 gboolean first_to_address;
5071 gboolean first_cc_address;
5073 ComposeHeaderEntry *headerentry;
5074 const gchar *headerentryname;
5075 const gchar *cc_hdr;
5076 const gchar *to_hdr;
5077 gboolean err = FALSE;
5079 debug_print("Writing redirect header\n");
5081 cc_hdr = prefs_common_translated_header_name("Cc:");
5082 to_hdr = prefs_common_translated_header_name("To:");
5084 first_to_address = TRUE;
5085 for (list = compose->header_list; list; list = list->next) {
5086 headerentry = ((ComposeHeaderEntry *)list->data);
5087 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5089 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5090 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5091 Xstrdup_a(str, entstr, return -1);
5093 if (str[0] != '\0') {
5094 compose_convert_header
5095 (compose, buf, sizeof(buf), str,
5096 strlen("Resent-To") + 2, TRUE);
5098 if (first_to_address) {
5099 err |= (fprintf(fp, "Resent-To: ") < 0);
5100 first_to_address = FALSE;
5102 err |= (fprintf(fp, ",") < 0);
5104 err |= (fprintf(fp, "%s", buf) < 0);
5108 if (!first_to_address) {
5109 err |= (fprintf(fp, "\n") < 0);
5112 first_cc_address = TRUE;
5113 for (list = compose->header_list; list; list = list->next) {
5114 headerentry = ((ComposeHeaderEntry *)list->data);
5115 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5117 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5118 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5119 Xstrdup_a(str, strg, return -1);
5121 if (str[0] != '\0') {
5122 compose_convert_header
5123 (compose, buf, sizeof(buf), str,
5124 strlen("Resent-Cc") + 2, TRUE);
5126 if (first_cc_address) {
5127 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5128 first_cc_address = FALSE;
5130 err |= (fprintf(fp, ",") < 0);
5132 err |= (fprintf(fp, "%s", buf) < 0);
5136 if (!first_cc_address) {
5137 err |= (fprintf(fp, "\n") < 0);
5140 return (err ? -1:0);
5143 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5145 gchar buf[BUFFSIZE];
5147 const gchar *entstr;
5148 /* struct utsname utsbuf; */
5149 gboolean err = FALSE;
5151 cm_return_val_if_fail(fp != NULL, -1);
5152 cm_return_val_if_fail(compose->account != NULL, -1);
5153 cm_return_val_if_fail(compose->account->address != NULL, -1);
5156 get_rfc822_date(buf, sizeof(buf));
5157 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5160 if (compose->account->name && *compose->account->name) {
5161 compose_convert_header
5162 (compose, buf, sizeof(buf), compose->account->name,
5163 strlen("From: "), TRUE);
5164 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5165 buf, compose->account->address) < 0);
5167 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5170 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5171 if (*entstr != '\0') {
5172 Xstrdup_a(str, entstr, return -1);
5175 compose_convert_header(compose, buf, sizeof(buf), str,
5176 strlen("Subject: "), FALSE);
5177 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5181 /* Resent-Message-ID */
5182 if (compose->account->set_domain && compose->account->domain) {
5183 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5184 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5185 g_snprintf(buf, sizeof(buf), "%s",
5186 strchr(compose->account->address, '@') ?
5187 strchr(compose->account->address, '@')+1 :
5188 compose->account->address);
5190 g_snprintf(buf, sizeof(buf), "%s", "");
5193 if (compose->account->gen_msgid) {
5195 if (compose->account->msgid_with_addr) {
5196 addr = compose->account->address;
5198 generate_msgid(buf, sizeof(buf), addr);
5199 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5200 compose->msgid = g_strdup(buf);
5202 compose->msgid = NULL;
5205 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5208 /* separator between header and body */
5209 err |= (fputs("\n", fp) == EOF);
5211 return (err ? -1:0);
5214 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5218 gchar buf[BUFFSIZE];
5220 gboolean skip = FALSE;
5221 gboolean err = FALSE;
5222 gchar *not_included[]={
5223 "Return-Path:", "Delivered-To:", "Received:",
5224 "Subject:", "X-UIDL:", "AF:",
5225 "NF:", "PS:", "SRH:",
5226 "SFN:", "DSR:", "MID:",
5227 "CFG:", "PT:", "S:",
5228 "RQ:", "SSV:", "NSV:",
5229 "SSH:", "R:", "MAID:",
5230 "NAID:", "RMID:", "FMID:",
5231 "SCF:", "RRCPT:", "NG:",
5232 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5233 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5234 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5235 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5236 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5239 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5240 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5244 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5246 for (i = 0; not_included[i] != NULL; i++) {
5247 if (g_ascii_strncasecmp(buf, not_included[i],
5248 strlen(not_included[i])) == 0) {
5255 if (fputs(buf, fdest) == -1)
5258 if (!prefs_common.redirect_keep_from) {
5259 if (g_ascii_strncasecmp(buf, "From:",
5260 strlen("From:")) == 0) {
5261 err |= (fputs(" (by way of ", fdest) == EOF);
5262 if (compose->account->name
5263 && *compose->account->name) {
5264 compose_convert_header
5265 (compose, buf, sizeof(buf),
5266 compose->account->name,
5269 err |= (fprintf(fdest, "%s <%s>",
5271 compose->account->address) < 0);
5273 err |= (fprintf(fdest, "%s",
5274 compose->account->address) < 0);
5275 err |= (fputs(")", fdest) == EOF);
5279 if (fputs("\n", fdest) == -1)
5286 if (compose_redirect_write_headers(compose, fdest))
5289 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5290 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5303 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5305 GtkTextBuffer *buffer;
5306 GtkTextIter start, end;
5309 const gchar *out_codeset;
5310 EncodingType encoding = ENC_UNKNOWN;
5311 MimeInfo *mimemsg, *mimetext;
5313 const gchar *src_codeset = CS_INTERNAL;
5314 gchar *from_addr = NULL;
5315 gchar *from_name = NULL;
5317 if (action == COMPOSE_WRITE_FOR_SEND)
5318 attach_parts = TRUE;
5320 /* create message MimeInfo */
5321 mimemsg = procmime_mimeinfo_new();
5322 mimemsg->type = MIMETYPE_MESSAGE;
5323 mimemsg->subtype = g_strdup("rfc822");
5324 mimemsg->content = MIMECONTENT_MEM;
5325 mimemsg->tmp = TRUE; /* must free content later */
5326 mimemsg->data.mem = compose_get_header(compose);
5328 /* Create text part MimeInfo */
5329 /* get all composed text */
5330 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5331 gtk_text_buffer_get_start_iter(buffer, &start);
5332 gtk_text_buffer_get_end_iter(buffer, &end);
5333 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5335 out_codeset = conv_get_charset_str(compose->out_encoding);
5337 if (!out_codeset && is_ascii_str(chars)) {
5338 out_codeset = CS_US_ASCII;
5339 } else if (prefs_common.outgoing_fallback_to_ascii &&
5340 is_ascii_str(chars)) {
5341 out_codeset = CS_US_ASCII;
5342 encoding = ENC_7BIT;
5346 gchar *test_conv_global_out = NULL;
5347 gchar *test_conv_reply = NULL;
5349 /* automatic mode. be automatic. */
5350 codeconv_set_strict(TRUE);
5352 out_codeset = conv_get_outgoing_charset_str();
5354 debug_print("trying to convert to %s\n", out_codeset);
5355 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5358 if (!test_conv_global_out && compose->orig_charset
5359 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5360 out_codeset = compose->orig_charset;
5361 debug_print("failure; trying to convert to %s\n", out_codeset);
5362 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5365 if (!test_conv_global_out && !test_conv_reply) {
5367 out_codeset = CS_INTERNAL;
5368 debug_print("failure; finally using %s\n", out_codeset);
5370 g_free(test_conv_global_out);
5371 g_free(test_conv_reply);
5372 codeconv_set_strict(FALSE);
5375 if (encoding == ENC_UNKNOWN) {
5376 if (prefs_common.encoding_method == CTE_BASE64)
5377 encoding = ENC_BASE64;
5378 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5379 encoding = ENC_QUOTED_PRINTABLE;
5380 else if (prefs_common.encoding_method == CTE_8BIT)
5381 encoding = ENC_8BIT;
5383 encoding = procmime_get_encoding_for_charset(out_codeset);
5386 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5387 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5389 if (action == COMPOSE_WRITE_FOR_SEND) {
5390 codeconv_set_strict(TRUE);
5391 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5392 codeconv_set_strict(FALSE);
5398 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5399 "to the specified %s charset.\n"
5400 "Send it as %s?"), out_codeset, src_codeset);
5401 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5402 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5405 if (aval != G_ALERTALTERNATE) {
5410 out_codeset = src_codeset;
5416 out_codeset = src_codeset;
5421 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5422 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5423 strstr(buf, "\nFrom ") != NULL) {
5424 encoding = ENC_QUOTED_PRINTABLE;
5428 mimetext = procmime_mimeinfo_new();
5429 mimetext->content = MIMECONTENT_MEM;
5430 mimetext->tmp = TRUE; /* must free content later */
5431 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5432 * and free the data, which we need later. */
5433 mimetext->data.mem = g_strdup(buf);
5434 mimetext->type = MIMETYPE_TEXT;
5435 mimetext->subtype = g_strdup("plain");
5436 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5437 g_strdup(out_codeset));
5439 /* protect trailing spaces when signing message */
5440 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5441 privacy_system_can_sign(compose->privacy_system)) {
5442 encoding = ENC_QUOTED_PRINTABLE;
5445 debug_print("main text: %zd bytes encoded as %s in %d\n",
5446 strlen(buf), out_codeset, encoding);
5448 /* check for line length limit */
5449 if (action == COMPOSE_WRITE_FOR_SEND &&
5450 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5451 check_line_length(buf, 1000, &line) < 0) {
5455 msg = g_strdup_printf
5456 (_("Line %d exceeds the line length limit (998 bytes).\n"
5457 "The contents of the message might be broken on the way to the delivery.\n"
5459 "Send it anyway?"), line + 1);
5460 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5462 if (aval != G_ALERTALTERNATE) {
5468 if (encoding != ENC_UNKNOWN)
5469 procmime_encode_content(mimetext, encoding);
5471 /* append attachment parts */
5472 if (compose_use_attach(compose) && attach_parts) {
5473 MimeInfo *mimempart;
5474 gchar *boundary = NULL;
5475 mimempart = procmime_mimeinfo_new();
5476 mimempart->content = MIMECONTENT_EMPTY;
5477 mimempart->type = MIMETYPE_MULTIPART;
5478 mimempart->subtype = g_strdup("mixed");
5482 boundary = generate_mime_boundary(NULL);
5483 } while (strstr(buf, boundary) != NULL);
5485 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5488 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5490 g_node_append(mimempart->node, mimetext->node);
5491 g_node_append(mimemsg->node, mimempart->node);
5493 if (compose_add_attachments(compose, mimempart) < 0)
5496 g_node_append(mimemsg->node, mimetext->node);
5500 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5501 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5502 /* extract name and address */
5503 if (strstr(spec, " <") && strstr(spec, ">")) {
5504 from_addr = g_strdup(strrchr(spec, '<')+1);
5505 *(strrchr(from_addr, '>')) = '\0';
5506 from_name = g_strdup(spec);
5507 *(strrchr(from_name, '<')) = '\0';
5514 /* sign message if sending */
5515 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5516 privacy_system_can_sign(compose->privacy_system))
5517 if (!privacy_sign(compose->privacy_system, mimemsg,
5518 compose->account, from_addr)) {
5525 procmime_write_mimeinfo(mimemsg, fp);
5527 procmime_mimeinfo_free_all(mimemsg);
5532 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5534 GtkTextBuffer *buffer;
5535 GtkTextIter start, end;
5540 if ((fp = g_fopen(file, "wb")) == NULL) {
5541 FILE_OP_ERROR(file, "fopen");
5545 /* chmod for security */
5546 if (change_file_mode_rw(fp, file) < 0) {
5547 FILE_OP_ERROR(file, "chmod");
5548 g_warning("can't change file mode\n");
5551 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5552 gtk_text_buffer_get_start_iter(buffer, &start);
5553 gtk_text_buffer_get_end_iter(buffer, &end);
5554 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5556 chars = conv_codeset_strdup
5557 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5560 if (!chars) return -1;
5563 len = strlen(chars);
5564 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5565 FILE_OP_ERROR(file, "fwrite");
5574 if (fclose(fp) == EOF) {
5575 FILE_OP_ERROR(file, "fclose");
5582 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5585 MsgInfo *msginfo = compose->targetinfo;
5587 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5588 if (!msginfo) return -1;
5590 if (!force && MSG_IS_LOCKED(msginfo->flags))
5593 item = msginfo->folder;
5594 cm_return_val_if_fail(item != NULL, -1);
5596 if (procmsg_msg_exist(msginfo) &&
5597 (folder_has_parent_of_type(item, F_QUEUE) ||
5598 folder_has_parent_of_type(item, F_DRAFT)
5599 || msginfo == compose->autosaved_draft)) {
5600 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5601 g_warning("can't remove the old message\n");
5604 debug_print("removed reedit target %d\n", msginfo->msgnum);
5611 static void compose_remove_draft(Compose *compose)
5614 MsgInfo *msginfo = compose->targetinfo;
5615 drafts = account_get_special_folder(compose->account, F_DRAFT);
5617 if (procmsg_msg_exist(msginfo)) {
5618 folder_item_remove_msg(drafts, msginfo->msgnum);
5623 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5624 gboolean remove_reedit_target)
5626 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5629 static gboolean compose_warn_encryption(Compose *compose)
5631 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5632 AlertValue val = G_ALERTALTERNATE;
5634 if (warning == NULL)
5637 val = alertpanel_full(_("Encryption warning"), warning,
5638 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5639 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5640 if (val & G_ALERTDISABLE) {
5641 val &= ~G_ALERTDISABLE;
5642 if (val == G_ALERTALTERNATE)
5643 privacy_inhibit_encrypt_warning(compose->privacy_system,
5647 if (val == G_ALERTALTERNATE) {
5654 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5655 gchar **msgpath, gboolean check_subject,
5656 gboolean remove_reedit_target)
5663 static gboolean lock = FALSE;
5664 PrefsAccount *mailac = NULL, *newsac = NULL;
5665 gboolean err = FALSE;
5667 debug_print("queueing message...\n");
5668 cm_return_val_if_fail(compose->account != NULL, -1);
5672 if (compose_check_entries(compose, check_subject) == FALSE) {
5674 if (compose->batch) {
5675 gtk_widget_show_all(compose->window);
5680 if (!compose->to_list && !compose->newsgroup_list) {
5681 g_warning("can't get recipient list.");
5686 if (compose->to_list) {
5687 if (compose->account->protocol != A_NNTP)
5688 mailac = compose->account;
5689 else if (cur_account && cur_account->protocol != A_NNTP)
5690 mailac = cur_account;
5691 else if (!(mailac = compose_current_mail_account())) {
5693 alertpanel_error(_("No account for sending mails available!"));
5698 if (compose->newsgroup_list) {
5699 if (compose->account->protocol == A_NNTP)
5700 newsac = compose->account;
5703 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5708 /* write queue header */
5709 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5710 G_DIR_SEPARATOR, compose, (guint) rand());
5711 debug_print("queuing to %s\n", tmp);
5712 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5713 FILE_OP_ERROR(tmp, "fopen");
5719 if (change_file_mode_rw(fp, tmp) < 0) {
5720 FILE_OP_ERROR(tmp, "chmod");
5721 g_warning("can't change file mode\n");
5724 /* queueing variables */
5725 err |= (fprintf(fp, "AF:\n") < 0);
5726 err |= (fprintf(fp, "NF:0\n") < 0);
5727 err |= (fprintf(fp, "PS:10\n") < 0);
5728 err |= (fprintf(fp, "SRH:1\n") < 0);
5729 err |= (fprintf(fp, "SFN:\n") < 0);
5730 err |= (fprintf(fp, "DSR:\n") < 0);
5732 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5734 err |= (fprintf(fp, "MID:\n") < 0);
5735 err |= (fprintf(fp, "CFG:\n") < 0);
5736 err |= (fprintf(fp, "PT:0\n") < 0);
5737 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5738 err |= (fprintf(fp, "RQ:\n") < 0);
5740 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5742 err |= (fprintf(fp, "SSV:\n") < 0);
5744 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5746 err |= (fprintf(fp, "NSV:\n") < 0);
5747 err |= (fprintf(fp, "SSH:\n") < 0);
5748 /* write recepient list */
5749 if (compose->to_list) {
5750 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5751 for (cur = compose->to_list->next; cur != NULL;
5753 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5754 err |= (fprintf(fp, "\n") < 0);
5756 /* write newsgroup list */
5757 if (compose->newsgroup_list) {
5758 err |= (fprintf(fp, "NG:") < 0);
5759 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5760 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5761 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5762 err |= (fprintf(fp, "\n") < 0);
5764 /* Sylpheed account IDs */
5766 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5768 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5771 if (compose->privacy_system != NULL) {
5772 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5773 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5774 if (compose->use_encryption) {
5776 if (!compose_warn_encryption(compose)) {
5783 if (mailac && mailac->encrypt_to_self) {
5784 GSList *tmp_list = g_slist_copy(compose->to_list);
5785 tmp_list = g_slist_append(tmp_list, compose->account->address);
5786 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5787 g_slist_free(tmp_list);
5789 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5791 if (encdata != NULL) {
5792 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5793 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5794 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5796 } /* else we finally dont want to encrypt */
5798 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5799 /* and if encdata was null, it means there's been a problem in
5811 /* Save copy folder */
5812 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5813 gchar *savefolderid;
5815 savefolderid = compose_get_save_to(compose);
5816 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5817 g_free(savefolderid);
5819 /* Save copy folder */
5820 if (compose->return_receipt) {
5821 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5823 /* Message-ID of message replying to */
5824 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5827 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5828 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5831 /* Message-ID of message forwarding to */
5832 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5835 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5836 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5840 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5841 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5843 /* end of headers */
5844 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5846 if (compose->redirect_filename != NULL) {
5847 if (compose_redirect_write_to_file(compose, fp) < 0) {
5856 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5861 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5865 g_warning("failed to write queue message\n");
5872 if (fclose(fp) == EOF) {
5873 FILE_OP_ERROR(tmp, "fclose");
5880 if (item && *item) {
5883 queue = account_get_special_folder(compose->account, F_QUEUE);
5886 g_warning("can't find queue folder\n");
5892 folder_item_scan(queue);
5893 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5894 g_warning("can't queue the message\n");
5901 if (msgpath == NULL) {
5907 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5908 compose_remove_reedit_target(compose, FALSE);
5911 if ((msgnum != NULL) && (item != NULL)) {
5919 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5922 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5924 struct stat statbuf;
5925 gchar *type, *subtype;
5926 GtkTreeModel *model;
5929 model = gtk_tree_view_get_model(tree_view);
5931 if (!gtk_tree_model_get_iter_first(model, &iter))
5934 gtk_tree_model_get(model, &iter,
5938 if (!is_file_exist(ainfo->file)) {
5939 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5940 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5941 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5943 if (val == G_ALERTDEFAULT) {
5948 mimepart = procmime_mimeinfo_new();
5949 mimepart->content = MIMECONTENT_FILE;
5950 mimepart->data.filename = g_strdup(ainfo->file);
5951 mimepart->tmp = FALSE; /* or we destroy our attachment */
5952 mimepart->offset = 0;
5954 g_stat(ainfo->file, &statbuf);
5955 mimepart->length = statbuf.st_size;
5957 type = g_strdup(ainfo->content_type);
5959 if (!strchr(type, '/')) {
5961 type = g_strdup("application/octet-stream");
5964 subtype = strchr(type, '/') + 1;
5965 *(subtype - 1) = '\0';
5966 mimepart->type = procmime_get_media_type(type);
5967 mimepart->subtype = g_strdup(subtype);
5970 if (mimepart->type == MIMETYPE_MESSAGE &&
5971 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5972 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5975 if (mimepart->type == MIMETYPE_APPLICATION &&
5976 !strcmp2(mimepart->subtype, "octet-stream"))
5977 g_hash_table_insert(mimepart->typeparameters,
5978 g_strdup("name"), g_strdup(ainfo->name));
5979 g_hash_table_insert(mimepart->dispositionparameters,
5980 g_strdup("filename"), g_strdup(ainfo->name));
5981 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5984 if (mimepart->type == MIMETYPE_TEXT && ainfo->charset)
5985 g_hash_table_insert(mimepart->typeparameters,
5986 g_strdup("charset"), g_strdup(ainfo->charset));
5988 if (mimepart->type == MIMETYPE_MESSAGE
5989 || mimepart->type == MIMETYPE_MULTIPART)
5990 ainfo->encoding = ENC_BINARY;
5991 else if (compose->use_signing) {
5992 if (ainfo->encoding == ENC_7BIT)
5993 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5994 else if (ainfo->encoding == ENC_8BIT)
5995 ainfo->encoding = ENC_BASE64;
6000 procmime_encode_content(mimepart, ainfo->encoding);
6002 g_node_append(parent->node, mimepart->node);
6003 } while (gtk_tree_model_iter_next(model, &iter));
6008 #define IS_IN_CUSTOM_HEADER(header) \
6009 (compose->account->add_customhdr && \
6010 custom_header_find(compose->account->customhdr_list, header) != NULL)
6012 static void compose_add_headerfield_from_headerlist(Compose *compose,
6014 const gchar *fieldname,
6015 const gchar *seperator)
6017 gchar *str, *fieldname_w_colon;
6018 gboolean add_field = FALSE;
6020 ComposeHeaderEntry *headerentry;
6021 const gchar *headerentryname;
6022 const gchar *trans_fieldname;
6025 if (IS_IN_CUSTOM_HEADER(fieldname))
6028 debug_print("Adding %s-fields\n", fieldname);
6030 fieldstr = g_string_sized_new(64);
6032 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6033 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6035 for (list = compose->header_list; list; list = list->next) {
6036 headerentry = ((ComposeHeaderEntry *)list->data);
6037 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6039 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6040 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6042 if (str[0] != '\0') {
6044 g_string_append(fieldstr, seperator);
6045 g_string_append(fieldstr, str);
6054 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6055 compose_convert_header
6056 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6057 strlen(fieldname) + 2, TRUE);
6058 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6062 g_free(fieldname_w_colon);
6063 g_string_free(fieldstr, TRUE);
6068 static gchar *compose_get_header(Compose *compose)
6070 gchar buf[BUFFSIZE];
6071 const gchar *entry_str;
6075 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6077 gchar *from_name = NULL, *from_address = NULL;
6080 cm_return_val_if_fail(compose->account != NULL, NULL);
6081 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6083 header = g_string_sized_new(64);
6086 get_rfc822_date(buf, sizeof(buf));
6087 g_string_append_printf(header, "Date: %s\n", buf);
6091 if (compose->account->name && *compose->account->name) {
6093 QUOTE_IF_REQUIRED(buf, compose->account->name);
6094 tmp = g_strdup_printf("%s <%s>",
6095 buf, compose->account->address);
6097 tmp = g_strdup_printf("%s",
6098 compose->account->address);
6100 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6101 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6103 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6104 from_address = g_strdup(compose->account->address);
6106 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6107 /* extract name and address */
6108 if (strstr(spec, " <") && strstr(spec, ">")) {
6109 from_address = g_strdup(strrchr(spec, '<')+1);
6110 *(strrchr(from_address, '>')) = '\0';
6111 from_name = g_strdup(spec);
6112 *(strrchr(from_name, '<')) = '\0';
6115 from_address = g_strdup(spec);
6122 if (from_name && *from_name) {
6123 compose_convert_header
6124 (compose, buf, sizeof(buf), from_name,
6125 strlen("From: "), TRUE);
6126 QUOTE_IF_REQUIRED(name, buf);
6128 g_string_append_printf(header, "From: %s <%s>\n",
6129 name, from_address);
6131 g_string_append_printf(header, "From: %s\n", from_address);
6134 g_free(from_address);
6137 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6140 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6143 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6147 * If this account is a NNTP account remove Bcc header from
6148 * message body since it otherwise will be publicly shown
6150 if (compose->account->protocol != A_NNTP)
6151 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6154 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6156 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6159 compose_convert_header(compose, buf, sizeof(buf), str,
6160 strlen("Subject: "), FALSE);
6161 g_string_append_printf(header, "Subject: %s\n", buf);
6167 if (compose->account->set_domain && compose->account->domain) {
6168 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6169 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6170 g_snprintf(buf, sizeof(buf), "%s",
6171 strchr(compose->account->address, '@') ?
6172 strchr(compose->account->address, '@')+1 :
6173 compose->account->address);
6175 g_snprintf(buf, sizeof(buf), "%s", "");
6178 if (compose->account->gen_msgid) {
6180 if (compose->account->msgid_with_addr) {
6181 addr = compose->account->address;
6183 generate_msgid(buf, sizeof(buf), addr);
6184 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6185 compose->msgid = g_strdup(buf);
6187 compose->msgid = NULL;
6190 if (compose->remove_references == FALSE) {
6192 if (compose->inreplyto && compose->to_list)
6193 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6196 if (compose->references)
6197 g_string_append_printf(header, "References: %s\n", compose->references);
6201 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6204 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6207 if (compose->account->organization &&
6208 strlen(compose->account->organization) &&
6209 !IS_IN_CUSTOM_HEADER("Organization")) {
6210 compose_convert_header(compose, buf, sizeof(buf),
6211 compose->account->organization,
6212 strlen("Organization: "), FALSE);
6213 g_string_append_printf(header, "Organization: %s\n", buf);
6216 /* Program version and system info */
6217 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6218 !compose->newsgroup_list) {
6219 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6221 gtk_major_version, gtk_minor_version, gtk_micro_version,
6224 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6225 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6227 gtk_major_version, gtk_minor_version, gtk_micro_version,
6231 /* custom headers */
6232 if (compose->account->add_customhdr) {
6235 for (cur = compose->account->customhdr_list; cur != NULL;
6237 CustomHeader *chdr = (CustomHeader *)cur->data;
6239 if (custom_header_is_allowed(chdr->name)) {
6240 compose_convert_header
6241 (compose, buf, sizeof(buf),
6242 chdr->value ? chdr->value : "",
6243 strlen(chdr->name) + 2, FALSE);
6244 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6249 /* Automatic Faces and X-Faces */
6250 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6251 g_string_append_printf(header, "X-Face: %s\n", buf);
6253 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6254 g_string_append_printf(header, "X-Face: %s\n", buf);
6256 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6257 g_string_append_printf(header, "Face: %s\n", buf);
6259 else if (get_default_face (buf, sizeof(buf)) == 0) {
6260 g_string_append_printf(header, "Face: %s\n", buf);
6264 switch (compose->priority) {
6265 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6266 "X-Priority: 1 (Highest)\n");
6268 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6269 "X-Priority: 2 (High)\n");
6271 case PRIORITY_NORMAL: break;
6272 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6273 "X-Priority: 4 (Low)\n");
6275 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6276 "X-Priority: 5 (Lowest)\n");
6278 default: debug_print("compose: priority unknown : %d\n",
6282 /* Request Return Receipt */
6283 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6284 if (compose->return_receipt) {
6285 if (compose->account->name
6286 && *compose->account->name) {
6287 compose_convert_header(compose, buf, sizeof(buf),
6288 compose->account->name,
6289 strlen("Disposition-Notification-To: "),
6291 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6293 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6297 /* get special headers */
6298 for (list = compose->header_list; list; list = list->next) {
6299 ComposeHeaderEntry *headerentry;
6302 gchar *headername_wcolon;
6303 const gchar *headername_trans;
6306 gboolean standard_header = FALSE;
6308 headerentry = ((ComposeHeaderEntry *)list->data);
6310 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6312 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6317 if (!strstr(tmp, ":")) {
6318 headername_wcolon = g_strconcat(tmp, ":", NULL);
6319 headername = g_strdup(tmp);
6321 headername_wcolon = g_strdup(tmp);
6322 headername = g_strdup(strtok(tmp, ":"));
6326 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6327 Xstrdup_a(headervalue, entry_str, return NULL);
6328 subst_char(headervalue, '\r', ' ');
6329 subst_char(headervalue, '\n', ' ');
6330 string = std_headers;
6331 while (*string != NULL) {
6332 headername_trans = prefs_common_translated_header_name(*string);
6333 if (!strcmp(headername_trans, headername_wcolon))
6334 standard_header = TRUE;
6337 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6338 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6341 g_free(headername_wcolon);
6345 g_string_free(header, FALSE);
6350 #undef IS_IN_CUSTOM_HEADER
6352 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6353 gint header_len, gboolean addr_field)
6355 gchar *tmpstr = NULL;
6356 const gchar *out_codeset = NULL;
6358 cm_return_if_fail(src != NULL);
6359 cm_return_if_fail(dest != NULL);
6361 if (len < 1) return;
6363 tmpstr = g_strdup(src);
6365 subst_char(tmpstr, '\n', ' ');
6366 subst_char(tmpstr, '\r', ' ');
6369 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6370 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6371 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6376 codeconv_set_strict(TRUE);
6377 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6378 conv_get_charset_str(compose->out_encoding));
6379 codeconv_set_strict(FALSE);
6381 if (!dest || *dest == '\0') {
6382 gchar *test_conv_global_out = NULL;
6383 gchar *test_conv_reply = NULL;
6385 /* automatic mode. be automatic. */
6386 codeconv_set_strict(TRUE);
6388 out_codeset = conv_get_outgoing_charset_str();
6390 debug_print("trying to convert to %s\n", out_codeset);
6391 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6394 if (!test_conv_global_out && compose->orig_charset
6395 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6396 out_codeset = compose->orig_charset;
6397 debug_print("failure; trying to convert to %s\n", out_codeset);
6398 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6401 if (!test_conv_global_out && !test_conv_reply) {
6403 out_codeset = CS_INTERNAL;
6404 debug_print("finally using %s\n", out_codeset);
6406 g_free(test_conv_global_out);
6407 g_free(test_conv_reply);
6408 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6410 codeconv_set_strict(FALSE);
6415 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6419 cm_return_if_fail(user_data != NULL);
6421 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6422 g_strstrip(address);
6423 if (*address != '\0') {
6424 gchar *name = procheader_get_fromname(address);
6425 extract_address(address);
6426 addressbook_add_contact(name, address, NULL, NULL);
6431 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6433 GtkWidget *menuitem;
6436 cm_return_if_fail(menu != NULL);
6437 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6439 menuitem = gtk_separator_menu_item_new();
6440 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6441 gtk_widget_show(menuitem);
6443 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6444 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6446 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6447 g_strstrip(address);
6448 if (*address == '\0') {
6449 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6452 g_signal_connect(G_OBJECT(menuitem), "activate",
6453 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6454 gtk_widget_show(menuitem);
6457 static void compose_create_header_entry(Compose *compose)
6459 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6466 const gchar *header = NULL;
6467 ComposeHeaderEntry *headerentry;
6468 gboolean standard_header = FALSE;
6469 GtkListStore *model;
6471 #if !(GTK_CHECK_VERSION(2,12,0))
6472 GtkTooltips *tips = compose->tooltips;
6475 headerentry = g_new0(ComposeHeaderEntry, 1);
6478 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6479 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6480 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6482 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6484 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6486 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6487 COMPOSE_NEWSGROUPS);
6488 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6490 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6491 COMPOSE_FOLLOWUPTO);
6493 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6494 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6495 G_CALLBACK(compose_grab_focus_cb), compose);
6496 gtk_widget_show(combo);
6497 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6498 compose->header_nextrow, compose->header_nextrow+1,
6499 GTK_SHRINK, GTK_FILL, 0, 0);
6500 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6501 const gchar *last_header_entry = gtk_entry_get_text(
6502 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6504 while (*string != NULL) {
6505 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6506 standard_header = TRUE;
6509 if (standard_header)
6510 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6512 if (!compose->header_last || !standard_header) {
6513 switch(compose->account->protocol) {
6515 header = prefs_common_translated_header_name("Newsgroups:");
6518 header = prefs_common_translated_header_name("To:");
6523 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6525 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6526 G_CALLBACK(compose_grab_focus_cb), compose);
6528 /* Entry field with cleanup button */
6529 button = gtk_button_new();
6530 gtk_button_set_image(GTK_BUTTON(button),
6531 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6532 gtk_widget_show(button);
6533 CLAWS_SET_TIP(button,
6534 _("Delete entry contents"));
6535 entry = gtk_entry_new();
6536 gtk_widget_show(entry);
6537 CLAWS_SET_TIP(entry,
6538 _("Use <tab> to autocomplete from addressbook"));
6539 hbox = gtk_hbox_new (FALSE, 0);
6540 gtk_widget_show(hbox);
6541 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6542 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6543 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6544 compose->header_nextrow, compose->header_nextrow+1,
6545 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6547 g_signal_connect(G_OBJECT(entry), "key-press-event",
6548 G_CALLBACK(compose_headerentry_key_press_event_cb),
6550 g_signal_connect(G_OBJECT(entry), "changed",
6551 G_CALLBACK(compose_headerentry_changed_cb),
6553 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6554 G_CALLBACK(compose_grab_focus_cb), compose);
6556 g_signal_connect(G_OBJECT(button), "clicked",
6557 G_CALLBACK(compose_headerentry_button_clicked_cb),
6561 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6562 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6563 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6564 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6565 G_CALLBACK(compose_header_drag_received_cb),
6567 g_signal_connect(G_OBJECT(entry), "drag-drop",
6568 G_CALLBACK(compose_drag_drop),
6570 g_signal_connect(G_OBJECT(entry), "populate-popup",
6571 G_CALLBACK(compose_entry_popup_extend),
6574 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6576 headerentry->compose = compose;
6577 headerentry->combo = combo;
6578 headerentry->entry = entry;
6579 headerentry->button = button;
6580 headerentry->hbox = hbox;
6581 headerentry->headernum = compose->header_nextrow;
6582 headerentry->type = PREF_NONE;
6584 compose->header_nextrow++;
6585 compose->header_last = headerentry;
6586 compose->header_list =
6587 g_slist_append(compose->header_list,
6591 static void compose_add_header_entry(Compose *compose, const gchar *header,
6592 gchar *text, ComposePrefType pref_type)
6594 ComposeHeaderEntry *last_header = compose->header_last;
6595 gchar *tmp = g_strdup(text), *email;
6596 gboolean replyto_hdr;
6598 replyto_hdr = (!strcasecmp(header,
6599 prefs_common_translated_header_name("Reply-To:")) ||
6601 prefs_common_translated_header_name("Followup-To:")) ||
6603 prefs_common_translated_header_name("In-Reply-To:")));
6605 extract_address(tmp);
6606 email = g_utf8_strdown(tmp, -1);
6608 if (replyto_hdr == FALSE &&
6609 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6611 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6612 header, text, (gint) pref_type);
6618 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6619 gtk_entry_set_text(GTK_ENTRY(
6620 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6622 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6623 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6624 last_header->type = pref_type;
6626 if (replyto_hdr == FALSE)
6627 g_hash_table_insert(compose->email_hashtable, email,
6628 GUINT_TO_POINTER(1));
6635 static void compose_destroy_headerentry(Compose *compose,
6636 ComposeHeaderEntry *headerentry)
6638 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6641 extract_address(text);
6642 email = g_utf8_strdown(text, -1);
6643 g_hash_table_remove(compose->email_hashtable, email);
6647 gtk_widget_destroy(headerentry->combo);
6648 gtk_widget_destroy(headerentry->entry);
6649 gtk_widget_destroy(headerentry->button);
6650 gtk_widget_destroy(headerentry->hbox);
6651 g_free(headerentry);
6654 static void compose_remove_header_entries(Compose *compose)
6657 for (list = compose->header_list; list; list = list->next)
6658 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6660 compose->header_last = NULL;
6661 g_slist_free(compose->header_list);
6662 compose->header_list = NULL;
6663 compose->header_nextrow = 1;
6664 compose_create_header_entry(compose);
6667 static GtkWidget *compose_create_header(Compose *compose)
6669 GtkWidget *from_optmenu_hbox;
6670 GtkWidget *header_scrolledwin;
6671 GtkWidget *header_table;
6675 /* header labels and entries */
6676 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6677 gtk_widget_show(header_scrolledwin);
6678 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6680 header_table = gtk_table_new(2, 2, FALSE);
6681 gtk_widget_show(header_table);
6682 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6683 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6684 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6687 /* option menu for selecting accounts */
6688 from_optmenu_hbox = compose_account_option_menu_create(compose);
6689 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6690 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6693 compose->header_table = header_table;
6694 compose->header_list = NULL;
6695 compose->header_nextrow = count;
6697 compose_create_header_entry(compose);
6699 compose->table = NULL;
6701 return header_scrolledwin ;
6704 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6706 Compose *compose = (Compose *)data;
6707 GdkEventButton event;
6710 event.time = gtk_get_current_event_time();
6712 return attach_button_pressed(compose->attach_clist, &event, compose);
6715 static GtkWidget *compose_create_attach(Compose *compose)
6717 GtkWidget *attach_scrwin;
6718 GtkWidget *attach_clist;
6720 GtkListStore *store;
6721 GtkCellRenderer *renderer;
6722 GtkTreeViewColumn *column;
6723 GtkTreeSelection *selection;
6725 /* attachment list */
6726 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6727 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6728 GTK_POLICY_AUTOMATIC,
6729 GTK_POLICY_AUTOMATIC);
6730 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6732 store = gtk_list_store_new(N_ATTACH_COLS,
6738 G_TYPE_AUTO_POINTER,
6740 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6741 (GTK_TREE_MODEL(store)));
6742 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6743 g_object_unref(store);
6745 renderer = gtk_cell_renderer_text_new();
6746 column = gtk_tree_view_column_new_with_attributes
6747 (_("Mime type"), renderer, "text",
6748 COL_MIMETYPE, NULL);
6749 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6751 renderer = gtk_cell_renderer_text_new();
6752 column = gtk_tree_view_column_new_with_attributes
6753 (_("Size"), renderer, "text",
6755 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6757 renderer = gtk_cell_renderer_text_new();
6758 column = gtk_tree_view_column_new_with_attributes
6759 (_("Name"), renderer, "text",
6761 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6763 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6764 prefs_common.use_stripes_everywhere);
6765 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6766 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6768 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6769 G_CALLBACK(attach_selected), compose);
6770 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6771 G_CALLBACK(attach_button_pressed), compose);
6773 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6774 G_CALLBACK(popup_attach_button_pressed), compose);
6776 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6777 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6778 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6779 G_CALLBACK(popup_attach_button_pressed), compose);
6781 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6782 G_CALLBACK(attach_key_pressed), compose);
6785 gtk_drag_dest_set(attach_clist,
6786 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6787 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6788 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6789 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6790 G_CALLBACK(compose_attach_drag_received_cb),
6792 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6793 G_CALLBACK(compose_drag_drop),
6796 compose->attach_scrwin = attach_scrwin;
6797 compose->attach_clist = attach_clist;
6799 return attach_scrwin;
6802 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6803 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6805 static GtkWidget *compose_create_others(Compose *compose)
6808 GtkWidget *savemsg_checkbtn;
6809 GtkWidget *savemsg_combo;
6810 GtkWidget *savemsg_select;
6813 gchar *folderidentifier;
6815 /* Table for settings */
6816 table = gtk_table_new(3, 1, FALSE);
6817 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6818 gtk_widget_show(table);
6819 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6822 /* Save Message to folder */
6823 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6824 gtk_widget_show(savemsg_checkbtn);
6825 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6826 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6827 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6829 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6830 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6832 savemsg_combo = gtk_combo_box_entry_new_text();
6833 compose->savemsg_checkbtn = savemsg_checkbtn;
6834 compose->savemsg_combo = savemsg_combo;
6835 gtk_widget_show(savemsg_combo);
6837 if (prefs_common.compose_save_to_history)
6838 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6839 prefs_common.compose_save_to_history);
6841 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6842 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6843 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6844 G_CALLBACK(compose_grab_focus_cb), compose);
6845 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6846 folderidentifier = folder_item_get_identifier(account_get_special_folder
6847 (compose->account, F_OUTBOX));
6848 compose_set_save_to(compose, folderidentifier);
6849 g_free(folderidentifier);
6852 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6853 gtk_widget_show(savemsg_select);
6854 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6855 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6856 G_CALLBACK(compose_savemsg_select_cb),
6864 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6866 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6867 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6870 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6875 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6878 path = folder_item_get_identifier(dest);
6880 compose_set_save_to(compose, path);
6884 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6885 GdkAtom clip, GtkTextIter *insert_place);
6888 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6892 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6894 if (event->button == 3) {
6896 GtkTextIter sel_start, sel_end;
6897 gboolean stuff_selected;
6899 /* move the cursor to allow GtkAspell to check the word
6900 * under the mouse */
6901 if (event->x && event->y) {
6902 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6903 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6905 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6908 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6909 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6912 stuff_selected = gtk_text_buffer_get_selection_bounds(
6914 &sel_start, &sel_end);
6916 gtk_text_buffer_place_cursor (buffer, &iter);
6917 /* reselect stuff */
6919 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6920 gtk_text_buffer_select_range(buffer,
6921 &sel_start, &sel_end);
6923 return FALSE; /* pass the event so that the right-click goes through */
6926 if (event->button == 2) {
6931 /* get the middle-click position to paste at the correct place */
6932 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6933 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6935 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6938 entry_paste_clipboard(compose, text,
6939 prefs_common.linewrap_pastes,
6940 GDK_SELECTION_PRIMARY, &iter);
6948 static void compose_spell_menu_changed(void *data)
6950 Compose *compose = (Compose *)data;
6952 GtkWidget *menuitem;
6953 GtkWidget *parent_item;
6954 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6957 if (compose->gtkaspell == NULL)
6960 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6961 "/Menu/Spelling/Options");
6963 /* setting the submenu removes /Spelling/Options from the factory
6964 * so we need to save it */
6966 if (parent_item == NULL) {
6967 parent_item = compose->aspell_options_menu;
6968 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6970 compose->aspell_options_menu = parent_item;
6972 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6974 spell_menu = g_slist_reverse(spell_menu);
6975 for (items = spell_menu;
6976 items; items = items->next) {
6977 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6978 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6979 gtk_widget_show(GTK_WIDGET(menuitem));
6981 g_slist_free(spell_menu);
6983 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6984 gtk_widget_show(parent_item);
6987 static void compose_dict_changed(void *data)
6989 Compose *compose = (Compose *) data;
6991 if(compose->gtkaspell &&
6992 compose->gtkaspell->recheck_when_changing_dict == FALSE)
6995 gtkaspell_highlight_all(compose->gtkaspell);
6996 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7000 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7002 Compose *compose = (Compose *)data;
7003 GdkEventButton event;
7006 event.time = gtk_get_current_event_time();
7010 return text_clicked(compose->text, &event, compose);
7013 static gboolean compose_force_window_origin = TRUE;
7014 static Compose *compose_create(PrefsAccount *account,
7023 GtkWidget *handlebox;
7025 GtkWidget *notebook;
7027 GtkWidget *attach_hbox;
7028 GtkWidget *attach_lab1;
7029 GtkWidget *attach_lab2;
7034 GtkWidget *subject_hbox;
7035 GtkWidget *subject_frame;
7036 GtkWidget *subject_entry;
7040 GtkWidget *edit_vbox;
7041 GtkWidget *ruler_hbox;
7043 GtkWidget *scrolledwin;
7045 GtkTextBuffer *buffer;
7046 GtkClipboard *clipboard;
7049 UndoMain *undostruct;
7051 gchar *titles[N_ATTACH_COLS];
7052 GtkWidget *popupmenu;
7053 GtkWidget *tmpl_menu;
7054 GtkActionGroup *action_group = NULL;
7057 GtkAspell * gtkaspell = NULL;
7060 static GdkGeometry geometry;
7062 cm_return_val_if_fail(account != NULL, NULL);
7064 debug_print("Creating compose window...\n");
7065 compose = g_new0(Compose, 1);
7067 titles[COL_MIMETYPE] = _("MIME type");
7068 titles[COL_SIZE] = _("Size");
7069 titles[COL_NAME] = _("Name");
7070 titles[COL_CHARSET] = _("Charset");
7072 compose->batch = batch;
7073 compose->account = account;
7074 compose->folder = folder;
7076 compose->mutex = g_mutex_new();
7077 compose->set_cursor_pos = -1;
7079 #if !(GTK_CHECK_VERSION(2,12,0))
7080 compose->tooltips = tips;
7083 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7085 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7086 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7088 if (!geometry.max_width) {
7089 geometry.max_width = gdk_screen_width();
7090 geometry.max_height = gdk_screen_height();
7093 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7094 &geometry, GDK_HINT_MAX_SIZE);
7095 if (!geometry.min_width) {
7096 geometry.min_width = 600;
7097 geometry.min_height = 440;
7099 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7100 &geometry, GDK_HINT_MIN_SIZE);
7102 #ifndef GENERIC_UMPC
7103 if (compose_force_window_origin)
7104 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7105 prefs_common.compose_y);
7107 g_signal_connect(G_OBJECT(window), "delete_event",
7108 G_CALLBACK(compose_delete_cb), compose);
7109 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7110 gtk_widget_realize(window);
7112 gtkut_widget_set_composer_icon(window);
7114 vbox = gtk_vbox_new(FALSE, 0);
7115 gtk_container_add(GTK_CONTAINER(window), vbox);
7117 compose->ui_manager = gtk_ui_manager_new();
7118 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7119 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7120 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7121 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7122 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7123 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7124 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7125 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7126 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7127 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7146 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7235 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7247 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)
7248 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)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7254 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)
7255 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)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7260 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)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7264 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)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7270 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)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7276 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)
7277 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)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7290 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)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7308 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7309 gtk_widget_show_all(menubar);
7311 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7313 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7315 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7318 if (prefs_common.toolbar_detachable) {
7319 handlebox = gtk_handle_box_new();
7321 handlebox = gtk_hbox_new(FALSE, 0);
7323 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7325 gtk_widget_realize(handlebox);
7327 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7330 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7334 vbox2 = gtk_vbox_new(FALSE, 2);
7335 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7336 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7339 notebook = gtk_notebook_new();
7340 gtk_widget_set_size_request(notebook, -1, 130);
7341 gtk_widget_show(notebook);
7343 /* header labels and entries */
7344 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7345 compose_create_header(compose),
7346 gtk_label_new_with_mnemonic(_("Hea_der")));
7347 /* attachment list */
7348 attach_hbox = gtk_hbox_new(FALSE, 0);
7349 gtk_widget_show(attach_hbox);
7351 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7352 gtk_widget_show(attach_lab1);
7353 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7355 attach_lab2 = gtk_label_new("");
7356 gtk_widget_show(attach_lab2);
7357 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7359 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7360 compose_create_attach(compose),
7363 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7364 compose_create_others(compose),
7365 gtk_label_new_with_mnemonic(_("Othe_rs")));
7368 subject_hbox = gtk_hbox_new(FALSE, 0);
7369 gtk_widget_show(subject_hbox);
7371 subject_frame = gtk_frame_new(NULL);
7372 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7373 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7374 gtk_widget_show(subject_frame);
7376 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7377 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7378 gtk_widget_show(subject);
7380 label = gtk_label_new(_("Subject:"));
7381 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7382 gtk_widget_show(label);
7385 subject_entry = claws_spell_entry_new();
7387 subject_entry = gtk_entry_new();
7389 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7390 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7391 G_CALLBACK(compose_grab_focus_cb), compose);
7392 gtk_widget_show(subject_entry);
7393 compose->subject_entry = subject_entry;
7394 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7396 edit_vbox = gtk_vbox_new(FALSE, 0);
7398 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7401 ruler_hbox = gtk_hbox_new(FALSE, 0);
7402 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7404 ruler = gtk_shruler_new();
7405 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7406 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7410 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7411 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7412 GTK_POLICY_AUTOMATIC,
7413 GTK_POLICY_AUTOMATIC);
7414 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7416 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7417 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7419 text = gtk_text_view_new();
7420 if (prefs_common.show_compose_margin) {
7421 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7422 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7424 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7425 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7426 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7427 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7428 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7430 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7432 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7433 G_CALLBACK(compose_edit_size_alloc),
7435 g_signal_connect(G_OBJECT(buffer), "changed",
7436 G_CALLBACK(compose_changed_cb), compose);
7437 g_signal_connect(G_OBJECT(text), "grab_focus",
7438 G_CALLBACK(compose_grab_focus_cb), compose);
7439 g_signal_connect(G_OBJECT(buffer), "insert_text",
7440 G_CALLBACK(text_inserted), compose);
7441 g_signal_connect(G_OBJECT(text), "button_press_event",
7442 G_CALLBACK(text_clicked), compose);
7444 g_signal_connect(G_OBJECT(text), "popup-menu",
7445 G_CALLBACK(compose_popup_menu), compose);
7447 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7448 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7449 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7450 G_CALLBACK(compose_popup_menu), compose);
7452 g_signal_connect(G_OBJECT(subject_entry), "changed",
7453 G_CALLBACK(compose_changed_cb), compose);
7456 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7457 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7458 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7459 g_signal_connect(G_OBJECT(text), "drag_data_received",
7460 G_CALLBACK(compose_insert_drag_received_cb),
7462 g_signal_connect(G_OBJECT(text), "drag-drop",
7463 G_CALLBACK(compose_drag_drop),
7465 gtk_widget_show_all(vbox);
7467 /* pane between attach clist and text */
7468 paned = gtk_vpaned_new();
7469 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7471 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7472 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7474 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7476 gtk_paned_add1(GTK_PANED(paned), notebook);
7477 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7478 gtk_widget_show_all(paned);
7481 if (prefs_common.textfont) {
7482 PangoFontDescription *font_desc;
7484 font_desc = pango_font_description_from_string
7485 (prefs_common.textfont);
7487 gtk_widget_modify_font(text, font_desc);
7488 pango_font_description_free(font_desc);
7492 gtk_action_group_add_actions(action_group, compose_popup_entries,
7493 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7494 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7495 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7496 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7497 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7498 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7499 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7501 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7504 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7505 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7507 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7509 undostruct = undo_init(text);
7510 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7513 address_completion_start(window);
7515 compose->window = window;
7516 compose->vbox = vbox;
7517 compose->menubar = menubar;
7518 compose->handlebox = handlebox;
7520 compose->vbox2 = vbox2;
7522 compose->paned = paned;
7524 compose->attach_label = attach_lab2;
7526 compose->notebook = notebook;
7527 compose->edit_vbox = edit_vbox;
7528 compose->ruler_hbox = ruler_hbox;
7529 compose->ruler = ruler;
7530 compose->scrolledwin = scrolledwin;
7531 compose->text = text;
7533 compose->focused_editable = NULL;
7535 compose->popupmenu = popupmenu;
7537 compose->tmpl_menu = tmpl_menu;
7539 compose->mode = mode;
7540 compose->rmode = mode;
7542 compose->targetinfo = NULL;
7543 compose->replyinfo = NULL;
7544 compose->fwdinfo = NULL;
7546 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7547 g_str_equal, (GDestroyNotify) g_free, NULL);
7549 compose->replyto = NULL;
7551 compose->bcc = NULL;
7552 compose->followup_to = NULL;
7554 compose->ml_post = NULL;
7556 compose->inreplyto = NULL;
7557 compose->references = NULL;
7558 compose->msgid = NULL;
7559 compose->boundary = NULL;
7561 compose->autowrap = prefs_common.autowrap;
7562 compose->autoindent = prefs_common.auto_indent;
7563 compose->use_signing = FALSE;
7564 compose->use_encryption = FALSE;
7565 compose->privacy_system = NULL;
7567 compose->modified = FALSE;
7569 compose->return_receipt = FALSE;
7571 compose->to_list = NULL;
7572 compose->newsgroup_list = NULL;
7574 compose->undostruct = undostruct;
7576 compose->sig_str = NULL;
7578 compose->exteditor_file = NULL;
7579 compose->exteditor_pid = -1;
7580 compose->exteditor_tag = -1;
7581 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7585 if (mode != COMPOSE_REDIRECT) {
7586 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7587 strcmp(prefs_common.dictionary, "")) {
7588 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7589 prefs_common.alt_dictionary,
7590 conv_get_locale_charset_str(),
7591 prefs_common.misspelled_col,
7592 prefs_common.check_while_typing,
7593 prefs_common.recheck_when_changing_dict,
7594 prefs_common.use_alternate,
7595 prefs_common.use_both_dicts,
7596 GTK_TEXT_VIEW(text),
7597 GTK_WINDOW(compose->window),
7598 compose_dict_changed,
7599 compose_spell_menu_changed,
7602 alertpanel_error(_("Spell checker could not "
7604 gtkaspell_checkers_strerror());
7605 gtkaspell_checkers_reset_error();
7607 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7611 compose->gtkaspell = gtkaspell;
7612 compose_spell_menu_changed(compose);
7613 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7616 compose_select_account(compose, account, TRUE);
7618 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7619 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7621 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7622 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7624 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7625 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7627 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7628 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7630 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7631 if (account->protocol != A_NNTP)
7632 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7633 prefs_common_translated_header_name("To:"));
7635 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7636 prefs_common_translated_header_name("Newsgroups:"));
7638 addressbook_set_target_compose(compose);
7640 if (mode != COMPOSE_REDIRECT)
7641 compose_set_template_menu(compose);
7643 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7646 compose_list = g_list_append(compose_list, compose);
7648 if (!prefs_common.show_ruler)
7649 gtk_widget_hide(ruler_hbox);
7651 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7654 compose->priority = PRIORITY_NORMAL;
7655 compose_update_priority_menu_item(compose);
7657 compose_set_out_encoding(compose);
7660 compose_update_actions_menu(compose);
7662 /* Privacy Systems menu */
7663 compose_update_privacy_systems_menu(compose);
7665 activate_privacy_system(compose, account, TRUE);
7666 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7668 gtk_widget_realize(window);
7670 gtk_widget_show(window);
7672 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7673 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7680 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7685 GtkWidget *optmenubox;
7688 GtkWidget *from_name = NULL;
7689 #if !(GTK_CHECK_VERSION(2,12,0))
7690 GtkTooltips *tips = compose->tooltips;
7693 gint num = 0, def_menu = 0;
7695 accounts = account_get_list();
7696 cm_return_val_if_fail(accounts != NULL, NULL);
7698 optmenubox = gtk_event_box_new();
7699 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7700 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7702 hbox = gtk_hbox_new(FALSE, 6);
7703 from_name = gtk_entry_new();
7705 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7706 G_CALLBACK(compose_grab_focus_cb), compose);
7708 for (; accounts != NULL; accounts = accounts->next, num++) {
7709 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7710 gchar *name, *from = NULL;
7712 if (ac == compose->account) def_menu = num;
7714 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7717 if (ac == compose->account) {
7718 if (ac->name && *ac->name) {
7720 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7721 from = g_strdup_printf("%s <%s>",
7723 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7725 from = g_strdup_printf("%s",
7727 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7730 COMBOBOX_ADD(menu, name, ac->account_id);
7735 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7737 g_signal_connect(G_OBJECT(optmenu), "changed",
7738 G_CALLBACK(account_activated),
7740 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7741 G_CALLBACK(compose_entry_popup_extend),
7744 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7745 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7747 CLAWS_SET_TIP(optmenubox,
7748 _("Account to use for this email"));
7749 CLAWS_SET_TIP(from_name,
7750 _("Sender address to be used"));
7752 compose->account_combo = optmenu;
7753 compose->from_name = from_name;
7758 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7760 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7761 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7762 Compose *compose = (Compose *) data;
7764 compose->priority = value;
7768 static void compose_reply_change_mode(Compose *compose,
7771 gboolean was_modified = compose->modified;
7773 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7775 cm_return_if_fail(compose->replyinfo != NULL);
7777 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7779 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7781 if (action == COMPOSE_REPLY_TO_ALL)
7783 if (action == COMPOSE_REPLY_TO_SENDER)
7785 if (action == COMPOSE_REPLY_TO_LIST)
7788 compose_remove_header_entries(compose);
7789 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7790 if (compose->account->set_autocc && compose->account->auto_cc)
7791 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7793 if (compose->account->set_autobcc && compose->account->auto_bcc)
7794 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7796 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7797 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7798 compose_show_first_last_header(compose, TRUE);
7799 compose->modified = was_modified;
7800 compose_set_title(compose);
7803 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7805 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7806 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7807 Compose *compose = (Compose *) data;
7810 compose_reply_change_mode(compose, value);
7813 static void compose_update_priority_menu_item(Compose * compose)
7815 GtkWidget *menuitem = NULL;
7816 switch (compose->priority) {
7817 case PRIORITY_HIGHEST:
7818 menuitem = gtk_ui_manager_get_widget
7819 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7822 menuitem = gtk_ui_manager_get_widget
7823 (compose->ui_manager, "/Menu/Options/Priority/High");
7825 case PRIORITY_NORMAL:
7826 menuitem = gtk_ui_manager_get_widget
7827 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7830 menuitem = gtk_ui_manager_get_widget
7831 (compose->ui_manager, "/Menu/Options/Priority/Low");
7833 case PRIORITY_LOWEST:
7834 menuitem = gtk_ui_manager_get_widget
7835 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7838 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7841 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7843 Compose *compose = (Compose *) data;
7845 gboolean can_sign = FALSE, can_encrypt = FALSE;
7847 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7849 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7852 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7853 g_free(compose->privacy_system);
7854 compose->privacy_system = NULL;
7855 if (systemid != NULL) {
7856 compose->privacy_system = g_strdup(systemid);
7858 can_sign = privacy_system_can_sign(systemid);
7859 can_encrypt = privacy_system_can_encrypt(systemid);
7862 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7864 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7865 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7868 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7870 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7871 GtkWidget *menuitem = NULL;
7873 gboolean can_sign = FALSE, can_encrypt = FALSE;
7874 gboolean found = FALSE;
7876 if (compose->privacy_system != NULL) {
7878 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7879 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7880 cm_return_if_fail(menuitem != NULL);
7882 amenu = GTK_MENU_SHELL(menuitem)->children;
7884 while (amenu != NULL) {
7885 GList *alist = amenu->next;
7887 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7888 if (systemid != NULL) {
7889 if (strcmp(systemid, compose->privacy_system) == 0 &&
7890 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7891 menuitem = GTK_WIDGET(amenu->data);
7893 can_sign = privacy_system_can_sign(systemid);
7894 can_encrypt = privacy_system_can_encrypt(systemid);
7898 } else if (strlen(compose->privacy_system) == 0 &&
7899 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7900 menuitem = GTK_WIDGET(amenu->data);
7903 can_encrypt = FALSE;
7910 if (menuitem != NULL)
7911 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7913 if (warn && !found && strlen(compose->privacy_system)) {
7914 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7915 "will not be able to sign or encrypt this message."),
7916 compose->privacy_system);
7920 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7921 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7924 static void compose_set_out_encoding(Compose *compose)
7926 CharSet out_encoding;
7927 const gchar *branch = NULL;
7928 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7930 switch(out_encoding) {
7931 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7932 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7933 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7934 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7935 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7936 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7937 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7938 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7939 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7940 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7941 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7942 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7943 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7944 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7945 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7946 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7947 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7948 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7949 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7950 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7951 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7952 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7953 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7954 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7955 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7956 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7957 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7958 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7959 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7960 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7961 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7962 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7963 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7965 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7968 static void compose_set_template_menu(Compose *compose)
7970 GSList *tmpl_list, *cur;
7974 tmpl_list = template_get_config();
7976 menu = gtk_menu_new();
7978 gtk_menu_set_accel_group (GTK_MENU (menu),
7979 gtk_ui_manager_get_accel_group(compose->ui_manager));
7980 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7981 Template *tmpl = (Template *)cur->data;
7982 gchar *accel_path = NULL;
7983 item = gtk_menu_item_new_with_label(tmpl->name);
7984 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7985 g_signal_connect(G_OBJECT(item), "activate",
7986 G_CALLBACK(compose_template_activate_cb),
7988 g_object_set_data(G_OBJECT(item), "template", tmpl);
7989 gtk_widget_show(item);
7990 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7991 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7995 gtk_widget_show(menu);
7996 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7999 void compose_update_actions_menu(Compose *compose)
8001 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8004 static void compose_update_privacy_systems_menu(Compose *compose)
8006 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8007 GSList *systems, *cur;
8009 GtkWidget *system_none;
8011 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8012 GtkWidget *privacy_menu = gtk_menu_new();
8014 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8015 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8017 g_signal_connect(G_OBJECT(system_none), "activate",
8018 G_CALLBACK(compose_set_privacy_system_cb), compose);
8020 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8021 gtk_widget_show(system_none);
8023 systems = privacy_get_system_ids();
8024 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8025 gchar *systemid = cur->data;
8027 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8028 widget = gtk_radio_menu_item_new_with_label(group,
8029 privacy_system_get_name(systemid));
8030 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8031 g_strdup(systemid), g_free);
8032 g_signal_connect(G_OBJECT(widget), "activate",
8033 G_CALLBACK(compose_set_privacy_system_cb), compose);
8035 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8036 gtk_widget_show(widget);
8039 g_slist_free(systems);
8040 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8041 gtk_widget_show_all(privacy_menu);
8042 gtk_widget_show_all(privacy_menuitem);
8045 void compose_reflect_prefs_all(void)
8050 for (cur = compose_list; cur != NULL; cur = cur->next) {
8051 compose = (Compose *)cur->data;
8052 compose_set_template_menu(compose);
8056 void compose_reflect_prefs_pixmap_theme(void)
8061 for (cur = compose_list; cur != NULL; cur = cur->next) {
8062 compose = (Compose *)cur->data;
8063 toolbar_update(TOOLBAR_COMPOSE, compose);
8067 static const gchar *compose_quote_char_from_context(Compose *compose)
8069 const gchar *qmark = NULL;
8071 cm_return_val_if_fail(compose != NULL, NULL);
8073 switch (compose->mode) {
8074 /* use forward-specific quote char */
8075 case COMPOSE_FORWARD:
8076 case COMPOSE_FORWARD_AS_ATTACH:
8077 case COMPOSE_FORWARD_INLINE:
8078 if (compose->folder && compose->folder->prefs &&
8079 compose->folder->prefs->forward_with_format)
8080 qmark = compose->folder->prefs->forward_quotemark;
8081 else if (compose->account->forward_with_format)
8082 qmark = compose->account->forward_quotemark;
8084 qmark = prefs_common.fw_quotemark;
8087 /* use reply-specific quote char in all other modes */
8089 if (compose->folder && compose->folder->prefs &&
8090 compose->folder->prefs->reply_with_format)
8091 qmark = compose->folder->prefs->reply_quotemark;
8092 else if (compose->account->reply_with_format)
8093 qmark = compose->account->reply_quotemark;
8095 qmark = prefs_common.quotemark;
8099 if (qmark == NULL || *qmark == '\0')
8105 static void compose_template_apply(Compose *compose, Template *tmpl,
8109 GtkTextBuffer *buffer;
8113 gchar *parsed_str = NULL;
8114 gint cursor_pos = 0;
8115 const gchar *err_msg = _("The body of the template has an error at line %d.");
8118 /* process the body */
8120 text = GTK_TEXT_VIEW(compose->text);
8121 buffer = gtk_text_view_get_buffer(text);
8124 qmark = compose_quote_char_from_context(compose);
8126 if (compose->replyinfo != NULL) {
8129 gtk_text_buffer_set_text(buffer, "", -1);
8130 mark = gtk_text_buffer_get_insert(buffer);
8131 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8133 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8134 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8136 } else if (compose->fwdinfo != NULL) {
8139 gtk_text_buffer_set_text(buffer, "", -1);
8140 mark = gtk_text_buffer_get_insert(buffer);
8141 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8143 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8144 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8147 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8149 GtkTextIter start, end;
8152 gtk_text_buffer_get_start_iter(buffer, &start);
8153 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8154 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8156 /* clear the buffer now */
8158 gtk_text_buffer_set_text(buffer, "", -1);
8160 parsed_str = compose_quote_fmt(compose, dummyinfo,
8161 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8162 procmsg_msginfo_free( dummyinfo );
8168 gtk_text_buffer_set_text(buffer, "", -1);
8169 mark = gtk_text_buffer_get_insert(buffer);
8170 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8173 if (replace && parsed_str && compose->account->auto_sig)
8174 compose_insert_sig(compose, FALSE);
8176 if (replace && parsed_str) {
8177 gtk_text_buffer_get_start_iter(buffer, &iter);
8178 gtk_text_buffer_place_cursor(buffer, &iter);
8182 cursor_pos = quote_fmt_get_cursor_pos();
8183 compose->set_cursor_pos = cursor_pos;
8184 if (cursor_pos == -1)
8186 gtk_text_buffer_get_start_iter(buffer, &iter);
8187 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8188 gtk_text_buffer_place_cursor(buffer, &iter);
8191 /* process the other fields */
8193 compose_template_apply_fields(compose, tmpl);
8194 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8195 quote_fmt_reset_vartable();
8196 compose_changed_cb(NULL, compose);
8199 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8200 gtkaspell_highlight_all(compose->gtkaspell);
8204 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8206 MsgInfo* dummyinfo = NULL;
8207 MsgInfo *msginfo = NULL;
8210 if (compose->replyinfo != NULL)
8211 msginfo = compose->replyinfo;
8212 else if (compose->fwdinfo != NULL)
8213 msginfo = compose->fwdinfo;
8215 dummyinfo = compose_msginfo_new_from_compose(compose);
8216 msginfo = dummyinfo;
8219 if (tmpl->from && *tmpl->from != '\0') {
8221 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8222 compose->gtkaspell);
8224 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8226 quote_fmt_scan_string(tmpl->from);
8229 buf = quote_fmt_get_buffer();
8231 alertpanel_error(_("Template From format error."));
8233 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8237 if (tmpl->to && *tmpl->to != '\0') {
8239 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8240 compose->gtkaspell);
8242 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8244 quote_fmt_scan_string(tmpl->to);
8247 buf = quote_fmt_get_buffer();
8249 alertpanel_error(_("Template To format error."));
8251 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8255 if (tmpl->cc && *tmpl->cc != '\0') {
8257 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8258 compose->gtkaspell);
8260 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8262 quote_fmt_scan_string(tmpl->cc);
8265 buf = quote_fmt_get_buffer();
8267 alertpanel_error(_("Template Cc format error."));
8269 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8273 if (tmpl->bcc && *tmpl->bcc != '\0') {
8275 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8276 compose->gtkaspell);
8278 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8280 quote_fmt_scan_string(tmpl->bcc);
8283 buf = quote_fmt_get_buffer();
8285 alertpanel_error(_("Template Bcc format error."));
8287 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8291 /* process the subject */
8292 if (tmpl->subject && *tmpl->subject != '\0') {
8294 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8295 compose->gtkaspell);
8297 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8299 quote_fmt_scan_string(tmpl->subject);
8302 buf = quote_fmt_get_buffer();
8304 alertpanel_error(_("Template subject format error."));
8306 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8310 procmsg_msginfo_free( dummyinfo );
8313 static void compose_destroy(Compose *compose)
8315 GtkTextBuffer *buffer;
8316 GtkClipboard *clipboard;
8318 compose_list = g_list_remove(compose_list, compose);
8320 if (compose->updating) {
8321 debug_print("danger, not destroying anything now\n");
8322 compose->deferred_destroy = TRUE;
8325 /* NOTE: address_completion_end() does nothing with the window
8326 * however this may change. */
8327 address_completion_end(compose->window);
8329 slist_free_strings(compose->to_list);
8330 g_slist_free(compose->to_list);
8331 slist_free_strings(compose->newsgroup_list);
8332 g_slist_free(compose->newsgroup_list);
8333 slist_free_strings(compose->header_list);
8334 g_slist_free(compose->header_list);
8336 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8338 g_hash_table_destroy(compose->email_hashtable);
8340 procmsg_msginfo_free(compose->targetinfo);
8341 procmsg_msginfo_free(compose->replyinfo);
8342 procmsg_msginfo_free(compose->fwdinfo);
8344 g_free(compose->replyto);
8345 g_free(compose->cc);
8346 g_free(compose->bcc);
8347 g_free(compose->newsgroups);
8348 g_free(compose->followup_to);
8350 g_free(compose->ml_post);
8352 g_free(compose->inreplyto);
8353 g_free(compose->references);
8354 g_free(compose->msgid);
8355 g_free(compose->boundary);
8357 g_free(compose->redirect_filename);
8358 if (compose->undostruct)
8359 undo_destroy(compose->undostruct);
8361 g_free(compose->sig_str);
8363 g_free(compose->exteditor_file);
8365 g_free(compose->orig_charset);
8367 g_free(compose->privacy_system);
8369 if (addressbook_get_target_compose() == compose)
8370 addressbook_set_target_compose(NULL);
8373 if (compose->gtkaspell) {
8374 gtkaspell_delete(compose->gtkaspell);
8375 compose->gtkaspell = NULL;
8379 if (!compose->batch) {
8380 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8381 prefs_common.compose_height = compose->window->allocation.height;
8384 if (!gtk_widget_get_parent(compose->paned))
8385 gtk_widget_destroy(compose->paned);
8386 gtk_widget_destroy(compose->popupmenu);
8388 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8389 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8390 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8392 gtk_widget_destroy(compose->window);
8393 toolbar_destroy(compose->toolbar);
8394 g_free(compose->toolbar);
8395 g_mutex_free(compose->mutex);
8399 static void compose_attach_info_free(AttachInfo *ainfo)
8401 g_free(ainfo->file);
8402 g_free(ainfo->content_type);
8403 g_free(ainfo->name);
8404 g_free(ainfo->charset);
8408 static void compose_attach_update_label(Compose *compose)
8413 GtkTreeModel *model;
8418 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8419 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8420 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8424 while(gtk_tree_model_iter_next(model, &iter))
8427 text = g_strdup_printf("(%d)", i);
8428 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8432 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8434 Compose *compose = (Compose *)data;
8435 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8436 GtkTreeSelection *selection;
8438 GtkTreeModel *model;
8440 selection = gtk_tree_view_get_selection(tree_view);
8441 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8446 for (cur = sel; cur != NULL; cur = cur->next) {
8447 GtkTreePath *path = cur->data;
8448 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8451 gtk_tree_path_free(path);
8454 for (cur = sel; cur != NULL; cur = cur->next) {
8455 GtkTreeRowReference *ref = cur->data;
8456 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8459 if (gtk_tree_model_get_iter(model, &iter, path))
8460 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8462 gtk_tree_path_free(path);
8463 gtk_tree_row_reference_free(ref);
8467 compose_attach_update_label(compose);
8470 static struct _AttachProperty
8473 GtkWidget *mimetype_entry;
8474 GtkWidget *encoding_optmenu;
8475 GtkWidget *path_entry;
8476 GtkWidget *filename_entry;
8478 GtkWidget *cancel_btn;
8481 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8483 gtk_tree_path_free((GtkTreePath *)ptr);
8486 static void compose_attach_property(GtkAction *action, gpointer data)
8488 Compose *compose = (Compose *)data;
8489 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8491 GtkComboBox *optmenu;
8492 GtkTreeSelection *selection;
8494 GtkTreeModel *model;
8497 static gboolean cancelled;
8499 /* only if one selected */
8500 selection = gtk_tree_view_get_selection(tree_view);
8501 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8504 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8508 path = (GtkTreePath *) sel->data;
8509 gtk_tree_model_get_iter(model, &iter, path);
8510 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8513 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8519 if (!attach_prop.window)
8520 compose_attach_property_create(&cancelled);
8521 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8522 gtk_widget_grab_focus(attach_prop.ok_btn);
8523 gtk_widget_show(attach_prop.window);
8524 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8526 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8527 if (ainfo->encoding == ENC_UNKNOWN)
8528 combobox_select_by_data(optmenu, ENC_BASE64);
8530 combobox_select_by_data(optmenu, ainfo->encoding);
8532 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8533 ainfo->content_type ? ainfo->content_type : "");
8534 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8535 ainfo->file ? ainfo->file : "");
8536 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8537 ainfo->name ? ainfo->name : "");
8540 const gchar *entry_text;
8542 gchar *cnttype = NULL;
8549 gtk_widget_hide(attach_prop.window);
8550 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8555 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8556 if (*entry_text != '\0') {
8559 text = g_strstrip(g_strdup(entry_text));
8560 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8561 cnttype = g_strdup(text);
8564 alertpanel_error(_("Invalid MIME type."));
8570 ainfo->encoding = combobox_get_active_data(optmenu);
8572 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8573 if (*entry_text != '\0') {
8574 if (is_file_exist(entry_text) &&
8575 (size = get_file_size(entry_text)) > 0)
8576 file = g_strdup(entry_text);
8579 (_("File doesn't exist or is empty."));
8585 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8586 if (*entry_text != '\0') {
8587 g_free(ainfo->name);
8588 ainfo->name = g_strdup(entry_text);
8592 g_free(ainfo->content_type);
8593 ainfo->content_type = cnttype;
8596 g_free(ainfo->file);
8600 ainfo->size = (goffset)size;
8602 /* update tree store */
8603 text = to_human_readable(ainfo->size);
8604 gtk_tree_model_get_iter(model, &iter, path);
8605 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8606 COL_MIMETYPE, ainfo->content_type,
8608 COL_NAME, ainfo->name,
8609 COL_CHARSET, ainfo->charset,
8615 gtk_tree_path_free(path);
8618 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8620 label = gtk_label_new(str); \
8621 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8622 GTK_FILL, 0, 0, 0); \
8623 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8625 entry = gtk_entry_new(); \
8626 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8627 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8630 static void compose_attach_property_create(gboolean *cancelled)
8636 GtkWidget *mimetype_entry;
8639 GtkListStore *optmenu_menu;
8640 GtkWidget *path_entry;
8641 GtkWidget *filename_entry;
8644 GtkWidget *cancel_btn;
8645 GList *mime_type_list, *strlist;
8648 debug_print("Creating attach_property window...\n");
8650 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8651 gtk_widget_set_size_request(window, 480, -1);
8652 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8653 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8654 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8655 g_signal_connect(G_OBJECT(window), "delete_event",
8656 G_CALLBACK(attach_property_delete_event),
8658 g_signal_connect(G_OBJECT(window), "key_press_event",
8659 G_CALLBACK(attach_property_key_pressed),
8662 vbox = gtk_vbox_new(FALSE, 8);
8663 gtk_container_add(GTK_CONTAINER(window), vbox);
8665 table = gtk_table_new(4, 2, FALSE);
8666 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8667 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8668 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8670 label = gtk_label_new(_("MIME type"));
8671 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8673 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8674 mimetype_entry = gtk_combo_box_entry_new_text();
8675 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8676 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8678 /* stuff with list */
8679 mime_type_list = procmime_get_mime_type_list();
8681 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8682 MimeType *type = (MimeType *) mime_type_list->data;
8685 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8687 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8690 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8691 (GCompareFunc)strcmp2);
8694 for (mime_type_list = strlist; mime_type_list != NULL;
8695 mime_type_list = mime_type_list->next) {
8696 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8697 g_free(mime_type_list->data);
8699 g_list_free(strlist);
8700 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8701 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8703 label = gtk_label_new(_("Encoding"));
8704 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8706 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8708 hbox = gtk_hbox_new(FALSE, 0);
8709 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8710 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8712 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8713 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8715 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8716 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8717 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8718 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8719 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8721 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8723 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8724 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8726 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8727 &ok_btn, GTK_STOCK_OK,
8729 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8730 gtk_widget_grab_default(ok_btn);
8732 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8733 G_CALLBACK(attach_property_ok),
8735 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8736 G_CALLBACK(attach_property_cancel),
8739 gtk_widget_show_all(vbox);
8741 attach_prop.window = window;
8742 attach_prop.mimetype_entry = mimetype_entry;
8743 attach_prop.encoding_optmenu = optmenu;
8744 attach_prop.path_entry = path_entry;
8745 attach_prop.filename_entry = filename_entry;
8746 attach_prop.ok_btn = ok_btn;
8747 attach_prop.cancel_btn = cancel_btn;
8750 #undef SET_LABEL_AND_ENTRY
8752 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8758 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8764 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8765 gboolean *cancelled)
8773 static gboolean attach_property_key_pressed(GtkWidget *widget,
8775 gboolean *cancelled)
8777 if (event && event->keyval == GDK_Escape) {
8781 if (event && event->keyval == GDK_Return) {
8789 static void compose_exec_ext_editor(Compose *compose)
8796 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8797 G_DIR_SEPARATOR, compose);
8799 if (pipe(pipe_fds) < 0) {
8805 if ((pid = fork()) < 0) {
8812 /* close the write side of the pipe */
8815 compose->exteditor_file = g_strdup(tmp);
8816 compose->exteditor_pid = pid;
8818 compose_set_ext_editor_sensitive(compose, FALSE);
8821 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8823 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8825 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8829 } else { /* process-monitoring process */
8835 /* close the read side of the pipe */
8838 if (compose_write_body_to_file(compose, tmp) < 0) {
8839 fd_write_all(pipe_fds[1], "2\n", 2);
8843 pid_ed = compose_exec_ext_editor_real(tmp);
8845 fd_write_all(pipe_fds[1], "1\n", 2);
8849 /* wait until editor is terminated */
8850 waitpid(pid_ed, NULL, 0);
8852 fd_write_all(pipe_fds[1], "0\n", 2);
8859 #endif /* G_OS_UNIX */
8863 static gint compose_exec_ext_editor_real(const gchar *file)
8870 cm_return_val_if_fail(file != NULL, -1);
8872 if ((pid = fork()) < 0) {
8877 if (pid != 0) return pid;
8879 /* grandchild process */
8881 if (setpgid(0, getppid()))
8884 if (prefs_common_get_ext_editor_cmd() &&
8885 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8886 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8887 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8889 if (prefs_common_get_ext_editor_cmd())
8890 g_warning("External editor command-line is invalid: '%s'\n",
8891 prefs_common_get_ext_editor_cmd());
8892 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8895 cmdline = strsplit_with_quote(buf, " ", 1024);
8896 execvp(cmdline[0], cmdline);
8899 g_strfreev(cmdline);
8904 static gboolean compose_ext_editor_kill(Compose *compose)
8906 pid_t pgid = compose->exteditor_pid * -1;
8909 ret = kill(pgid, 0);
8911 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8915 msg = g_strdup_printf
8916 (_("The external editor is still working.\n"
8917 "Force terminating the process?\n"
8918 "process group id: %d"), -pgid);
8919 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8920 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8924 if (val == G_ALERTALTERNATE) {
8925 g_source_remove(compose->exteditor_tag);
8926 g_io_channel_shutdown(compose->exteditor_ch,
8928 g_io_channel_unref(compose->exteditor_ch);
8930 if (kill(pgid, SIGTERM) < 0) perror("kill");
8931 waitpid(compose->exteditor_pid, NULL, 0);
8933 g_warning("Terminated process group id: %d", -pgid);
8934 g_warning("Temporary file: %s",
8935 compose->exteditor_file);
8937 compose_set_ext_editor_sensitive(compose, TRUE);
8939 g_free(compose->exteditor_file);
8940 compose->exteditor_file = NULL;
8941 compose->exteditor_pid = -1;
8942 compose->exteditor_ch = NULL;
8943 compose->exteditor_tag = -1;
8951 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8955 Compose *compose = (Compose *)data;
8958 debug_print(_("Compose: input from monitoring process\n"));
8960 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8962 g_io_channel_shutdown(source, FALSE, NULL);
8963 g_io_channel_unref(source);
8965 waitpid(compose->exteditor_pid, NULL, 0);
8967 if (buf[0] == '0') { /* success */
8968 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8969 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8971 gtk_text_buffer_set_text(buffer, "", -1);
8972 compose_insert_file(compose, compose->exteditor_file);
8973 compose_changed_cb(NULL, compose);
8974 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8976 if (claws_unlink(compose->exteditor_file) < 0)
8977 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8978 } else if (buf[0] == '1') { /* failed */
8979 g_warning("Couldn't exec external editor\n");
8980 if (claws_unlink(compose->exteditor_file) < 0)
8981 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8982 } else if (buf[0] == '2') {
8983 g_warning("Couldn't write to file\n");
8984 } else if (buf[0] == '3') {
8985 g_warning("Pipe read failed\n");
8988 compose_set_ext_editor_sensitive(compose, TRUE);
8990 g_free(compose->exteditor_file);
8991 compose->exteditor_file = NULL;
8992 compose->exteditor_pid = -1;
8993 compose->exteditor_ch = NULL;
8994 compose->exteditor_tag = -1;
8999 static void compose_set_ext_editor_sensitive(Compose *compose,
9002 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9003 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9004 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9005 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9006 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9007 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9008 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9010 gtk_widget_set_sensitive(compose->text, sensitive);
9011 if (compose->toolbar->send_btn)
9012 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9013 if (compose->toolbar->sendl_btn)
9014 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9015 if (compose->toolbar->draft_btn)
9016 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9017 if (compose->toolbar->insert_btn)
9018 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9019 if (compose->toolbar->sig_btn)
9020 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9021 if (compose->toolbar->exteditor_btn)
9022 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9023 if (compose->toolbar->linewrap_current_btn)
9024 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9025 if (compose->toolbar->linewrap_all_btn)
9026 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9028 #endif /* G_OS_UNIX */
9031 * compose_undo_state_changed:
9033 * Change the sensivity of the menuentries undo and redo
9035 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9036 gint redo_state, gpointer data)
9038 Compose *compose = (Compose *)data;
9040 switch (undo_state) {
9041 case UNDO_STATE_TRUE:
9042 if (!undostruct->undo_state) {
9043 undostruct->undo_state = TRUE;
9044 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9047 case UNDO_STATE_FALSE:
9048 if (undostruct->undo_state) {
9049 undostruct->undo_state = FALSE;
9050 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9053 case UNDO_STATE_UNCHANGED:
9055 case UNDO_STATE_REFRESH:
9056 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9059 g_warning("Undo state not recognized");
9063 switch (redo_state) {
9064 case UNDO_STATE_TRUE:
9065 if (!undostruct->redo_state) {
9066 undostruct->redo_state = TRUE;
9067 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9070 case UNDO_STATE_FALSE:
9071 if (undostruct->redo_state) {
9072 undostruct->redo_state = FALSE;
9073 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9076 case UNDO_STATE_UNCHANGED:
9078 case UNDO_STATE_REFRESH:
9079 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9082 g_warning("Redo state not recognized");
9087 /* callback functions */
9089 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9090 * includes "non-client" (windows-izm) in calculation, so this calculation
9091 * may not be accurate.
9093 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9094 GtkAllocation *allocation,
9095 GtkSHRuler *shruler)
9097 if (prefs_common.show_ruler) {
9098 gint char_width = 0, char_height = 0;
9099 gint line_width_in_chars;
9101 gtkut_get_font_size(GTK_WIDGET(widget),
9102 &char_width, &char_height);
9103 line_width_in_chars =
9104 (allocation->width - allocation->x) / char_width;
9106 /* got the maximum */
9107 gtk_ruler_set_range(GTK_RULER(shruler),
9108 0.0, line_width_in_chars, 0,
9109 /*line_width_in_chars*/ char_width);
9118 ComposePrefType type;
9119 gboolean entry_marked;
9122 static void account_activated(GtkComboBox *optmenu, gpointer data)
9124 Compose *compose = (Compose *)data;
9127 gchar *folderidentifier;
9128 gint account_id = 0;
9131 GSList *list, *saved_list = NULL;
9132 HeaderEntryState *state;
9133 GtkRcStyle *style = NULL;
9134 static GdkColor yellow;
9135 static gboolean color_set = FALSE;
9137 /* Get ID of active account in the combo box */
9138 menu = gtk_combo_box_get_model(optmenu);
9139 gtk_combo_box_get_active_iter(optmenu, &iter);
9140 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9142 ac = account_find_from_id(account_id);
9143 cm_return_if_fail(ac != NULL);
9145 if (ac != compose->account) {
9146 compose_select_account(compose, ac, FALSE);
9148 for (list = compose->header_list; list; list = list->next) {
9149 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9151 if (hentry->type == PREF_ACCOUNT || !list->next) {
9152 compose_destroy_headerentry(compose, hentry);
9156 state = g_malloc0(sizeof(HeaderEntryState));
9157 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9158 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9159 state->entry = gtk_editable_get_chars(
9160 GTK_EDITABLE(hentry->entry), 0, -1);
9161 state->type = hentry->type;
9164 gdk_color_parse("#f5f6be", &yellow);
9165 color_set = gdk_colormap_alloc_color(
9166 gdk_colormap_get_system(),
9167 &yellow, FALSE, TRUE);
9170 style = gtk_widget_get_modifier_style(hentry->entry);
9171 state->entry_marked = gdk_color_equal(&yellow,
9172 &style->base[GTK_STATE_NORMAL]);
9174 saved_list = g_slist_append(saved_list, state);
9175 compose_destroy_headerentry(compose, hentry);
9178 compose->header_last = NULL;
9179 g_slist_free(compose->header_list);
9180 compose->header_list = NULL;
9181 compose->header_nextrow = 1;
9182 compose_create_header_entry(compose);
9184 if (ac->set_autocc && ac->auto_cc)
9185 compose_entry_append(compose, ac->auto_cc,
9186 COMPOSE_CC, PREF_ACCOUNT);
9188 if (ac->set_autobcc && ac->auto_bcc)
9189 compose_entry_append(compose, ac->auto_bcc,
9190 COMPOSE_BCC, PREF_ACCOUNT);
9192 if (ac->set_autoreplyto && ac->auto_replyto)
9193 compose_entry_append(compose, ac->auto_replyto,
9194 COMPOSE_REPLYTO, PREF_ACCOUNT);
9196 for (list = saved_list; list; list = list->next) {
9197 state = (HeaderEntryState *) list->data;
9199 compose_add_header_entry(compose, state->header,
9200 state->entry, state->type);
9201 if (state->entry_marked)
9202 compose_entry_mark_default_to(compose, state->entry);
9204 g_free(state->header);
9205 g_free(state->entry);
9208 g_slist_free(saved_list);
9210 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9211 (ac->protocol == A_NNTP) ?
9212 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9215 /* Set message save folder */
9216 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9217 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9219 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9220 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9222 compose_set_save_to(compose, NULL);
9223 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9224 folderidentifier = folder_item_get_identifier(account_get_special_folder
9225 (compose->account, F_OUTBOX));
9226 compose_set_save_to(compose, folderidentifier);
9227 g_free(folderidentifier);
9231 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9232 GtkTreeViewColumn *column, Compose *compose)
9234 compose_attach_property(NULL, compose);
9237 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9240 Compose *compose = (Compose *)data;
9241 GtkTreeSelection *attach_selection;
9242 gint attach_nr_selected;
9244 if (!event) return FALSE;
9246 if (event->button == 3) {
9247 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9248 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9250 if (attach_nr_selected > 0)
9252 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9253 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9255 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9256 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9259 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9260 NULL, NULL, event->button, event->time);
9267 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9270 Compose *compose = (Compose *)data;
9272 if (!event) return FALSE;
9274 switch (event->keyval) {
9276 compose_attach_remove_selected(NULL, compose);
9282 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9284 toolbar_comp_set_sensitive(compose, allow);
9285 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9286 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9288 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9290 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9291 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9292 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9294 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9298 static void compose_send_cb(GtkAction *action, gpointer data)
9300 Compose *compose = (Compose *)data;
9302 if (prefs_common.work_offline &&
9303 !inc_offline_should_override(TRUE,
9304 _("Claws Mail needs network access in order "
9305 "to send this email.")))
9308 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9309 g_source_remove(compose->draft_timeout_tag);
9310 compose->draft_timeout_tag = -1;
9313 compose_send(compose);
9316 static void compose_send_later_cb(GtkAction *action, gpointer data)
9318 Compose *compose = (Compose *)data;
9322 compose_allow_user_actions(compose, FALSE);
9323 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9324 compose_allow_user_actions(compose, TRUE);
9328 compose_close(compose);
9329 } else if (val == -1) {
9330 alertpanel_error(_("Could not queue message."));
9331 } else if (val == -2) {
9332 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9333 } else if (val == -3) {
9334 if (privacy_peek_error())
9335 alertpanel_error(_("Could not queue message for sending:\n\n"
9336 "Signature failed: %s"), privacy_get_error());
9337 } else if (val == -4) {
9338 alertpanel_error(_("Could not queue message for sending:\n\n"
9339 "Charset conversion failed."));
9340 } else if (val == -5) {
9341 alertpanel_error(_("Could not queue message for sending:\n\n"
9342 "Couldn't get recipient encryption key."));
9343 } else if (val == -6) {
9346 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9349 #define DRAFTED_AT_EXIT "drafted_at_exit"
9350 static void compose_register_draft(MsgInfo *info)
9352 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9353 DRAFTED_AT_EXIT, NULL);
9354 FILE *fp = g_fopen(filepath, "ab");
9357 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9365 gboolean compose_draft (gpointer data, guint action)
9367 Compose *compose = (Compose *)data;
9371 MsgFlags flag = {0, 0};
9372 static gboolean lock = FALSE;
9373 MsgInfo *newmsginfo;
9375 gboolean target_locked = FALSE;
9376 gboolean err = FALSE;
9378 if (lock) return FALSE;
9380 if (compose->sending)
9383 draft = account_get_special_folder(compose->account, F_DRAFT);
9384 cm_return_val_if_fail(draft != NULL, FALSE);
9386 if (!g_mutex_trylock(compose->mutex)) {
9387 /* we don't want to lock the mutex once it's available,
9388 * because as the only other part of compose.c locking
9389 * it is compose_close - which means once unlocked,
9390 * the compose struct will be freed */
9391 debug_print("couldn't lock mutex, probably sending\n");
9397 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9398 G_DIR_SEPARATOR, compose);
9399 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9400 FILE_OP_ERROR(tmp, "fopen");
9404 /* chmod for security */
9405 if (change_file_mode_rw(fp, tmp) < 0) {
9406 FILE_OP_ERROR(tmp, "chmod");
9407 g_warning("can't change file mode\n");
9410 /* Save draft infos */
9411 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9412 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9414 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9415 gchar *savefolderid;
9417 savefolderid = compose_get_save_to(compose);
9418 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9419 g_free(savefolderid);
9421 if (compose->return_receipt) {
9422 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9424 if (compose->privacy_system) {
9425 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9426 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9427 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9430 /* Message-ID of message replying to */
9431 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9434 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9435 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9438 /* Message-ID of message forwarding to */
9439 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9442 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9443 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9447 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9448 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9450 /* end of headers */
9451 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9458 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9462 if (fclose(fp) == EOF) {
9466 if (compose->targetinfo) {
9467 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9468 flag.perm_flags = target_locked?MSG_LOCKED:0;
9470 flag.tmp_flags = MSG_DRAFT;
9472 folder_item_scan(draft);
9473 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9474 MsgInfo *tmpinfo = NULL;
9475 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9476 if (compose->msgid) {
9477 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9480 msgnum = tmpinfo->msgnum;
9481 procmsg_msginfo_free(tmpinfo);
9482 debug_print("got draft msgnum %d from scanning\n", msgnum);
9484 debug_print("didn't get draft msgnum after scanning\n");
9487 debug_print("got draft msgnum %d from adding\n", msgnum);
9493 if (action != COMPOSE_AUTO_SAVE) {
9494 if (action != COMPOSE_DRAFT_FOR_EXIT)
9495 alertpanel_error(_("Could not save draft."));
9498 gtkut_window_popup(compose->window);
9499 val = alertpanel_full(_("Could not save draft"),
9500 _("Could not save draft.\n"
9501 "Do you want to cancel exit or discard this email?"),
9502 _("_Cancel exit"), _("_Discard email"), NULL,
9503 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9504 if (val == G_ALERTALTERNATE) {
9506 g_mutex_unlock(compose->mutex); /* must be done before closing */
9507 compose_close(compose);
9511 g_mutex_unlock(compose->mutex); /* must be done before closing */
9520 if (compose->mode == COMPOSE_REEDIT) {
9521 compose_remove_reedit_target(compose, TRUE);
9524 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9527 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9529 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9531 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9532 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9533 procmsg_msginfo_set_flags(newmsginfo, 0,
9534 MSG_HAS_ATTACHMENT);
9536 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9537 compose_register_draft(newmsginfo);
9539 procmsg_msginfo_free(newmsginfo);
9542 folder_item_scan(draft);
9544 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9546 g_mutex_unlock(compose->mutex); /* must be done before closing */
9547 compose_close(compose);
9553 path = folder_item_fetch_msg(draft, msgnum);
9555 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9558 if (g_stat(path, &s) < 0) {
9559 FILE_OP_ERROR(path, "stat");
9565 procmsg_msginfo_free(compose->targetinfo);
9566 compose->targetinfo = procmsg_msginfo_new();
9567 compose->targetinfo->msgnum = msgnum;
9568 compose->targetinfo->size = (goffset)s.st_size;
9569 compose->targetinfo->mtime = s.st_mtime;
9570 compose->targetinfo->folder = draft;
9572 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9573 compose->mode = COMPOSE_REEDIT;
9575 if (action == COMPOSE_AUTO_SAVE) {
9576 compose->autosaved_draft = compose->targetinfo;
9578 compose->modified = FALSE;
9579 compose_set_title(compose);
9583 g_mutex_unlock(compose->mutex);
9587 void compose_clear_exit_drafts(void)
9589 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9590 DRAFTED_AT_EXIT, NULL);
9591 if (is_file_exist(filepath))
9592 claws_unlink(filepath);
9597 void compose_reopen_exit_drafts(void)
9599 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9600 DRAFTED_AT_EXIT, NULL);
9601 FILE *fp = g_fopen(filepath, "rb");
9605 while (fgets(buf, sizeof(buf), fp)) {
9606 gchar **parts = g_strsplit(buf, "\t", 2);
9607 const gchar *folder = parts[0];
9608 int msgnum = parts[1] ? atoi(parts[1]):-1;
9610 if (folder && *folder && msgnum > -1) {
9611 FolderItem *item = folder_find_item_from_identifier(folder);
9612 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9614 compose_reedit(info, FALSE);
9621 compose_clear_exit_drafts();
9624 static void compose_save_cb(GtkAction *action, gpointer data)
9626 Compose *compose = (Compose *)data;
9627 compose_draft(compose, COMPOSE_KEEP_EDITING);
9628 compose->rmode = COMPOSE_REEDIT;
9631 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9633 if (compose && file_list) {
9636 for ( tmp = file_list; tmp; tmp = tmp->next) {
9637 gchar *file = (gchar *) tmp->data;
9638 gchar *utf8_filename = conv_filename_to_utf8(file);
9639 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9640 compose_changed_cb(NULL, compose);
9645 g_free(utf8_filename);
9650 static void compose_attach_cb(GtkAction *action, gpointer data)
9652 Compose *compose = (Compose *)data;
9655 if (compose->redirect_filename != NULL)
9658 file_list = filesel_select_multiple_files_open(_("Select file"));
9661 compose_attach_from_list(compose, file_list, TRUE);
9662 g_list_free(file_list);
9666 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9668 Compose *compose = (Compose *)data;
9670 gint files_inserted = 0;
9672 file_list = filesel_select_multiple_files_open(_("Select file"));
9677 for ( tmp = file_list; tmp; tmp = tmp->next) {
9678 gchar *file = (gchar *) tmp->data;
9679 gchar *filedup = g_strdup(file);
9680 gchar *shortfile = g_path_get_basename(filedup);
9681 ComposeInsertResult res;
9682 /* insert the file if the file is short or if the user confirmed that
9683 he/she wants to insert the large file */
9684 res = compose_insert_file(compose, file);
9685 if (res == COMPOSE_INSERT_READ_ERROR) {
9686 alertpanel_error(_("File '%s' could not be read."), shortfile);
9687 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9688 alertpanel_error(_("File '%s' contained invalid characters\n"
9689 "for the current encoding, insertion may be incorrect."),
9691 } else if (res == COMPOSE_INSERT_SUCCESS)
9698 g_list_free(file_list);
9702 if (files_inserted > 0 && compose->gtkaspell &&
9703 compose->gtkaspell->check_while_typing)
9704 gtkaspell_highlight_all(compose->gtkaspell);
9708 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9710 Compose *compose = (Compose *)data;
9712 compose_insert_sig(compose, FALSE);
9715 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9719 Compose *compose = (Compose *)data;
9721 gtkut_widget_get_uposition(widget, &x, &y);
9722 if (!compose->batch) {
9723 prefs_common.compose_x = x;
9724 prefs_common.compose_y = y;
9726 if (compose->sending || compose->updating)
9728 compose_close_cb(NULL, compose);
9732 void compose_close_toolbar(Compose *compose)
9734 compose_close_cb(NULL, compose);
9737 static void compose_close_cb(GtkAction *action, gpointer data)
9739 Compose *compose = (Compose *)data;
9743 if (compose->exteditor_tag != -1) {
9744 if (!compose_ext_editor_kill(compose))
9749 if (compose->modified) {
9750 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9751 if (!g_mutex_trylock(compose->mutex)) {
9752 /* we don't want to lock the mutex once it's available,
9753 * because as the only other part of compose.c locking
9754 * it is compose_close - which means once unlocked,
9755 * the compose struct will be freed */
9756 debug_print("couldn't lock mutex, probably sending\n");
9760 val = alertpanel(_("Discard message"),
9761 _("This message has been modified. Discard it?"),
9762 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9764 val = alertpanel(_("Save changes"),
9765 _("This message has been modified. Save the latest changes?"),
9766 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9768 g_mutex_unlock(compose->mutex);
9770 case G_ALERTDEFAULT:
9771 if (prefs_common.autosave && !reedit)
9772 compose_remove_draft(compose);
9774 case G_ALERTALTERNATE:
9775 compose_draft(data, COMPOSE_QUIT_EDITING);
9782 compose_close(compose);
9785 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9787 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9788 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9789 Compose *compose = (Compose *) data;
9792 compose->out_encoding = (CharSet)value;
9795 static void compose_address_cb(GtkAction *action, gpointer data)
9797 Compose *compose = (Compose *)data;
9799 addressbook_open(compose);
9802 static void about_show_cb(GtkAction *action, gpointer data)
9807 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9809 Compose *compose = (Compose *)data;
9814 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9815 cm_return_if_fail(tmpl != NULL);
9817 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9819 val = alertpanel(_("Apply template"), msg,
9820 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9823 if (val == G_ALERTDEFAULT)
9824 compose_template_apply(compose, tmpl, TRUE);
9825 else if (val == G_ALERTALTERNATE)
9826 compose_template_apply(compose, tmpl, FALSE);
9829 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9831 Compose *compose = (Compose *)data;
9833 compose_exec_ext_editor(compose);
9836 static void compose_undo_cb(GtkAction *action, gpointer data)
9838 Compose *compose = (Compose *)data;
9839 gboolean prev_autowrap = compose->autowrap;
9841 compose->autowrap = FALSE;
9842 undo_undo(compose->undostruct);
9843 compose->autowrap = prev_autowrap;
9846 static void compose_redo_cb(GtkAction *action, gpointer data)
9848 Compose *compose = (Compose *)data;
9849 gboolean prev_autowrap = compose->autowrap;
9851 compose->autowrap = FALSE;
9852 undo_redo(compose->undostruct);
9853 compose->autowrap = prev_autowrap;
9856 static void entry_cut_clipboard(GtkWidget *entry)
9858 if (GTK_IS_EDITABLE(entry))
9859 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9860 else if (GTK_IS_TEXT_VIEW(entry))
9861 gtk_text_buffer_cut_clipboard(
9862 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9863 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9867 static void entry_copy_clipboard(GtkWidget *entry)
9869 if (GTK_IS_EDITABLE(entry))
9870 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9871 else if (GTK_IS_TEXT_VIEW(entry))
9872 gtk_text_buffer_copy_clipboard(
9873 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9874 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9877 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9878 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9880 if (GTK_IS_TEXT_VIEW(entry)) {
9881 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9882 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9883 GtkTextIter start_iter, end_iter;
9885 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9887 if (contents == NULL)
9890 /* we shouldn't delete the selection when middle-click-pasting, or we
9891 * can't mid-click-paste our own selection */
9892 if (clip != GDK_SELECTION_PRIMARY) {
9893 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9894 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9897 if (insert_place == NULL) {
9898 /* if insert_place isn't specified, insert at the cursor.
9899 * used for Ctrl-V pasting */
9900 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9901 start = gtk_text_iter_get_offset(&start_iter);
9902 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9904 /* if insert_place is specified, paste here.
9905 * used for mid-click-pasting */
9906 start = gtk_text_iter_get_offset(insert_place);
9907 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9908 if (prefs_common.primary_paste_unselects)
9909 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9913 /* paste unwrapped: mark the paste so it's not wrapped later */
9914 end = start + strlen(contents);
9915 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9916 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9917 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9918 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9919 /* rewrap paragraph now (after a mid-click-paste) */
9920 mark_start = gtk_text_buffer_get_insert(buffer);
9921 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9922 gtk_text_iter_backward_char(&start_iter);
9923 compose_beautify_paragraph(compose, &start_iter, TRUE);
9925 } else if (GTK_IS_EDITABLE(entry))
9926 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9928 compose->modified = TRUE;
9931 static void entry_allsel(GtkWidget *entry)
9933 if (GTK_IS_EDITABLE(entry))
9934 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9935 else if (GTK_IS_TEXT_VIEW(entry)) {
9936 GtkTextIter startiter, enditer;
9937 GtkTextBuffer *textbuf;
9939 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9940 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9941 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9943 gtk_text_buffer_move_mark_by_name(textbuf,
9944 "selection_bound", &startiter);
9945 gtk_text_buffer_move_mark_by_name(textbuf,
9946 "insert", &enditer);
9950 static void compose_cut_cb(GtkAction *action, gpointer data)
9952 Compose *compose = (Compose *)data;
9953 if (compose->focused_editable
9954 #ifndef GENERIC_UMPC
9955 && gtkut_widget_has_focus(compose->focused_editable)
9958 entry_cut_clipboard(compose->focused_editable);
9961 static void compose_copy_cb(GtkAction *action, gpointer data)
9963 Compose *compose = (Compose *)data;
9964 if (compose->focused_editable
9965 #ifndef GENERIC_UMPC
9966 && gtkut_widget_has_focus(compose->focused_editable)
9969 entry_copy_clipboard(compose->focused_editable);
9972 static void compose_paste_cb(GtkAction *action, gpointer data)
9974 Compose *compose = (Compose *)data;
9976 GtkTextBuffer *buffer;
9978 if (compose->focused_editable &&
9979 gtkut_widget_has_focus(compose->focused_editable))
9980 entry_paste_clipboard(compose, compose->focused_editable,
9981 prefs_common.linewrap_pastes,
9982 GDK_SELECTION_CLIPBOARD, NULL);
9986 if (gtkut_widget_has_focus(compose->text) &&
9987 compose->gtkaspell &&
9988 compose->gtkaspell->check_while_typing)
9989 gtkaspell_highlight_all(compose->gtkaspell);
9993 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9995 Compose *compose = (Compose *)data;
9996 gint wrap_quote = prefs_common.linewrap_quote;
9997 if (compose->focused_editable
9998 #ifndef GENERIC_UMPC
9999 && gtkut_widget_has_focus(compose->focused_editable)
10002 /* let text_insert() (called directly or at a later time
10003 * after the gtk_editable_paste_clipboard) know that
10004 * text is to be inserted as a quotation. implemented
10005 * by using a simple refcount... */
10006 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10007 G_OBJECT(compose->focused_editable),
10008 "paste_as_quotation"));
10009 g_object_set_data(G_OBJECT(compose->focused_editable),
10010 "paste_as_quotation",
10011 GINT_TO_POINTER(paste_as_quotation + 1));
10012 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10013 entry_paste_clipboard(compose, compose->focused_editable,
10014 prefs_common.linewrap_pastes,
10015 GDK_SELECTION_CLIPBOARD, NULL);
10016 prefs_common.linewrap_quote = wrap_quote;
10020 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10022 Compose *compose = (Compose *)data;
10023 gint prev_autowrap;
10024 GtkTextBuffer *buffer;
10026 if (compose->focused_editable
10027 #ifndef GENERIC_UMPC
10028 && gtkut_widget_has_focus(compose->focused_editable)
10031 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10032 GDK_SELECTION_CLIPBOARD, NULL);
10036 if (gtkut_widget_has_focus(compose->text) &&
10037 compose->gtkaspell &&
10038 compose->gtkaspell->check_while_typing)
10039 gtkaspell_highlight_all(compose->gtkaspell);
10043 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10045 Compose *compose = (Compose *)data;
10046 gint prev_autowrap;
10047 GtkTextBuffer *buffer;
10049 if (compose->focused_editable
10050 #ifndef GENERIC_UMPC
10051 && gtkut_widget_has_focus(compose->focused_editable)
10054 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10055 GDK_SELECTION_CLIPBOARD, NULL);
10059 if (gtkut_widget_has_focus(compose->text) &&
10060 compose->gtkaspell &&
10061 compose->gtkaspell->check_while_typing)
10062 gtkaspell_highlight_all(compose->gtkaspell);
10066 static void compose_allsel_cb(GtkAction *action, gpointer data)
10068 Compose *compose = (Compose *)data;
10069 if (compose->focused_editable
10070 #ifndef GENERIC_UMPC
10071 && gtkut_widget_has_focus(compose->focused_editable)
10074 entry_allsel(compose->focused_editable);
10077 static void textview_move_beginning_of_line (GtkTextView *text)
10079 GtkTextBuffer *buffer;
10083 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10085 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10086 mark = gtk_text_buffer_get_insert(buffer);
10087 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10088 gtk_text_iter_set_line_offset(&ins, 0);
10089 gtk_text_buffer_place_cursor(buffer, &ins);
10092 static void textview_move_forward_character (GtkTextView *text)
10094 GtkTextBuffer *buffer;
10098 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10100 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10101 mark = gtk_text_buffer_get_insert(buffer);
10102 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10103 if (gtk_text_iter_forward_cursor_position(&ins))
10104 gtk_text_buffer_place_cursor(buffer, &ins);
10107 static void textview_move_backward_character (GtkTextView *text)
10109 GtkTextBuffer *buffer;
10113 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10115 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10116 mark = gtk_text_buffer_get_insert(buffer);
10117 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10118 if (gtk_text_iter_backward_cursor_position(&ins))
10119 gtk_text_buffer_place_cursor(buffer, &ins);
10122 static void textview_move_forward_word (GtkTextView *text)
10124 GtkTextBuffer *buffer;
10129 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10131 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10132 mark = gtk_text_buffer_get_insert(buffer);
10133 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10134 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10135 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10136 gtk_text_iter_backward_word_start(&ins);
10137 gtk_text_buffer_place_cursor(buffer, &ins);
10141 static void textview_move_backward_word (GtkTextView *text)
10143 GtkTextBuffer *buffer;
10148 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10150 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10151 mark = gtk_text_buffer_get_insert(buffer);
10152 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10153 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10154 if (gtk_text_iter_backward_word_starts(&ins, 1))
10155 gtk_text_buffer_place_cursor(buffer, &ins);
10158 static void textview_move_end_of_line (GtkTextView *text)
10160 GtkTextBuffer *buffer;
10164 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10166 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10167 mark = gtk_text_buffer_get_insert(buffer);
10168 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10169 if (gtk_text_iter_forward_to_line_end(&ins))
10170 gtk_text_buffer_place_cursor(buffer, &ins);
10173 static void textview_move_next_line (GtkTextView *text)
10175 GtkTextBuffer *buffer;
10180 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10182 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10183 mark = gtk_text_buffer_get_insert(buffer);
10184 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10185 offset = gtk_text_iter_get_line_offset(&ins);
10186 if (gtk_text_iter_forward_line(&ins)) {
10187 gtk_text_iter_set_line_offset(&ins, offset);
10188 gtk_text_buffer_place_cursor(buffer, &ins);
10192 static void textview_move_previous_line (GtkTextView *text)
10194 GtkTextBuffer *buffer;
10199 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10201 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10202 mark = gtk_text_buffer_get_insert(buffer);
10203 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10204 offset = gtk_text_iter_get_line_offset(&ins);
10205 if (gtk_text_iter_backward_line(&ins)) {
10206 gtk_text_iter_set_line_offset(&ins, offset);
10207 gtk_text_buffer_place_cursor(buffer, &ins);
10211 static void textview_delete_forward_character (GtkTextView *text)
10213 GtkTextBuffer *buffer;
10215 GtkTextIter ins, end_iter;
10217 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10219 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10220 mark = gtk_text_buffer_get_insert(buffer);
10221 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10223 if (gtk_text_iter_forward_char(&end_iter)) {
10224 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10228 static void textview_delete_backward_character (GtkTextView *text)
10230 GtkTextBuffer *buffer;
10232 GtkTextIter ins, end_iter;
10234 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10236 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10237 mark = gtk_text_buffer_get_insert(buffer);
10238 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10240 if (gtk_text_iter_backward_char(&end_iter)) {
10241 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10245 static void textview_delete_forward_word (GtkTextView *text)
10247 GtkTextBuffer *buffer;
10249 GtkTextIter ins, end_iter;
10251 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10253 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10254 mark = gtk_text_buffer_get_insert(buffer);
10255 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10257 if (gtk_text_iter_forward_word_end(&end_iter)) {
10258 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10262 static void textview_delete_backward_word (GtkTextView *text)
10264 GtkTextBuffer *buffer;
10266 GtkTextIter ins, end_iter;
10268 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10270 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10271 mark = gtk_text_buffer_get_insert(buffer);
10272 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10274 if (gtk_text_iter_backward_word_start(&end_iter)) {
10275 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10279 static void textview_delete_line (GtkTextView *text)
10281 GtkTextBuffer *buffer;
10283 GtkTextIter ins, start_iter, end_iter;
10285 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10287 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10288 mark = gtk_text_buffer_get_insert(buffer);
10289 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10292 gtk_text_iter_set_line_offset(&start_iter, 0);
10295 if (gtk_text_iter_ends_line(&end_iter)){
10296 if (!gtk_text_iter_forward_char(&end_iter))
10297 gtk_text_iter_backward_char(&start_iter);
10300 gtk_text_iter_forward_to_line_end(&end_iter);
10301 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10304 static void textview_delete_to_line_end (GtkTextView *text)
10306 GtkTextBuffer *buffer;
10308 GtkTextIter ins, end_iter;
10310 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10312 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10313 mark = gtk_text_buffer_get_insert(buffer);
10314 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10316 if (gtk_text_iter_ends_line(&end_iter))
10317 gtk_text_iter_forward_char(&end_iter);
10319 gtk_text_iter_forward_to_line_end(&end_iter);
10320 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10323 #define DO_ACTION(name, act) { \
10324 if(!strcmp(name, a_name)) { \
10328 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10330 const gchar *a_name = gtk_action_get_name(action);
10331 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10332 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10333 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10334 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10335 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10336 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10337 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10338 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10339 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10340 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10341 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10342 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10343 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10344 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10348 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10350 Compose *compose = (Compose *)data;
10351 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10352 ComposeCallAdvancedAction action = -1;
10354 action = compose_call_advanced_action_from_path(gaction);
10357 void (*do_action) (GtkTextView *text);
10358 } action_table[] = {
10359 {textview_move_beginning_of_line},
10360 {textview_move_forward_character},
10361 {textview_move_backward_character},
10362 {textview_move_forward_word},
10363 {textview_move_backward_word},
10364 {textview_move_end_of_line},
10365 {textview_move_next_line},
10366 {textview_move_previous_line},
10367 {textview_delete_forward_character},
10368 {textview_delete_backward_character},
10369 {textview_delete_forward_word},
10370 {textview_delete_backward_word},
10371 {textview_delete_line},
10372 {textview_delete_to_line_end}
10375 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10377 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10378 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10379 if (action_table[action].do_action)
10380 action_table[action].do_action(text);
10382 g_warning("Not implemented yet.");
10386 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10390 if (GTK_IS_EDITABLE(widget)) {
10391 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10392 gtk_editable_set_position(GTK_EDITABLE(widget),
10395 if (widget->parent && widget->parent->parent
10396 && widget->parent->parent->parent) {
10397 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10398 gint y = widget->allocation.y;
10399 gint height = widget->allocation.height;
10400 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10401 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10403 if (y < (int)shown->value) {
10404 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10406 if (y + height > (int)shown->value + (int)shown->page_size) {
10407 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10408 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10409 y + height - (int)shown->page_size - 1);
10411 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10412 (int)shown->upper - (int)shown->page_size - 1);
10419 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10420 compose->focused_editable = widget;
10422 #ifdef GENERIC_UMPC
10423 if (GTK_IS_TEXT_VIEW(widget)
10424 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10425 g_object_ref(compose->notebook);
10426 g_object_ref(compose->edit_vbox);
10427 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10428 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10429 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10430 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10431 g_object_unref(compose->notebook);
10432 g_object_unref(compose->edit_vbox);
10433 g_signal_handlers_block_by_func(G_OBJECT(widget),
10434 G_CALLBACK(compose_grab_focus_cb),
10436 gtk_widget_grab_focus(widget);
10437 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10438 G_CALLBACK(compose_grab_focus_cb),
10440 } else if (!GTK_IS_TEXT_VIEW(widget)
10441 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10442 g_object_ref(compose->notebook);
10443 g_object_ref(compose->edit_vbox);
10444 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10445 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10446 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10447 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10448 g_object_unref(compose->notebook);
10449 g_object_unref(compose->edit_vbox);
10450 g_signal_handlers_block_by_func(G_OBJECT(widget),
10451 G_CALLBACK(compose_grab_focus_cb),
10453 gtk_widget_grab_focus(widget);
10454 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10455 G_CALLBACK(compose_grab_focus_cb),
10461 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10463 compose->modified = TRUE;
10464 // compose_beautify_paragraph(compose, NULL, TRUE);
10465 #ifndef GENERIC_UMPC
10466 compose_set_title(compose);
10470 static void compose_wrap_cb(GtkAction *action, gpointer data)
10472 Compose *compose = (Compose *)data;
10473 compose_beautify_paragraph(compose, NULL, TRUE);
10476 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10478 Compose *compose = (Compose *)data;
10479 compose_wrap_all_full(compose, TRUE);
10482 static void compose_find_cb(GtkAction *action, gpointer data)
10484 Compose *compose = (Compose *)data;
10486 message_search_compose(compose);
10489 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10492 Compose *compose = (Compose *)data;
10493 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10494 if (compose->autowrap)
10495 compose_wrap_all_full(compose, TRUE);
10496 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10499 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10502 Compose *compose = (Compose *)data;
10503 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10506 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10508 Compose *compose = (Compose *)data;
10510 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10513 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10515 Compose *compose = (Compose *)data;
10517 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10520 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10522 g_free(compose->privacy_system);
10524 compose->privacy_system = g_strdup(account->default_privacy_system);
10525 compose_update_privacy_system_menu_item(compose, warn);
10528 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10530 Compose *compose = (Compose *)data;
10532 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10533 gtk_widget_show(compose->ruler_hbox);
10534 prefs_common.show_ruler = TRUE;
10536 gtk_widget_hide(compose->ruler_hbox);
10537 gtk_widget_queue_resize(compose->edit_vbox);
10538 prefs_common.show_ruler = FALSE;
10542 static void compose_attach_drag_received_cb (GtkWidget *widget,
10543 GdkDragContext *context,
10546 GtkSelectionData *data,
10549 gpointer user_data)
10551 Compose *compose = (Compose *)user_data;
10554 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10556 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10558 ) && gtk_drag_get_source_widget(context) !=
10559 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10560 list = uri_list_extract_filenames((const gchar *)data->data);
10561 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10562 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10563 compose_attach_append
10564 (compose, (const gchar *)tmp->data,
10565 utf8_filename, NULL, NULL);
10566 g_free(utf8_filename);
10568 if (list) compose_changed_cb(NULL, compose);
10569 list_free_strings(list);
10571 } else if (gtk_drag_get_source_widget(context)
10572 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10573 /* comes from our summaryview */
10574 SummaryView * summaryview = NULL;
10575 GSList * list = NULL, *cur = NULL;
10577 if (mainwindow_get_mainwindow())
10578 summaryview = mainwindow_get_mainwindow()->summaryview;
10581 list = summary_get_selected_msg_list(summaryview);
10583 for (cur = list; cur; cur = cur->next) {
10584 MsgInfo *msginfo = (MsgInfo *)cur->data;
10585 gchar *file = NULL;
10587 file = procmsg_get_message_file_full(msginfo,
10590 compose_attach_append(compose, (const gchar *)file,
10591 (const gchar *)file, "message/rfc822", NULL);
10595 g_slist_free(list);
10599 static gboolean compose_drag_drop(GtkWidget *widget,
10600 GdkDragContext *drag_context,
10602 guint time, gpointer user_data)
10604 /* not handling this signal makes compose_insert_drag_received_cb
10609 static void compose_insert_drag_received_cb (GtkWidget *widget,
10610 GdkDragContext *drag_context,
10613 GtkSelectionData *data,
10616 gpointer user_data)
10618 Compose *compose = (Compose *)user_data;
10621 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10624 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10626 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10628 AlertValue val = G_ALERTDEFAULT;
10630 list = uri_list_extract_filenames((const gchar *)data->data);
10631 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10632 /* Assume a list of no files, and data has ://, is a remote link */
10633 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10634 gchar *tmpfile = get_tmp_file();
10635 str_write_to_file(tmpdata, tmpfile);
10637 compose_insert_file(compose, tmpfile);
10638 claws_unlink(tmpfile);
10640 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10641 compose_beautify_paragraph(compose, NULL, TRUE);
10644 switch (prefs_common.compose_dnd_mode) {
10645 case COMPOSE_DND_ASK:
10646 val = alertpanel_full(_("Insert or attach?"),
10647 _("Do you want to insert the contents of the file(s) "
10648 "into the message body, or attach it to the email?"),
10649 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10650 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10652 case COMPOSE_DND_INSERT:
10653 val = G_ALERTALTERNATE;
10655 case COMPOSE_DND_ATTACH:
10656 val = G_ALERTOTHER;
10659 /* unexpected case */
10660 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10663 if (val & G_ALERTDISABLE) {
10664 val &= ~G_ALERTDISABLE;
10665 /* remember what action to perform by default, only if we don't click Cancel */
10666 if (val == G_ALERTALTERNATE)
10667 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10668 else if (val == G_ALERTOTHER)
10669 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10672 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10673 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10674 list_free_strings(list);
10677 } else if (val == G_ALERTOTHER) {
10678 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10679 list_free_strings(list);
10684 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10685 compose_insert_file(compose, (const gchar *)tmp->data);
10687 list_free_strings(list);
10689 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10694 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10697 static void compose_header_drag_received_cb (GtkWidget *widget,
10698 GdkDragContext *drag_context,
10701 GtkSelectionData *data,
10704 gpointer user_data)
10706 GtkEditable *entry = (GtkEditable *)user_data;
10707 gchar *email = (gchar *)data->data;
10709 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10712 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10713 gchar *decoded=g_new(gchar, strlen(email));
10716 email += strlen("mailto:");
10717 decode_uri(decoded, email); /* will fit */
10718 gtk_editable_delete_text(entry, 0, -1);
10719 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10720 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10724 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10727 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10729 Compose *compose = (Compose *)data;
10731 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10732 compose->return_receipt = TRUE;
10734 compose->return_receipt = FALSE;
10737 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10739 Compose *compose = (Compose *)data;
10741 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10742 compose->remove_references = TRUE;
10744 compose->remove_references = FALSE;
10747 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10748 ComposeHeaderEntry *headerentry)
10750 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10754 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10755 GdkEventKey *event,
10756 ComposeHeaderEntry *headerentry)
10758 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10759 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10760 !(event->state & GDK_MODIFIER_MASK) &&
10761 (event->keyval == GDK_BackSpace) &&
10762 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10763 gtk_container_remove
10764 (GTK_CONTAINER(headerentry->compose->header_table),
10765 headerentry->combo);
10766 gtk_container_remove
10767 (GTK_CONTAINER(headerentry->compose->header_table),
10768 headerentry->entry);
10769 headerentry->compose->header_list =
10770 g_slist_remove(headerentry->compose->header_list,
10772 g_free(headerentry);
10773 } else if (event->keyval == GDK_Tab) {
10774 if (headerentry->compose->header_last == headerentry) {
10775 /* Override default next focus, and give it to subject_entry
10776 * instead of notebook tabs
10778 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10779 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10786 static gboolean scroll_postpone(gpointer data)
10788 Compose *compose = (Compose *)data;
10789 GTK_EVENTS_FLUSH();
10790 compose_show_first_last_header(compose, FALSE);
10794 static void compose_headerentry_changed_cb(GtkWidget *entry,
10795 ComposeHeaderEntry *headerentry)
10797 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10798 compose_create_header_entry(headerentry->compose);
10799 g_signal_handlers_disconnect_matched
10800 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10801 0, 0, NULL, NULL, headerentry);
10803 g_timeout_add(0, scroll_postpone, headerentry->compose);
10807 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10809 GtkAdjustment *vadj;
10811 cm_return_if_fail(compose);
10812 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10813 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10814 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10815 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10816 gtk_adjustment_changed(vadj);
10819 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10820 const gchar *text, gint len, Compose *compose)
10822 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10823 (G_OBJECT(compose->text), "paste_as_quotation"));
10826 cm_return_if_fail(text != NULL);
10828 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10829 G_CALLBACK(text_inserted),
10831 if (paste_as_quotation) {
10833 const gchar *qmark;
10835 GtkTextIter start_iter;
10838 len = strlen(text);
10840 new_text = g_strndup(text, len);
10842 qmark = compose_quote_char_from_context(compose);
10844 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10845 gtk_text_buffer_place_cursor(buffer, iter);
10847 pos = gtk_text_iter_get_offset(iter);
10849 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10850 _("Quote format error at line %d."));
10851 quote_fmt_reset_vartable();
10853 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10854 GINT_TO_POINTER(paste_as_quotation - 1));
10856 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10857 gtk_text_buffer_place_cursor(buffer, iter);
10858 gtk_text_buffer_delete_mark(buffer, mark);
10860 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10861 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10862 compose_beautify_paragraph(compose, &start_iter, FALSE);
10863 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10864 gtk_text_buffer_delete_mark(buffer, mark);
10866 if (strcmp(text, "\n") || compose->automatic_break
10867 || gtk_text_iter_starts_line(iter)) {
10868 GtkTextIter before_ins;
10869 gtk_text_buffer_insert(buffer, iter, text, len);
10870 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10871 before_ins = *iter;
10872 gtk_text_iter_backward_chars(&before_ins, len);
10873 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10876 /* check if the preceding is just whitespace or quote */
10877 GtkTextIter start_line;
10878 gchar *tmp = NULL, *quote = NULL;
10879 gint quote_len = 0, is_normal = 0;
10880 start_line = *iter;
10881 gtk_text_iter_set_line_offset(&start_line, 0);
10882 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10885 if (*tmp == '\0') {
10888 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10896 gtk_text_buffer_insert(buffer, iter, text, len);
10898 gtk_text_buffer_insert_with_tags_by_name(buffer,
10899 iter, text, len, "no_join", NULL);
10904 if (!paste_as_quotation) {
10905 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10906 compose_beautify_paragraph(compose, iter, FALSE);
10907 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10908 gtk_text_buffer_delete_mark(buffer, mark);
10911 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10912 G_CALLBACK(text_inserted),
10914 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10916 if (prefs_common.autosave &&
10917 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10918 compose->draft_timeout_tag != -2 /* disabled while loading */)
10919 compose->draft_timeout_tag = g_timeout_add
10920 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10922 static gint compose_defer_auto_save_draft(Compose *compose)
10924 compose->draft_timeout_tag = -1;
10925 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10930 static void compose_check_all(GtkAction *action, gpointer data)
10932 Compose *compose = (Compose *)data;
10933 if (!compose->gtkaspell)
10936 if (gtkut_widget_has_focus(compose->subject_entry))
10937 claws_spell_entry_check_all(
10938 CLAWS_SPELL_ENTRY(compose->subject_entry));
10940 gtkaspell_check_all(compose->gtkaspell);
10943 static void compose_highlight_all(GtkAction *action, gpointer data)
10945 Compose *compose = (Compose *)data;
10946 if (compose->gtkaspell) {
10947 claws_spell_entry_recheck_all(
10948 CLAWS_SPELL_ENTRY(compose->subject_entry));
10949 gtkaspell_highlight_all(compose->gtkaspell);
10953 static void compose_check_backwards(GtkAction *action, gpointer data)
10955 Compose *compose = (Compose *)data;
10956 if (!compose->gtkaspell) {
10957 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10961 if (gtkut_widget_has_focus(compose->subject_entry))
10962 claws_spell_entry_check_backwards(
10963 CLAWS_SPELL_ENTRY(compose->subject_entry));
10965 gtkaspell_check_backwards(compose->gtkaspell);
10968 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10970 Compose *compose = (Compose *)data;
10971 if (!compose->gtkaspell) {
10972 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10976 if (gtkut_widget_has_focus(compose->subject_entry))
10977 claws_spell_entry_check_forwards_go(
10978 CLAWS_SPELL_ENTRY(compose->subject_entry));
10980 gtkaspell_check_forwards_go(compose->gtkaspell);
10985 *\brief Guess originating forward account from MsgInfo and several
10986 * "common preference" settings. Return NULL if no guess.
10988 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10990 PrefsAccount *account = NULL;
10992 cm_return_val_if_fail(msginfo, NULL);
10993 cm_return_val_if_fail(msginfo->folder, NULL);
10994 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10996 if (msginfo->folder->prefs->enable_default_account)
10997 account = account_find_from_id(msginfo->folder->prefs->default_account);
11000 account = msginfo->folder->folder->account;
11002 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11004 Xstrdup_a(to, msginfo->to, return NULL);
11005 extract_address(to);
11006 account = account_find_from_address(to, FALSE);
11009 if (!account && prefs_common.forward_account_autosel) {
11010 gchar cc[BUFFSIZE];
11011 if (!procheader_get_header_from_msginfo
11012 (msginfo, cc,sizeof cc , "Cc:")) {
11013 gchar *buf = cc + strlen("Cc:");
11014 extract_address(buf);
11015 account = account_find_from_address(buf, FALSE);
11019 if (!account && prefs_common.forward_account_autosel) {
11020 gchar deliveredto[BUFFSIZE];
11021 if (!procheader_get_header_from_msginfo
11022 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11023 gchar *buf = deliveredto + strlen("Delivered-To:");
11024 extract_address(buf);
11025 account = account_find_from_address(buf, FALSE);
11032 gboolean compose_close(Compose *compose)
11036 if (!g_mutex_trylock(compose->mutex)) {
11037 /* we have to wait for the (possibly deferred by auto-save)
11038 * drafting to be done, before destroying the compose under
11040 debug_print("waiting for drafting to finish...\n");
11041 compose_allow_user_actions(compose, FALSE);
11042 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11045 cm_return_val_if_fail(compose, FALSE);
11046 gtkut_widget_get_uposition(compose->window, &x, &y);
11047 if (!compose->batch) {
11048 prefs_common.compose_x = x;
11049 prefs_common.compose_y = y;
11051 g_mutex_unlock(compose->mutex);
11052 compose_destroy(compose);
11057 * Add entry field for each address in list.
11058 * \param compose E-Mail composition object.
11059 * \param listAddress List of (formatted) E-Mail addresses.
11061 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11064 node = listAddress;
11066 addr = ( gchar * ) node->data;
11067 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11068 node = g_list_next( node );
11072 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11073 guint action, gboolean opening_multiple)
11075 gchar *body = NULL;
11076 GSList *new_msglist = NULL;
11077 MsgInfo *tmp_msginfo = NULL;
11078 gboolean originally_enc = FALSE;
11079 gboolean originally_sig = FALSE;
11080 Compose *compose = NULL;
11081 gchar *s_system = NULL;
11083 cm_return_if_fail(msgview != NULL);
11085 cm_return_if_fail(msginfo_list != NULL);
11087 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11088 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11089 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11091 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11092 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11093 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11094 orig_msginfo, mimeinfo);
11095 if (tmp_msginfo != NULL) {
11096 new_msglist = g_slist_append(NULL, tmp_msginfo);
11098 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11099 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11100 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11102 tmp_msginfo->folder = orig_msginfo->folder;
11103 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11104 if (orig_msginfo->tags) {
11105 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11106 tmp_msginfo->folder->tags_dirty = TRUE;
11112 if (!opening_multiple)
11113 body = messageview_get_selection(msgview);
11116 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11117 procmsg_msginfo_free(tmp_msginfo);
11118 g_slist_free(new_msglist);
11120 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11122 if (compose && originally_enc) {
11123 compose_force_encryption(compose, compose->account, FALSE, s_system);
11126 if (compose && originally_sig && compose->account->default_sign_reply) {
11127 compose_force_signing(compose, compose->account, s_system);
11131 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11134 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11137 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11138 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11139 GSList *cur = msginfo_list;
11140 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11141 "messages. Opening the windows "
11142 "could take some time. Do you "
11143 "want to continue?"),
11144 g_slist_length(msginfo_list));
11145 if (g_slist_length(msginfo_list) > 9
11146 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11147 != G_ALERTALTERNATE) {
11152 /* We'll open multiple compose windows */
11153 /* let the WM place the next windows */
11154 compose_force_window_origin = FALSE;
11155 for (; cur; cur = cur->next) {
11157 tmplist.data = cur->data;
11158 tmplist.next = NULL;
11159 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11161 compose_force_window_origin = TRUE;
11163 /* forwarding multiple mails as attachments is done via a
11164 * single compose window */
11165 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11169 void compose_check_for_email_account(Compose *compose)
11171 PrefsAccount *ac = NULL, *curr = NULL;
11177 if (compose->account && compose->account->protocol == A_NNTP) {
11178 ac = account_get_cur_account();
11179 if (ac->protocol == A_NNTP) {
11180 list = account_get_list();
11182 for( ; list != NULL ; list = g_list_next(list)) {
11183 curr = (PrefsAccount *) list->data;
11184 if (curr->protocol != A_NNTP) {
11190 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11195 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11196 const gchar *address)
11198 GSList *msginfo_list = NULL;
11199 gchar *body = messageview_get_selection(msgview);
11202 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11204 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11205 compose_check_for_email_account(compose);
11206 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11207 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11208 compose_reply_set_subject(compose, msginfo);
11211 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11214 void compose_set_position(Compose *compose, gint pos)
11216 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11218 gtkut_text_view_set_position(text, pos);
11221 gboolean compose_search_string(Compose *compose,
11222 const gchar *str, gboolean case_sens)
11224 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11226 return gtkut_text_view_search_string(text, str, case_sens);
11229 gboolean compose_search_string_backward(Compose *compose,
11230 const gchar *str, gboolean case_sens)
11232 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11234 return gtkut_text_view_search_string_backward(text, str, case_sens);
11237 /* allocate a msginfo structure and populate its data from a compose data structure */
11238 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11240 MsgInfo *newmsginfo;
11242 gchar buf[BUFFSIZE];
11244 cm_return_val_if_fail( compose != NULL, NULL );
11246 newmsginfo = procmsg_msginfo_new();
11249 get_rfc822_date(buf, sizeof(buf));
11250 newmsginfo->date = g_strdup(buf);
11253 if (compose->from_name) {
11254 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11255 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11259 if (compose->subject_entry)
11260 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11262 /* to, cc, reply-to, newsgroups */
11263 for (list = compose->header_list; list; list = list->next) {
11264 gchar *header = gtk_editable_get_chars(
11266 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11267 gchar *entry = gtk_editable_get_chars(
11268 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11270 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11271 if ( newmsginfo->to == NULL ) {
11272 newmsginfo->to = g_strdup(entry);
11273 } else if (entry && *entry) {
11274 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11275 g_free(newmsginfo->to);
11276 newmsginfo->to = tmp;
11279 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11280 if ( newmsginfo->cc == NULL ) {
11281 newmsginfo->cc = g_strdup(entry);
11282 } else if (entry && *entry) {
11283 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11284 g_free(newmsginfo->cc);
11285 newmsginfo->cc = tmp;
11288 if ( strcasecmp(header,
11289 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11290 if ( newmsginfo->newsgroups == NULL ) {
11291 newmsginfo->newsgroups = g_strdup(entry);
11292 } else if (entry && *entry) {
11293 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11294 g_free(newmsginfo->newsgroups);
11295 newmsginfo->newsgroups = tmp;
11303 /* other data is unset */
11309 /* update compose's dictionaries from folder dict settings */
11310 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11311 FolderItem *folder_item)
11313 cm_return_if_fail(compose != NULL);
11315 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11316 FolderItemPrefs *prefs = folder_item->prefs;
11318 if (prefs->enable_default_dictionary)
11319 gtkaspell_change_dict(compose->gtkaspell,
11320 prefs->default_dictionary, FALSE);
11321 if (folder_item->prefs->enable_default_alt_dictionary)
11322 gtkaspell_change_alt_dict(compose->gtkaspell,
11323 prefs->default_alt_dictionary);
11324 if (prefs->enable_default_dictionary
11325 || prefs->enable_default_alt_dictionary)
11326 compose_spell_menu_changed(compose);