2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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 PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
530 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
533 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
534 FolderItem *folder_item);
536 static void compose_attach_update_label(Compose *compose);
537 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
538 gboolean respect_default_to);
540 static GtkActionEntry compose_popup_entries[] =
542 {"Compose", NULL, "Compose" },
543 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
544 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
545 {"Compose/---", NULL, "---", NULL, NULL, NULL },
546 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
549 static GtkActionEntry compose_entries[] =
551 {"Menu", NULL, "Menu" },
553 {"Message", NULL, N_("_Message") },
554 {"Edit", NULL, N_("_Edit") },
556 {"Spelling", NULL, N_("_Spelling") },
558 {"Options", NULL, N_("_Options") },
559 {"Tools", NULL, N_("_Tools") },
560 {"Help", NULL, N_("_Help") },
562 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
563 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
564 {"Message/---", NULL, "---" },
566 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
567 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
568 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
569 /* {"Message/---", NULL, "---" }, */
570 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
571 /* {"Message/---", NULL, "---" }, */
572 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
575 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
576 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
577 {"Edit/---", NULL, "---" },
579 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
580 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
581 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
583 {"Edit/SpecialPaste", NULL, N_("Special paste") },
584 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
585 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
586 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
588 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
590 {"Edit/Advanced", NULL, N_("A_dvanced") },
591 {"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*/
592 {"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*/
593 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
594 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
595 {"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*/
596 {"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*/
597 {"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*/
598 {"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*/
599 {"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*/
600 {"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*/
601 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
602 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
603 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
604 {"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*/
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
611 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
612 /* {"Edit/---", NULL, "---" }, */
613 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
616 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
617 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
618 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
619 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
621 {"Spelling/---", NULL, "---" },
622 {"Spelling/Options", NULL, N_("_Options") },
627 {"Options/ReplyMode", NULL, N_("Reply _mode") },
628 {"Options/---", NULL, "---" },
629 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
630 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
632 /* {"Options/---", NULL, "---" }, */
634 {"Options/Priority", NULL, N_("_Priority") },
636 {"Options/Encoding", NULL, N_("Character _encoding") },
637 {"Options/Encoding/---", NULL, "---" },
638 #define ENC_ACTION(cs_char,c_char,string) \
639 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
641 {"Options/Encoding/Western", NULL, N_("Western European") },
642 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
643 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
644 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
645 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
646 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
647 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
648 {"Options/Encoding/Korean", NULL, N_("Korean") },
649 {"Options/Encoding/Thai", NULL, N_("Thai") },
652 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
654 {"Tools/Template", NULL, N_("_Template") },
655 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 {"Tools/Actions", NULL, N_("Actio_ns") },
657 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
660 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
663 static GtkToggleActionEntry compose_toggle_entries[] =
665 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
666 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
667 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
668 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
669 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
670 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
671 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
674 static GtkRadioActionEntry compose_radio_rm_entries[] =
676 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
677 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
682 static GtkRadioActionEntry compose_radio_prio_entries[] =
684 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
691 static GtkRadioActionEntry compose_radio_enc_entries[] =
693 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
727 static GtkTargetEntry compose_mime_types[] =
729 {"text/uri-list", 0, 0},
730 {"UTF8_STRING", 0, 0},
734 static gboolean compose_put_existing_to_front(MsgInfo *info)
736 GList *compose_list = compose_get_compose_list();
740 for (elem = compose_list; elem != NULL && elem->data != NULL;
742 Compose *c = (Compose*)elem->data;
744 if (!c->targetinfo || !c->targetinfo->msgid ||
748 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
749 gtkut_window_popup(c->window);
757 static GdkColor quote_color1 =
758 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor quote_color2 =
760 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor quote_color3 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_bgcolor1 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
766 static GdkColor quote_bgcolor2 =
767 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor quote_bgcolor3 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
771 static GdkColor signature_color = {
778 static GdkColor uri_color = {
785 static void compose_create_tags(GtkTextView *text, Compose *compose)
787 GtkTextBuffer *buffer;
788 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
794 buffer = gtk_text_view_get_buffer(text);
796 if (prefs_common.enable_color) {
797 /* grab the quote colors, converting from an int to a GdkColor */
798 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
800 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
806 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
810 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
812 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
815 signature_color = quote_color1 = quote_color2 = quote_color3 =
816 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
819 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
820 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
821 "foreground-gdk", "e_color1,
822 "paragraph-background-gdk", "e_bgcolor1,
824 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
825 "foreground-gdk", "e_color2,
826 "paragraph-background-gdk", "e_bgcolor2,
828 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
829 "foreground-gdk", "e_color3,
830 "paragraph-background-gdk", "e_bgcolor3,
833 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
834 "foreground-gdk", "e_color1,
836 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
837 "foreground-gdk", "e_color2,
839 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
840 "foreground-gdk", "e_color3,
844 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
845 "foreground-gdk", &signature_color,
848 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
849 "foreground-gdk", &uri_color,
851 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
852 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
854 color[0] = quote_color1;
855 color[1] = quote_color2;
856 color[2] = quote_color3;
857 color[3] = quote_bgcolor1;
858 color[4] = quote_bgcolor2;
859 color[5] = quote_bgcolor3;
860 color[6] = signature_color;
861 color[7] = uri_color;
862 cmap = gdk_drawable_get_colormap(compose->window->window);
863 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
865 for (i = 0; i < 8; i++) {
866 if (success[i] == FALSE) {
869 g_warning("Compose: color allocation failed.\n");
870 style = gtk_widget_get_style(GTK_WIDGET(text));
871 quote_color1 = quote_color2 = quote_color3 =
872 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
873 signature_color = uri_color = black;
878 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
879 GPtrArray *attach_files)
881 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
884 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
886 return compose_generic_new(account, mailto, item, NULL, NULL);
889 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
891 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
894 #define SCROLL_TO_CURSOR(compose) { \
895 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
896 gtk_text_view_get_buffer( \
897 GTK_TEXT_VIEW(compose->text))); \
898 gtk_text_view_scroll_mark_onscreen( \
899 GTK_TEXT_VIEW(compose->text), \
903 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
906 if (folderidentifier) {
907 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
908 prefs_common.compose_save_to_history = add_history(
909 prefs_common.compose_save_to_history, folderidentifier);
910 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
911 prefs_common.compose_save_to_history);
914 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
915 if (folderidentifier)
916 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
918 gtk_entry_set_text(GTK_ENTRY(entry), "");
921 static gchar *compose_get_save_to(Compose *compose)
924 gchar *result = NULL;
925 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
926 result = gtk_editable_get_chars(entry, 0, -1);
929 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
930 prefs_common.compose_save_to_history = add_history(
931 prefs_common.compose_save_to_history, result);
932 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
933 prefs_common.compose_save_to_history);
938 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
939 GPtrArray *attach_files, GList *listAddress )
942 GtkTextView *textview;
943 GtkTextBuffer *textbuf;
945 const gchar *subject_format = NULL;
946 const gchar *body_format = NULL;
947 gchar *mailto_from = NULL;
948 PrefsAccount *mailto_account = NULL;
949 MsgInfo* dummyinfo = NULL;
950 gint cursor_pos = -1;
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 = g_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 = g_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 cursor_pos = quote_fmt_get_cursor_pos();
1202 if (cursor_pos == -1)
1203 gtk_widget_grab_focus(compose->header_last->entry);
1205 gtk_widget_grab_focus(compose->text);
1209 undo_unblock(compose->undostruct);
1211 if (prefs_common.auto_exteditor)
1212 compose_exec_ext_editor(compose);
1214 compose->draft_timeout_tag = -1;
1215 SCROLL_TO_CURSOR(compose);
1217 compose->modified = FALSE;
1218 compose_set_title(compose);
1220 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1225 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1226 gboolean override_pref, const gchar *system)
1228 const gchar *privacy = NULL;
1230 cm_return_if_fail(compose != NULL);
1231 cm_return_if_fail(account != NULL);
1233 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1238 else if (account->default_privacy_system
1239 && strlen(account->default_privacy_system)) {
1240 privacy = account->default_privacy_system;
1242 GSList *privacy_avail = privacy_get_system_ids();
1243 if (privacy_avail && g_slist_length(privacy_avail)) {
1244 privacy = (gchar *)(privacy_avail->data);
1247 if (privacy != NULL) {
1249 g_free(compose->privacy_system);
1250 compose->privacy_system = NULL;
1252 if (compose->privacy_system == NULL)
1253 compose->privacy_system = g_strdup(privacy);
1254 else if (*(compose->privacy_system) == '\0') {
1255 g_free(compose->privacy_system);
1256 compose->privacy_system = g_strdup(privacy);
1258 compose_update_privacy_system_menu_item(compose, FALSE);
1259 compose_use_encryption(compose, TRUE);
1263 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1265 const gchar *privacy = NULL;
1269 else if (account->default_privacy_system
1270 && strlen(account->default_privacy_system)) {
1271 privacy = account->default_privacy_system;
1273 GSList *privacy_avail = privacy_get_system_ids();
1274 if (privacy_avail && g_slist_length(privacy_avail)) {
1275 privacy = (gchar *)(privacy_avail->data);
1279 if (privacy != NULL) {
1281 g_free(compose->privacy_system);
1282 compose->privacy_system = NULL;
1284 if (compose->privacy_system == NULL)
1285 compose->privacy_system = g_strdup(privacy);
1286 compose_update_privacy_system_menu_item(compose, FALSE);
1287 compose_use_signing(compose, TRUE);
1291 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1295 Compose *compose = NULL;
1297 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1299 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1300 cm_return_val_if_fail(msginfo != NULL, NULL);
1302 list_len = g_slist_length(msginfo_list);
1306 case COMPOSE_REPLY_TO_ADDRESS:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1308 FALSE, prefs_common.default_reply_list, FALSE, body);
1310 case COMPOSE_REPLY_WITH_QUOTE:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1312 FALSE, prefs_common.default_reply_list, FALSE, body);
1314 case COMPOSE_REPLY_WITHOUT_QUOTE:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1316 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1318 case COMPOSE_REPLY_TO_SENDER:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1320 FALSE, FALSE, TRUE, body);
1322 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1323 compose = compose_followup_and_reply_to(msginfo,
1324 COMPOSE_QUOTE_CHECK,
1325 FALSE, FALSE, body);
1327 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1329 FALSE, FALSE, TRUE, body);
1331 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1333 FALSE, FALSE, TRUE, NULL);
1335 case COMPOSE_REPLY_TO_ALL:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1337 TRUE, FALSE, FALSE, body);
1339 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1341 TRUE, FALSE, FALSE, body);
1343 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1345 TRUE, FALSE, FALSE, NULL);
1347 case COMPOSE_REPLY_TO_LIST:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1349 FALSE, TRUE, FALSE, body);
1351 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1353 FALSE, TRUE, FALSE, body);
1355 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1357 FALSE, TRUE, FALSE, NULL);
1359 case COMPOSE_FORWARD:
1360 if (prefs_common.forward_as_attachment) {
1361 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1364 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1368 case COMPOSE_FORWARD_INLINE:
1369 /* check if we reply to more than one Message */
1370 if (list_len == 1) {
1371 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1374 /* more messages FALL THROUGH */
1375 case COMPOSE_FORWARD_AS_ATTACH:
1376 compose = compose_forward_multiple(NULL, msginfo_list);
1378 case COMPOSE_REDIRECT:
1379 compose = compose_redirect(NULL, msginfo, FALSE);
1382 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1385 if (compose == NULL) {
1386 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1390 compose->rmode = mode;
1391 switch (compose->rmode) {
1393 case COMPOSE_REPLY_WITH_QUOTE:
1394 case COMPOSE_REPLY_WITHOUT_QUOTE:
1395 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1396 debug_print("reply mode Normal\n");
1397 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1398 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1400 case COMPOSE_REPLY_TO_SENDER:
1401 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1402 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1403 debug_print("reply mode Sender\n");
1404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1406 case COMPOSE_REPLY_TO_ALL:
1407 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1408 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1409 debug_print("reply mode All\n");
1410 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1412 case COMPOSE_REPLY_TO_LIST:
1413 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1414 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1415 debug_print("reply mode List\n");
1416 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1418 case COMPOSE_REPLY_TO_ADDRESS:
1419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1427 static Compose *compose_reply(MsgInfo *msginfo,
1428 ComposeQuoteMode quote_mode,
1434 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1435 to_sender, FALSE, body);
1438 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1439 ComposeQuoteMode quote_mode,
1444 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1445 to_sender, TRUE, body);
1448 static void compose_extract_original_charset(Compose *compose)
1450 MsgInfo *info = NULL;
1451 if (compose->replyinfo) {
1452 info = compose->replyinfo;
1453 } else if (compose->fwdinfo) {
1454 info = compose->fwdinfo;
1455 } else if (compose->targetinfo) {
1456 info = compose->targetinfo;
1459 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1460 MimeInfo *partinfo = mimeinfo;
1461 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1462 partinfo = procmime_mimeinfo_next(partinfo);
1464 compose->orig_charset =
1465 g_strdup(procmime_mimeinfo_get_parameter(
1466 partinfo, "charset"));
1468 procmime_mimeinfo_free_all(mimeinfo);
1472 #define SIGNAL_BLOCK(buffer) { \
1473 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1474 G_CALLBACK(compose_changed_cb), \
1476 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1477 G_CALLBACK(text_inserted), \
1481 #define SIGNAL_UNBLOCK(buffer) { \
1482 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1483 G_CALLBACK(compose_changed_cb), \
1485 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1486 G_CALLBACK(text_inserted), \
1490 static Compose *compose_generic_reply(MsgInfo *msginfo,
1491 ComposeQuoteMode quote_mode,
1492 gboolean to_all, gboolean to_ml,
1494 gboolean followup_and_reply_to,
1498 PrefsAccount *account = NULL;
1499 GtkTextView *textview;
1500 GtkTextBuffer *textbuf;
1501 gboolean quote = FALSE;
1502 const gchar *qmark = NULL;
1503 const gchar *body_fmt = NULL;
1504 gchar *s_system = NULL;
1506 cm_return_val_if_fail(msginfo != NULL, NULL);
1507 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1509 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1511 cm_return_val_if_fail(account != NULL, NULL);
1513 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1515 compose->updating = TRUE;
1517 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1518 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1520 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1521 if (!compose->replyinfo)
1522 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1524 compose_extract_original_charset(compose);
1526 if (msginfo->folder && msginfo->folder->ret_rcpt)
1527 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1529 /* Set save folder */
1530 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1531 gchar *folderidentifier;
1533 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1534 folderidentifier = folder_item_get_identifier(msginfo->folder);
1535 compose_set_save_to(compose, folderidentifier);
1536 g_free(folderidentifier);
1539 if (compose_parse_header(compose, msginfo) < 0) {
1540 compose->updating = FALSE;
1541 compose_destroy(compose);
1545 /* override from name according to folder properties */
1546 if (msginfo->folder && msginfo->folder->prefs &&
1547 msginfo->folder->prefs->reply_with_format &&
1548 msginfo->folder->prefs->reply_override_from_format &&
1549 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1554 /* decode \-escape sequences in the internal representation of the quote format */
1555 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1556 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1559 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1560 compose->gtkaspell);
1562 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1564 quote_fmt_scan_string(tmp);
1567 buf = quote_fmt_get_buffer();
1569 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1571 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1572 quote_fmt_reset_vartable();
1577 textview = (GTK_TEXT_VIEW(compose->text));
1578 textbuf = gtk_text_view_get_buffer(textview);
1579 compose_create_tags(textview, compose);
1581 undo_block(compose->undostruct);
1583 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1586 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1587 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1588 /* use the reply format of folder (if enabled), or the account's one
1589 (if enabled) or fallback to the global reply format, which is always
1590 enabled (even if empty), and use the relevant quotemark */
1592 if (msginfo->folder && msginfo->folder->prefs &&
1593 msginfo->folder->prefs->reply_with_format) {
1594 qmark = msginfo->folder->prefs->reply_quotemark;
1595 body_fmt = msginfo->folder->prefs->reply_body_format;
1597 } else if (account->reply_with_format) {
1598 qmark = account->reply_quotemark;
1599 body_fmt = account->reply_body_format;
1602 qmark = prefs_common.quotemark;
1603 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1604 body_fmt = gettext(prefs_common.quotefmt);
1611 /* empty quotemark is not allowed */
1612 if (qmark == NULL || *qmark == '\0')
1614 compose_quote_fmt(compose, compose->replyinfo,
1615 body_fmt, qmark, body, FALSE, TRUE,
1616 _("The body of the \"Reply\" template has an error at line %d."));
1617 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1618 quote_fmt_reset_vartable();
1620 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1621 gtkaspell_highlight_all(compose->gtkaspell);
1625 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1626 compose_force_encryption(compose, account, FALSE, s_system);
1629 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1630 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1631 compose_force_signing(compose, account, s_system);
1635 SIGNAL_BLOCK(textbuf);
1637 if (account->auto_sig)
1638 compose_insert_sig(compose, FALSE);
1640 compose_wrap_all(compose);
1642 SIGNAL_UNBLOCK(textbuf);
1644 gtk_widget_grab_focus(compose->text);
1646 undo_unblock(compose->undostruct);
1648 if (prefs_common.auto_exteditor)
1649 compose_exec_ext_editor(compose);
1651 compose->modified = FALSE;
1652 compose_set_title(compose);
1654 compose->updating = FALSE;
1655 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1656 SCROLL_TO_CURSOR(compose);
1658 if (compose->deferred_destroy) {
1659 compose_destroy(compose);
1667 #define INSERT_FW_HEADER(var, hdr) \
1668 if (msginfo->var && *msginfo->var) { \
1669 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1670 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1671 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1674 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1675 gboolean as_attach, const gchar *body,
1676 gboolean no_extedit,
1680 GtkTextView *textview;
1681 GtkTextBuffer *textbuf;
1682 gint cursor_pos = -1;
1685 cm_return_val_if_fail(msginfo != NULL, NULL);
1686 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1689 !(account = compose_guess_forward_account_from_msginfo
1691 account = cur_account;
1693 if (!prefs_common.forward_as_attachment)
1694 mode = COMPOSE_FORWARD_INLINE;
1696 mode = COMPOSE_FORWARD;
1697 compose = compose_create(account, msginfo->folder, mode, batch);
1699 compose->updating = TRUE;
1700 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1701 if (!compose->fwdinfo)
1702 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1704 compose_extract_original_charset(compose);
1706 if (msginfo->subject && *msginfo->subject) {
1707 gchar *buf, *buf2, *p;
1709 buf = p = g_strdup(msginfo->subject);
1710 p += subject_get_prefix_length(p);
1711 memmove(buf, p, strlen(p) + 1);
1713 buf2 = g_strdup_printf("Fw: %s", buf);
1714 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1720 /* override from name according to folder properties */
1721 if (msginfo->folder && msginfo->folder->prefs &&
1722 msginfo->folder->prefs->forward_with_format &&
1723 msginfo->folder->prefs->forward_override_from_format &&
1724 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1728 MsgInfo *full_msginfo = NULL;
1731 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1733 full_msginfo = procmsg_msginfo_copy(msginfo);
1735 /* decode \-escape sequences in the internal representation of the quote format */
1736 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1737 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1740 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1741 compose->gtkaspell);
1743 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1745 quote_fmt_scan_string(tmp);
1748 buf = quote_fmt_get_buffer();
1750 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1752 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1753 quote_fmt_reset_vartable();
1756 procmsg_msginfo_free(full_msginfo);
1759 textview = GTK_TEXT_VIEW(compose->text);
1760 textbuf = gtk_text_view_get_buffer(textview);
1761 compose_create_tags(textview, compose);
1763 undo_block(compose->undostruct);
1767 msgfile = procmsg_get_message_file(msginfo);
1768 if (!is_file_exist(msgfile))
1769 g_warning("%s: file not exist\n", msgfile);
1771 compose_attach_append(compose, msgfile, msgfile,
1772 "message/rfc822", NULL);
1776 const gchar *qmark = NULL;
1777 const gchar *body_fmt = NULL;
1778 MsgInfo *full_msginfo;
1780 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1781 body_fmt = gettext(prefs_common.fw_quotefmt);
1785 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1787 full_msginfo = procmsg_msginfo_copy(msginfo);
1789 /* use the forward format of folder (if enabled), or the account's one
1790 (if enabled) or fallback to the global forward format, which is always
1791 enabled (even if empty), and use the relevant quotemark */
1792 if (msginfo->folder && msginfo->folder->prefs &&
1793 msginfo->folder->prefs->forward_with_format) {
1794 qmark = msginfo->folder->prefs->forward_quotemark;
1795 body_fmt = msginfo->folder->prefs->forward_body_format;
1797 } else if (account->forward_with_format) {
1798 qmark = account->forward_quotemark;
1799 body_fmt = account->forward_body_format;
1802 qmark = prefs_common.fw_quotemark;
1803 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1804 body_fmt = gettext(prefs_common.fw_quotefmt);
1809 /* empty quotemark is not allowed */
1810 if (qmark == NULL || *qmark == '\0')
1813 compose_quote_fmt(compose, full_msginfo,
1814 body_fmt, qmark, body, FALSE, TRUE,
1815 _("The body of the \"Forward\" template has an error at line %d."));
1816 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1817 quote_fmt_reset_vartable();
1818 compose_attach_parts(compose, msginfo);
1820 procmsg_msginfo_free(full_msginfo);
1822 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1823 gtkaspell_highlight_all(compose->gtkaspell);
1827 SIGNAL_BLOCK(textbuf);
1829 if (account->auto_sig)
1830 compose_insert_sig(compose, FALSE);
1832 compose_wrap_all(compose);
1834 SIGNAL_UNBLOCK(textbuf);
1836 cursor_pos = quote_fmt_get_cursor_pos();
1837 if (cursor_pos == -1)
1838 gtk_widget_grab_focus(compose->header_last->entry);
1840 gtk_widget_grab_focus(compose->text);
1842 if (!no_extedit && prefs_common.auto_exteditor)
1843 compose_exec_ext_editor(compose);
1846 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1847 gchar *folderidentifier;
1849 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1850 folderidentifier = folder_item_get_identifier(msginfo->folder);
1851 compose_set_save_to(compose, folderidentifier);
1852 g_free(folderidentifier);
1855 undo_unblock(compose->undostruct);
1857 compose->modified = FALSE;
1858 compose_set_title(compose);
1860 compose->updating = FALSE;
1861 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1862 SCROLL_TO_CURSOR(compose);
1864 if (compose->deferred_destroy) {
1865 compose_destroy(compose);
1869 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1874 #undef INSERT_FW_HEADER
1876 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1879 GtkTextView *textview;
1880 GtkTextBuffer *textbuf;
1884 gboolean single_mail = TRUE;
1886 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1888 if (g_slist_length(msginfo_list) > 1)
1889 single_mail = FALSE;
1891 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1892 if (((MsgInfo *)msginfo->data)->folder == NULL)
1895 /* guess account from first selected message */
1897 !(account = compose_guess_forward_account_from_msginfo
1898 (msginfo_list->data)))
1899 account = cur_account;
1901 cm_return_val_if_fail(account != NULL, NULL);
1903 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1904 if (msginfo->data) {
1905 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1906 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1910 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1911 g_warning("no msginfo_list");
1915 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1917 compose->updating = TRUE;
1919 /* override from name according to folder properties */
1920 if (msginfo_list->data) {
1921 MsgInfo *msginfo = msginfo_list->data;
1923 if (msginfo->folder && msginfo->folder->prefs &&
1924 msginfo->folder->prefs->forward_with_format &&
1925 msginfo->folder->prefs->forward_override_from_format &&
1926 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1931 /* decode \-escape sequences in the internal representation of the quote format */
1932 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1933 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1936 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1937 compose->gtkaspell);
1939 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1941 quote_fmt_scan_string(tmp);
1944 buf = quote_fmt_get_buffer();
1946 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1948 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1949 quote_fmt_reset_vartable();
1955 textview = GTK_TEXT_VIEW(compose->text);
1956 textbuf = gtk_text_view_get_buffer(textview);
1957 compose_create_tags(textview, compose);
1959 undo_block(compose->undostruct);
1960 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1961 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1963 if (!is_file_exist(msgfile))
1964 g_warning("%s: file not exist\n", msgfile);
1966 compose_attach_append(compose, msgfile, msgfile,
1967 "message/rfc822", NULL);
1972 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1973 if (info->subject && *info->subject) {
1974 gchar *buf, *buf2, *p;
1976 buf = p = g_strdup(info->subject);
1977 p += subject_get_prefix_length(p);
1978 memmove(buf, p, strlen(p) + 1);
1980 buf2 = g_strdup_printf("Fw: %s", buf);
1981 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1987 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1988 _("Fw: multiple emails"));
1991 SIGNAL_BLOCK(textbuf);
1993 if (account->auto_sig)
1994 compose_insert_sig(compose, FALSE);
1996 compose_wrap_all(compose);
1998 SIGNAL_UNBLOCK(textbuf);
2000 gtk_text_buffer_get_start_iter(textbuf, &iter);
2001 gtk_text_buffer_place_cursor(textbuf, &iter);
2003 gtk_widget_grab_focus(compose->header_last->entry);
2004 undo_unblock(compose->undostruct);
2005 compose->modified = FALSE;
2006 compose_set_title(compose);
2008 compose->updating = FALSE;
2009 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2010 SCROLL_TO_CURSOR(compose);
2012 if (compose->deferred_destroy) {
2013 compose_destroy(compose);
2017 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2022 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2024 GtkTextIter start = *iter;
2025 GtkTextIter end_iter;
2026 int start_pos = gtk_text_iter_get_offset(&start);
2028 if (!compose->account->sig_sep)
2031 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2032 start_pos+strlen(compose->account->sig_sep));
2034 /* check sig separator */
2035 str = gtk_text_iter_get_text(&start, &end_iter);
2036 if (!strcmp(str, compose->account->sig_sep)) {
2038 /* check end of line (\n) */
2039 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2040 start_pos+strlen(compose->account->sig_sep));
2041 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2042 start_pos+strlen(compose->account->sig_sep)+1);
2043 tmp = gtk_text_iter_get_text(&start, &end_iter);
2044 if (!strcmp(tmp,"\n")) {
2056 static void compose_colorize_signature(Compose *compose)
2058 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2060 GtkTextIter end_iter;
2061 gtk_text_buffer_get_start_iter(buffer, &iter);
2062 while (gtk_text_iter_forward_line(&iter))
2063 if (compose_is_sig_separator(compose, buffer, &iter)) {
2064 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2065 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2069 #define BLOCK_WRAP() { \
2070 prev_autowrap = compose->autowrap; \
2071 buffer = gtk_text_view_get_buffer( \
2072 GTK_TEXT_VIEW(compose->text)); \
2073 compose->autowrap = FALSE; \
2075 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2076 G_CALLBACK(compose_changed_cb), \
2078 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2079 G_CALLBACK(text_inserted), \
2082 #define UNBLOCK_WRAP() { \
2083 compose->autowrap = prev_autowrap; \
2084 if (compose->autowrap) { \
2085 gint old = compose->draft_timeout_tag; \
2086 compose->draft_timeout_tag = -2; \
2087 compose_wrap_all(compose); \
2088 compose->draft_timeout_tag = old; \
2091 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2092 G_CALLBACK(compose_changed_cb), \
2094 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2095 G_CALLBACK(text_inserted), \
2099 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2101 Compose *compose = NULL;
2102 PrefsAccount *account = NULL;
2103 GtkTextView *textview;
2104 GtkTextBuffer *textbuf;
2108 gchar buf[BUFFSIZE];
2109 gboolean use_signing = FALSE;
2110 gboolean use_encryption = FALSE;
2111 gchar *privacy_system = NULL;
2112 int priority = PRIORITY_NORMAL;
2113 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2114 gboolean autowrap = prefs_common.autowrap;
2115 gboolean autoindent = prefs_common.auto_indent;
2117 cm_return_val_if_fail(msginfo != NULL, NULL);
2118 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2120 if (compose_put_existing_to_front(msginfo)) {
2124 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2125 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2126 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2127 gchar queueheader_buf[BUFFSIZE];
2130 /* Select Account from queue headers */
2131 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2132 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2133 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2134 account = account_find_from_id(id);
2136 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2138 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2139 account = account_find_from_id(id);
2141 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2142 sizeof(queueheader_buf), "NAID:")) {
2143 id = atoi(&queueheader_buf[strlen("NAID:")]);
2144 account = account_find_from_id(id);
2146 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2147 sizeof(queueheader_buf), "MAID:")) {
2148 id = atoi(&queueheader_buf[strlen("MAID:")]);
2149 account = account_find_from_id(id);
2151 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2152 sizeof(queueheader_buf), "S:")) {
2153 account = account_find_from_address(queueheader_buf, FALSE);
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2157 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2158 use_signing = param;
2161 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2162 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2163 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2164 use_signing = param;
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2169 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2170 use_encryption = param;
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2174 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2175 use_encryption = param;
2177 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2179 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2182 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2184 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2189 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2191 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2192 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2193 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "X-Priority: ")) {
2197 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2200 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2201 sizeof(queueheader_buf), "RMID:")) {
2202 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2203 if (tokens[0] && tokens[1] && tokens[2]) {
2204 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2205 if (orig_item != NULL) {
2206 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2211 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2212 sizeof(queueheader_buf), "FMID:")) {
2213 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2214 if (tokens[0] && tokens[1] && tokens[2]) {
2215 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2216 if (orig_item != NULL) {
2217 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2223 account = msginfo->folder->folder->account;
2226 if (!account && prefs_common.reedit_account_autosel) {
2227 gchar from[BUFFSIZE];
2228 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2229 extract_address(from);
2230 account = account_find_from_address(from, FALSE);
2234 account = cur_account;
2236 cm_return_val_if_fail(account != NULL, NULL);
2238 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2240 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2241 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2242 compose->autowrap = autowrap;
2243 compose->replyinfo = replyinfo;
2244 compose->fwdinfo = fwdinfo;
2246 compose->updating = TRUE;
2247 compose->priority = priority;
2249 if (privacy_system != NULL) {
2250 compose->privacy_system = privacy_system;
2251 compose_use_signing(compose, use_signing);
2252 compose_use_encryption(compose, use_encryption);
2253 compose_update_privacy_system_menu_item(compose, FALSE);
2255 activate_privacy_system(compose, account, FALSE);
2258 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2260 compose_extract_original_charset(compose);
2262 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2263 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2264 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2265 gchar queueheader_buf[BUFFSIZE];
2267 /* Set message save folder */
2268 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2269 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2270 compose_set_save_to(compose, &queueheader_buf[4]);
2272 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2273 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2275 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2280 if (compose_parse_header(compose, msginfo) < 0) {
2281 compose->updating = FALSE;
2282 compose_destroy(compose);
2285 compose_reedit_set_entry(compose, msginfo);
2287 textview = GTK_TEXT_VIEW(compose->text);
2288 textbuf = gtk_text_view_get_buffer(textview);
2289 compose_create_tags(textview, compose);
2291 mark = gtk_text_buffer_get_insert(textbuf);
2292 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2294 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2295 G_CALLBACK(compose_changed_cb),
2298 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2299 fp = procmime_get_first_encrypted_text_content(msginfo);
2301 compose_force_encryption(compose, account, TRUE, NULL);
2304 fp = procmime_get_first_text_content(msginfo);
2307 g_warning("Can't get text part\n");
2311 gboolean prev_autowrap = compose->autowrap;
2312 GtkTextBuffer *buffer = textbuf;
2314 while (fgets(buf, sizeof(buf), fp) != NULL) {
2316 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2322 compose_attach_parts(compose, msginfo);
2324 compose_colorize_signature(compose);
2326 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2327 G_CALLBACK(compose_changed_cb),
2330 gtk_widget_grab_focus(compose->text);
2332 if (prefs_common.auto_exteditor) {
2333 compose_exec_ext_editor(compose);
2335 compose->modified = FALSE;
2336 compose_set_title(compose);
2338 compose->updating = FALSE;
2339 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2340 SCROLL_TO_CURSOR(compose);
2342 if (compose->deferred_destroy) {
2343 compose_destroy(compose);
2347 compose->sig_str = account_get_signature_str(compose->account);
2349 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2354 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2361 cm_return_val_if_fail(msginfo != NULL, NULL);
2364 account = account_get_reply_account(msginfo,
2365 prefs_common.reply_account_autosel);
2366 cm_return_val_if_fail(account != NULL, NULL);
2368 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2370 compose->updating = TRUE;
2372 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2373 compose->replyinfo = NULL;
2374 compose->fwdinfo = NULL;
2376 compose_show_first_last_header(compose, TRUE);
2378 gtk_widget_grab_focus(compose->header_last->entry);
2380 filename = procmsg_get_message_file(msginfo);
2382 if (filename == NULL) {
2383 compose->updating = FALSE;
2384 compose_destroy(compose);
2389 compose->redirect_filename = filename;
2391 /* Set save folder */
2392 item = msginfo->folder;
2393 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2394 gchar *folderidentifier;
2396 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2397 folderidentifier = folder_item_get_identifier(item);
2398 compose_set_save_to(compose, folderidentifier);
2399 g_free(folderidentifier);
2402 compose_attach_parts(compose, msginfo);
2404 if (msginfo->subject)
2405 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2407 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2409 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2410 _("The body of the \"Redirect\" template has an error at line %d."));
2411 quote_fmt_reset_vartable();
2412 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2414 compose_colorize_signature(compose);
2417 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2424 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2426 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2427 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2428 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2430 if (compose->toolbar->draft_btn)
2431 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2432 if (compose->toolbar->insert_btn)
2433 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2434 if (compose->toolbar->attach_btn)
2435 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2436 if (compose->toolbar->sig_btn)
2437 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2438 if (compose->toolbar->exteditor_btn)
2439 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2440 if (compose->toolbar->linewrap_current_btn)
2441 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2442 if (compose->toolbar->linewrap_all_btn)
2443 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2445 compose->modified = FALSE;
2446 compose_set_title(compose);
2447 compose->updating = FALSE;
2448 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2449 SCROLL_TO_CURSOR(compose);
2451 if (compose->deferred_destroy) {
2452 compose_destroy(compose);
2456 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2461 GList *compose_get_compose_list(void)
2463 return compose_list;
2466 void compose_entry_append(Compose *compose, const gchar *address,
2467 ComposeEntryType type, ComposePrefType pref_type)
2469 const gchar *header;
2471 gboolean in_quote = FALSE;
2472 if (!address || *address == '\0') return;
2479 header = N_("Bcc:");
2481 case COMPOSE_REPLYTO:
2482 header = N_("Reply-To:");
2484 case COMPOSE_NEWSGROUPS:
2485 header = N_("Newsgroups:");
2487 case COMPOSE_FOLLOWUPTO:
2488 header = N_( "Followup-To:");
2490 case COMPOSE_INREPLYTO:
2491 header = N_( "In-Reply-To:");
2498 header = prefs_common_translated_header_name(header);
2500 cur = begin = (gchar *)address;
2502 /* we separate the line by commas, but not if we're inside a quoted
2504 while (*cur != '\0') {
2506 in_quote = !in_quote;
2507 if (*cur == ',' && !in_quote) {
2508 gchar *tmp = g_strdup(begin);
2510 tmp[cur-begin]='\0';
2513 while (*tmp == ' ' || *tmp == '\t')
2515 compose_add_header_entry(compose, header, tmp, pref_type);
2522 gchar *tmp = g_strdup(begin);
2524 tmp[cur-begin]='\0';
2527 while (*tmp == ' ' || *tmp == '\t')
2529 compose_add_header_entry(compose, header, tmp, pref_type);
2534 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2536 static GdkColor yellow;
2537 static GdkColor black;
2538 static gboolean yellow_initialised = FALSE;
2542 if (!yellow_initialised) {
2543 gdk_color_parse("#f5f6be", &yellow);
2544 gdk_color_parse("#000000", &black);
2545 yellow_initialised = gdk_colormap_alloc_color(
2546 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2547 yellow_initialised &= gdk_colormap_alloc_color(
2548 gdk_colormap_get_system(), &black, FALSE, TRUE);
2551 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2552 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2553 if (gtk_entry_get_text(entry) &&
2554 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2555 if (yellow_initialised) {
2556 gtk_widget_modify_base(
2557 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2558 GTK_STATE_NORMAL, &yellow);
2559 gtk_widget_modify_text(
2560 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2561 GTK_STATE_NORMAL, &black);
2567 void compose_toolbar_cb(gint action, gpointer data)
2569 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2570 Compose *compose = (Compose*)toolbar_item->parent;
2572 cm_return_if_fail(compose != NULL);
2576 compose_send_cb(NULL, compose);
2579 compose_send_later_cb(NULL, compose);
2582 compose_draft(compose, COMPOSE_QUIT_EDITING);
2585 compose_insert_file_cb(NULL, compose);
2588 compose_attach_cb(NULL, compose);
2591 compose_insert_sig(compose, FALSE);
2594 compose_ext_editor_cb(NULL, compose);
2596 case A_LINEWRAP_CURRENT:
2597 compose_beautify_paragraph(compose, NULL, TRUE);
2599 case A_LINEWRAP_ALL:
2600 compose_wrap_all_full(compose, TRUE);
2603 compose_address_cb(NULL, compose);
2606 case A_CHECK_SPELLING:
2607 compose_check_all(NULL, compose);
2615 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2620 gchar *subject = NULL;
2624 gchar **attach = NULL;
2625 gchar *inreplyto = NULL;
2626 MailField mfield = NO_FIELD_PRESENT;
2628 /* get mailto parts but skip from */
2629 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2632 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2633 mfield = TO_FIELD_PRESENT;
2636 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2638 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2640 if (!g_utf8_validate (subject, -1, NULL)) {
2641 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2642 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2645 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2647 mfield = SUBJECT_FIELD_PRESENT;
2650 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2651 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2654 gboolean prev_autowrap = compose->autowrap;
2656 compose->autowrap = FALSE;
2658 mark = gtk_text_buffer_get_insert(buffer);
2659 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2661 if (!g_utf8_validate (body, -1, NULL)) {
2662 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2663 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2666 gtk_text_buffer_insert(buffer, &iter, body, -1);
2668 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2670 compose->autowrap = prev_autowrap;
2671 if (compose->autowrap)
2672 compose_wrap_all(compose);
2673 mfield = BODY_FIELD_PRESENT;
2677 gint i = 0, att = 0;
2678 gchar *warn_files = NULL;
2679 while (attach[i] != NULL) {
2680 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2681 if (utf8_filename) {
2682 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2683 gchar *tmp = g_strdup_printf("%s%s\n",
2684 warn_files?warn_files:"",
2690 g_free(utf8_filename);
2692 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2697 alertpanel_notice(ngettext(
2698 "The following file has been attached: \n%s",
2699 "The following files have been attached: \n%s", att), warn_files);
2704 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2717 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2719 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2720 {"Cc:", NULL, TRUE},
2721 {"References:", NULL, FALSE},
2722 {"Bcc:", NULL, TRUE},
2723 {"Newsgroups:", NULL, TRUE},
2724 {"Followup-To:", NULL, TRUE},
2725 {"List-Post:", NULL, FALSE},
2726 {"X-Priority:", NULL, FALSE},
2727 {NULL, NULL, FALSE}};
2743 cm_return_val_if_fail(msginfo != NULL, -1);
2745 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2746 procheader_get_header_fields(fp, hentry);
2749 if (hentry[H_REPLY_TO].body != NULL) {
2750 if (hentry[H_REPLY_TO].body[0] != '\0') {
2752 conv_unmime_header(hentry[H_REPLY_TO].body,
2755 g_free(hentry[H_REPLY_TO].body);
2756 hentry[H_REPLY_TO].body = NULL;
2758 if (hentry[H_CC].body != NULL) {
2759 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2760 g_free(hentry[H_CC].body);
2761 hentry[H_CC].body = NULL;
2763 if (hentry[H_REFERENCES].body != NULL) {
2764 if (compose->mode == COMPOSE_REEDIT)
2765 compose->references = hentry[H_REFERENCES].body;
2767 compose->references = compose_parse_references
2768 (hentry[H_REFERENCES].body, msginfo->msgid);
2769 g_free(hentry[H_REFERENCES].body);
2771 hentry[H_REFERENCES].body = NULL;
2773 if (hentry[H_BCC].body != NULL) {
2774 if (compose->mode == COMPOSE_REEDIT)
2776 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2777 g_free(hentry[H_BCC].body);
2778 hentry[H_BCC].body = NULL;
2780 if (hentry[H_NEWSGROUPS].body != NULL) {
2781 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2782 hentry[H_NEWSGROUPS].body = NULL;
2784 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2785 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2786 compose->followup_to =
2787 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2790 g_free(hentry[H_FOLLOWUP_TO].body);
2791 hentry[H_FOLLOWUP_TO].body = NULL;
2793 if (hentry[H_LIST_POST].body != NULL) {
2794 gchar *to = NULL, *start = NULL;
2796 extract_address(hentry[H_LIST_POST].body);
2797 if (hentry[H_LIST_POST].body[0] != '\0') {
2798 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2800 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2801 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2804 g_free(compose->ml_post);
2805 compose->ml_post = to;
2808 g_free(hentry[H_LIST_POST].body);
2809 hentry[H_LIST_POST].body = NULL;
2812 /* CLAWS - X-Priority */
2813 if (compose->mode == COMPOSE_REEDIT)
2814 if (hentry[H_X_PRIORITY].body != NULL) {
2817 priority = atoi(hentry[H_X_PRIORITY].body);
2818 g_free(hentry[H_X_PRIORITY].body);
2820 hentry[H_X_PRIORITY].body = NULL;
2822 if (priority < PRIORITY_HIGHEST ||
2823 priority > PRIORITY_LOWEST)
2824 priority = PRIORITY_NORMAL;
2826 compose->priority = priority;
2829 if (compose->mode == COMPOSE_REEDIT) {
2830 if (msginfo->inreplyto && *msginfo->inreplyto)
2831 compose->inreplyto = g_strdup(msginfo->inreplyto);
2835 if (msginfo->msgid && *msginfo->msgid)
2836 compose->inreplyto = g_strdup(msginfo->msgid);
2838 if (!compose->references) {
2839 if (msginfo->msgid && *msginfo->msgid) {
2840 if (msginfo->inreplyto && *msginfo->inreplyto)
2841 compose->references =
2842 g_strdup_printf("<%s>\n\t<%s>",
2846 compose->references =
2847 g_strconcat("<", msginfo->msgid, ">",
2849 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2850 compose->references =
2851 g_strconcat("<", msginfo->inreplyto, ">",
2859 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2861 GSList *ref_id_list, *cur;
2865 ref_id_list = references_list_append(NULL, ref);
2866 if (!ref_id_list) return NULL;
2867 if (msgid && *msgid)
2868 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2873 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2874 /* "<" + Message-ID + ">" + CR+LF+TAB */
2875 len += strlen((gchar *)cur->data) + 5;
2877 if (len > MAX_REFERENCES_LEN) {
2878 /* remove second message-ID */
2879 if (ref_id_list && ref_id_list->next &&
2880 ref_id_list->next->next) {
2881 g_free(ref_id_list->next->data);
2882 ref_id_list = g_slist_remove
2883 (ref_id_list, ref_id_list->next->data);
2885 slist_free_strings(ref_id_list);
2886 g_slist_free(ref_id_list);
2893 new_ref = g_string_new("");
2894 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2895 if (new_ref->len > 0)
2896 g_string_append(new_ref, "\n\t");
2897 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2900 slist_free_strings(ref_id_list);
2901 g_slist_free(ref_id_list);
2903 new_ref_str = new_ref->str;
2904 g_string_free(new_ref, FALSE);
2909 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2910 const gchar *fmt, const gchar *qmark,
2911 const gchar *body, gboolean rewrap,
2912 gboolean need_unescape,
2913 const gchar *err_msg)
2915 MsgInfo* dummyinfo = NULL;
2916 gchar *quote_str = NULL;
2918 gboolean prev_autowrap;
2919 const gchar *trimmed_body = body;
2920 gint cursor_pos = -1;
2921 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2922 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2927 SIGNAL_BLOCK(buffer);
2930 dummyinfo = compose_msginfo_new_from_compose(compose);
2931 msginfo = dummyinfo;
2934 if (qmark != NULL) {
2936 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2937 compose->gtkaspell);
2939 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2941 quote_fmt_scan_string(qmark);
2944 buf = quote_fmt_get_buffer();
2946 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2948 Xstrdup_a(quote_str, buf, goto error)
2951 if (fmt && *fmt != '\0') {
2954 while (*trimmed_body == '\n')
2958 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2959 compose->gtkaspell);
2961 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2963 if (need_unescape) {
2966 /* decode \-escape sequences in the internal representation of the quote format */
2967 tmp = g_malloc(strlen(fmt)+1);
2968 pref_get_unescaped_pref(tmp, fmt);
2969 quote_fmt_scan_string(tmp);
2973 quote_fmt_scan_string(fmt);
2977 buf = quote_fmt_get_buffer();
2979 gint line = quote_fmt_get_line();
2980 alertpanel_error(err_msg, line);
2986 prev_autowrap = compose->autowrap;
2987 compose->autowrap = FALSE;
2989 mark = gtk_text_buffer_get_insert(buffer);
2990 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2991 if (g_utf8_validate(buf, -1, NULL)) {
2992 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2994 gchar *tmpout = NULL;
2995 tmpout = conv_codeset_strdup
2996 (buf, conv_get_locale_charset_str_no_utf8(),
2998 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3000 tmpout = g_malloc(strlen(buf)*2+1);
3001 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3003 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3007 cursor_pos = quote_fmt_get_cursor_pos();
3008 if (cursor_pos == -1)
3009 cursor_pos = gtk_text_iter_get_offset(&iter);
3010 compose->set_cursor_pos = cursor_pos;
3012 gtk_text_buffer_get_start_iter(buffer, &iter);
3013 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3014 gtk_text_buffer_place_cursor(buffer, &iter);
3016 compose->autowrap = prev_autowrap;
3017 if (compose->autowrap && rewrap)
3018 compose_wrap_all(compose);
3025 SIGNAL_UNBLOCK(buffer);
3027 procmsg_msginfo_free( dummyinfo );
3032 /* if ml_post is of type addr@host and from is of type
3033 * addr-anything@host, return TRUE
3035 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3037 gchar *left_ml = NULL;
3038 gchar *right_ml = NULL;
3039 gchar *left_from = NULL;
3040 gchar *right_from = NULL;
3041 gboolean result = FALSE;
3043 if (!ml_post || !from)
3046 left_ml = g_strdup(ml_post);
3047 if (strstr(left_ml, "@")) {
3048 right_ml = strstr(left_ml, "@")+1;
3049 *(strstr(left_ml, "@")) = '\0';
3052 left_from = g_strdup(from);
3053 if (strstr(left_from, "@")) {
3054 right_from = strstr(left_from, "@")+1;
3055 *(strstr(left_from, "@")) = '\0';
3058 if (left_ml && left_from && right_ml && right_from
3059 && !strncmp(left_from, left_ml, strlen(left_ml))
3060 && !strcmp(right_from, right_ml)) {
3069 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3070 gboolean respect_default_to)
3074 if (!folder || !folder->prefs)
3077 if (respect_default_to && folder->prefs->enable_default_to) {
3078 compose_entry_append(compose, folder->prefs->default_to,
3079 COMPOSE_TO, PREF_FOLDER);
3080 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3082 if (folder->prefs->enable_default_cc)
3083 compose_entry_append(compose, folder->prefs->default_cc,
3084 COMPOSE_CC, PREF_FOLDER);
3085 if (folder->prefs->enable_default_bcc)
3086 compose_entry_append(compose, folder->prefs->default_bcc,
3087 COMPOSE_BCC, PREF_FOLDER);
3088 if (folder->prefs->enable_default_replyto)
3089 compose_entry_append(compose, folder->prefs->default_replyto,
3090 COMPOSE_REPLYTO, PREF_FOLDER);
3093 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3098 if (!compose || !msginfo)
3101 if (msginfo->subject && *msginfo->subject) {
3102 buf = p = g_strdup(msginfo->subject);
3103 p += subject_get_prefix_length(p);
3104 memmove(buf, p, strlen(p) + 1);
3106 buf2 = g_strdup_printf("Re: %s", buf);
3107 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3112 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3115 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3116 gboolean to_all, gboolean to_ml,
3118 gboolean followup_and_reply_to)
3120 GSList *cc_list = NULL;
3123 gchar *replyto = NULL;
3124 gchar *ac_email = NULL;
3126 gboolean reply_to_ml = FALSE;
3127 gboolean default_reply_to = FALSE;
3129 cm_return_if_fail(compose->account != NULL);
3130 cm_return_if_fail(msginfo != NULL);
3132 reply_to_ml = to_ml && compose->ml_post;
3134 default_reply_to = msginfo->folder &&
3135 msginfo->folder->prefs->enable_default_reply_to;
3137 if (compose->account->protocol != A_NNTP) {
3138 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3140 if (reply_to_ml && !default_reply_to) {
3142 gboolean is_subscr = is_subscription(compose->ml_post,
3145 /* normal answer to ml post with a reply-to */
3146 compose_entry_append(compose,
3148 COMPOSE_TO, PREF_ML);
3149 if (compose->replyto)
3150 compose_entry_append(compose,
3152 COMPOSE_CC, PREF_ML);
3154 /* answer to subscription confirmation */
3155 if (compose->replyto)
3156 compose_entry_append(compose,
3158 COMPOSE_TO, PREF_ML);
3159 else if (msginfo->from)
3160 compose_entry_append(compose,
3162 COMPOSE_TO, PREF_ML);
3165 else if (!(to_all || to_sender) && default_reply_to) {
3166 compose_entry_append(compose,
3167 msginfo->folder->prefs->default_reply_to,
3168 COMPOSE_TO, PREF_FOLDER);
3169 compose_entry_mark_default_to(compose,
3170 msginfo->folder->prefs->default_reply_to);
3175 Xstrdup_a(tmp1, msginfo->from, return);
3176 extract_address(tmp1);
3177 if (to_all || to_sender ||
3178 !account_find_from_address(tmp1, FALSE))
3179 compose_entry_append(compose,
3180 (compose->replyto && !to_sender)
3181 ? compose->replyto :
3182 msginfo->from ? msginfo->from : "",
3183 COMPOSE_TO, PREF_NONE);
3184 else if (!to_all && !to_sender) {
3185 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3186 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3187 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3188 if (compose->replyto) {
3189 compose_entry_append(compose,
3191 COMPOSE_TO, PREF_NONE);
3193 compose_entry_append(compose,
3194 msginfo->from ? msginfo->from : "",
3195 COMPOSE_TO, PREF_NONE);
3198 /* replying to own mail, use original recp */
3199 compose_entry_append(compose,
3200 msginfo->to ? msginfo->to : "",
3201 COMPOSE_TO, PREF_NONE);
3202 compose_entry_append(compose,
3203 msginfo->cc ? msginfo->cc : "",
3204 COMPOSE_CC, PREF_NONE);
3209 if (to_sender || (compose->followup_to &&
3210 !strncmp(compose->followup_to, "poster", 6)))
3211 compose_entry_append
3213 (compose->replyto ? compose->replyto :
3214 msginfo->from ? msginfo->from : ""),
3215 COMPOSE_TO, PREF_NONE);
3217 else if (followup_and_reply_to || to_all) {
3218 compose_entry_append
3220 (compose->replyto ? compose->replyto :
3221 msginfo->from ? msginfo->from : ""),
3222 COMPOSE_TO, PREF_NONE);
3224 compose_entry_append
3226 compose->followup_to ? compose->followup_to :
3227 compose->newsgroups ? compose->newsgroups : "",
3228 COMPOSE_NEWSGROUPS, PREF_NONE);
3231 compose_entry_append
3233 compose->followup_to ? compose->followup_to :
3234 compose->newsgroups ? compose->newsgroups : "",
3235 COMPOSE_NEWSGROUPS, PREF_NONE);
3237 compose_reply_set_subject(compose, msginfo);
3239 if (to_ml && compose->ml_post) return;
3240 if (!to_all || compose->account->protocol == A_NNTP) return;
3242 if (compose->replyto) {
3243 Xstrdup_a(replyto, compose->replyto, return);
3244 extract_address(replyto);
3246 if (msginfo->from) {
3247 Xstrdup_a(from, msginfo->from, return);
3248 extract_address(from);
3251 if (replyto && from)
3252 cc_list = address_list_append_with_comments(cc_list, from);
3253 if (to_all && msginfo->folder &&
3254 msginfo->folder->prefs->enable_default_reply_to)
3255 cc_list = address_list_append_with_comments(cc_list,
3256 msginfo->folder->prefs->default_reply_to);
3257 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3258 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3260 ac_email = g_utf8_strdown(compose->account->address, -1);
3263 for (cur = cc_list; cur != NULL; cur = cur->next) {
3264 gchar *addr = g_utf8_strdown(cur->data, -1);
3265 extract_address(addr);
3267 if (strcmp(ac_email, addr))
3268 compose_entry_append(compose, (gchar *)cur->data,
3269 COMPOSE_CC, PREF_NONE);
3271 debug_print("Cc address same as compose account's, ignoring\n");
3276 slist_free_strings(cc_list);
3277 g_slist_free(cc_list);
3283 #define SET_ENTRY(entry, str) \
3286 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3289 #define SET_ADDRESS(type, str) \
3292 compose_entry_append(compose, str, type, PREF_NONE); \
3295 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3297 cm_return_if_fail(msginfo != NULL);
3299 SET_ENTRY(subject_entry, msginfo->subject);
3300 SET_ENTRY(from_name, msginfo->from);
3301 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3302 SET_ADDRESS(COMPOSE_CC, compose->cc);
3303 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3304 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3305 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3306 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3308 compose_update_priority_menu_item(compose);
3309 compose_update_privacy_system_menu_item(compose, FALSE);
3310 compose_show_first_last_header(compose, TRUE);
3316 static void compose_insert_sig(Compose *compose, gboolean replace)
3318 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3319 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3321 GtkTextIter iter, iter_end;
3322 gint cur_pos, ins_pos;
3323 gboolean prev_autowrap;
3324 gboolean found = FALSE;
3325 gboolean exists = FALSE;
3327 cm_return_if_fail(compose->account != NULL);
3331 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3332 G_CALLBACK(compose_changed_cb),
3335 mark = gtk_text_buffer_get_insert(buffer);
3336 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3337 cur_pos = gtk_text_iter_get_offset (&iter);
3340 gtk_text_buffer_get_end_iter(buffer, &iter);
3342 exists = (compose->sig_str != NULL);
3345 GtkTextIter first_iter, start_iter, end_iter;
3347 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3349 if (!exists || compose->sig_str[0] == '\0')
3352 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3353 compose->signature_tag);
3356 /* include previous \n\n */
3357 gtk_text_iter_backward_chars(&first_iter, 1);
3358 start_iter = first_iter;
3359 end_iter = first_iter;
3361 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3362 compose->signature_tag);
3363 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3364 compose->signature_tag);
3366 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3372 g_free(compose->sig_str);
3373 compose->sig_str = account_get_signature_str(compose->account);
3375 cur_pos = gtk_text_iter_get_offset(&iter);
3377 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3378 g_free(compose->sig_str);
3379 compose->sig_str = NULL;
3381 if (compose->sig_inserted == FALSE)
3382 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3383 compose->sig_inserted = TRUE;
3385 cur_pos = gtk_text_iter_get_offset(&iter);
3386 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3388 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3389 gtk_text_iter_forward_chars(&iter, 1);
3390 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3391 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3393 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3394 cur_pos = gtk_text_buffer_get_char_count (buffer);
3397 /* put the cursor where it should be
3398 * either where the quote_fmt says, either where it was */
3399 if (compose->set_cursor_pos < 0)
3400 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3402 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3403 compose->set_cursor_pos);
3405 compose->set_cursor_pos = -1;
3406 gtk_text_buffer_place_cursor(buffer, &iter);
3407 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3408 G_CALLBACK(compose_changed_cb),
3414 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3417 GtkTextBuffer *buffer;
3420 const gchar *cur_encoding;
3421 gchar buf[BUFFSIZE];
3424 gboolean prev_autowrap;
3425 gboolean badtxt = FALSE;
3426 struct stat file_stat;
3429 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3431 /* get the size of the file we are about to insert */
3432 ret = g_stat(file, &file_stat);
3434 gchar *shortfile = g_path_get_basename(file);
3435 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3437 return COMPOSE_INSERT_NO_FILE;
3438 } else if (prefs_common.warn_large_insert == TRUE) {
3440 /* ask user for confirmation if the file is large */
3441 if (prefs_common.warn_large_insert_size < 0 ||
3442 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3446 msg = g_strdup_printf(_("You are about to insert a file of %s "
3447 "in the message body. Are you sure you want to do that?"),
3448 to_human_readable(file_stat.st_size));
3449 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3450 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3453 /* do we ask for confirmation next time? */
3454 if (aval & G_ALERTDISABLE) {
3455 /* no confirmation next time, disable feature in preferences */
3456 aval &= ~G_ALERTDISABLE;
3457 prefs_common.warn_large_insert = FALSE;
3460 /* abort file insertion if user canceled action */
3461 if (aval != G_ALERTALTERNATE) {
3462 return COMPOSE_INSERT_NO_FILE;
3468 if ((fp = g_fopen(file, "rb")) == NULL) {
3469 FILE_OP_ERROR(file, "fopen");
3470 return COMPOSE_INSERT_READ_ERROR;
3473 prev_autowrap = compose->autowrap;
3474 compose->autowrap = FALSE;
3476 text = GTK_TEXT_VIEW(compose->text);
3477 buffer = gtk_text_view_get_buffer(text);
3478 mark = gtk_text_buffer_get_insert(buffer);
3479 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3481 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3482 G_CALLBACK(text_inserted),
3485 cur_encoding = conv_get_locale_charset_str_no_utf8();
3487 while (fgets(buf, sizeof(buf), fp) != NULL) {
3490 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3491 str = g_strdup(buf);
3493 str = conv_codeset_strdup
3494 (buf, cur_encoding, CS_INTERNAL);
3497 /* strip <CR> if DOS/Windows file,
3498 replace <CR> with <LF> if Macintosh file. */
3501 if (len > 0 && str[len - 1] != '\n') {
3503 if (str[len] == '\r') str[len] = '\n';
3506 gtk_text_buffer_insert(buffer, &iter, str, -1);
3510 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3511 G_CALLBACK(text_inserted),
3513 compose->autowrap = prev_autowrap;
3514 if (compose->autowrap)
3515 compose_wrap_all(compose);
3520 return COMPOSE_INSERT_INVALID_CHARACTER;
3522 return COMPOSE_INSERT_SUCCESS;
3525 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3526 const gchar *filename,
3527 const gchar *content_type,
3528 const gchar *charset)
3536 GtkListStore *store;
3538 gboolean has_binary = FALSE;
3540 if (!is_file_exist(file)) {
3541 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3542 gboolean result = FALSE;
3543 if (file_from_uri && is_file_exist(file_from_uri)) {
3544 result = compose_attach_append(
3545 compose, file_from_uri,
3546 filename, content_type,
3549 g_free(file_from_uri);
3552 alertpanel_error("File %s doesn't exist\n", filename);
3555 if ((size = get_file_size(file)) < 0) {
3556 alertpanel_error("Can't get file size of %s\n", filename);
3560 alertpanel_error(_("File %s is empty."), filename);
3563 if ((fp = g_fopen(file, "rb")) == NULL) {
3564 alertpanel_error(_("Can't read %s."), filename);
3569 ainfo = g_new0(AttachInfo, 1);
3570 auto_ainfo = g_auto_pointer_new_with_free
3571 (ainfo, (GFreeFunc) compose_attach_info_free);
3572 ainfo->file = g_strdup(file);
3575 ainfo->content_type = g_strdup(content_type);
3576 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3578 MsgFlags flags = {0, 0};
3580 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3581 ainfo->encoding = ENC_7BIT;
3583 ainfo->encoding = ENC_8BIT;
3585 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3586 if (msginfo && msginfo->subject)
3587 name = g_strdup(msginfo->subject);
3589 name = g_path_get_basename(filename ? filename : file);
3591 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3593 procmsg_msginfo_free(msginfo);
3595 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3596 ainfo->charset = g_strdup(charset);
3597 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3599 ainfo->encoding = ENC_BASE64;
3601 name = g_path_get_basename(filename ? filename : file);
3602 ainfo->name = g_strdup(name);
3606 ainfo->content_type = procmime_get_mime_type(file);
3607 if (!ainfo->content_type) {
3608 ainfo->content_type =
3609 g_strdup("application/octet-stream");
3610 ainfo->encoding = ENC_BASE64;
3611 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3613 procmime_get_encoding_for_text_file(file, &has_binary);
3615 ainfo->encoding = ENC_BASE64;
3616 name = g_path_get_basename(filename ? filename : file);
3617 ainfo->name = g_strdup(name);
3621 if (ainfo->name != NULL
3622 && !strcmp(ainfo->name, ".")) {
3623 g_free(ainfo->name);
3627 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3628 g_free(ainfo->content_type);
3629 ainfo->content_type = g_strdup("application/octet-stream");
3630 g_free(ainfo->charset);
3631 ainfo->charset = NULL;
3634 ainfo->size = (goffset)size;
3635 size_text = to_human_readable((goffset)size);
3637 store = GTK_LIST_STORE(gtk_tree_view_get_model
3638 (GTK_TREE_VIEW(compose->attach_clist)));
3640 gtk_list_store_append(store, &iter);
3641 gtk_list_store_set(store, &iter,
3642 COL_MIMETYPE, ainfo->content_type,
3643 COL_SIZE, size_text,
3644 COL_NAME, ainfo->name,
3645 COL_CHARSET, ainfo->charset,
3647 COL_AUTODATA, auto_ainfo,
3650 g_auto_pointer_free(auto_ainfo);
3651 compose_attach_update_label(compose);
3655 static void compose_use_signing(Compose *compose, gboolean use_signing)
3657 compose->use_signing = use_signing;
3658 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3661 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3663 compose->use_encryption = use_encryption;
3664 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3667 #define NEXT_PART_NOT_CHILD(info) \
3669 node = info->node; \
3670 while (node->children) \
3671 node = g_node_last_child(node); \
3672 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3675 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3679 MimeInfo *firsttext = NULL;
3680 MimeInfo *encrypted = NULL;
3683 const gchar *partname = NULL;
3685 mimeinfo = procmime_scan_message(msginfo);
3686 if (!mimeinfo) return;
3688 if (mimeinfo->node->children == NULL) {
3689 procmime_mimeinfo_free_all(mimeinfo);
3693 /* find first content part */
3694 child = (MimeInfo *) mimeinfo->node->children->data;
3695 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3696 child = (MimeInfo *)child->node->children->data;
3699 if (child->type == MIMETYPE_TEXT) {
3701 debug_print("First text part found\n");
3702 } else if (compose->mode == COMPOSE_REEDIT &&
3703 child->type == MIMETYPE_APPLICATION &&
3704 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3705 encrypted = (MimeInfo *)child->node->parent->data;
3708 child = (MimeInfo *) mimeinfo->node->children->data;
3709 while (child != NULL) {
3712 if (child == encrypted) {
3713 /* skip this part of tree */
3714 NEXT_PART_NOT_CHILD(child);
3718 if (child->type == MIMETYPE_MULTIPART) {
3719 /* get the actual content */
3720 child = procmime_mimeinfo_next(child);
3724 if (child == firsttext) {
3725 child = procmime_mimeinfo_next(child);
3729 outfile = procmime_get_tmp_file_name(child);
3730 if ((err = procmime_get_part(outfile, child)) < 0)
3731 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3733 gchar *content_type;
3735 content_type = procmime_get_content_type_str(child->type, child->subtype);
3737 /* if we meet a pgp signature, we don't attach it, but
3738 * we force signing. */
3739 if ((strcmp(content_type, "application/pgp-signature") &&
3740 strcmp(content_type, "application/pkcs7-signature") &&
3741 strcmp(content_type, "application/x-pkcs7-signature"))
3742 || compose->mode == COMPOSE_REDIRECT) {
3743 partname = procmime_mimeinfo_get_parameter(child, "filename");
3744 if (partname == NULL)
3745 partname = procmime_mimeinfo_get_parameter(child, "name");
3746 if (partname == NULL)
3748 compose_attach_append(compose, outfile,
3749 partname, content_type,
3750 procmime_mimeinfo_get_parameter(child, "charset"));
3752 compose_force_signing(compose, compose->account, NULL);
3754 g_free(content_type);
3757 NEXT_PART_NOT_CHILD(child);
3759 procmime_mimeinfo_free_all(mimeinfo);
3762 #undef NEXT_PART_NOT_CHILD
3767 WAIT_FOR_INDENT_CHAR,
3768 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3771 /* return indent length, we allow:
3772 indent characters followed by indent characters or spaces/tabs,
3773 alphabets and numbers immediately followed by indent characters,
3774 and the repeating sequences of the above
3775 If quote ends with multiple spaces, only the first one is included. */
3776 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3777 const GtkTextIter *start, gint *len)
3779 GtkTextIter iter = *start;
3783 IndentState state = WAIT_FOR_INDENT_CHAR;
3786 gint alnum_count = 0;
3787 gint space_count = 0;
3790 if (prefs_common.quote_chars == NULL) {
3794 while (!gtk_text_iter_ends_line(&iter)) {
3795 wc = gtk_text_iter_get_char(&iter);
3796 if (g_unichar_iswide(wc))
3798 clen = g_unichar_to_utf8(wc, ch);
3802 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3803 is_space = g_unichar_isspace(wc);
3805 if (state == WAIT_FOR_INDENT_CHAR) {
3806 if (!is_indent && !g_unichar_isalnum(wc))
3809 quote_len += alnum_count + space_count + 1;
3810 alnum_count = space_count = 0;
3811 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3814 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3815 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3819 else if (is_indent) {
3820 quote_len += alnum_count + space_count + 1;
3821 alnum_count = space_count = 0;
3824 state = WAIT_FOR_INDENT_CHAR;
3828 gtk_text_iter_forward_char(&iter);
3831 if (quote_len > 0 && space_count > 0)
3837 if (quote_len > 0) {
3839 gtk_text_iter_forward_chars(&iter, quote_len);
3840 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3846 /* return >0 if the line is itemized */
3847 static int compose_itemized_length(GtkTextBuffer *buffer,
3848 const GtkTextIter *start)
3850 GtkTextIter iter = *start;
3855 if (gtk_text_iter_ends_line(&iter))
3860 wc = gtk_text_iter_get_char(&iter);
3861 if (!g_unichar_isspace(wc))
3863 gtk_text_iter_forward_char(&iter);
3864 if (gtk_text_iter_ends_line(&iter))
3868 clen = g_unichar_to_utf8(wc, ch);
3872 if (!strchr("*-+", ch[0]))
3875 gtk_text_iter_forward_char(&iter);
3876 if (gtk_text_iter_ends_line(&iter))
3878 wc = gtk_text_iter_get_char(&iter);
3879 if (g_unichar_isspace(wc)) {
3885 /* return the string at the start of the itemization */
3886 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3887 const GtkTextIter *start)
3889 GtkTextIter iter = *start;
3892 GString *item_chars = g_string_new("");
3895 if (gtk_text_iter_ends_line(&iter))
3900 wc = gtk_text_iter_get_char(&iter);
3901 if (!g_unichar_isspace(wc))
3903 gtk_text_iter_forward_char(&iter);
3904 if (gtk_text_iter_ends_line(&iter))
3906 g_string_append_unichar(item_chars, wc);
3909 str = item_chars->str;
3910 g_string_free(item_chars, FALSE);
3914 /* return the number of spaces at a line's start */
3915 static int compose_left_offset_length(GtkTextBuffer *buffer,
3916 const GtkTextIter *start)
3918 GtkTextIter iter = *start;
3921 if (gtk_text_iter_ends_line(&iter))
3925 wc = gtk_text_iter_get_char(&iter);
3926 if (!g_unichar_isspace(wc))
3929 gtk_text_iter_forward_char(&iter);
3930 if (gtk_text_iter_ends_line(&iter))
3934 gtk_text_iter_forward_char(&iter);
3935 if (gtk_text_iter_ends_line(&iter))
3940 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3941 const GtkTextIter *start,
3942 GtkTextIter *break_pos,
3946 GtkTextIter iter = *start, line_end = *start;
3947 PangoLogAttr *attrs;
3954 gboolean can_break = FALSE;
3955 gboolean do_break = FALSE;
3956 gboolean was_white = FALSE;
3957 gboolean prev_dont_break = FALSE;
3959 gtk_text_iter_forward_to_line_end(&line_end);
3960 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3961 len = g_utf8_strlen(str, -1);
3965 g_warning("compose_get_line_break_pos: len = 0!\n");
3969 /* g_print("breaking line: %d: %s (len = %d)\n",
3970 gtk_text_iter_get_line(&iter), str, len); */
3972 attrs = g_new(PangoLogAttr, len + 1);
3974 pango_default_break(str, -1, NULL, attrs, len + 1);
3978 /* skip quote and leading spaces */
3979 for (i = 0; *p != '\0' && i < len; i++) {
3982 wc = g_utf8_get_char(p);
3983 if (i >= quote_len && !g_unichar_isspace(wc))
3985 if (g_unichar_iswide(wc))
3987 else if (*p == '\t')
3991 p = g_utf8_next_char(p);
3994 for (; *p != '\0' && i < len; i++) {
3995 PangoLogAttr *attr = attrs + i;
3999 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4002 was_white = attr->is_white;
4004 /* don't wrap URI */
4005 if ((uri_len = get_uri_len(p)) > 0) {
4007 if (pos > 0 && col > max_col) {
4017 wc = g_utf8_get_char(p);
4018 if (g_unichar_iswide(wc)) {
4020 if (prev_dont_break && can_break && attr->is_line_break)
4022 } else if (*p == '\t')
4026 if (pos > 0 && col > max_col) {
4031 if (*p == '-' || *p == '/')
4032 prev_dont_break = TRUE;
4034 prev_dont_break = FALSE;
4036 p = g_utf8_next_char(p);
4040 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4045 *break_pos = *start;
4046 gtk_text_iter_set_line_offset(break_pos, pos);
4051 static gboolean compose_join_next_line(Compose *compose,
4052 GtkTextBuffer *buffer,
4054 const gchar *quote_str)
4056 GtkTextIter iter_ = *iter, cur, prev, next, end;
4057 PangoLogAttr attrs[3];
4059 gchar *next_quote_str;
4062 gboolean keep_cursor = FALSE;
4064 if (!gtk_text_iter_forward_line(&iter_) ||
4065 gtk_text_iter_ends_line(&iter_)) {
4068 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4070 if ((quote_str || next_quote_str) &&
4071 strcmp2(quote_str, next_quote_str) != 0) {
4072 g_free(next_quote_str);
4075 g_free(next_quote_str);
4078 if (quote_len > 0) {
4079 gtk_text_iter_forward_chars(&end, quote_len);
4080 if (gtk_text_iter_ends_line(&end)) {
4085 /* don't join itemized lines */
4086 if (compose_itemized_length(buffer, &end) > 0) {
4090 /* don't join signature separator */
4091 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4094 /* delete quote str */
4096 gtk_text_buffer_delete(buffer, &iter_, &end);
4098 /* don't join line breaks put by the user */
4100 gtk_text_iter_backward_char(&cur);
4101 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4102 gtk_text_iter_forward_char(&cur);
4106 gtk_text_iter_forward_char(&cur);
4107 /* delete linebreak and extra spaces */
4108 while (gtk_text_iter_backward_char(&cur)) {
4109 wc1 = gtk_text_iter_get_char(&cur);
4110 if (!g_unichar_isspace(wc1))
4115 while (!gtk_text_iter_ends_line(&cur)) {
4116 wc1 = gtk_text_iter_get_char(&cur);
4117 if (!g_unichar_isspace(wc1))
4119 gtk_text_iter_forward_char(&cur);
4122 if (!gtk_text_iter_equal(&prev, &next)) {
4125 mark = gtk_text_buffer_get_insert(buffer);
4126 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4127 if (gtk_text_iter_equal(&prev, &cur))
4129 gtk_text_buffer_delete(buffer, &prev, &next);
4133 /* insert space if required */
4134 gtk_text_iter_backward_char(&prev);
4135 wc1 = gtk_text_iter_get_char(&prev);
4136 wc2 = gtk_text_iter_get_char(&next);
4137 gtk_text_iter_forward_char(&next);
4138 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4139 pango_default_break(str, -1, NULL, attrs, 3);
4140 if (!attrs[1].is_line_break ||
4141 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4142 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4144 gtk_text_iter_backward_char(&iter_);
4145 gtk_text_buffer_place_cursor(buffer, &iter_);
4154 #define ADD_TXT_POS(bp_, ep_, pti_) \
4155 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4156 last = last->next; \
4157 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4158 last->next = NULL; \
4160 g_warning("alloc error scanning URIs\n"); \
4163 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4165 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4166 GtkTextBuffer *buffer;
4167 GtkTextIter iter, break_pos, end_of_line;
4168 gchar *quote_str = NULL;
4170 gboolean wrap_quote = prefs_common.linewrap_quote;
4171 gboolean prev_autowrap = compose->autowrap;
4172 gint startq_offset = -1, noq_offset = -1;
4173 gint uri_start = -1, uri_stop = -1;
4174 gint nouri_start = -1, nouri_stop = -1;
4175 gint num_blocks = 0;
4176 gint quotelevel = -1;
4177 gboolean modified = force;
4178 gboolean removed = FALSE;
4179 gboolean modified_before_remove = FALSE;
4181 gboolean start = TRUE;
4182 gint itemized_len = 0, rem_item_len = 0;
4183 gchar *itemized_chars = NULL;
4184 gboolean item_continuation = FALSE;
4189 if (compose->draft_timeout_tag == -2) {
4193 compose->autowrap = FALSE;
4195 buffer = gtk_text_view_get_buffer(text);
4196 undo_wrapping(compose->undostruct, TRUE);
4201 mark = gtk_text_buffer_get_insert(buffer);
4202 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4206 if (compose->draft_timeout_tag == -2) {
4207 if (gtk_text_iter_ends_line(&iter)) {
4208 while (gtk_text_iter_ends_line(&iter) &&
4209 gtk_text_iter_forward_line(&iter))
4212 while (gtk_text_iter_backward_line(&iter)) {
4213 if (gtk_text_iter_ends_line(&iter)) {
4214 gtk_text_iter_forward_line(&iter);
4220 /* move to line start */
4221 gtk_text_iter_set_line_offset(&iter, 0);
4224 itemized_len = compose_itemized_length(buffer, &iter);
4226 if (!itemized_len) {
4227 itemized_len = compose_left_offset_length(buffer, &iter);
4228 item_continuation = TRUE;
4232 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4234 /* go until paragraph end (empty line) */
4235 while (start || !gtk_text_iter_ends_line(&iter)) {
4236 gchar *scanpos = NULL;
4237 /* parse table - in order of priority */
4239 const gchar *needle; /* token */
4241 /* token search function */
4242 gchar *(*search) (const gchar *haystack,
4243 const gchar *needle);
4244 /* part parsing function */
4245 gboolean (*parse) (const gchar *start,
4246 const gchar *scanpos,
4250 /* part to URI function */
4251 gchar *(*build_uri) (const gchar *bp,
4255 static struct table parser[] = {
4256 {"http://", strcasestr, get_uri_part, make_uri_string},
4257 {"https://", strcasestr, get_uri_part, make_uri_string},
4258 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4259 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4260 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4261 {"www.", strcasestr, get_uri_part, make_http_string},
4262 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4263 {"@", strcasestr, get_email_part, make_email_string}
4265 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4266 gint last_index = PARSE_ELEMS;
4268 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4272 if (!prev_autowrap && num_blocks == 0) {
4274 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4275 G_CALLBACK(text_inserted),
4278 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4281 uri_start = uri_stop = -1;
4283 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4286 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4287 if (startq_offset == -1)
4288 startq_offset = gtk_text_iter_get_offset(&iter);
4289 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4290 if (quotelevel > 2) {
4291 /* recycle colors */
4292 if (prefs_common.recycle_quote_colors)
4301 if (startq_offset == -1)
4302 noq_offset = gtk_text_iter_get_offset(&iter);
4306 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4309 if (gtk_text_iter_ends_line(&iter)) {
4311 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4312 prefs_common.linewrap_len,
4314 GtkTextIter prev, next, cur;
4315 if (prev_autowrap != FALSE || force) {
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);
4325 } else if (quote_str && wrap_quote) {
4326 compose->automatic_break = TRUE;
4328 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4329 compose->automatic_break = FALSE;
4330 if (itemized_len && compose->autoindent) {
4331 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4332 if (!item_continuation)
4333 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4337 /* remove trailing spaces */
4339 rem_item_len = itemized_len;
4340 while (compose->autoindent && rem_item_len-- > 0)
4341 gtk_text_iter_backward_char(&cur);
4342 gtk_text_iter_backward_char(&cur);
4345 while (!gtk_text_iter_starts_line(&cur)) {
4348 gtk_text_iter_backward_char(&cur);
4349 wc = gtk_text_iter_get_char(&cur);
4350 if (!g_unichar_isspace(wc))
4354 if (!gtk_text_iter_equal(&prev, &next)) {
4355 gtk_text_buffer_delete(buffer, &prev, &next);
4357 gtk_text_iter_forward_char(&break_pos);
4361 gtk_text_buffer_insert(buffer, &break_pos,
4365 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4367 /* move iter to current line start */
4368 gtk_text_iter_set_line_offset(&iter, 0);
4375 /* move iter to next line start */
4381 if (!prev_autowrap && num_blocks > 0) {
4383 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4384 G_CALLBACK(text_inserted),
4388 while (!gtk_text_iter_ends_line(&end_of_line)) {
4389 gtk_text_iter_forward_char(&end_of_line);
4391 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4393 nouri_start = gtk_text_iter_get_offset(&iter);
4394 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4396 walk_pos = gtk_text_iter_get_offset(&iter);
4397 /* FIXME: this looks phony. scanning for anything in the parse table */
4398 for (n = 0; n < PARSE_ELEMS; n++) {
4401 tmp = parser[n].search(walk, parser[n].needle);
4403 if (scanpos == NULL || tmp < scanpos) {
4412 /* check if URI can be parsed */
4413 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4414 (const gchar **)&ep, FALSE)
4415 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4419 strlen(parser[last_index].needle);
4422 uri_start = walk_pos + (bp - o_walk);
4423 uri_stop = walk_pos + (ep - o_walk);
4427 gtk_text_iter_forward_line(&iter);
4430 if (startq_offset != -1) {
4431 GtkTextIter startquote, endquote;
4432 gtk_text_buffer_get_iter_at_offset(
4433 buffer, &startquote, startq_offset);
4436 switch (quotelevel) {
4438 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4439 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4440 gtk_text_buffer_apply_tag_by_name(
4441 buffer, "quote0", &startquote, &endquote);
4442 gtk_text_buffer_remove_tag_by_name(
4443 buffer, "quote1", &startquote, &endquote);
4444 gtk_text_buffer_remove_tag_by_name(
4445 buffer, "quote2", &startquote, &endquote);
4450 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4451 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4452 gtk_text_buffer_apply_tag_by_name(
4453 buffer, "quote1", &startquote, &endquote);
4454 gtk_text_buffer_remove_tag_by_name(
4455 buffer, "quote0", &startquote, &endquote);
4456 gtk_text_buffer_remove_tag_by_name(
4457 buffer, "quote2", &startquote, &endquote);
4462 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4463 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4464 gtk_text_buffer_apply_tag_by_name(
4465 buffer, "quote2", &startquote, &endquote);
4466 gtk_text_buffer_remove_tag_by_name(
4467 buffer, "quote0", &startquote, &endquote);
4468 gtk_text_buffer_remove_tag_by_name(
4469 buffer, "quote1", &startquote, &endquote);
4475 } else if (noq_offset != -1) {
4476 GtkTextIter startnoquote, endnoquote;
4477 gtk_text_buffer_get_iter_at_offset(
4478 buffer, &startnoquote, noq_offset);
4481 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4482 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4483 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4484 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4485 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4486 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4487 gtk_text_buffer_remove_tag_by_name(
4488 buffer, "quote0", &startnoquote, &endnoquote);
4489 gtk_text_buffer_remove_tag_by_name(
4490 buffer, "quote1", &startnoquote, &endnoquote);
4491 gtk_text_buffer_remove_tag_by_name(
4492 buffer, "quote2", &startnoquote, &endnoquote);
4498 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4499 GtkTextIter nouri_start_iter, nouri_end_iter;
4500 gtk_text_buffer_get_iter_at_offset(
4501 buffer, &nouri_start_iter, nouri_start);
4502 gtk_text_buffer_get_iter_at_offset(
4503 buffer, &nouri_end_iter, nouri_stop);
4504 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4505 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4506 gtk_text_buffer_remove_tag_by_name(
4507 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4508 modified_before_remove = modified;
4513 if (uri_start >= 0 && uri_stop > 0) {
4514 GtkTextIter uri_start_iter, uri_end_iter, back;
4515 gtk_text_buffer_get_iter_at_offset(
4516 buffer, &uri_start_iter, uri_start);
4517 gtk_text_buffer_get_iter_at_offset(
4518 buffer, &uri_end_iter, uri_stop);
4519 back = uri_end_iter;
4520 gtk_text_iter_backward_char(&back);
4521 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4522 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4523 gtk_text_buffer_apply_tag_by_name(
4524 buffer, "link", &uri_start_iter, &uri_end_iter);
4526 if (removed && !modified_before_remove) {
4532 // debug_print("not modified, out after %d lines\n", lines);
4536 // debug_print("modified, out after %d lines\n", lines);
4538 g_free(itemized_chars);
4541 undo_wrapping(compose->undostruct, FALSE);
4542 compose->autowrap = prev_autowrap;
4547 void compose_action_cb(void *data)
4549 Compose *compose = (Compose *)data;
4550 compose_wrap_all(compose);
4553 static void compose_wrap_all(Compose *compose)
4555 compose_wrap_all_full(compose, FALSE);
4558 static void compose_wrap_all_full(Compose *compose, gboolean force)
4560 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4561 GtkTextBuffer *buffer;
4563 gboolean modified = TRUE;
4565 buffer = gtk_text_view_get_buffer(text);
4567 gtk_text_buffer_get_start_iter(buffer, &iter);
4568 while (!gtk_text_iter_is_end(&iter) && modified)
4569 modified = compose_beautify_paragraph(compose, &iter, force);
4573 static void compose_set_title(Compose *compose)
4579 edited = compose->modified ? _(" [Edited]") : "";
4581 subject = gtk_editable_get_chars(
4582 GTK_EDITABLE(compose->subject_entry), 0, -1);
4584 #ifndef GENERIC_UMPC
4585 if (subject && strlen(subject))
4586 str = g_strdup_printf(_("%s - Compose message%s"),
4589 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4591 str = g_strdup(_("Compose message"));
4594 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4600 * compose_current_mail_account:
4602 * Find a current mail account (the currently selected account, or the
4603 * default account, if a news account is currently selected). If a
4604 * mail account cannot be found, display an error message.
4606 * Return value: Mail account, or NULL if not found.
4608 static PrefsAccount *
4609 compose_current_mail_account(void)
4613 if (cur_account && cur_account->protocol != A_NNTP)
4616 ac = account_get_default();
4617 if (!ac || ac->protocol == A_NNTP) {
4618 alertpanel_error(_("Account for sending mail is not specified.\n"
4619 "Please select a mail account before sending."));
4626 #define QUOTE_IF_REQUIRED(out, str) \
4628 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4632 len = strlen(str) + 3; \
4633 if ((__tmp = alloca(len)) == NULL) { \
4634 g_warning("can't allocate memory\n"); \
4635 g_string_free(header, TRUE); \
4638 g_snprintf(__tmp, len, "\"%s\"", str); \
4643 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4644 g_warning("can't allocate memory\n"); \
4645 g_string_free(header, TRUE); \
4648 strcpy(__tmp, str); \
4654 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4656 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4660 len = strlen(str) + 3; \
4661 if ((__tmp = alloca(len)) == NULL) { \
4662 g_warning("can't allocate memory\n"); \
4665 g_snprintf(__tmp, len, "\"%s\"", str); \
4670 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4671 g_warning("can't allocate memory\n"); \
4674 strcpy(__tmp, str); \
4680 static void compose_select_account(Compose *compose, PrefsAccount *account,
4683 gchar *from = NULL, *header;
4684 ComposeHeaderEntry *header_entry;
4686 cm_return_if_fail(account != NULL);
4688 compose->account = account;
4689 if (account->name && *account->name) {
4691 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4692 from = g_strdup_printf("%s <%s>",
4693 buf, account->address);
4694 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4696 from = g_strdup_printf("<%s>",
4698 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4703 compose_set_title(compose);
4705 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4706 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4708 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4709 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4710 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4712 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4714 activate_privacy_system(compose, account, FALSE);
4716 if (!init && compose->mode != COMPOSE_REDIRECT) {
4717 undo_block(compose->undostruct);
4718 compose_insert_sig(compose, TRUE);
4719 undo_unblock(compose->undostruct);
4722 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4723 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4725 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4726 if (account->protocol == A_NNTP) {
4727 if (!strcmp(header, _("To:")))
4728 combobox_select_by_text(
4729 GTK_COMBO_BOX(header_entry->combo),
4732 if (!strcmp(header, _("Newsgroups:")))
4733 combobox_select_by_text(
4734 GTK_COMBO_BOX(header_entry->combo),
4742 /* use account's dict info if set */
4743 if (compose->gtkaspell) {
4744 if (account->enable_default_dictionary)
4745 gtkaspell_change_dict(compose->gtkaspell,
4746 account->default_dictionary, FALSE);
4747 if (account->enable_default_alt_dictionary)
4748 gtkaspell_change_alt_dict(compose->gtkaspell,
4749 account->default_alt_dictionary);
4750 if (account->enable_default_dictionary
4751 || account->enable_default_alt_dictionary)
4752 compose_spell_menu_changed(compose);
4757 gboolean compose_check_for_valid_recipient(Compose *compose) {
4758 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4759 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4760 gboolean recipient_found = FALSE;
4764 /* free to and newsgroup list */
4765 slist_free_strings(compose->to_list);
4766 g_slist_free(compose->to_list);
4767 compose->to_list = NULL;
4769 slist_free_strings(compose->newsgroup_list);
4770 g_slist_free(compose->newsgroup_list);
4771 compose->newsgroup_list = NULL;
4773 /* search header entries for to and newsgroup entries */
4774 for (list = compose->header_list; list; list = list->next) {
4777 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4778 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4781 if (entry[0] != '\0') {
4782 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4783 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4784 compose->to_list = address_list_append(compose->to_list, entry);
4785 recipient_found = TRUE;
4788 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4789 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4790 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4791 recipient_found = TRUE;
4798 return recipient_found;
4801 static gboolean compose_check_for_set_recipients(Compose *compose)
4803 if (compose->account->set_autocc && compose->account->auto_cc) {
4804 gboolean found_other = FALSE;
4806 /* search header entries for to and newsgroup entries */
4807 for (list = compose->header_list; list; list = list->next) {
4810 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4811 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4814 if (strcmp(entry, compose->account->auto_cc)
4815 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4825 if (compose->batch) {
4826 gtk_widget_show_all(compose->window);
4828 aval = alertpanel(_("Send"),
4829 _("The only recipient is the default CC address. Send anyway?"),
4830 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4831 if (aval != G_ALERTALTERNATE)
4835 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4836 gboolean found_other = FALSE;
4838 /* search header entries for to and newsgroup entries */
4839 for (list = compose->header_list; list; list = list->next) {
4842 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4843 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4846 if (strcmp(entry, compose->account->auto_bcc)
4847 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4857 if (compose->batch) {
4858 gtk_widget_show_all(compose->window);
4860 aval = alertpanel(_("Send"),
4861 _("The only recipient is the default BCC address. Send anyway?"),
4862 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4863 if (aval != G_ALERTALTERNATE)
4870 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4874 if (compose_check_for_valid_recipient(compose) == FALSE) {
4875 if (compose->batch) {
4876 gtk_widget_show_all(compose->window);
4878 alertpanel_error(_("Recipient is not specified."));
4882 if (compose_check_for_set_recipients(compose) == FALSE) {
4886 if (!compose->batch) {
4887 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4888 if (*str == '\0' && check_everything == TRUE &&
4889 compose->mode != COMPOSE_REDIRECT) {
4891 gchar *button_label;
4894 if (compose->sending)
4895 button_label = _("+_Send");
4897 button_label = _("+_Queue");
4898 message = g_strdup_printf(_("Subject is empty. %s"),
4899 compose->sending?_("Send it anyway?"):
4900 _("Queue it anyway?"));
4902 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4903 GTK_STOCK_CANCEL, button_label, NULL);
4905 if (aval != G_ALERTALTERNATE)
4910 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4916 gint compose_send(Compose *compose)
4919 FolderItem *folder = NULL;
4921 gchar *msgpath = NULL;
4922 gboolean discard_window = FALSE;
4923 gchar *errstr = NULL;
4924 gchar *tmsgid = NULL;
4925 MainWindow *mainwin = mainwindow_get_mainwindow();
4926 gboolean queued_removed = FALSE;
4928 if (prefs_common.send_dialog_invisible
4929 || compose->batch == TRUE)
4930 discard_window = TRUE;
4932 compose_allow_user_actions (compose, FALSE);
4933 compose->sending = TRUE;
4935 if (compose_check_entries(compose, TRUE) == FALSE) {
4936 if (compose->batch) {
4937 gtk_widget_show_all(compose->window);
4943 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4946 if (compose->batch) {
4947 gtk_widget_show_all(compose->window);
4950 alertpanel_error(_("Could not queue message for sending:\n\n"
4951 "Charset conversion failed."));
4952 } else if (val == -5) {
4953 alertpanel_error(_("Could not queue message for sending:\n\n"
4954 "Couldn't get recipient encryption key."));
4955 } else if (val == -6) {
4957 } else if (val == -3) {
4958 if (privacy_peek_error())
4959 alertpanel_error(_("Could not queue message for sending:\n\n"
4960 "Signature failed: %s"), privacy_get_error());
4961 } else if (val == -2 && errno != 0) {
4962 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4964 alertpanel_error(_("Could not queue message for sending."));
4969 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4970 if (discard_window) {
4971 compose->sending = FALSE;
4972 compose_close(compose);
4973 /* No more compose access in the normal codepath
4974 * after this point! */
4979 alertpanel_error(_("The message was queued but could not be "
4980 "sent.\nUse \"Send queued messages\" from "
4981 "the main window to retry."));
4982 if (!discard_window) {
4989 if (msgpath == NULL) {
4990 msgpath = folder_item_fetch_msg(folder, msgnum);
4991 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4994 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4995 claws_unlink(msgpath);
4998 if (!discard_window) {
5000 if (!queued_removed)
5001 folder_item_remove_msg(folder, msgnum);
5002 folder_item_scan(folder);
5004 /* make sure we delete that */
5005 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5007 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5008 folder_item_remove_msg(folder, tmp->msgnum);
5009 procmsg_msginfo_free(tmp);
5016 if (!queued_removed)
5017 folder_item_remove_msg(folder, msgnum);
5018 folder_item_scan(folder);
5020 /* make sure we delete that */
5021 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5023 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5024 folder_item_remove_msg(folder, tmp->msgnum);
5025 procmsg_msginfo_free(tmp);
5028 if (!discard_window) {
5029 compose->sending = FALSE;
5030 compose_allow_user_actions (compose, TRUE);
5031 compose_close(compose);
5035 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5036 "the main window to retry."), errstr);
5039 alertpanel_error_log(_("The message was queued but could not be "
5040 "sent.\nUse \"Send queued messages\" from "
5041 "the main window to retry."));
5043 if (!discard_window) {
5052 toolbar_main_set_sensitive(mainwin);
5053 main_window_set_menu_sensitive(mainwin);
5059 compose_allow_user_actions (compose, TRUE);
5060 compose->sending = FALSE;
5061 compose->modified = TRUE;
5062 toolbar_main_set_sensitive(mainwin);
5063 main_window_set_menu_sensitive(mainwin);
5068 static gboolean compose_use_attach(Compose *compose)
5070 GtkTreeModel *model = gtk_tree_view_get_model
5071 (GTK_TREE_VIEW(compose->attach_clist));
5072 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5075 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5078 gchar buf[BUFFSIZE];
5080 gboolean first_to_address;
5081 gboolean first_cc_address;
5083 ComposeHeaderEntry *headerentry;
5084 const gchar *headerentryname;
5085 const gchar *cc_hdr;
5086 const gchar *to_hdr;
5087 gboolean err = FALSE;
5089 debug_print("Writing redirect header\n");
5091 cc_hdr = prefs_common_translated_header_name("Cc:");
5092 to_hdr = prefs_common_translated_header_name("To:");
5094 first_to_address = TRUE;
5095 for (list = compose->header_list; list; list = list->next) {
5096 headerentry = ((ComposeHeaderEntry *)list->data);
5097 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5099 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5100 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5101 Xstrdup_a(str, entstr, return -1);
5103 if (str[0] != '\0') {
5104 compose_convert_header
5105 (compose, buf, sizeof(buf), str,
5106 strlen("Resent-To") + 2, TRUE);
5108 if (first_to_address) {
5109 err |= (fprintf(fp, "Resent-To: ") < 0);
5110 first_to_address = FALSE;
5112 err |= (fprintf(fp, ",") < 0);
5114 err |= (fprintf(fp, "%s", buf) < 0);
5118 if (!first_to_address) {
5119 err |= (fprintf(fp, "\n") < 0);
5122 first_cc_address = TRUE;
5123 for (list = compose->header_list; list; list = list->next) {
5124 headerentry = ((ComposeHeaderEntry *)list->data);
5125 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5127 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5128 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5129 Xstrdup_a(str, strg, return -1);
5131 if (str[0] != '\0') {
5132 compose_convert_header
5133 (compose, buf, sizeof(buf), str,
5134 strlen("Resent-Cc") + 2, TRUE);
5136 if (first_cc_address) {
5137 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5138 first_cc_address = FALSE;
5140 err |= (fprintf(fp, ",") < 0);
5142 err |= (fprintf(fp, "%s", buf) < 0);
5146 if (!first_cc_address) {
5147 err |= (fprintf(fp, "\n") < 0);
5150 return (err ? -1:0);
5153 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5155 gchar buf[BUFFSIZE];
5157 const gchar *entstr;
5158 /* struct utsname utsbuf; */
5159 gboolean err = FALSE;
5161 cm_return_val_if_fail(fp != NULL, -1);
5162 cm_return_val_if_fail(compose->account != NULL, -1);
5163 cm_return_val_if_fail(compose->account->address != NULL, -1);
5166 get_rfc822_date(buf, sizeof(buf));
5167 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5170 if (compose->account->name && *compose->account->name) {
5171 compose_convert_header
5172 (compose, buf, sizeof(buf), compose->account->name,
5173 strlen("From: "), TRUE);
5174 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5175 buf, compose->account->address) < 0);
5177 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5180 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5181 if (*entstr != '\0') {
5182 Xstrdup_a(str, entstr, return -1);
5185 compose_convert_header(compose, buf, sizeof(buf), str,
5186 strlen("Subject: "), FALSE);
5187 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5191 /* Resent-Message-ID */
5192 if (compose->account->set_domain && compose->account->domain) {
5193 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5194 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5195 g_snprintf(buf, sizeof(buf), "%s",
5196 strchr(compose->account->address, '@') ?
5197 strchr(compose->account->address, '@')+1 :
5198 compose->account->address);
5200 g_snprintf(buf, sizeof(buf), "%s", "");
5203 if (compose->account->gen_msgid) {
5205 if (compose->account->msgid_with_addr) {
5206 addr = compose->account->address;
5208 generate_msgid(buf, sizeof(buf), addr);
5209 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5210 compose->msgid = g_strdup(buf);
5212 compose->msgid = NULL;
5215 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5218 /* separator between header and body */
5219 err |= (fputs("\n", fp) == EOF);
5221 return (err ? -1:0);
5224 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5228 gchar buf[BUFFSIZE];
5230 gboolean skip = FALSE;
5231 gboolean err = FALSE;
5232 gchar *not_included[]={
5233 "Return-Path:", "Delivered-To:", "Received:",
5234 "Subject:", "X-UIDL:", "AF:",
5235 "NF:", "PS:", "SRH:",
5236 "SFN:", "DSR:", "MID:",
5237 "CFG:", "PT:", "S:",
5238 "RQ:", "SSV:", "NSV:",
5239 "SSH:", "R:", "MAID:",
5240 "NAID:", "RMID:", "FMID:",
5241 "SCF:", "RRCPT:", "NG:",
5242 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5243 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5244 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5245 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5246 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5249 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5250 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5254 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5256 for (i = 0; not_included[i] != NULL; i++) {
5257 if (g_ascii_strncasecmp(buf, not_included[i],
5258 strlen(not_included[i])) == 0) {
5265 if (fputs(buf, fdest) == -1)
5268 if (!prefs_common.redirect_keep_from) {
5269 if (g_ascii_strncasecmp(buf, "From:",
5270 strlen("From:")) == 0) {
5271 err |= (fputs(" (by way of ", fdest) == EOF);
5272 if (compose->account->name
5273 && *compose->account->name) {
5274 compose_convert_header
5275 (compose, buf, sizeof(buf),
5276 compose->account->name,
5279 err |= (fprintf(fdest, "%s <%s>",
5281 compose->account->address) < 0);
5283 err |= (fprintf(fdest, "%s",
5284 compose->account->address) < 0);
5285 err |= (fputs(")", fdest) == EOF);
5289 if (fputs("\n", fdest) == -1)
5296 if (compose_redirect_write_headers(compose, fdest))
5299 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5300 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5313 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5315 GtkTextBuffer *buffer;
5316 GtkTextIter start, end;
5319 const gchar *out_codeset;
5320 EncodingType encoding = ENC_UNKNOWN;
5321 MimeInfo *mimemsg, *mimetext;
5323 const gchar *src_codeset = CS_INTERNAL;
5324 gchar *from_addr = NULL;
5325 gchar *from_name = NULL;
5327 if (action == COMPOSE_WRITE_FOR_SEND)
5328 attach_parts = TRUE;
5330 /* create message MimeInfo */
5331 mimemsg = procmime_mimeinfo_new();
5332 mimemsg->type = MIMETYPE_MESSAGE;
5333 mimemsg->subtype = g_strdup("rfc822");
5334 mimemsg->content = MIMECONTENT_MEM;
5335 mimemsg->tmp = TRUE; /* must free content later */
5336 mimemsg->data.mem = compose_get_header(compose);
5338 /* Create text part MimeInfo */
5339 /* get all composed text */
5340 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5341 gtk_text_buffer_get_start_iter(buffer, &start);
5342 gtk_text_buffer_get_end_iter(buffer, &end);
5343 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5345 out_codeset = conv_get_charset_str(compose->out_encoding);
5347 if (!out_codeset && is_ascii_str(chars)) {
5348 out_codeset = CS_US_ASCII;
5349 } else if (prefs_common.outgoing_fallback_to_ascii &&
5350 is_ascii_str(chars)) {
5351 out_codeset = CS_US_ASCII;
5352 encoding = ENC_7BIT;
5356 gchar *test_conv_global_out = NULL;
5357 gchar *test_conv_reply = NULL;
5359 /* automatic mode. be automatic. */
5360 codeconv_set_strict(TRUE);
5362 out_codeset = conv_get_outgoing_charset_str();
5364 debug_print("trying to convert to %s\n", out_codeset);
5365 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5368 if (!test_conv_global_out && compose->orig_charset
5369 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5370 out_codeset = compose->orig_charset;
5371 debug_print("failure; trying to convert to %s\n", out_codeset);
5372 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5375 if (!test_conv_global_out && !test_conv_reply) {
5377 out_codeset = CS_INTERNAL;
5378 debug_print("failure; finally using %s\n", out_codeset);
5380 g_free(test_conv_global_out);
5381 g_free(test_conv_reply);
5382 codeconv_set_strict(FALSE);
5385 if (encoding == ENC_UNKNOWN) {
5386 if (prefs_common.encoding_method == CTE_BASE64)
5387 encoding = ENC_BASE64;
5388 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5389 encoding = ENC_QUOTED_PRINTABLE;
5390 else if (prefs_common.encoding_method == CTE_8BIT)
5391 encoding = ENC_8BIT;
5393 encoding = procmime_get_encoding_for_charset(out_codeset);
5396 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5397 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5399 if (action == COMPOSE_WRITE_FOR_SEND) {
5400 codeconv_set_strict(TRUE);
5401 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5402 codeconv_set_strict(FALSE);
5408 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5409 "to the specified %s charset.\n"
5410 "Send it as %s?"), out_codeset, src_codeset);
5411 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5412 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5415 if (aval != G_ALERTALTERNATE) {
5420 out_codeset = src_codeset;
5426 out_codeset = src_codeset;
5431 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5432 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5433 strstr(buf, "\nFrom ") != NULL) {
5434 encoding = ENC_QUOTED_PRINTABLE;
5438 mimetext = procmime_mimeinfo_new();
5439 mimetext->content = MIMECONTENT_MEM;
5440 mimetext->tmp = TRUE; /* must free content later */
5441 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5442 * and free the data, which we need later. */
5443 mimetext->data.mem = g_strdup(buf);
5444 mimetext->type = MIMETYPE_TEXT;
5445 mimetext->subtype = g_strdup("plain");
5446 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5447 g_strdup(out_codeset));
5449 /* protect trailing spaces when signing message */
5450 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5451 privacy_system_can_sign(compose->privacy_system)) {
5452 encoding = ENC_QUOTED_PRINTABLE;
5455 debug_print("main text: %zd bytes encoded as %s in %d\n",
5456 strlen(buf), out_codeset, encoding);
5458 /* check for line length limit */
5459 if (action == COMPOSE_WRITE_FOR_SEND &&
5460 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5461 check_line_length(buf, 1000, &line) < 0) {
5465 msg = g_strdup_printf
5466 (_("Line %d exceeds the line length limit (998 bytes).\n"
5467 "The contents of the message might be broken on the way to the delivery.\n"
5469 "Send it anyway?"), line + 1);
5470 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5472 if (aval != G_ALERTALTERNATE) {
5478 if (encoding != ENC_UNKNOWN)
5479 procmime_encode_content(mimetext, encoding);
5481 /* append attachment parts */
5482 if (compose_use_attach(compose) && attach_parts) {
5483 MimeInfo *mimempart;
5484 gchar *boundary = NULL;
5485 mimempart = procmime_mimeinfo_new();
5486 mimempart->content = MIMECONTENT_EMPTY;
5487 mimempart->type = MIMETYPE_MULTIPART;
5488 mimempart->subtype = g_strdup("mixed");
5492 boundary = generate_mime_boundary(NULL);
5493 } while (strstr(buf, boundary) != NULL);
5495 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5498 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5500 g_node_append(mimempart->node, mimetext->node);
5501 g_node_append(mimemsg->node, mimempart->node);
5503 if (compose_add_attachments(compose, mimempart) < 0)
5506 g_node_append(mimemsg->node, mimetext->node);
5510 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5511 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5512 /* extract name and address */
5513 if (strstr(spec, " <") && strstr(spec, ">")) {
5514 from_addr = g_strdup(strrchr(spec, '<')+1);
5515 *(strrchr(from_addr, '>')) = '\0';
5516 from_name = g_strdup(spec);
5517 *(strrchr(from_name, '<')) = '\0';
5524 /* sign message if sending */
5525 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5526 privacy_system_can_sign(compose->privacy_system))
5527 if (!privacy_sign(compose->privacy_system, mimemsg,
5528 compose->account, from_addr)) {
5535 procmime_write_mimeinfo(mimemsg, fp);
5537 procmime_mimeinfo_free_all(mimemsg);
5542 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5544 GtkTextBuffer *buffer;
5545 GtkTextIter start, end;
5550 if ((fp = g_fopen(file, "wb")) == NULL) {
5551 FILE_OP_ERROR(file, "fopen");
5555 /* chmod for security */
5556 if (change_file_mode_rw(fp, file) < 0) {
5557 FILE_OP_ERROR(file, "chmod");
5558 g_warning("can't change file mode\n");
5561 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5562 gtk_text_buffer_get_start_iter(buffer, &start);
5563 gtk_text_buffer_get_end_iter(buffer, &end);
5564 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5566 chars = conv_codeset_strdup
5567 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5570 if (!chars) return -1;
5573 len = strlen(chars);
5574 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5575 FILE_OP_ERROR(file, "fwrite");
5584 if (fclose(fp) == EOF) {
5585 FILE_OP_ERROR(file, "fclose");
5592 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5595 MsgInfo *msginfo = compose->targetinfo;
5597 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5598 if (!msginfo) return -1;
5600 if (!force && MSG_IS_LOCKED(msginfo->flags))
5603 item = msginfo->folder;
5604 cm_return_val_if_fail(item != NULL, -1);
5606 if (procmsg_msg_exist(msginfo) &&
5607 (folder_has_parent_of_type(item, F_QUEUE) ||
5608 folder_has_parent_of_type(item, F_DRAFT)
5609 || msginfo == compose->autosaved_draft)) {
5610 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5611 g_warning("can't remove the old message\n");
5614 debug_print("removed reedit target %d\n", msginfo->msgnum);
5621 static void compose_remove_draft(Compose *compose)
5624 MsgInfo *msginfo = compose->targetinfo;
5625 drafts = account_get_special_folder(compose->account, F_DRAFT);
5627 if (procmsg_msg_exist(msginfo)) {
5628 folder_item_remove_msg(drafts, msginfo->msgnum);
5633 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5634 gboolean remove_reedit_target)
5636 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5639 static gboolean compose_warn_encryption(Compose *compose)
5641 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5642 AlertValue val = G_ALERTALTERNATE;
5644 if (warning == NULL)
5647 val = alertpanel_full(_("Encryption warning"), warning,
5648 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5649 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5650 if (val & G_ALERTDISABLE) {
5651 val &= ~G_ALERTDISABLE;
5652 if (val == G_ALERTALTERNATE)
5653 privacy_inhibit_encrypt_warning(compose->privacy_system,
5657 if (val == G_ALERTALTERNATE) {
5664 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5665 gchar **msgpath, gboolean check_subject,
5666 gboolean remove_reedit_target)
5673 static gboolean lock = FALSE;
5674 PrefsAccount *mailac = NULL, *newsac = NULL;
5675 gboolean err = FALSE;
5677 debug_print("queueing message...\n");
5678 cm_return_val_if_fail(compose->account != NULL, -1);
5682 if (compose_check_entries(compose, check_subject) == FALSE) {
5684 if (compose->batch) {
5685 gtk_widget_show_all(compose->window);
5690 if (!compose->to_list && !compose->newsgroup_list) {
5691 g_warning("can't get recipient list.");
5696 if (compose->to_list) {
5697 if (compose->account->protocol != A_NNTP)
5698 mailac = compose->account;
5699 else if (cur_account && cur_account->protocol != A_NNTP)
5700 mailac = cur_account;
5701 else if (!(mailac = compose_current_mail_account())) {
5703 alertpanel_error(_("No account for sending mails available!"));
5708 if (compose->newsgroup_list) {
5709 if (compose->account->protocol == A_NNTP)
5710 newsac = compose->account;
5713 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5718 /* write queue header */
5719 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5720 G_DIR_SEPARATOR, compose, (guint) rand());
5721 debug_print("queuing to %s\n", tmp);
5722 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5723 FILE_OP_ERROR(tmp, "fopen");
5729 if (change_file_mode_rw(fp, tmp) < 0) {
5730 FILE_OP_ERROR(tmp, "chmod");
5731 g_warning("can't change file mode\n");
5734 /* queueing variables */
5735 err |= (fprintf(fp, "AF:\n") < 0);
5736 err |= (fprintf(fp, "NF:0\n") < 0);
5737 err |= (fprintf(fp, "PS:10\n") < 0);
5738 err |= (fprintf(fp, "SRH:1\n") < 0);
5739 err |= (fprintf(fp, "SFN:\n") < 0);
5740 err |= (fprintf(fp, "DSR:\n") < 0);
5742 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5744 err |= (fprintf(fp, "MID:\n") < 0);
5745 err |= (fprintf(fp, "CFG:\n") < 0);
5746 err |= (fprintf(fp, "PT:0\n") < 0);
5747 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5748 err |= (fprintf(fp, "RQ:\n") < 0);
5750 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5752 err |= (fprintf(fp, "SSV:\n") < 0);
5754 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5756 err |= (fprintf(fp, "NSV:\n") < 0);
5757 err |= (fprintf(fp, "SSH:\n") < 0);
5758 /* write recepient list */
5759 if (compose->to_list) {
5760 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5761 for (cur = compose->to_list->next; cur != NULL;
5763 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5764 err |= (fprintf(fp, "\n") < 0);
5766 /* write newsgroup list */
5767 if (compose->newsgroup_list) {
5768 err |= (fprintf(fp, "NG:") < 0);
5769 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5770 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5771 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5772 err |= (fprintf(fp, "\n") < 0);
5774 /* Sylpheed account IDs */
5776 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5778 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5781 if (compose->privacy_system != NULL) {
5782 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5783 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5784 if (compose->use_encryption) {
5786 if (!compose_warn_encryption(compose)) {
5793 if (mailac && mailac->encrypt_to_self) {
5794 GSList *tmp_list = g_slist_copy(compose->to_list);
5795 tmp_list = g_slist_append(tmp_list, compose->account->address);
5796 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5797 g_slist_free(tmp_list);
5799 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5801 if (encdata != NULL) {
5802 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5803 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5804 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5806 } /* else we finally dont want to encrypt */
5808 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5809 /* and if encdata was null, it means there's been a problem in
5821 /* Save copy folder */
5822 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5823 gchar *savefolderid;
5825 savefolderid = compose_get_save_to(compose);
5826 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5827 g_free(savefolderid);
5829 /* Save copy folder */
5830 if (compose->return_receipt) {
5831 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5833 /* Message-ID of message replying to */
5834 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5837 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5838 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5841 /* Message-ID of message forwarding to */
5842 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5845 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5846 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5850 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5851 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5853 /* end of headers */
5854 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5856 if (compose->redirect_filename != NULL) {
5857 if (compose_redirect_write_to_file(compose, fp) < 0) {
5866 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5871 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5875 g_warning("failed to write queue message\n");
5882 if (fclose(fp) == EOF) {
5883 FILE_OP_ERROR(tmp, "fclose");
5890 if (item && *item) {
5893 queue = account_get_special_folder(compose->account, F_QUEUE);
5896 g_warning("can't find queue folder\n");
5902 folder_item_scan(queue);
5903 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5904 g_warning("can't queue the message\n");
5911 if (msgpath == NULL) {
5917 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5918 compose_remove_reedit_target(compose, FALSE);
5921 if ((msgnum != NULL) && (item != NULL)) {
5929 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5932 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5934 struct stat statbuf;
5935 gchar *type, *subtype;
5936 GtkTreeModel *model;
5939 model = gtk_tree_view_get_model(tree_view);
5941 if (!gtk_tree_model_get_iter_first(model, &iter))
5944 gtk_tree_model_get(model, &iter,
5948 if (!is_file_exist(ainfo->file)) {
5949 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5950 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5951 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5953 if (val == G_ALERTDEFAULT) {
5958 mimepart = procmime_mimeinfo_new();
5959 mimepart->content = MIMECONTENT_FILE;
5960 mimepart->data.filename = g_strdup(ainfo->file);
5961 mimepart->tmp = FALSE; /* or we destroy our attachment */
5962 mimepart->offset = 0;
5964 g_stat(ainfo->file, &statbuf);
5965 mimepart->length = statbuf.st_size;
5967 type = g_strdup(ainfo->content_type);
5969 if (!strchr(type, '/')) {
5971 type = g_strdup("application/octet-stream");
5974 subtype = strchr(type, '/') + 1;
5975 *(subtype - 1) = '\0';
5976 mimepart->type = procmime_get_media_type(type);
5977 mimepart->subtype = g_strdup(subtype);
5980 if (mimepart->type == MIMETYPE_MESSAGE &&
5981 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5982 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5983 } else if (mimepart->type == MIMETYPE_TEXT) {
5984 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
5985 /* Text parts with no name come from multipart/alternative
5986 * forwards. Make sure the recipient won't look at the
5987 * original HTML part by mistake. */
5988 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5989 ainfo->name = g_strdup_printf(_("Original %s part"),
5993 g_hash_table_insert(mimepart->typeparameters,
5994 g_strdup("charset"), g_strdup(ainfo->charset));
5996 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
5997 if (mimepart->type == MIMETYPE_APPLICATION &&
5998 !strcmp2(mimepart->subtype, "octet-stream"))
5999 g_hash_table_insert(mimepart->typeparameters,
6000 g_strdup("name"), g_strdup(ainfo->name));
6001 g_hash_table_insert(mimepart->dispositionparameters,
6002 g_strdup("filename"), g_strdup(ainfo->name));
6003 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6006 if (mimepart->type == MIMETYPE_MESSAGE
6007 || mimepart->type == MIMETYPE_MULTIPART)
6008 ainfo->encoding = ENC_BINARY;
6009 else if (compose->use_signing) {
6010 if (ainfo->encoding == ENC_7BIT)
6011 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6012 else if (ainfo->encoding == ENC_8BIT)
6013 ainfo->encoding = ENC_BASE64;
6018 procmime_encode_content(mimepart, ainfo->encoding);
6020 g_node_append(parent->node, mimepart->node);
6021 } while (gtk_tree_model_iter_next(model, &iter));
6026 #define IS_IN_CUSTOM_HEADER(header) \
6027 (compose->account->add_customhdr && \
6028 custom_header_find(compose->account->customhdr_list, header) != NULL)
6030 static void compose_add_headerfield_from_headerlist(Compose *compose,
6032 const gchar *fieldname,
6033 const gchar *seperator)
6035 gchar *str, *fieldname_w_colon;
6036 gboolean add_field = FALSE;
6038 ComposeHeaderEntry *headerentry;
6039 const gchar *headerentryname;
6040 const gchar *trans_fieldname;
6043 if (IS_IN_CUSTOM_HEADER(fieldname))
6046 debug_print("Adding %s-fields\n", fieldname);
6048 fieldstr = g_string_sized_new(64);
6050 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6051 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6053 for (list = compose->header_list; list; list = list->next) {
6054 headerentry = ((ComposeHeaderEntry *)list->data);
6055 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6057 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6058 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6060 if (str[0] != '\0') {
6062 g_string_append(fieldstr, seperator);
6063 g_string_append(fieldstr, str);
6072 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6073 compose_convert_header
6074 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6075 strlen(fieldname) + 2, TRUE);
6076 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6080 g_free(fieldname_w_colon);
6081 g_string_free(fieldstr, TRUE);
6086 static gchar *compose_get_header(Compose *compose)
6088 gchar buf[BUFFSIZE];
6089 const gchar *entry_str;
6093 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6095 gchar *from_name = NULL, *from_address = NULL;
6098 cm_return_val_if_fail(compose->account != NULL, NULL);
6099 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6101 header = g_string_sized_new(64);
6104 get_rfc822_date(buf, sizeof(buf));
6105 g_string_append_printf(header, "Date: %s\n", buf);
6109 if (compose->account->name && *compose->account->name) {
6111 QUOTE_IF_REQUIRED(buf, compose->account->name);
6112 tmp = g_strdup_printf("%s <%s>",
6113 buf, compose->account->address);
6115 tmp = g_strdup_printf("%s",
6116 compose->account->address);
6118 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6119 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6121 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6122 from_address = g_strdup(compose->account->address);
6124 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6125 /* extract name and address */
6126 if (strstr(spec, " <") && strstr(spec, ">")) {
6127 from_address = g_strdup(strrchr(spec, '<')+1);
6128 *(strrchr(from_address, '>')) = '\0';
6129 from_name = g_strdup(spec);
6130 *(strrchr(from_name, '<')) = '\0';
6133 from_address = g_strdup(spec);
6140 if (from_name && *from_name) {
6141 compose_convert_header
6142 (compose, buf, sizeof(buf), from_name,
6143 strlen("From: "), TRUE);
6144 QUOTE_IF_REQUIRED(name, buf);
6146 g_string_append_printf(header, "From: %s <%s>\n",
6147 name, from_address);
6149 g_string_append_printf(header, "From: %s\n", from_address);
6152 g_free(from_address);
6155 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6158 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6161 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6165 * If this account is a NNTP account remove Bcc header from
6166 * message body since it otherwise will be publicly shown
6168 if (compose->account->protocol != A_NNTP)
6169 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6172 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6174 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6177 compose_convert_header(compose, buf, sizeof(buf), str,
6178 strlen("Subject: "), FALSE);
6179 g_string_append_printf(header, "Subject: %s\n", buf);
6185 if (compose->account->set_domain && compose->account->domain) {
6186 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6187 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6188 g_snprintf(buf, sizeof(buf), "%s",
6189 strchr(compose->account->address, '@') ?
6190 strchr(compose->account->address, '@')+1 :
6191 compose->account->address);
6193 g_snprintf(buf, sizeof(buf), "%s", "");
6196 if (compose->account->gen_msgid) {
6198 if (compose->account->msgid_with_addr) {
6199 addr = compose->account->address;
6201 generate_msgid(buf, sizeof(buf), addr);
6202 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6203 compose->msgid = g_strdup(buf);
6205 compose->msgid = NULL;
6208 if (compose->remove_references == FALSE) {
6210 if (compose->inreplyto && compose->to_list)
6211 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6214 if (compose->references)
6215 g_string_append_printf(header, "References: %s\n", compose->references);
6219 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6222 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6225 if (compose->account->organization &&
6226 strlen(compose->account->organization) &&
6227 !IS_IN_CUSTOM_HEADER("Organization")) {
6228 compose_convert_header(compose, buf, sizeof(buf),
6229 compose->account->organization,
6230 strlen("Organization: "), FALSE);
6231 g_string_append_printf(header, "Organization: %s\n", buf);
6234 /* Program version and system info */
6235 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6236 !compose->newsgroup_list) {
6237 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6239 gtk_major_version, gtk_minor_version, gtk_micro_version,
6242 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6243 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6245 gtk_major_version, gtk_minor_version, gtk_micro_version,
6249 /* custom headers */
6250 if (compose->account->add_customhdr) {
6253 for (cur = compose->account->customhdr_list; cur != NULL;
6255 CustomHeader *chdr = (CustomHeader *)cur->data;
6257 if (custom_header_is_allowed(chdr->name)) {
6258 compose_convert_header
6259 (compose, buf, sizeof(buf),
6260 chdr->value ? chdr->value : "",
6261 strlen(chdr->name) + 2, FALSE);
6262 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6267 /* Automatic Faces and X-Faces */
6268 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6269 g_string_append_printf(header, "X-Face: %s\n", buf);
6271 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6272 g_string_append_printf(header, "X-Face: %s\n", buf);
6274 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6275 g_string_append_printf(header, "Face: %s\n", buf);
6277 else if (get_default_face (buf, sizeof(buf)) == 0) {
6278 g_string_append_printf(header, "Face: %s\n", buf);
6282 switch (compose->priority) {
6283 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6284 "X-Priority: 1 (Highest)\n");
6286 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6287 "X-Priority: 2 (High)\n");
6289 case PRIORITY_NORMAL: break;
6290 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6291 "X-Priority: 4 (Low)\n");
6293 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6294 "X-Priority: 5 (Lowest)\n");
6296 default: debug_print("compose: priority unknown : %d\n",
6300 /* Request Return Receipt */
6301 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6302 if (compose->return_receipt) {
6303 if (compose->account->name
6304 && *compose->account->name) {
6305 compose_convert_header(compose, buf, sizeof(buf),
6306 compose->account->name,
6307 strlen("Disposition-Notification-To: "),
6309 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6311 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6315 /* get special headers */
6316 for (list = compose->header_list; list; list = list->next) {
6317 ComposeHeaderEntry *headerentry;
6320 gchar *headername_wcolon;
6321 const gchar *headername_trans;
6324 gboolean standard_header = FALSE;
6326 headerentry = ((ComposeHeaderEntry *)list->data);
6328 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6330 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6335 if (!strstr(tmp, ":")) {
6336 headername_wcolon = g_strconcat(tmp, ":", NULL);
6337 headername = g_strdup(tmp);
6339 headername_wcolon = g_strdup(tmp);
6340 headername = g_strdup(strtok(tmp, ":"));
6344 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6345 Xstrdup_a(headervalue, entry_str, return NULL);
6346 subst_char(headervalue, '\r', ' ');
6347 subst_char(headervalue, '\n', ' ');
6348 string = std_headers;
6349 while (*string != NULL) {
6350 headername_trans = prefs_common_translated_header_name(*string);
6351 if (!strcmp(headername_trans, headername_wcolon))
6352 standard_header = TRUE;
6355 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6356 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6359 g_free(headername_wcolon);
6363 g_string_free(header, FALSE);
6368 #undef IS_IN_CUSTOM_HEADER
6370 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6371 gint header_len, gboolean addr_field)
6373 gchar *tmpstr = NULL;
6374 const gchar *out_codeset = NULL;
6376 cm_return_if_fail(src != NULL);
6377 cm_return_if_fail(dest != NULL);
6379 if (len < 1) return;
6381 tmpstr = g_strdup(src);
6383 subst_char(tmpstr, '\n', ' ');
6384 subst_char(tmpstr, '\r', ' ');
6387 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6388 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6389 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6394 codeconv_set_strict(TRUE);
6395 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6396 conv_get_charset_str(compose->out_encoding));
6397 codeconv_set_strict(FALSE);
6399 if (!dest || *dest == '\0') {
6400 gchar *test_conv_global_out = NULL;
6401 gchar *test_conv_reply = NULL;
6403 /* automatic mode. be automatic. */
6404 codeconv_set_strict(TRUE);
6406 out_codeset = conv_get_outgoing_charset_str();
6408 debug_print("trying to convert to %s\n", out_codeset);
6409 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6412 if (!test_conv_global_out && compose->orig_charset
6413 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6414 out_codeset = compose->orig_charset;
6415 debug_print("failure; trying to convert to %s\n", out_codeset);
6416 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6419 if (!test_conv_global_out && !test_conv_reply) {
6421 out_codeset = CS_INTERNAL;
6422 debug_print("finally using %s\n", out_codeset);
6424 g_free(test_conv_global_out);
6425 g_free(test_conv_reply);
6426 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6428 codeconv_set_strict(FALSE);
6433 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6437 cm_return_if_fail(user_data != NULL);
6439 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6440 g_strstrip(address);
6441 if (*address != '\0') {
6442 gchar *name = procheader_get_fromname(address);
6443 extract_address(address);
6444 addressbook_add_contact(name, address, NULL, NULL);
6449 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6451 GtkWidget *menuitem;
6454 cm_return_if_fail(menu != NULL);
6455 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6457 menuitem = gtk_separator_menu_item_new();
6458 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6459 gtk_widget_show(menuitem);
6461 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6462 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6464 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6465 g_strstrip(address);
6466 if (*address == '\0') {
6467 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6470 g_signal_connect(G_OBJECT(menuitem), "activate",
6471 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6472 gtk_widget_show(menuitem);
6475 static void compose_create_header_entry(Compose *compose)
6477 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6484 const gchar *header = NULL;
6485 ComposeHeaderEntry *headerentry;
6486 gboolean standard_header = FALSE;
6487 GtkListStore *model;
6489 #if !(GTK_CHECK_VERSION(2,12,0))
6490 GtkTooltips *tips = compose->tooltips;
6493 headerentry = g_new0(ComposeHeaderEntry, 1);
6496 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6497 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6498 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6500 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6502 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6504 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6505 COMPOSE_NEWSGROUPS);
6506 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6508 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6509 COMPOSE_FOLLOWUPTO);
6511 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6512 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6513 G_CALLBACK(compose_grab_focus_cb), compose);
6514 gtk_widget_show(combo);
6515 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6516 compose->header_nextrow, compose->header_nextrow+1,
6517 GTK_SHRINK, GTK_FILL, 0, 0);
6518 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6519 const gchar *last_header_entry = gtk_entry_get_text(
6520 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6522 while (*string != NULL) {
6523 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6524 standard_header = TRUE;
6527 if (standard_header)
6528 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6530 if (!compose->header_last || !standard_header) {
6531 switch(compose->account->protocol) {
6533 header = prefs_common_translated_header_name("Newsgroups:");
6536 header = prefs_common_translated_header_name("To:");
6541 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6543 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6544 G_CALLBACK(compose_grab_focus_cb), compose);
6546 /* Entry field with cleanup button */
6547 button = gtk_button_new();
6548 gtk_button_set_image(GTK_BUTTON(button),
6549 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6550 gtk_widget_show(button);
6551 CLAWS_SET_TIP(button,
6552 _("Delete entry contents"));
6553 entry = gtk_entry_new();
6554 gtk_widget_show(entry);
6555 CLAWS_SET_TIP(entry,
6556 _("Use <tab> to autocomplete from addressbook"));
6557 hbox = gtk_hbox_new (FALSE, 0);
6558 gtk_widget_show(hbox);
6559 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6560 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6561 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6562 compose->header_nextrow, compose->header_nextrow+1,
6563 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6565 g_signal_connect(G_OBJECT(entry), "key-press-event",
6566 G_CALLBACK(compose_headerentry_key_press_event_cb),
6568 g_signal_connect(G_OBJECT(entry), "changed",
6569 G_CALLBACK(compose_headerentry_changed_cb),
6571 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6572 G_CALLBACK(compose_grab_focus_cb), compose);
6574 g_signal_connect(G_OBJECT(button), "clicked",
6575 G_CALLBACK(compose_headerentry_button_clicked_cb),
6579 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6580 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6581 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6582 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6583 G_CALLBACK(compose_header_drag_received_cb),
6585 g_signal_connect(G_OBJECT(entry), "drag-drop",
6586 G_CALLBACK(compose_drag_drop),
6588 g_signal_connect(G_OBJECT(entry), "populate-popup",
6589 G_CALLBACK(compose_entry_popup_extend),
6592 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6594 headerentry->compose = compose;
6595 headerentry->combo = combo;
6596 headerentry->entry = entry;
6597 headerentry->button = button;
6598 headerentry->hbox = hbox;
6599 headerentry->headernum = compose->header_nextrow;
6600 headerentry->type = PREF_NONE;
6602 compose->header_nextrow++;
6603 compose->header_last = headerentry;
6604 compose->header_list =
6605 g_slist_append(compose->header_list,
6609 static void compose_add_header_entry(Compose *compose, const gchar *header,
6610 gchar *text, ComposePrefType pref_type)
6612 ComposeHeaderEntry *last_header = compose->header_last;
6613 gchar *tmp = g_strdup(text), *email;
6614 gboolean replyto_hdr;
6616 replyto_hdr = (!strcasecmp(header,
6617 prefs_common_translated_header_name("Reply-To:")) ||
6619 prefs_common_translated_header_name("Followup-To:")) ||
6621 prefs_common_translated_header_name("In-Reply-To:")));
6623 extract_address(tmp);
6624 email = g_utf8_strdown(tmp, -1);
6626 if (replyto_hdr == FALSE &&
6627 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6629 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6630 header, text, (gint) pref_type);
6636 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6637 gtk_entry_set_text(GTK_ENTRY(
6638 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6640 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6641 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6642 last_header->type = pref_type;
6644 if (replyto_hdr == FALSE)
6645 g_hash_table_insert(compose->email_hashtable, email,
6646 GUINT_TO_POINTER(1));
6653 static void compose_destroy_headerentry(Compose *compose,
6654 ComposeHeaderEntry *headerentry)
6656 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6659 extract_address(text);
6660 email = g_utf8_strdown(text, -1);
6661 g_hash_table_remove(compose->email_hashtable, email);
6665 gtk_widget_destroy(headerentry->combo);
6666 gtk_widget_destroy(headerentry->entry);
6667 gtk_widget_destroy(headerentry->button);
6668 gtk_widget_destroy(headerentry->hbox);
6669 g_free(headerentry);
6672 static void compose_remove_header_entries(Compose *compose)
6675 for (list = compose->header_list; list; list = list->next)
6676 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6678 compose->header_last = NULL;
6679 g_slist_free(compose->header_list);
6680 compose->header_list = NULL;
6681 compose->header_nextrow = 1;
6682 compose_create_header_entry(compose);
6685 static GtkWidget *compose_create_header(Compose *compose)
6687 GtkWidget *from_optmenu_hbox;
6688 GtkWidget *header_scrolledwin;
6689 GtkWidget *header_table;
6693 /* header labels and entries */
6694 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6695 gtk_widget_show(header_scrolledwin);
6696 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6698 header_table = gtk_table_new(2, 2, FALSE);
6699 gtk_widget_show(header_table);
6700 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6701 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6702 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6705 /* option menu for selecting accounts */
6706 from_optmenu_hbox = compose_account_option_menu_create(compose);
6707 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6708 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6711 compose->header_table = header_table;
6712 compose->header_list = NULL;
6713 compose->header_nextrow = count;
6715 compose_create_header_entry(compose);
6717 compose->table = NULL;
6719 return header_scrolledwin ;
6722 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6724 Compose *compose = (Compose *)data;
6725 GdkEventButton event;
6728 event.time = gtk_get_current_event_time();
6730 return attach_button_pressed(compose->attach_clist, &event, compose);
6733 static GtkWidget *compose_create_attach(Compose *compose)
6735 GtkWidget *attach_scrwin;
6736 GtkWidget *attach_clist;
6738 GtkListStore *store;
6739 GtkCellRenderer *renderer;
6740 GtkTreeViewColumn *column;
6741 GtkTreeSelection *selection;
6743 /* attachment list */
6744 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6745 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6746 GTK_POLICY_AUTOMATIC,
6747 GTK_POLICY_AUTOMATIC);
6748 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6750 store = gtk_list_store_new(N_ATTACH_COLS,
6756 G_TYPE_AUTO_POINTER,
6758 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6759 (GTK_TREE_MODEL(store)));
6760 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6761 g_object_unref(store);
6763 renderer = gtk_cell_renderer_text_new();
6764 column = gtk_tree_view_column_new_with_attributes
6765 (_("Mime type"), renderer, "text",
6766 COL_MIMETYPE, NULL);
6767 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6769 renderer = gtk_cell_renderer_text_new();
6770 column = gtk_tree_view_column_new_with_attributes
6771 (_("Size"), renderer, "text",
6773 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6775 renderer = gtk_cell_renderer_text_new();
6776 column = gtk_tree_view_column_new_with_attributes
6777 (_("Name"), renderer, "text",
6779 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6781 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6782 prefs_common.use_stripes_everywhere);
6783 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6784 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6786 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6787 G_CALLBACK(attach_selected), compose);
6788 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6789 G_CALLBACK(attach_button_pressed), compose);
6791 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6792 G_CALLBACK(popup_attach_button_pressed), compose);
6794 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6795 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6796 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6797 G_CALLBACK(popup_attach_button_pressed), compose);
6799 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6800 G_CALLBACK(attach_key_pressed), compose);
6803 gtk_drag_dest_set(attach_clist,
6804 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6805 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6806 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6807 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6808 G_CALLBACK(compose_attach_drag_received_cb),
6810 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6811 G_CALLBACK(compose_drag_drop),
6814 compose->attach_scrwin = attach_scrwin;
6815 compose->attach_clist = attach_clist;
6817 return attach_scrwin;
6820 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6821 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6823 static GtkWidget *compose_create_others(Compose *compose)
6826 GtkWidget *savemsg_checkbtn;
6827 GtkWidget *savemsg_combo;
6828 GtkWidget *savemsg_select;
6831 gchar *folderidentifier;
6833 /* Table for settings */
6834 table = gtk_table_new(3, 1, FALSE);
6835 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6836 gtk_widget_show(table);
6837 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6840 /* Save Message to folder */
6841 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6842 gtk_widget_show(savemsg_checkbtn);
6843 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6844 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6845 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6847 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6848 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6850 savemsg_combo = gtk_combo_box_entry_new_text();
6851 compose->savemsg_checkbtn = savemsg_checkbtn;
6852 compose->savemsg_combo = savemsg_combo;
6853 gtk_widget_show(savemsg_combo);
6855 if (prefs_common.compose_save_to_history)
6856 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6857 prefs_common.compose_save_to_history);
6859 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6860 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6861 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6862 G_CALLBACK(compose_grab_focus_cb), compose);
6863 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6864 folderidentifier = folder_item_get_identifier(account_get_special_folder
6865 (compose->account, F_OUTBOX));
6866 compose_set_save_to(compose, folderidentifier);
6867 g_free(folderidentifier);
6870 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6871 gtk_widget_show(savemsg_select);
6872 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6873 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6874 G_CALLBACK(compose_savemsg_select_cb),
6882 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6884 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6885 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6888 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6893 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6896 path = folder_item_get_identifier(dest);
6898 compose_set_save_to(compose, path);
6902 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6903 GdkAtom clip, GtkTextIter *insert_place);
6906 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6910 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6912 if (event->button == 3) {
6914 GtkTextIter sel_start, sel_end;
6915 gboolean stuff_selected;
6917 /* move the cursor to allow GtkAspell to check the word
6918 * under the mouse */
6919 if (event->x && event->y) {
6920 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6921 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6923 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6926 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6927 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6930 stuff_selected = gtk_text_buffer_get_selection_bounds(
6932 &sel_start, &sel_end);
6934 gtk_text_buffer_place_cursor (buffer, &iter);
6935 /* reselect stuff */
6937 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6938 gtk_text_buffer_select_range(buffer,
6939 &sel_start, &sel_end);
6941 return FALSE; /* pass the event so that the right-click goes through */
6944 if (event->button == 2) {
6949 /* get the middle-click position to paste at the correct place */
6950 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6951 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6953 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6956 entry_paste_clipboard(compose, text,
6957 prefs_common.linewrap_pastes,
6958 GDK_SELECTION_PRIMARY, &iter);
6966 static void compose_spell_menu_changed(void *data)
6968 Compose *compose = (Compose *)data;
6970 GtkWidget *menuitem;
6971 GtkWidget *parent_item;
6972 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6975 if (compose->gtkaspell == NULL)
6978 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6979 "/Menu/Spelling/Options");
6981 /* setting the submenu removes /Spelling/Options from the factory
6982 * so we need to save it */
6984 if (parent_item == NULL) {
6985 parent_item = compose->aspell_options_menu;
6986 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6988 compose->aspell_options_menu = parent_item;
6990 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6992 spell_menu = g_slist_reverse(spell_menu);
6993 for (items = spell_menu;
6994 items; items = items->next) {
6995 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6996 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6997 gtk_widget_show(GTK_WIDGET(menuitem));
6999 g_slist_free(spell_menu);
7001 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7002 gtk_widget_show(parent_item);
7005 static void compose_dict_changed(void *data)
7007 Compose *compose = (Compose *) data;
7009 if(compose->gtkaspell &&
7010 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7013 gtkaspell_highlight_all(compose->gtkaspell);
7014 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7018 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7020 Compose *compose = (Compose *)data;
7021 GdkEventButton event;
7024 event.time = gtk_get_current_event_time();
7028 return text_clicked(compose->text, &event, compose);
7031 static gboolean compose_force_window_origin = TRUE;
7032 static Compose *compose_create(PrefsAccount *account,
7041 GtkWidget *handlebox;
7043 GtkWidget *notebook;
7045 GtkWidget *attach_hbox;
7046 GtkWidget *attach_lab1;
7047 GtkWidget *attach_lab2;
7052 GtkWidget *subject_hbox;
7053 GtkWidget *subject_frame;
7054 GtkWidget *subject_entry;
7058 GtkWidget *edit_vbox;
7059 GtkWidget *ruler_hbox;
7061 GtkWidget *scrolledwin;
7063 GtkTextBuffer *buffer;
7064 GtkClipboard *clipboard;
7067 UndoMain *undostruct;
7069 gchar *titles[N_ATTACH_COLS];
7070 GtkWidget *popupmenu;
7071 GtkWidget *tmpl_menu;
7072 GtkActionGroup *action_group = NULL;
7075 GtkAspell * gtkaspell = NULL;
7078 static GdkGeometry geometry;
7080 cm_return_val_if_fail(account != NULL, NULL);
7082 debug_print("Creating compose window...\n");
7083 compose = g_new0(Compose, 1);
7085 titles[COL_MIMETYPE] = _("MIME type");
7086 titles[COL_SIZE] = _("Size");
7087 titles[COL_NAME] = _("Name");
7088 titles[COL_CHARSET] = _("Charset");
7090 compose->batch = batch;
7091 compose->account = account;
7092 compose->folder = folder;
7094 compose->mutex = g_mutex_new();
7095 compose->set_cursor_pos = -1;
7097 #if !(GTK_CHECK_VERSION(2,12,0))
7098 compose->tooltips = tips;
7101 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7103 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7104 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7106 if (!geometry.max_width) {
7107 geometry.max_width = gdk_screen_width();
7108 geometry.max_height = gdk_screen_height();
7111 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7112 &geometry, GDK_HINT_MAX_SIZE);
7113 if (!geometry.min_width) {
7114 geometry.min_width = 600;
7115 geometry.min_height = 440;
7117 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7118 &geometry, GDK_HINT_MIN_SIZE);
7120 #ifndef GENERIC_UMPC
7121 if (compose_force_window_origin)
7122 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7123 prefs_common.compose_y);
7125 g_signal_connect(G_OBJECT(window), "delete_event",
7126 G_CALLBACK(compose_delete_cb), compose);
7127 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7128 gtk_widget_realize(window);
7130 gtkut_widget_set_composer_icon(window);
7132 vbox = gtk_vbox_new(FALSE, 0);
7133 gtk_container_add(GTK_CONTAINER(window), vbox);
7135 compose->ui_manager = gtk_ui_manager_new();
7136 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7137 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7138 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7139 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7140 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7141 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7142 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7143 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7144 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7145 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7235 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7254 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7260 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7265 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)
7266 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)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7272 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)
7273 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)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7277 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7278 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)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7282 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)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7288 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)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7294 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)
7295 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)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7304 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7308 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)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7317 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7320 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7326 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7327 gtk_widget_show_all(menubar);
7329 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7331 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7333 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7336 if (prefs_common.toolbar_detachable) {
7337 handlebox = gtk_handle_box_new();
7339 handlebox = gtk_hbox_new(FALSE, 0);
7341 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7343 gtk_widget_realize(handlebox);
7345 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7348 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7352 vbox2 = gtk_vbox_new(FALSE, 2);
7353 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7354 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7357 notebook = gtk_notebook_new();
7358 gtk_widget_set_size_request(notebook, -1, 130);
7359 gtk_widget_show(notebook);
7361 /* header labels and entries */
7362 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7363 compose_create_header(compose),
7364 gtk_label_new_with_mnemonic(_("Hea_der")));
7365 /* attachment list */
7366 attach_hbox = gtk_hbox_new(FALSE, 0);
7367 gtk_widget_show(attach_hbox);
7369 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7370 gtk_widget_show(attach_lab1);
7371 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7373 attach_lab2 = gtk_label_new("");
7374 gtk_widget_show(attach_lab2);
7375 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7377 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7378 compose_create_attach(compose),
7381 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7382 compose_create_others(compose),
7383 gtk_label_new_with_mnemonic(_("Othe_rs")));
7386 subject_hbox = gtk_hbox_new(FALSE, 0);
7387 gtk_widget_show(subject_hbox);
7389 subject_frame = gtk_frame_new(NULL);
7390 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7391 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7392 gtk_widget_show(subject_frame);
7394 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7395 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7396 gtk_widget_show(subject);
7398 label = gtk_label_new(_("Subject:"));
7399 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7400 gtk_widget_show(label);
7403 subject_entry = claws_spell_entry_new();
7405 subject_entry = gtk_entry_new();
7407 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7408 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7409 G_CALLBACK(compose_grab_focus_cb), compose);
7410 gtk_widget_show(subject_entry);
7411 compose->subject_entry = subject_entry;
7412 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7414 edit_vbox = gtk_vbox_new(FALSE, 0);
7416 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7419 ruler_hbox = gtk_hbox_new(FALSE, 0);
7420 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7422 ruler = gtk_shruler_new();
7423 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7424 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7428 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7429 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7430 GTK_POLICY_AUTOMATIC,
7431 GTK_POLICY_AUTOMATIC);
7432 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7434 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7435 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7437 text = gtk_text_view_new();
7438 if (prefs_common.show_compose_margin) {
7439 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7440 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7442 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7443 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7444 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7445 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7446 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7448 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7450 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7451 G_CALLBACK(compose_edit_size_alloc),
7453 g_signal_connect(G_OBJECT(buffer), "changed",
7454 G_CALLBACK(compose_changed_cb), compose);
7455 g_signal_connect(G_OBJECT(text), "grab_focus",
7456 G_CALLBACK(compose_grab_focus_cb), compose);
7457 g_signal_connect(G_OBJECT(buffer), "insert_text",
7458 G_CALLBACK(text_inserted), compose);
7459 g_signal_connect(G_OBJECT(text), "button_press_event",
7460 G_CALLBACK(text_clicked), compose);
7462 g_signal_connect(G_OBJECT(text), "popup-menu",
7463 G_CALLBACK(compose_popup_menu), compose);
7465 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7466 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7467 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7468 G_CALLBACK(compose_popup_menu), compose);
7470 g_signal_connect(G_OBJECT(subject_entry), "changed",
7471 G_CALLBACK(compose_changed_cb), compose);
7474 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7475 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7476 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7477 g_signal_connect(G_OBJECT(text), "drag_data_received",
7478 G_CALLBACK(compose_insert_drag_received_cb),
7480 g_signal_connect(G_OBJECT(text), "drag-drop",
7481 G_CALLBACK(compose_drag_drop),
7483 gtk_widget_show_all(vbox);
7485 /* pane between attach clist and text */
7486 paned = gtk_vpaned_new();
7487 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7489 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7490 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7492 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7494 gtk_paned_add1(GTK_PANED(paned), notebook);
7495 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7496 gtk_widget_show_all(paned);
7499 if (prefs_common.textfont) {
7500 PangoFontDescription *font_desc;
7502 font_desc = pango_font_description_from_string
7503 (prefs_common.textfont);
7505 gtk_widget_modify_font(text, font_desc);
7506 pango_font_description_free(font_desc);
7510 gtk_action_group_add_actions(action_group, compose_popup_entries,
7511 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7512 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7513 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7514 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7515 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7516 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7517 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7519 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7521 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7522 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7523 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7525 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7527 undostruct = undo_init(text);
7528 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7531 address_completion_start(window);
7533 compose->window = window;
7534 compose->vbox = vbox;
7535 compose->menubar = menubar;
7536 compose->handlebox = handlebox;
7538 compose->vbox2 = vbox2;
7540 compose->paned = paned;
7542 compose->attach_label = attach_lab2;
7544 compose->notebook = notebook;
7545 compose->edit_vbox = edit_vbox;
7546 compose->ruler_hbox = ruler_hbox;
7547 compose->ruler = ruler;
7548 compose->scrolledwin = scrolledwin;
7549 compose->text = text;
7551 compose->focused_editable = NULL;
7553 compose->popupmenu = popupmenu;
7555 compose->tmpl_menu = tmpl_menu;
7557 compose->mode = mode;
7558 compose->rmode = mode;
7560 compose->targetinfo = NULL;
7561 compose->replyinfo = NULL;
7562 compose->fwdinfo = NULL;
7564 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7565 g_str_equal, (GDestroyNotify) g_free, NULL);
7567 compose->replyto = NULL;
7569 compose->bcc = NULL;
7570 compose->followup_to = NULL;
7572 compose->ml_post = NULL;
7574 compose->inreplyto = NULL;
7575 compose->references = NULL;
7576 compose->msgid = NULL;
7577 compose->boundary = NULL;
7579 compose->autowrap = prefs_common.autowrap;
7580 compose->autoindent = prefs_common.auto_indent;
7581 compose->use_signing = FALSE;
7582 compose->use_encryption = FALSE;
7583 compose->privacy_system = NULL;
7585 compose->modified = FALSE;
7587 compose->return_receipt = FALSE;
7589 compose->to_list = NULL;
7590 compose->newsgroup_list = NULL;
7592 compose->undostruct = undostruct;
7594 compose->sig_str = NULL;
7596 compose->exteditor_file = NULL;
7597 compose->exteditor_pid = -1;
7598 compose->exteditor_tag = -1;
7599 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7602 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7603 if (mode != COMPOSE_REDIRECT) {
7604 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7605 strcmp(prefs_common.dictionary, "")) {
7606 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7607 prefs_common.alt_dictionary,
7608 conv_get_locale_charset_str(),
7609 prefs_common.misspelled_col,
7610 prefs_common.check_while_typing,
7611 prefs_common.recheck_when_changing_dict,
7612 prefs_common.use_alternate,
7613 prefs_common.use_both_dicts,
7614 GTK_TEXT_VIEW(text),
7615 GTK_WINDOW(compose->window),
7616 compose_dict_changed,
7617 compose_spell_menu_changed,
7620 alertpanel_error(_("Spell checker could not "
7622 gtkaspell_checkers_strerror());
7623 gtkaspell_checkers_reset_error();
7625 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7629 compose->gtkaspell = gtkaspell;
7630 compose_spell_menu_changed(compose);
7631 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7634 compose_select_account(compose, account, TRUE);
7636 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7637 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7639 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7640 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7642 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7643 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7645 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7646 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7648 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7649 if (account->protocol != A_NNTP)
7650 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7651 prefs_common_translated_header_name("To:"));
7653 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7654 prefs_common_translated_header_name("Newsgroups:"));
7656 addressbook_set_target_compose(compose);
7658 if (mode != COMPOSE_REDIRECT)
7659 compose_set_template_menu(compose);
7661 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7664 compose_list = g_list_append(compose_list, compose);
7666 if (!prefs_common.show_ruler)
7667 gtk_widget_hide(ruler_hbox);
7669 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7672 compose->priority = PRIORITY_NORMAL;
7673 compose_update_priority_menu_item(compose);
7675 compose_set_out_encoding(compose);
7678 compose_update_actions_menu(compose);
7680 /* Privacy Systems menu */
7681 compose_update_privacy_systems_menu(compose);
7683 activate_privacy_system(compose, account, TRUE);
7684 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7686 gtk_widget_realize(window);
7688 gtk_widget_show(window);
7690 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7691 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7698 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7703 GtkWidget *optmenubox;
7706 GtkWidget *from_name = NULL;
7707 #if !(GTK_CHECK_VERSION(2,12,0))
7708 GtkTooltips *tips = compose->tooltips;
7711 gint num = 0, def_menu = 0;
7713 accounts = account_get_list();
7714 cm_return_val_if_fail(accounts != NULL, NULL);
7716 optmenubox = gtk_event_box_new();
7717 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7718 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7720 hbox = gtk_hbox_new(FALSE, 6);
7721 from_name = gtk_entry_new();
7723 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7724 G_CALLBACK(compose_grab_focus_cb), compose);
7726 for (; accounts != NULL; accounts = accounts->next, num++) {
7727 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7728 gchar *name, *from = NULL;
7730 if (ac == compose->account) def_menu = num;
7732 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7735 if (ac == compose->account) {
7736 if (ac->name && *ac->name) {
7738 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7739 from = g_strdup_printf("%s <%s>",
7741 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7743 from = g_strdup_printf("%s",
7745 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7748 COMBOBOX_ADD(menu, name, ac->account_id);
7753 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7755 g_signal_connect(G_OBJECT(optmenu), "changed",
7756 G_CALLBACK(account_activated),
7758 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7759 G_CALLBACK(compose_entry_popup_extend),
7762 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7763 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7765 CLAWS_SET_TIP(optmenubox,
7766 _("Account to use for this email"));
7767 CLAWS_SET_TIP(from_name,
7768 _("Sender address to be used"));
7770 compose->account_combo = optmenu;
7771 compose->from_name = from_name;
7776 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7778 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7779 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7780 Compose *compose = (Compose *) data;
7782 compose->priority = value;
7786 static void compose_reply_change_mode(Compose *compose,
7789 gboolean was_modified = compose->modified;
7791 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7793 cm_return_if_fail(compose->replyinfo != NULL);
7795 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7797 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7799 if (action == COMPOSE_REPLY_TO_ALL)
7801 if (action == COMPOSE_REPLY_TO_SENDER)
7803 if (action == COMPOSE_REPLY_TO_LIST)
7806 compose_remove_header_entries(compose);
7807 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7808 if (compose->account->set_autocc && compose->account->auto_cc)
7809 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7811 if (compose->account->set_autobcc && compose->account->auto_bcc)
7812 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7814 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7815 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7816 compose_show_first_last_header(compose, TRUE);
7817 compose->modified = was_modified;
7818 compose_set_title(compose);
7821 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7823 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7824 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7825 Compose *compose = (Compose *) data;
7828 compose_reply_change_mode(compose, value);
7831 static void compose_update_priority_menu_item(Compose * compose)
7833 GtkWidget *menuitem = NULL;
7834 switch (compose->priority) {
7835 case PRIORITY_HIGHEST:
7836 menuitem = gtk_ui_manager_get_widget
7837 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7840 menuitem = gtk_ui_manager_get_widget
7841 (compose->ui_manager, "/Menu/Options/Priority/High");
7843 case PRIORITY_NORMAL:
7844 menuitem = gtk_ui_manager_get_widget
7845 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7848 menuitem = gtk_ui_manager_get_widget
7849 (compose->ui_manager, "/Menu/Options/Priority/Low");
7851 case PRIORITY_LOWEST:
7852 menuitem = gtk_ui_manager_get_widget
7853 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7856 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7859 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7861 Compose *compose = (Compose *) data;
7863 gboolean can_sign = FALSE, can_encrypt = FALSE;
7865 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7867 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7870 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7871 g_free(compose->privacy_system);
7872 compose->privacy_system = NULL;
7873 if (systemid != NULL) {
7874 compose->privacy_system = g_strdup(systemid);
7876 can_sign = privacy_system_can_sign(systemid);
7877 can_encrypt = privacy_system_can_encrypt(systemid);
7880 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7882 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7883 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7886 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7888 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7889 GtkWidget *menuitem = NULL;
7891 gboolean can_sign = FALSE, can_encrypt = FALSE;
7892 gboolean found = FALSE;
7894 if (compose->privacy_system != NULL) {
7896 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7897 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7898 cm_return_if_fail(menuitem != NULL);
7900 amenu = GTK_MENU_SHELL(menuitem)->children;
7902 while (amenu != NULL) {
7903 GList *alist = amenu->next;
7905 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7906 if (systemid != NULL) {
7907 if (strcmp(systemid, compose->privacy_system) == 0 &&
7908 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7909 menuitem = GTK_WIDGET(amenu->data);
7911 can_sign = privacy_system_can_sign(systemid);
7912 can_encrypt = privacy_system_can_encrypt(systemid);
7916 } else if (strlen(compose->privacy_system) == 0 &&
7917 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7918 menuitem = GTK_WIDGET(amenu->data);
7921 can_encrypt = FALSE;
7928 if (menuitem != NULL)
7929 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7931 if (warn && !found && strlen(compose->privacy_system)) {
7932 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7933 "will not be able to sign or encrypt this message."),
7934 compose->privacy_system);
7938 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7939 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7942 static void compose_set_out_encoding(Compose *compose)
7944 CharSet out_encoding;
7945 const gchar *branch = NULL;
7946 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7948 switch(out_encoding) {
7949 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7950 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7951 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7952 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7953 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7954 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7955 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7956 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7957 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7958 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7959 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7960 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7961 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7962 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7963 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7964 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7965 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7966 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7967 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7968 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7969 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7970 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7971 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7972 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7973 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7974 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7975 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7976 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7977 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7978 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7979 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7980 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7981 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7983 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7986 static void compose_set_template_menu(Compose *compose)
7988 GSList *tmpl_list, *cur;
7992 tmpl_list = template_get_config();
7994 menu = gtk_menu_new();
7996 gtk_menu_set_accel_group (GTK_MENU (menu),
7997 gtk_ui_manager_get_accel_group(compose->ui_manager));
7998 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7999 Template *tmpl = (Template *)cur->data;
8000 gchar *accel_path = NULL;
8001 item = gtk_menu_item_new_with_label(tmpl->name);
8002 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8003 g_signal_connect(G_OBJECT(item), "activate",
8004 G_CALLBACK(compose_template_activate_cb),
8006 g_object_set_data(G_OBJECT(item), "template", tmpl);
8007 gtk_widget_show(item);
8008 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8009 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8013 gtk_widget_show(menu);
8014 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8017 void compose_update_actions_menu(Compose *compose)
8019 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8022 static void compose_update_privacy_systems_menu(Compose *compose)
8024 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8025 GSList *systems, *cur;
8027 GtkWidget *system_none;
8029 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8030 GtkWidget *privacy_menu = gtk_menu_new();
8032 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8033 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8035 g_signal_connect(G_OBJECT(system_none), "activate",
8036 G_CALLBACK(compose_set_privacy_system_cb), compose);
8038 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8039 gtk_widget_show(system_none);
8041 systems = privacy_get_system_ids();
8042 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8043 gchar *systemid = cur->data;
8045 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8046 widget = gtk_radio_menu_item_new_with_label(group,
8047 privacy_system_get_name(systemid));
8048 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8049 g_strdup(systemid), g_free);
8050 g_signal_connect(G_OBJECT(widget), "activate",
8051 G_CALLBACK(compose_set_privacy_system_cb), compose);
8053 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8054 gtk_widget_show(widget);
8057 g_slist_free(systems);
8058 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8059 gtk_widget_show_all(privacy_menu);
8060 gtk_widget_show_all(privacy_menuitem);
8063 void compose_reflect_prefs_all(void)
8068 for (cur = compose_list; cur != NULL; cur = cur->next) {
8069 compose = (Compose *)cur->data;
8070 compose_set_template_menu(compose);
8074 void compose_reflect_prefs_pixmap_theme(void)
8079 for (cur = compose_list; cur != NULL; cur = cur->next) {
8080 compose = (Compose *)cur->data;
8081 toolbar_update(TOOLBAR_COMPOSE, compose);
8085 static const gchar *compose_quote_char_from_context(Compose *compose)
8087 const gchar *qmark = NULL;
8089 cm_return_val_if_fail(compose != NULL, NULL);
8091 switch (compose->mode) {
8092 /* use forward-specific quote char */
8093 case COMPOSE_FORWARD:
8094 case COMPOSE_FORWARD_AS_ATTACH:
8095 case COMPOSE_FORWARD_INLINE:
8096 if (compose->folder && compose->folder->prefs &&
8097 compose->folder->prefs->forward_with_format)
8098 qmark = compose->folder->prefs->forward_quotemark;
8099 else if (compose->account->forward_with_format)
8100 qmark = compose->account->forward_quotemark;
8102 qmark = prefs_common.fw_quotemark;
8105 /* use reply-specific quote char in all other modes */
8107 if (compose->folder && compose->folder->prefs &&
8108 compose->folder->prefs->reply_with_format)
8109 qmark = compose->folder->prefs->reply_quotemark;
8110 else if (compose->account->reply_with_format)
8111 qmark = compose->account->reply_quotemark;
8113 qmark = prefs_common.quotemark;
8117 if (qmark == NULL || *qmark == '\0')
8123 static void compose_template_apply(Compose *compose, Template *tmpl,
8127 GtkTextBuffer *buffer;
8131 gchar *parsed_str = NULL;
8132 gint cursor_pos = 0;
8133 const gchar *err_msg = _("The body of the template has an error at line %d.");
8136 /* process the body */
8138 text = GTK_TEXT_VIEW(compose->text);
8139 buffer = gtk_text_view_get_buffer(text);
8142 qmark = compose_quote_char_from_context(compose);
8144 if (compose->replyinfo != NULL) {
8147 gtk_text_buffer_set_text(buffer, "", -1);
8148 mark = gtk_text_buffer_get_insert(buffer);
8149 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8151 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8152 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8154 } else if (compose->fwdinfo != NULL) {
8157 gtk_text_buffer_set_text(buffer, "", -1);
8158 mark = gtk_text_buffer_get_insert(buffer);
8159 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8161 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8162 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8165 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8167 GtkTextIter start, end;
8170 gtk_text_buffer_get_start_iter(buffer, &start);
8171 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8172 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8174 /* clear the buffer now */
8176 gtk_text_buffer_set_text(buffer, "", -1);
8178 parsed_str = compose_quote_fmt(compose, dummyinfo,
8179 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8180 procmsg_msginfo_free( dummyinfo );
8186 gtk_text_buffer_set_text(buffer, "", -1);
8187 mark = gtk_text_buffer_get_insert(buffer);
8188 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8191 if (replace && parsed_str && compose->account->auto_sig)
8192 compose_insert_sig(compose, FALSE);
8194 if (replace && parsed_str) {
8195 gtk_text_buffer_get_start_iter(buffer, &iter);
8196 gtk_text_buffer_place_cursor(buffer, &iter);
8200 cursor_pos = quote_fmt_get_cursor_pos();
8201 compose->set_cursor_pos = cursor_pos;
8202 if (cursor_pos == -1)
8204 gtk_text_buffer_get_start_iter(buffer, &iter);
8205 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8206 gtk_text_buffer_place_cursor(buffer, &iter);
8209 /* process the other fields */
8211 compose_template_apply_fields(compose, tmpl);
8212 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8213 quote_fmt_reset_vartable();
8214 compose_changed_cb(NULL, compose);
8217 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8218 gtkaspell_highlight_all(compose->gtkaspell);
8222 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8224 MsgInfo* dummyinfo = NULL;
8225 MsgInfo *msginfo = NULL;
8228 if (compose->replyinfo != NULL)
8229 msginfo = compose->replyinfo;
8230 else if (compose->fwdinfo != NULL)
8231 msginfo = compose->fwdinfo;
8233 dummyinfo = compose_msginfo_new_from_compose(compose);
8234 msginfo = dummyinfo;
8237 if (tmpl->from && *tmpl->from != '\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->from);
8247 buf = quote_fmt_get_buffer();
8249 alertpanel_error(_("Template From format error."));
8251 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8255 if (tmpl->to && *tmpl->to != '\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->to);
8265 buf = quote_fmt_get_buffer();
8267 alertpanel_error(_("Template To format error."));
8269 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8273 if (tmpl->cc && *tmpl->cc != '\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->cc);
8283 buf = quote_fmt_get_buffer();
8285 alertpanel_error(_("Template Cc format error."));
8287 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8291 if (tmpl->bcc && *tmpl->bcc != '\0') {
8293 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8294 compose->gtkaspell);
8296 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8298 quote_fmt_scan_string(tmpl->bcc);
8301 buf = quote_fmt_get_buffer();
8303 alertpanel_error(_("Template Bcc format error."));
8305 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8309 /* process the subject */
8310 if (tmpl->subject && *tmpl->subject != '\0') {
8312 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8313 compose->gtkaspell);
8315 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8317 quote_fmt_scan_string(tmpl->subject);
8320 buf = quote_fmt_get_buffer();
8322 alertpanel_error(_("Template subject format error."));
8324 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8328 procmsg_msginfo_free( dummyinfo );
8331 static void compose_destroy(Compose *compose)
8333 GtkTextBuffer *buffer;
8334 GtkClipboard *clipboard;
8336 compose_list = g_list_remove(compose_list, compose);
8338 if (compose->updating) {
8339 debug_print("danger, not destroying anything now\n");
8340 compose->deferred_destroy = TRUE;
8343 /* NOTE: address_completion_end() does nothing with the window
8344 * however this may change. */
8345 address_completion_end(compose->window);
8347 slist_free_strings(compose->to_list);
8348 g_slist_free(compose->to_list);
8349 slist_free_strings(compose->newsgroup_list);
8350 g_slist_free(compose->newsgroup_list);
8351 slist_free_strings(compose->header_list);
8352 g_slist_free(compose->header_list);
8354 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8356 g_hash_table_destroy(compose->email_hashtable);
8358 procmsg_msginfo_free(compose->targetinfo);
8359 procmsg_msginfo_free(compose->replyinfo);
8360 procmsg_msginfo_free(compose->fwdinfo);
8362 g_free(compose->replyto);
8363 g_free(compose->cc);
8364 g_free(compose->bcc);
8365 g_free(compose->newsgroups);
8366 g_free(compose->followup_to);
8368 g_free(compose->ml_post);
8370 g_free(compose->inreplyto);
8371 g_free(compose->references);
8372 g_free(compose->msgid);
8373 g_free(compose->boundary);
8375 g_free(compose->redirect_filename);
8376 if (compose->undostruct)
8377 undo_destroy(compose->undostruct);
8379 g_free(compose->sig_str);
8381 g_free(compose->exteditor_file);
8383 g_free(compose->orig_charset);
8385 g_free(compose->privacy_system);
8387 if (addressbook_get_target_compose() == compose)
8388 addressbook_set_target_compose(NULL);
8391 if (compose->gtkaspell) {
8392 gtkaspell_delete(compose->gtkaspell);
8393 compose->gtkaspell = NULL;
8397 if (!compose->batch) {
8398 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8399 prefs_common.compose_height = compose->window->allocation.height;
8402 if (!gtk_widget_get_parent(compose->paned))
8403 gtk_widget_destroy(compose->paned);
8404 gtk_widget_destroy(compose->popupmenu);
8406 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8407 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8408 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8410 gtk_widget_destroy(compose->window);
8411 toolbar_destroy(compose->toolbar);
8412 g_free(compose->toolbar);
8413 g_mutex_free(compose->mutex);
8417 static void compose_attach_info_free(AttachInfo *ainfo)
8419 g_free(ainfo->file);
8420 g_free(ainfo->content_type);
8421 g_free(ainfo->name);
8422 g_free(ainfo->charset);
8426 static void compose_attach_update_label(Compose *compose)
8431 GtkTreeModel *model;
8436 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8437 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8438 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8442 while(gtk_tree_model_iter_next(model, &iter))
8445 text = g_strdup_printf("(%d)", i);
8446 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8450 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8452 Compose *compose = (Compose *)data;
8453 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8454 GtkTreeSelection *selection;
8456 GtkTreeModel *model;
8458 selection = gtk_tree_view_get_selection(tree_view);
8459 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8464 for (cur = sel; cur != NULL; cur = cur->next) {
8465 GtkTreePath *path = cur->data;
8466 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8469 gtk_tree_path_free(path);
8472 for (cur = sel; cur != NULL; cur = cur->next) {
8473 GtkTreeRowReference *ref = cur->data;
8474 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8477 if (gtk_tree_model_get_iter(model, &iter, path))
8478 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8480 gtk_tree_path_free(path);
8481 gtk_tree_row_reference_free(ref);
8485 compose_attach_update_label(compose);
8488 static struct _AttachProperty
8491 GtkWidget *mimetype_entry;
8492 GtkWidget *encoding_optmenu;
8493 GtkWidget *path_entry;
8494 GtkWidget *filename_entry;
8496 GtkWidget *cancel_btn;
8499 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8501 gtk_tree_path_free((GtkTreePath *)ptr);
8504 static void compose_attach_property(GtkAction *action, gpointer data)
8506 Compose *compose = (Compose *)data;
8507 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8509 GtkComboBox *optmenu;
8510 GtkTreeSelection *selection;
8512 GtkTreeModel *model;
8515 static gboolean cancelled;
8517 /* only if one selected */
8518 selection = gtk_tree_view_get_selection(tree_view);
8519 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8522 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8526 path = (GtkTreePath *) sel->data;
8527 gtk_tree_model_get_iter(model, &iter, path);
8528 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8531 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8537 if (!attach_prop.window)
8538 compose_attach_property_create(&cancelled);
8539 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8540 gtk_widget_grab_focus(attach_prop.ok_btn);
8541 gtk_widget_show(attach_prop.window);
8542 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8544 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8545 if (ainfo->encoding == ENC_UNKNOWN)
8546 combobox_select_by_data(optmenu, ENC_BASE64);
8548 combobox_select_by_data(optmenu, ainfo->encoding);
8550 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8551 ainfo->content_type ? ainfo->content_type : "");
8552 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8553 ainfo->file ? ainfo->file : "");
8554 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8555 ainfo->name ? ainfo->name : "");
8558 const gchar *entry_text;
8560 gchar *cnttype = NULL;
8567 gtk_widget_hide(attach_prop.window);
8568 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8573 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8574 if (*entry_text != '\0') {
8577 text = g_strstrip(g_strdup(entry_text));
8578 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8579 cnttype = g_strdup(text);
8582 alertpanel_error(_("Invalid MIME type."));
8588 ainfo->encoding = combobox_get_active_data(optmenu);
8590 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8591 if (*entry_text != '\0') {
8592 if (is_file_exist(entry_text) &&
8593 (size = get_file_size(entry_text)) > 0)
8594 file = g_strdup(entry_text);
8597 (_("File doesn't exist or is empty."));
8603 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8604 if (*entry_text != '\0') {
8605 g_free(ainfo->name);
8606 ainfo->name = g_strdup(entry_text);
8610 g_free(ainfo->content_type);
8611 ainfo->content_type = cnttype;
8614 g_free(ainfo->file);
8618 ainfo->size = (goffset)size;
8620 /* update tree store */
8621 text = to_human_readable(ainfo->size);
8622 gtk_tree_model_get_iter(model, &iter, path);
8623 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8624 COL_MIMETYPE, ainfo->content_type,
8626 COL_NAME, ainfo->name,
8627 COL_CHARSET, ainfo->charset,
8633 gtk_tree_path_free(path);
8636 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8638 label = gtk_label_new(str); \
8639 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8640 GTK_FILL, 0, 0, 0); \
8641 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8643 entry = gtk_entry_new(); \
8644 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8645 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8648 static void compose_attach_property_create(gboolean *cancelled)
8654 GtkWidget *mimetype_entry;
8657 GtkListStore *optmenu_menu;
8658 GtkWidget *path_entry;
8659 GtkWidget *filename_entry;
8662 GtkWidget *cancel_btn;
8663 GList *mime_type_list, *strlist;
8666 debug_print("Creating attach_property window...\n");
8668 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8669 gtk_widget_set_size_request(window, 480, -1);
8670 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8671 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8672 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8673 g_signal_connect(G_OBJECT(window), "delete_event",
8674 G_CALLBACK(attach_property_delete_event),
8676 g_signal_connect(G_OBJECT(window), "key_press_event",
8677 G_CALLBACK(attach_property_key_pressed),
8680 vbox = gtk_vbox_new(FALSE, 8);
8681 gtk_container_add(GTK_CONTAINER(window), vbox);
8683 table = gtk_table_new(4, 2, FALSE);
8684 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8685 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8686 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8688 label = gtk_label_new(_("MIME type"));
8689 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8691 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8692 mimetype_entry = gtk_combo_box_entry_new_text();
8693 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8694 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8696 /* stuff with list */
8697 mime_type_list = procmime_get_mime_type_list();
8699 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8700 MimeType *type = (MimeType *) mime_type_list->data;
8703 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8705 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8708 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8709 (GCompareFunc)strcmp2);
8712 for (mime_type_list = strlist; mime_type_list != NULL;
8713 mime_type_list = mime_type_list->next) {
8714 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8715 g_free(mime_type_list->data);
8717 g_list_free(strlist);
8718 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8719 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8721 label = gtk_label_new(_("Encoding"));
8722 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8724 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8726 hbox = gtk_hbox_new(FALSE, 0);
8727 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8728 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8730 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8731 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8733 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8734 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8735 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8736 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8737 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8739 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8741 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8742 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8744 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8745 &ok_btn, GTK_STOCK_OK,
8747 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8748 gtk_widget_grab_default(ok_btn);
8750 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8751 G_CALLBACK(attach_property_ok),
8753 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8754 G_CALLBACK(attach_property_cancel),
8757 gtk_widget_show_all(vbox);
8759 attach_prop.window = window;
8760 attach_prop.mimetype_entry = mimetype_entry;
8761 attach_prop.encoding_optmenu = optmenu;
8762 attach_prop.path_entry = path_entry;
8763 attach_prop.filename_entry = filename_entry;
8764 attach_prop.ok_btn = ok_btn;
8765 attach_prop.cancel_btn = cancel_btn;
8768 #undef SET_LABEL_AND_ENTRY
8770 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8776 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8782 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8783 gboolean *cancelled)
8791 static gboolean attach_property_key_pressed(GtkWidget *widget,
8793 gboolean *cancelled)
8795 if (event && event->keyval == GDK_Escape) {
8799 if (event && event->keyval == GDK_Return) {
8807 static void compose_exec_ext_editor(Compose *compose)
8814 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8815 G_DIR_SEPARATOR, compose);
8817 if (pipe(pipe_fds) < 0) {
8823 if ((pid = fork()) < 0) {
8830 /* close the write side of the pipe */
8833 compose->exteditor_file = g_strdup(tmp);
8834 compose->exteditor_pid = pid;
8836 compose_set_ext_editor_sensitive(compose, FALSE);
8839 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8841 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8843 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8847 } else { /* process-monitoring process */
8853 /* close the read side of the pipe */
8856 if (compose_write_body_to_file(compose, tmp) < 0) {
8857 fd_write_all(pipe_fds[1], "2\n", 2);
8861 pid_ed = compose_exec_ext_editor_real(tmp);
8863 fd_write_all(pipe_fds[1], "1\n", 2);
8867 /* wait until editor is terminated */
8868 waitpid(pid_ed, NULL, 0);
8870 fd_write_all(pipe_fds[1], "0\n", 2);
8877 #endif /* G_OS_UNIX */
8881 static gint compose_exec_ext_editor_real(const gchar *file)
8888 cm_return_val_if_fail(file != NULL, -1);
8890 if ((pid = fork()) < 0) {
8895 if (pid != 0) return pid;
8897 /* grandchild process */
8899 if (setpgid(0, getppid()))
8902 if (prefs_common_get_ext_editor_cmd() &&
8903 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8904 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8905 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8907 if (prefs_common_get_ext_editor_cmd())
8908 g_warning("External editor command-line is invalid: '%s'\n",
8909 prefs_common_get_ext_editor_cmd());
8910 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8913 cmdline = strsplit_with_quote(buf, " ", 1024);
8914 execvp(cmdline[0], cmdline);
8917 g_strfreev(cmdline);
8922 static gboolean compose_ext_editor_kill(Compose *compose)
8924 pid_t pgid = compose->exteditor_pid * -1;
8927 ret = kill(pgid, 0);
8929 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8933 msg = g_strdup_printf
8934 (_("The external editor is still working.\n"
8935 "Force terminating the process?\n"
8936 "process group id: %d"), -pgid);
8937 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8938 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8942 if (val == G_ALERTALTERNATE) {
8943 g_source_remove(compose->exteditor_tag);
8944 g_io_channel_shutdown(compose->exteditor_ch,
8946 g_io_channel_unref(compose->exteditor_ch);
8948 if (kill(pgid, SIGTERM) < 0) perror("kill");
8949 waitpid(compose->exteditor_pid, NULL, 0);
8951 g_warning("Terminated process group id: %d", -pgid);
8952 g_warning("Temporary file: %s",
8953 compose->exteditor_file);
8955 compose_set_ext_editor_sensitive(compose, TRUE);
8957 g_free(compose->exteditor_file);
8958 compose->exteditor_file = NULL;
8959 compose->exteditor_pid = -1;
8960 compose->exteditor_ch = NULL;
8961 compose->exteditor_tag = -1;
8969 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8973 Compose *compose = (Compose *)data;
8976 debug_print("Compose: input from monitoring process\n");
8978 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8980 g_io_channel_shutdown(source, FALSE, NULL);
8981 g_io_channel_unref(source);
8983 waitpid(compose->exteditor_pid, NULL, 0);
8985 if (buf[0] == '0') { /* success */
8986 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8987 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8989 gtk_text_buffer_set_text(buffer, "", -1);
8990 compose_insert_file(compose, compose->exteditor_file);
8991 compose_changed_cb(NULL, compose);
8992 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8994 if (claws_unlink(compose->exteditor_file) < 0)
8995 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8996 } else if (buf[0] == '1') { /* failed */
8997 g_warning("Couldn't exec external editor\n");
8998 if (claws_unlink(compose->exteditor_file) < 0)
8999 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9000 } else if (buf[0] == '2') {
9001 g_warning("Couldn't write to file\n");
9002 } else if (buf[0] == '3') {
9003 g_warning("Pipe read failed\n");
9006 compose_set_ext_editor_sensitive(compose, TRUE);
9008 g_free(compose->exteditor_file);
9009 compose->exteditor_file = NULL;
9010 compose->exteditor_pid = -1;
9011 compose->exteditor_ch = NULL;
9012 compose->exteditor_tag = -1;
9017 static void compose_set_ext_editor_sensitive(Compose *compose,
9020 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9021 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9022 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9023 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9024 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9025 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9026 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9028 gtk_widget_set_sensitive(compose->text, sensitive);
9029 if (compose->toolbar->send_btn)
9030 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9031 if (compose->toolbar->sendl_btn)
9032 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9033 if (compose->toolbar->draft_btn)
9034 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9035 if (compose->toolbar->insert_btn)
9036 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9037 if (compose->toolbar->sig_btn)
9038 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9039 if (compose->toolbar->exteditor_btn)
9040 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9041 if (compose->toolbar->linewrap_current_btn)
9042 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9043 if (compose->toolbar->linewrap_all_btn)
9044 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9046 #endif /* G_OS_UNIX */
9049 * compose_undo_state_changed:
9051 * Change the sensivity of the menuentries undo and redo
9053 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9054 gint redo_state, gpointer data)
9056 Compose *compose = (Compose *)data;
9058 switch (undo_state) {
9059 case UNDO_STATE_TRUE:
9060 if (!undostruct->undo_state) {
9061 undostruct->undo_state = TRUE;
9062 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9065 case UNDO_STATE_FALSE:
9066 if (undostruct->undo_state) {
9067 undostruct->undo_state = FALSE;
9068 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9071 case UNDO_STATE_UNCHANGED:
9073 case UNDO_STATE_REFRESH:
9074 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9077 g_warning("Undo state not recognized");
9081 switch (redo_state) {
9082 case UNDO_STATE_TRUE:
9083 if (!undostruct->redo_state) {
9084 undostruct->redo_state = TRUE;
9085 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9088 case UNDO_STATE_FALSE:
9089 if (undostruct->redo_state) {
9090 undostruct->redo_state = FALSE;
9091 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9094 case UNDO_STATE_UNCHANGED:
9096 case UNDO_STATE_REFRESH:
9097 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9100 g_warning("Redo state not recognized");
9105 /* callback functions */
9107 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9108 * includes "non-client" (windows-izm) in calculation, so this calculation
9109 * may not be accurate.
9111 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9112 GtkAllocation *allocation,
9113 GtkSHRuler *shruler)
9115 if (prefs_common.show_ruler) {
9116 gint char_width = 0, char_height = 0;
9117 gint line_width_in_chars;
9119 gtkut_get_font_size(GTK_WIDGET(widget),
9120 &char_width, &char_height);
9121 line_width_in_chars =
9122 (allocation->width - allocation->x) / char_width;
9124 /* got the maximum */
9125 gtk_ruler_set_range(GTK_RULER(shruler),
9126 0.0, line_width_in_chars, 0,
9127 /*line_width_in_chars*/ char_width);
9136 ComposePrefType type;
9137 gboolean entry_marked;
9140 static void account_activated(GtkComboBox *optmenu, gpointer data)
9142 Compose *compose = (Compose *)data;
9145 gchar *folderidentifier;
9146 gint account_id = 0;
9149 GSList *list, *saved_list = NULL;
9150 HeaderEntryState *state;
9151 GtkRcStyle *style = NULL;
9152 static GdkColor yellow;
9153 static gboolean color_set = FALSE;
9155 /* Get ID of active account in the combo box */
9156 menu = gtk_combo_box_get_model(optmenu);
9157 gtk_combo_box_get_active_iter(optmenu, &iter);
9158 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9160 ac = account_find_from_id(account_id);
9161 cm_return_if_fail(ac != NULL);
9163 if (ac != compose->account) {
9164 compose_select_account(compose, ac, FALSE);
9166 for (list = compose->header_list; list; list = list->next) {
9167 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9169 if (hentry->type == PREF_ACCOUNT || !list->next) {
9170 compose_destroy_headerentry(compose, hentry);
9174 state = g_malloc0(sizeof(HeaderEntryState));
9175 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9176 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9177 state->entry = gtk_editable_get_chars(
9178 GTK_EDITABLE(hentry->entry), 0, -1);
9179 state->type = hentry->type;
9182 gdk_color_parse("#f5f6be", &yellow);
9183 color_set = gdk_colormap_alloc_color(
9184 gdk_colormap_get_system(),
9185 &yellow, FALSE, TRUE);
9188 style = gtk_widget_get_modifier_style(hentry->entry);
9189 state->entry_marked = gdk_color_equal(&yellow,
9190 &style->base[GTK_STATE_NORMAL]);
9192 saved_list = g_slist_append(saved_list, state);
9193 compose_destroy_headerentry(compose, hentry);
9196 compose->header_last = NULL;
9197 g_slist_free(compose->header_list);
9198 compose->header_list = NULL;
9199 compose->header_nextrow = 1;
9200 compose_create_header_entry(compose);
9202 if (ac->set_autocc && ac->auto_cc)
9203 compose_entry_append(compose, ac->auto_cc,
9204 COMPOSE_CC, PREF_ACCOUNT);
9206 if (ac->set_autobcc && ac->auto_bcc)
9207 compose_entry_append(compose, ac->auto_bcc,
9208 COMPOSE_BCC, PREF_ACCOUNT);
9210 if (ac->set_autoreplyto && ac->auto_replyto)
9211 compose_entry_append(compose, ac->auto_replyto,
9212 COMPOSE_REPLYTO, PREF_ACCOUNT);
9214 for (list = saved_list; list; list = list->next) {
9215 state = (HeaderEntryState *) list->data;
9217 compose_add_header_entry(compose, state->header,
9218 state->entry, state->type);
9219 if (state->entry_marked)
9220 compose_entry_mark_default_to(compose, state->entry);
9222 g_free(state->header);
9223 g_free(state->entry);
9226 g_slist_free(saved_list);
9228 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9229 (ac->protocol == A_NNTP) ?
9230 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9233 /* Set message save folder */
9234 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9237 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9238 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9240 compose_set_save_to(compose, NULL);
9241 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9242 folderidentifier = folder_item_get_identifier(account_get_special_folder
9243 (compose->account, F_OUTBOX));
9244 compose_set_save_to(compose, folderidentifier);
9245 g_free(folderidentifier);
9249 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9250 GtkTreeViewColumn *column, Compose *compose)
9252 compose_attach_property(NULL, compose);
9255 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9258 Compose *compose = (Compose *)data;
9259 GtkTreeSelection *attach_selection;
9260 gint attach_nr_selected;
9262 if (!event) return FALSE;
9264 if (event->button == 3) {
9265 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9266 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9268 if (attach_nr_selected > 0)
9270 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9273 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9274 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9277 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9278 NULL, NULL, event->button, event->time);
9285 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9288 Compose *compose = (Compose *)data;
9290 if (!event) return FALSE;
9292 switch (event->keyval) {
9294 compose_attach_remove_selected(NULL, compose);
9300 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9302 toolbar_comp_set_sensitive(compose, allow);
9303 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9304 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9306 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9308 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9309 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9310 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9312 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9316 static void compose_send_cb(GtkAction *action, gpointer data)
9318 Compose *compose = (Compose *)data;
9320 if (prefs_common.work_offline &&
9321 !inc_offline_should_override(TRUE,
9322 _("Claws Mail needs network access in order "
9323 "to send this email.")))
9326 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9327 g_source_remove(compose->draft_timeout_tag);
9328 compose->draft_timeout_tag = -1;
9331 compose_send(compose);
9334 static void compose_send_later_cb(GtkAction *action, gpointer data)
9336 Compose *compose = (Compose *)data;
9340 compose_allow_user_actions(compose, FALSE);
9341 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9342 compose_allow_user_actions(compose, TRUE);
9346 compose_close(compose);
9347 } else if (val == -1) {
9348 alertpanel_error(_("Could not queue message."));
9349 } else if (val == -2) {
9350 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9351 } else if (val == -3) {
9352 if (privacy_peek_error())
9353 alertpanel_error(_("Could not queue message for sending:\n\n"
9354 "Signature failed: %s"), privacy_get_error());
9355 } else if (val == -4) {
9356 alertpanel_error(_("Could not queue message for sending:\n\n"
9357 "Charset conversion failed."));
9358 } else if (val == -5) {
9359 alertpanel_error(_("Could not queue message for sending:\n\n"
9360 "Couldn't get recipient encryption key."));
9361 } else if (val == -6) {
9364 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9367 #define DRAFTED_AT_EXIT "drafted_at_exit"
9368 static void compose_register_draft(MsgInfo *info)
9370 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9371 DRAFTED_AT_EXIT, NULL);
9372 FILE *fp = g_fopen(filepath, "ab");
9375 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9383 gboolean compose_draft (gpointer data, guint action)
9385 Compose *compose = (Compose *)data;
9389 MsgFlags flag = {0, 0};
9390 static gboolean lock = FALSE;
9391 MsgInfo *newmsginfo;
9393 gboolean target_locked = FALSE;
9394 gboolean err = FALSE;
9396 if (lock) return FALSE;
9398 if (compose->sending)
9401 draft = account_get_special_folder(compose->account, F_DRAFT);
9402 cm_return_val_if_fail(draft != NULL, FALSE);
9404 if (!g_mutex_trylock(compose->mutex)) {
9405 /* we don't want to lock the mutex once it's available,
9406 * because as the only other part of compose.c locking
9407 * it is compose_close - which means once unlocked,
9408 * the compose struct will be freed */
9409 debug_print("couldn't lock mutex, probably sending\n");
9415 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9416 G_DIR_SEPARATOR, compose);
9417 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9418 FILE_OP_ERROR(tmp, "fopen");
9422 /* chmod for security */
9423 if (change_file_mode_rw(fp, tmp) < 0) {
9424 FILE_OP_ERROR(tmp, "chmod");
9425 g_warning("can't change file mode\n");
9428 /* Save draft infos */
9429 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9430 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9432 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9433 gchar *savefolderid;
9435 savefolderid = compose_get_save_to(compose);
9436 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9437 g_free(savefolderid);
9439 if (compose->return_receipt) {
9440 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9442 if (compose->privacy_system) {
9443 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9444 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9445 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9448 /* Message-ID of message replying to */
9449 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9452 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9453 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9456 /* Message-ID of message forwarding to */
9457 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9460 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9461 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9465 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9466 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9468 /* end of headers */
9469 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9476 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9480 if (fclose(fp) == EOF) {
9484 if (compose->targetinfo) {
9485 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9486 flag.perm_flags = target_locked?MSG_LOCKED:0;
9488 flag.tmp_flags = MSG_DRAFT;
9490 folder_item_scan(draft);
9491 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9492 MsgInfo *tmpinfo = NULL;
9493 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9494 if (compose->msgid) {
9495 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9498 msgnum = tmpinfo->msgnum;
9499 procmsg_msginfo_free(tmpinfo);
9500 debug_print("got draft msgnum %d from scanning\n", msgnum);
9502 debug_print("didn't get draft msgnum after scanning\n");
9505 debug_print("got draft msgnum %d from adding\n", msgnum);
9511 if (action != COMPOSE_AUTO_SAVE) {
9512 if (action != COMPOSE_DRAFT_FOR_EXIT)
9513 alertpanel_error(_("Could not save draft."));
9516 gtkut_window_popup(compose->window);
9517 val = alertpanel_full(_("Could not save draft"),
9518 _("Could not save draft.\n"
9519 "Do you want to cancel exit or discard this email?"),
9520 _("_Cancel exit"), _("_Discard email"), NULL,
9521 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9522 if (val == G_ALERTALTERNATE) {
9524 g_mutex_unlock(compose->mutex); /* must be done before closing */
9525 compose_close(compose);
9529 g_mutex_unlock(compose->mutex); /* must be done before closing */
9538 if (compose->mode == COMPOSE_REEDIT) {
9539 compose_remove_reedit_target(compose, TRUE);
9542 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9545 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9547 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9549 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9550 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9551 procmsg_msginfo_set_flags(newmsginfo, 0,
9552 MSG_HAS_ATTACHMENT);
9554 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9555 compose_register_draft(newmsginfo);
9557 procmsg_msginfo_free(newmsginfo);
9560 folder_item_scan(draft);
9562 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9564 g_mutex_unlock(compose->mutex); /* must be done before closing */
9565 compose_close(compose);
9571 path = folder_item_fetch_msg(draft, msgnum);
9573 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9576 if (g_stat(path, &s) < 0) {
9577 FILE_OP_ERROR(path, "stat");
9583 procmsg_msginfo_free(compose->targetinfo);
9584 compose->targetinfo = procmsg_msginfo_new();
9585 compose->targetinfo->msgnum = msgnum;
9586 compose->targetinfo->size = (goffset)s.st_size;
9587 compose->targetinfo->mtime = s.st_mtime;
9588 compose->targetinfo->folder = draft;
9590 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9591 compose->mode = COMPOSE_REEDIT;
9593 if (action == COMPOSE_AUTO_SAVE) {
9594 compose->autosaved_draft = compose->targetinfo;
9596 compose->modified = FALSE;
9597 compose_set_title(compose);
9601 g_mutex_unlock(compose->mutex);
9605 void compose_clear_exit_drafts(void)
9607 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9608 DRAFTED_AT_EXIT, NULL);
9609 if (is_file_exist(filepath))
9610 claws_unlink(filepath);
9615 void compose_reopen_exit_drafts(void)
9617 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9618 DRAFTED_AT_EXIT, NULL);
9619 FILE *fp = g_fopen(filepath, "rb");
9623 while (fgets(buf, sizeof(buf), fp)) {
9624 gchar **parts = g_strsplit(buf, "\t", 2);
9625 const gchar *folder = parts[0];
9626 int msgnum = parts[1] ? atoi(parts[1]):-1;
9628 if (folder && *folder && msgnum > -1) {
9629 FolderItem *item = folder_find_item_from_identifier(folder);
9630 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9632 compose_reedit(info, FALSE);
9639 compose_clear_exit_drafts();
9642 static void compose_save_cb(GtkAction *action, gpointer data)
9644 Compose *compose = (Compose *)data;
9645 compose_draft(compose, COMPOSE_KEEP_EDITING);
9646 compose->rmode = COMPOSE_REEDIT;
9649 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9651 if (compose && file_list) {
9654 for ( tmp = file_list; tmp; tmp = tmp->next) {
9655 gchar *file = (gchar *) tmp->data;
9656 gchar *utf8_filename = conv_filename_to_utf8(file);
9657 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9658 compose_changed_cb(NULL, compose);
9663 g_free(utf8_filename);
9668 static void compose_attach_cb(GtkAction *action, gpointer data)
9670 Compose *compose = (Compose *)data;
9673 if (compose->redirect_filename != NULL)
9676 file_list = filesel_select_multiple_files_open(_("Select file"));
9679 compose_attach_from_list(compose, file_list, TRUE);
9680 g_list_free(file_list);
9684 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9686 Compose *compose = (Compose *)data;
9688 gint files_inserted = 0;
9690 file_list = filesel_select_multiple_files_open(_("Select file"));
9695 for ( tmp = file_list; tmp; tmp = tmp->next) {
9696 gchar *file = (gchar *) tmp->data;
9697 gchar *filedup = g_strdup(file);
9698 gchar *shortfile = g_path_get_basename(filedup);
9699 ComposeInsertResult res;
9700 /* insert the file if the file is short or if the user confirmed that
9701 he/she wants to insert the large file */
9702 res = compose_insert_file(compose, file);
9703 if (res == COMPOSE_INSERT_READ_ERROR) {
9704 alertpanel_error(_("File '%s' could not be read."), shortfile);
9705 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9706 alertpanel_error(_("File '%s' contained invalid characters\n"
9707 "for the current encoding, insertion may be incorrect."),
9709 } else if (res == COMPOSE_INSERT_SUCCESS)
9716 g_list_free(file_list);
9720 if (files_inserted > 0 && compose->gtkaspell &&
9721 compose->gtkaspell->check_while_typing)
9722 gtkaspell_highlight_all(compose->gtkaspell);
9726 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9728 Compose *compose = (Compose *)data;
9730 compose_insert_sig(compose, FALSE);
9733 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9737 Compose *compose = (Compose *)data;
9739 gtkut_widget_get_uposition(widget, &x, &y);
9740 if (!compose->batch) {
9741 prefs_common.compose_x = x;
9742 prefs_common.compose_y = y;
9744 if (compose->sending || compose->updating)
9746 compose_close_cb(NULL, compose);
9750 void compose_close_toolbar(Compose *compose)
9752 compose_close_cb(NULL, compose);
9755 static void compose_close_cb(GtkAction *action, gpointer data)
9757 Compose *compose = (Compose *)data;
9761 if (compose->exteditor_tag != -1) {
9762 if (!compose_ext_editor_kill(compose))
9767 if (compose->modified) {
9768 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9769 if (!g_mutex_trylock(compose->mutex)) {
9770 /* we don't want to lock the mutex once it's available,
9771 * because as the only other part of compose.c locking
9772 * it is compose_close - which means once unlocked,
9773 * the compose struct will be freed */
9774 debug_print("couldn't lock mutex, probably sending\n");
9778 val = alertpanel(_("Discard message"),
9779 _("This message has been modified. Discard it?"),
9780 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9782 val = alertpanel(_("Save changes"),
9783 _("This message has been modified. Save the latest changes?"),
9784 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9786 g_mutex_unlock(compose->mutex);
9788 case G_ALERTDEFAULT:
9789 if (prefs_common.autosave && !reedit)
9790 compose_remove_draft(compose);
9792 case G_ALERTALTERNATE:
9793 compose_draft(data, COMPOSE_QUIT_EDITING);
9800 compose_close(compose);
9803 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9805 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9806 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9807 Compose *compose = (Compose *) data;
9810 compose->out_encoding = (CharSet)value;
9813 static void compose_address_cb(GtkAction *action, gpointer data)
9815 Compose *compose = (Compose *)data;
9817 addressbook_open(compose);
9820 static void about_show_cb(GtkAction *action, gpointer data)
9825 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9827 Compose *compose = (Compose *)data;
9832 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9833 cm_return_if_fail(tmpl != NULL);
9835 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9837 val = alertpanel(_("Apply template"), msg,
9838 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9841 if (val == G_ALERTDEFAULT)
9842 compose_template_apply(compose, tmpl, TRUE);
9843 else if (val == G_ALERTALTERNATE)
9844 compose_template_apply(compose, tmpl, FALSE);
9847 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9849 Compose *compose = (Compose *)data;
9851 compose_exec_ext_editor(compose);
9854 static void compose_undo_cb(GtkAction *action, gpointer data)
9856 Compose *compose = (Compose *)data;
9857 gboolean prev_autowrap = compose->autowrap;
9859 compose->autowrap = FALSE;
9860 undo_undo(compose->undostruct);
9861 compose->autowrap = prev_autowrap;
9864 static void compose_redo_cb(GtkAction *action, gpointer data)
9866 Compose *compose = (Compose *)data;
9867 gboolean prev_autowrap = compose->autowrap;
9869 compose->autowrap = FALSE;
9870 undo_redo(compose->undostruct);
9871 compose->autowrap = prev_autowrap;
9874 static void entry_cut_clipboard(GtkWidget *entry)
9876 if (GTK_IS_EDITABLE(entry))
9877 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9878 else if (GTK_IS_TEXT_VIEW(entry))
9879 gtk_text_buffer_cut_clipboard(
9880 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9881 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9885 static void entry_copy_clipboard(GtkWidget *entry)
9887 if (GTK_IS_EDITABLE(entry))
9888 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9889 else if (GTK_IS_TEXT_VIEW(entry))
9890 gtk_text_buffer_copy_clipboard(
9891 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9892 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9895 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9896 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9898 if (GTK_IS_TEXT_VIEW(entry)) {
9899 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9900 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9901 GtkTextIter start_iter, end_iter;
9903 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9905 if (contents == NULL)
9908 /* we shouldn't delete the selection when middle-click-pasting, or we
9909 * can't mid-click-paste our own selection */
9910 if (clip != GDK_SELECTION_PRIMARY) {
9911 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9912 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9915 if (insert_place == NULL) {
9916 /* if insert_place isn't specified, insert at the cursor.
9917 * used for Ctrl-V pasting */
9918 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9919 start = gtk_text_iter_get_offset(&start_iter);
9920 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9922 /* if insert_place is specified, paste here.
9923 * used for mid-click-pasting */
9924 start = gtk_text_iter_get_offset(insert_place);
9925 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9926 if (prefs_common.primary_paste_unselects)
9927 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9931 /* paste unwrapped: mark the paste so it's not wrapped later */
9932 end = start + strlen(contents);
9933 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9934 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9935 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9936 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9937 /* rewrap paragraph now (after a mid-click-paste) */
9938 mark_start = gtk_text_buffer_get_insert(buffer);
9939 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9940 gtk_text_iter_backward_char(&start_iter);
9941 compose_beautify_paragraph(compose, &start_iter, TRUE);
9943 } else if (GTK_IS_EDITABLE(entry))
9944 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9946 compose->modified = TRUE;
9949 static void entry_allsel(GtkWidget *entry)
9951 if (GTK_IS_EDITABLE(entry))
9952 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9953 else if (GTK_IS_TEXT_VIEW(entry)) {
9954 GtkTextIter startiter, enditer;
9955 GtkTextBuffer *textbuf;
9957 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9958 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9959 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9961 gtk_text_buffer_move_mark_by_name(textbuf,
9962 "selection_bound", &startiter);
9963 gtk_text_buffer_move_mark_by_name(textbuf,
9964 "insert", &enditer);
9968 static void compose_cut_cb(GtkAction *action, gpointer data)
9970 Compose *compose = (Compose *)data;
9971 if (compose->focused_editable
9972 #ifndef GENERIC_UMPC
9973 && gtkut_widget_has_focus(compose->focused_editable)
9976 entry_cut_clipboard(compose->focused_editable);
9979 static void compose_copy_cb(GtkAction *action, gpointer data)
9981 Compose *compose = (Compose *)data;
9982 if (compose->focused_editable
9983 #ifndef GENERIC_UMPC
9984 && gtkut_widget_has_focus(compose->focused_editable)
9987 entry_copy_clipboard(compose->focused_editable);
9990 static void compose_paste_cb(GtkAction *action, gpointer data)
9992 Compose *compose = (Compose *)data;
9994 GtkTextBuffer *buffer;
9996 if (compose->focused_editable &&
9997 gtkut_widget_has_focus(compose->focused_editable))
9998 entry_paste_clipboard(compose, compose->focused_editable,
9999 prefs_common.linewrap_pastes,
10000 GDK_SELECTION_CLIPBOARD, NULL);
10004 if (gtkut_widget_has_focus(compose->text) &&
10005 compose->gtkaspell &&
10006 compose->gtkaspell->check_while_typing)
10007 gtkaspell_highlight_all(compose->gtkaspell);
10011 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10013 Compose *compose = (Compose *)data;
10014 gint wrap_quote = prefs_common.linewrap_quote;
10015 if (compose->focused_editable
10016 #ifndef GENERIC_UMPC
10017 && gtkut_widget_has_focus(compose->focused_editable)
10020 /* let text_insert() (called directly or at a later time
10021 * after the gtk_editable_paste_clipboard) know that
10022 * text is to be inserted as a quotation. implemented
10023 * by using a simple refcount... */
10024 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10025 G_OBJECT(compose->focused_editable),
10026 "paste_as_quotation"));
10027 g_object_set_data(G_OBJECT(compose->focused_editable),
10028 "paste_as_quotation",
10029 GINT_TO_POINTER(paste_as_quotation + 1));
10030 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10031 entry_paste_clipboard(compose, compose->focused_editable,
10032 prefs_common.linewrap_pastes,
10033 GDK_SELECTION_CLIPBOARD, NULL);
10034 prefs_common.linewrap_quote = wrap_quote;
10038 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10040 Compose *compose = (Compose *)data;
10041 gint prev_autowrap;
10042 GtkTextBuffer *buffer;
10044 if (compose->focused_editable
10045 #ifndef GENERIC_UMPC
10046 && gtkut_widget_has_focus(compose->focused_editable)
10049 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10050 GDK_SELECTION_CLIPBOARD, NULL);
10054 if (gtkut_widget_has_focus(compose->text) &&
10055 compose->gtkaspell &&
10056 compose->gtkaspell->check_while_typing)
10057 gtkaspell_highlight_all(compose->gtkaspell);
10061 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10063 Compose *compose = (Compose *)data;
10064 gint prev_autowrap;
10065 GtkTextBuffer *buffer;
10067 if (compose->focused_editable
10068 #ifndef GENERIC_UMPC
10069 && gtkut_widget_has_focus(compose->focused_editable)
10072 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10073 GDK_SELECTION_CLIPBOARD, NULL);
10077 if (gtkut_widget_has_focus(compose->text) &&
10078 compose->gtkaspell &&
10079 compose->gtkaspell->check_while_typing)
10080 gtkaspell_highlight_all(compose->gtkaspell);
10084 static void compose_allsel_cb(GtkAction *action, gpointer data)
10086 Compose *compose = (Compose *)data;
10087 if (compose->focused_editable
10088 #ifndef GENERIC_UMPC
10089 && gtkut_widget_has_focus(compose->focused_editable)
10092 entry_allsel(compose->focused_editable);
10095 static void textview_move_beginning_of_line (GtkTextView *text)
10097 GtkTextBuffer *buffer;
10101 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10103 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10104 mark = gtk_text_buffer_get_insert(buffer);
10105 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10106 gtk_text_iter_set_line_offset(&ins, 0);
10107 gtk_text_buffer_place_cursor(buffer, &ins);
10110 static void textview_move_forward_character (GtkTextView *text)
10112 GtkTextBuffer *buffer;
10116 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10118 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10119 mark = gtk_text_buffer_get_insert(buffer);
10120 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10121 if (gtk_text_iter_forward_cursor_position(&ins))
10122 gtk_text_buffer_place_cursor(buffer, &ins);
10125 static void textview_move_backward_character (GtkTextView *text)
10127 GtkTextBuffer *buffer;
10131 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10133 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10134 mark = gtk_text_buffer_get_insert(buffer);
10135 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10136 if (gtk_text_iter_backward_cursor_position(&ins))
10137 gtk_text_buffer_place_cursor(buffer, &ins);
10140 static void textview_move_forward_word (GtkTextView *text)
10142 GtkTextBuffer *buffer;
10147 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10149 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10150 mark = gtk_text_buffer_get_insert(buffer);
10151 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10152 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10153 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10154 gtk_text_iter_backward_word_start(&ins);
10155 gtk_text_buffer_place_cursor(buffer, &ins);
10159 static void textview_move_backward_word (GtkTextView *text)
10161 GtkTextBuffer *buffer;
10166 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10168 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10169 mark = gtk_text_buffer_get_insert(buffer);
10170 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10171 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10172 if (gtk_text_iter_backward_word_starts(&ins, 1))
10173 gtk_text_buffer_place_cursor(buffer, &ins);
10176 static void textview_move_end_of_line (GtkTextView *text)
10178 GtkTextBuffer *buffer;
10182 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10184 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10185 mark = gtk_text_buffer_get_insert(buffer);
10186 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10187 if (gtk_text_iter_forward_to_line_end(&ins))
10188 gtk_text_buffer_place_cursor(buffer, &ins);
10191 static void textview_move_next_line (GtkTextView *text)
10193 GtkTextBuffer *buffer;
10198 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10200 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10201 mark = gtk_text_buffer_get_insert(buffer);
10202 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10203 offset = gtk_text_iter_get_line_offset(&ins);
10204 if (gtk_text_iter_forward_line(&ins)) {
10205 gtk_text_iter_set_line_offset(&ins, offset);
10206 gtk_text_buffer_place_cursor(buffer, &ins);
10210 static void textview_move_previous_line (GtkTextView *text)
10212 GtkTextBuffer *buffer;
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);
10222 offset = gtk_text_iter_get_line_offset(&ins);
10223 if (gtk_text_iter_backward_line(&ins)) {
10224 gtk_text_iter_set_line_offset(&ins, offset);
10225 gtk_text_buffer_place_cursor(buffer, &ins);
10229 static void textview_delete_forward_character (GtkTextView *text)
10231 GtkTextBuffer *buffer;
10233 GtkTextIter ins, end_iter;
10235 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10237 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10238 mark = gtk_text_buffer_get_insert(buffer);
10239 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10241 if (gtk_text_iter_forward_char(&end_iter)) {
10242 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10246 static void textview_delete_backward_character (GtkTextView *text)
10248 GtkTextBuffer *buffer;
10250 GtkTextIter ins, end_iter;
10252 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10254 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10255 mark = gtk_text_buffer_get_insert(buffer);
10256 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10258 if (gtk_text_iter_backward_char(&end_iter)) {
10259 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10263 static void textview_delete_forward_word (GtkTextView *text)
10265 GtkTextBuffer *buffer;
10267 GtkTextIter ins, end_iter;
10269 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10272 mark = gtk_text_buffer_get_insert(buffer);
10273 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10275 if (gtk_text_iter_forward_word_end(&end_iter)) {
10276 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10280 static void textview_delete_backward_word (GtkTextView *text)
10282 GtkTextBuffer *buffer;
10284 GtkTextIter ins, end_iter;
10286 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10288 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10289 mark = gtk_text_buffer_get_insert(buffer);
10290 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10292 if (gtk_text_iter_backward_word_start(&end_iter)) {
10293 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10297 static void textview_delete_line (GtkTextView *text)
10299 GtkTextBuffer *buffer;
10301 GtkTextIter ins, start_iter, end_iter;
10303 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10305 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10306 mark = gtk_text_buffer_get_insert(buffer);
10307 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10310 gtk_text_iter_set_line_offset(&start_iter, 0);
10313 if (gtk_text_iter_ends_line(&end_iter)){
10314 if (!gtk_text_iter_forward_char(&end_iter))
10315 gtk_text_iter_backward_char(&start_iter);
10318 gtk_text_iter_forward_to_line_end(&end_iter);
10319 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10322 static void textview_delete_to_line_end (GtkTextView *text)
10324 GtkTextBuffer *buffer;
10326 GtkTextIter ins, end_iter;
10328 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10330 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10331 mark = gtk_text_buffer_get_insert(buffer);
10332 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10334 if (gtk_text_iter_ends_line(&end_iter))
10335 gtk_text_iter_forward_char(&end_iter);
10337 gtk_text_iter_forward_to_line_end(&end_iter);
10338 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10341 #define DO_ACTION(name, act) { \
10342 if(!strcmp(name, a_name)) { \
10346 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10348 const gchar *a_name = gtk_action_get_name(action);
10349 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10350 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10351 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10352 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10353 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10354 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10355 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10356 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10357 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10358 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10359 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10360 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10361 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10362 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10366 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10368 Compose *compose = (Compose *)data;
10369 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10370 ComposeCallAdvancedAction action = -1;
10372 action = compose_call_advanced_action_from_path(gaction);
10375 void (*do_action) (GtkTextView *text);
10376 } action_table[] = {
10377 {textview_move_beginning_of_line},
10378 {textview_move_forward_character},
10379 {textview_move_backward_character},
10380 {textview_move_forward_word},
10381 {textview_move_backward_word},
10382 {textview_move_end_of_line},
10383 {textview_move_next_line},
10384 {textview_move_previous_line},
10385 {textview_delete_forward_character},
10386 {textview_delete_backward_character},
10387 {textview_delete_forward_word},
10388 {textview_delete_backward_word},
10389 {textview_delete_line},
10390 {textview_delete_to_line_end}
10393 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10395 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10396 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10397 if (action_table[action].do_action)
10398 action_table[action].do_action(text);
10400 g_warning("Not implemented yet.");
10404 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10408 if (GTK_IS_EDITABLE(widget)) {
10409 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10410 gtk_editable_set_position(GTK_EDITABLE(widget),
10413 if (widget->parent && widget->parent->parent
10414 && widget->parent->parent->parent) {
10415 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10416 gint y = widget->allocation.y;
10417 gint height = widget->allocation.height;
10418 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10419 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10421 if (y < (int)shown->value) {
10422 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10424 if (y + height > (int)shown->value + (int)shown->page_size) {
10425 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10426 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10427 y + height - (int)shown->page_size - 1);
10429 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10430 (int)shown->upper - (int)shown->page_size - 1);
10437 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10438 compose->focused_editable = widget;
10440 #ifdef GENERIC_UMPC
10441 if (GTK_IS_TEXT_VIEW(widget)
10442 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10443 g_object_ref(compose->notebook);
10444 g_object_ref(compose->edit_vbox);
10445 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10446 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10447 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10448 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10449 g_object_unref(compose->notebook);
10450 g_object_unref(compose->edit_vbox);
10451 g_signal_handlers_block_by_func(G_OBJECT(widget),
10452 G_CALLBACK(compose_grab_focus_cb),
10454 gtk_widget_grab_focus(widget);
10455 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10456 G_CALLBACK(compose_grab_focus_cb),
10458 } else if (!GTK_IS_TEXT_VIEW(widget)
10459 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10460 g_object_ref(compose->notebook);
10461 g_object_ref(compose->edit_vbox);
10462 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10463 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10464 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10465 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10466 g_object_unref(compose->notebook);
10467 g_object_unref(compose->edit_vbox);
10468 g_signal_handlers_block_by_func(G_OBJECT(widget),
10469 G_CALLBACK(compose_grab_focus_cb),
10471 gtk_widget_grab_focus(widget);
10472 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10473 G_CALLBACK(compose_grab_focus_cb),
10479 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10481 compose->modified = TRUE;
10482 // compose_beautify_paragraph(compose, NULL, TRUE);
10483 #ifndef GENERIC_UMPC
10484 compose_set_title(compose);
10488 static void compose_wrap_cb(GtkAction *action, gpointer data)
10490 Compose *compose = (Compose *)data;
10491 compose_beautify_paragraph(compose, NULL, TRUE);
10494 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10496 Compose *compose = (Compose *)data;
10497 compose_wrap_all_full(compose, TRUE);
10500 static void compose_find_cb(GtkAction *action, gpointer data)
10502 Compose *compose = (Compose *)data;
10504 message_search_compose(compose);
10507 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10510 Compose *compose = (Compose *)data;
10511 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10512 if (compose->autowrap)
10513 compose_wrap_all_full(compose, TRUE);
10514 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10517 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10520 Compose *compose = (Compose *)data;
10521 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10524 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10526 Compose *compose = (Compose *)data;
10528 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10531 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10533 Compose *compose = (Compose *)data;
10535 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10538 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10540 g_free(compose->privacy_system);
10542 compose->privacy_system = g_strdup(account->default_privacy_system);
10543 compose_update_privacy_system_menu_item(compose, warn);
10546 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10548 Compose *compose = (Compose *)data;
10550 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10551 gtk_widget_show(compose->ruler_hbox);
10552 prefs_common.show_ruler = TRUE;
10554 gtk_widget_hide(compose->ruler_hbox);
10555 gtk_widget_queue_resize(compose->edit_vbox);
10556 prefs_common.show_ruler = FALSE;
10560 static void compose_attach_drag_received_cb (GtkWidget *widget,
10561 GdkDragContext *context,
10564 GtkSelectionData *data,
10567 gpointer user_data)
10569 Compose *compose = (Compose *)user_data;
10572 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10574 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10576 ) && gtk_drag_get_source_widget(context) !=
10577 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10578 list = uri_list_extract_filenames((const gchar *)data->data);
10579 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10580 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10581 compose_attach_append
10582 (compose, (const gchar *)tmp->data,
10583 utf8_filename, NULL, NULL);
10584 g_free(utf8_filename);
10586 if (list) compose_changed_cb(NULL, compose);
10587 list_free_strings(list);
10589 } else if (gtk_drag_get_source_widget(context)
10590 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10591 /* comes from our summaryview */
10592 SummaryView * summaryview = NULL;
10593 GSList * list = NULL, *cur = NULL;
10595 if (mainwindow_get_mainwindow())
10596 summaryview = mainwindow_get_mainwindow()->summaryview;
10599 list = summary_get_selected_msg_list(summaryview);
10601 for (cur = list; cur; cur = cur->next) {
10602 MsgInfo *msginfo = (MsgInfo *)cur->data;
10603 gchar *file = NULL;
10605 file = procmsg_get_message_file_full(msginfo,
10608 compose_attach_append(compose, (const gchar *)file,
10609 (const gchar *)file, "message/rfc822", NULL);
10613 g_slist_free(list);
10617 static gboolean compose_drag_drop(GtkWidget *widget,
10618 GdkDragContext *drag_context,
10620 guint time, gpointer user_data)
10622 /* not handling this signal makes compose_insert_drag_received_cb
10627 static void compose_insert_drag_received_cb (GtkWidget *widget,
10628 GdkDragContext *drag_context,
10631 GtkSelectionData *data,
10634 gpointer user_data)
10636 Compose *compose = (Compose *)user_data;
10639 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10642 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10644 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10646 AlertValue val = G_ALERTDEFAULT;
10648 list = uri_list_extract_filenames((const gchar *)data->data);
10649 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10650 /* Assume a list of no files, and data has ://, is a remote link */
10651 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10652 gchar *tmpfile = get_tmp_file();
10653 str_write_to_file(tmpdata, tmpfile);
10655 compose_insert_file(compose, tmpfile);
10656 claws_unlink(tmpfile);
10658 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10659 compose_beautify_paragraph(compose, NULL, TRUE);
10662 switch (prefs_common.compose_dnd_mode) {
10663 case COMPOSE_DND_ASK:
10664 val = alertpanel_full(_("Insert or attach?"),
10665 _("Do you want to insert the contents of the file(s) "
10666 "into the message body, or attach it to the email?"),
10667 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10668 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10670 case COMPOSE_DND_INSERT:
10671 val = G_ALERTALTERNATE;
10673 case COMPOSE_DND_ATTACH:
10674 val = G_ALERTOTHER;
10677 /* unexpected case */
10678 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10681 if (val & G_ALERTDISABLE) {
10682 val &= ~G_ALERTDISABLE;
10683 /* remember what action to perform by default, only if we don't click Cancel */
10684 if (val == G_ALERTALTERNATE)
10685 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10686 else if (val == G_ALERTOTHER)
10687 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10690 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10691 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10692 list_free_strings(list);
10695 } else if (val == G_ALERTOTHER) {
10696 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10697 list_free_strings(list);
10702 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10703 compose_insert_file(compose, (const gchar *)tmp->data);
10705 list_free_strings(list);
10707 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10712 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10715 static void compose_header_drag_received_cb (GtkWidget *widget,
10716 GdkDragContext *drag_context,
10719 GtkSelectionData *data,
10722 gpointer user_data)
10724 GtkEditable *entry = (GtkEditable *)user_data;
10725 gchar *email = (gchar *)data->data;
10727 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10730 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10731 gchar *decoded=g_new(gchar, strlen(email));
10734 email += strlen("mailto:");
10735 decode_uri(decoded, email); /* will fit */
10736 gtk_editable_delete_text(entry, 0, -1);
10737 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10738 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10742 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10745 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10747 Compose *compose = (Compose *)data;
10749 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10750 compose->return_receipt = TRUE;
10752 compose->return_receipt = FALSE;
10755 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10757 Compose *compose = (Compose *)data;
10759 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10760 compose->remove_references = TRUE;
10762 compose->remove_references = FALSE;
10765 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10766 ComposeHeaderEntry *headerentry)
10768 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10772 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10773 GdkEventKey *event,
10774 ComposeHeaderEntry *headerentry)
10776 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10777 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10778 !(event->state & GDK_MODIFIER_MASK) &&
10779 (event->keyval == GDK_BackSpace) &&
10780 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10781 gtk_container_remove
10782 (GTK_CONTAINER(headerentry->compose->header_table),
10783 headerentry->combo);
10784 gtk_container_remove
10785 (GTK_CONTAINER(headerentry->compose->header_table),
10786 headerentry->entry);
10787 headerentry->compose->header_list =
10788 g_slist_remove(headerentry->compose->header_list,
10790 g_free(headerentry);
10791 } else if (event->keyval == GDK_Tab) {
10792 if (headerentry->compose->header_last == headerentry) {
10793 /* Override default next focus, and give it to subject_entry
10794 * instead of notebook tabs
10796 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10797 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10804 static gboolean scroll_postpone(gpointer data)
10806 Compose *compose = (Compose *)data;
10808 cm_return_val_if_fail(!compose->batch, FALSE);
10810 GTK_EVENTS_FLUSH();
10811 compose_show_first_last_header(compose, FALSE);
10815 static void compose_headerentry_changed_cb(GtkWidget *entry,
10816 ComposeHeaderEntry *headerentry)
10818 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10819 compose_create_header_entry(headerentry->compose);
10820 g_signal_handlers_disconnect_matched
10821 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10822 0, 0, NULL, NULL, headerentry);
10824 if (!headerentry->compose->batch)
10825 g_timeout_add(0, scroll_postpone, headerentry->compose);
10829 static gboolean compose_defer_auto_save_draft(Compose *compose)
10831 compose->draft_timeout_tag = -1;
10832 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10836 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10838 GtkAdjustment *vadj;
10840 cm_return_if_fail(compose);
10841 cm_return_if_fail(!compose->batch);
10842 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10843 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10844 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10845 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10846 gtk_adjustment_changed(vadj);
10849 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10850 const gchar *text, gint len, Compose *compose)
10852 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10853 (G_OBJECT(compose->text), "paste_as_quotation"));
10856 cm_return_if_fail(text != NULL);
10858 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10859 G_CALLBACK(text_inserted),
10861 if (paste_as_quotation) {
10863 const gchar *qmark;
10865 GtkTextIter start_iter;
10868 len = strlen(text);
10870 new_text = g_strndup(text, len);
10872 qmark = compose_quote_char_from_context(compose);
10874 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10875 gtk_text_buffer_place_cursor(buffer, iter);
10877 pos = gtk_text_iter_get_offset(iter);
10879 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10880 _("Quote format error at line %d."));
10881 quote_fmt_reset_vartable();
10883 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10884 GINT_TO_POINTER(paste_as_quotation - 1));
10886 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10887 gtk_text_buffer_place_cursor(buffer, iter);
10888 gtk_text_buffer_delete_mark(buffer, mark);
10890 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10891 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10892 compose_beautify_paragraph(compose, &start_iter, FALSE);
10893 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10894 gtk_text_buffer_delete_mark(buffer, mark);
10896 if (strcmp(text, "\n") || compose->automatic_break
10897 || gtk_text_iter_starts_line(iter)) {
10898 GtkTextIter before_ins;
10899 gtk_text_buffer_insert(buffer, iter, text, len);
10900 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10901 before_ins = *iter;
10902 gtk_text_iter_backward_chars(&before_ins, len);
10903 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10906 /* check if the preceding is just whitespace or quote */
10907 GtkTextIter start_line;
10908 gchar *tmp = NULL, *quote = NULL;
10909 gint quote_len = 0, is_normal = 0;
10910 start_line = *iter;
10911 gtk_text_iter_set_line_offset(&start_line, 0);
10912 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10915 if (*tmp == '\0') {
10918 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10926 gtk_text_buffer_insert(buffer, iter, text, len);
10928 gtk_text_buffer_insert_with_tags_by_name(buffer,
10929 iter, text, len, "no_join", NULL);
10934 if (!paste_as_quotation) {
10935 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10936 compose_beautify_paragraph(compose, iter, FALSE);
10937 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10938 gtk_text_buffer_delete_mark(buffer, mark);
10941 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10942 G_CALLBACK(text_inserted),
10944 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10946 if (prefs_common.autosave &&
10947 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10948 compose->draft_timeout_tag != -2 /* disabled while loading */)
10949 compose->draft_timeout_tag = g_timeout_add
10950 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
10954 static void compose_check_all(GtkAction *action, gpointer data)
10956 Compose *compose = (Compose *)data;
10957 if (!compose->gtkaspell)
10960 if (gtkut_widget_has_focus(compose->subject_entry))
10961 claws_spell_entry_check_all(
10962 CLAWS_SPELL_ENTRY(compose->subject_entry));
10964 gtkaspell_check_all(compose->gtkaspell);
10967 static void compose_highlight_all(GtkAction *action, gpointer data)
10969 Compose *compose = (Compose *)data;
10970 if (compose->gtkaspell) {
10971 claws_spell_entry_recheck_all(
10972 CLAWS_SPELL_ENTRY(compose->subject_entry));
10973 gtkaspell_highlight_all(compose->gtkaspell);
10977 static void compose_check_backwards(GtkAction *action, gpointer data)
10979 Compose *compose = (Compose *)data;
10980 if (!compose->gtkaspell) {
10981 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10985 if (gtkut_widget_has_focus(compose->subject_entry))
10986 claws_spell_entry_check_backwards(
10987 CLAWS_SPELL_ENTRY(compose->subject_entry));
10989 gtkaspell_check_backwards(compose->gtkaspell);
10992 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10994 Compose *compose = (Compose *)data;
10995 if (!compose->gtkaspell) {
10996 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11000 if (gtkut_widget_has_focus(compose->subject_entry))
11001 claws_spell_entry_check_forwards_go(
11002 CLAWS_SPELL_ENTRY(compose->subject_entry));
11004 gtkaspell_check_forwards_go(compose->gtkaspell);
11009 *\brief Guess originating forward account from MsgInfo and several
11010 * "common preference" settings. Return NULL if no guess.
11012 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11014 PrefsAccount *account = NULL;
11016 cm_return_val_if_fail(msginfo, NULL);
11017 cm_return_val_if_fail(msginfo->folder, NULL);
11018 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11020 if (msginfo->folder->prefs->enable_default_account)
11021 account = account_find_from_id(msginfo->folder->prefs->default_account);
11024 account = msginfo->folder->folder->account;
11026 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11028 Xstrdup_a(to, msginfo->to, return NULL);
11029 extract_address(to);
11030 account = account_find_from_address(to, FALSE);
11033 if (!account && prefs_common.forward_account_autosel) {
11034 gchar cc[BUFFSIZE];
11035 if (!procheader_get_header_from_msginfo
11036 (msginfo, cc,sizeof cc , "Cc:")) {
11037 gchar *buf = cc + strlen("Cc:");
11038 extract_address(buf);
11039 account = account_find_from_address(buf, FALSE);
11043 if (!account && prefs_common.forward_account_autosel) {
11044 gchar deliveredto[BUFFSIZE];
11045 if (!procheader_get_header_from_msginfo
11046 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11047 gchar *buf = deliveredto + strlen("Delivered-To:");
11048 extract_address(buf);
11049 account = account_find_from_address(buf, FALSE);
11056 gboolean compose_close(Compose *compose)
11060 if (!g_mutex_trylock(compose->mutex)) {
11061 /* we have to wait for the (possibly deferred by auto-save)
11062 * drafting to be done, before destroying the compose under
11064 debug_print("waiting for drafting to finish...\n");
11065 compose_allow_user_actions(compose, FALSE);
11066 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11069 cm_return_val_if_fail(compose, FALSE);
11070 gtkut_widget_get_uposition(compose->window, &x, &y);
11071 if (!compose->batch) {
11072 prefs_common.compose_x = x;
11073 prefs_common.compose_y = y;
11075 g_mutex_unlock(compose->mutex);
11076 compose_destroy(compose);
11081 * Add entry field for each address in list.
11082 * \param compose E-Mail composition object.
11083 * \param listAddress List of (formatted) E-Mail addresses.
11085 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11088 node = listAddress;
11090 addr = ( gchar * ) node->data;
11091 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11092 node = g_list_next( node );
11096 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11097 guint action, gboolean opening_multiple)
11099 gchar *body = NULL;
11100 GSList *new_msglist = NULL;
11101 MsgInfo *tmp_msginfo = NULL;
11102 gboolean originally_enc = FALSE;
11103 gboolean originally_sig = FALSE;
11104 Compose *compose = NULL;
11105 gchar *s_system = NULL;
11107 cm_return_if_fail(msgview != NULL);
11109 cm_return_if_fail(msginfo_list != NULL);
11111 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11112 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11113 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11115 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11116 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11117 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11118 orig_msginfo, mimeinfo);
11119 if (tmp_msginfo != NULL) {
11120 new_msglist = g_slist_append(NULL, tmp_msginfo);
11122 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11123 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11124 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11126 tmp_msginfo->folder = orig_msginfo->folder;
11127 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11128 if (orig_msginfo->tags) {
11129 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11130 tmp_msginfo->folder->tags_dirty = TRUE;
11136 if (!opening_multiple)
11137 body = messageview_get_selection(msgview);
11140 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11141 procmsg_msginfo_free(tmp_msginfo);
11142 g_slist_free(new_msglist);
11144 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11146 if (compose && originally_enc) {
11147 compose_force_encryption(compose, compose->account, FALSE, s_system);
11150 if (compose && originally_sig && compose->account->default_sign_reply) {
11151 compose_force_signing(compose, compose->account, s_system);
11155 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11158 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11161 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11162 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11163 GSList *cur = msginfo_list;
11164 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11165 "messages. Opening the windows "
11166 "could take some time. Do you "
11167 "want to continue?"),
11168 g_slist_length(msginfo_list));
11169 if (g_slist_length(msginfo_list) > 9
11170 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11171 != G_ALERTALTERNATE) {
11176 /* We'll open multiple compose windows */
11177 /* let the WM place the next windows */
11178 compose_force_window_origin = FALSE;
11179 for (; cur; cur = cur->next) {
11181 tmplist.data = cur->data;
11182 tmplist.next = NULL;
11183 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11185 compose_force_window_origin = TRUE;
11187 /* forwarding multiple mails as attachments is done via a
11188 * single compose window */
11189 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11193 void compose_check_for_email_account(Compose *compose)
11195 PrefsAccount *ac = NULL, *curr = NULL;
11201 if (compose->account && compose->account->protocol == A_NNTP) {
11202 ac = account_get_cur_account();
11203 if (ac->protocol == A_NNTP) {
11204 list = account_get_list();
11206 for( ; list != NULL ; list = g_list_next(list)) {
11207 curr = (PrefsAccount *) list->data;
11208 if (curr->protocol != A_NNTP) {
11214 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11219 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11220 const gchar *address)
11222 GSList *msginfo_list = NULL;
11223 gchar *body = messageview_get_selection(msgview);
11226 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11228 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11229 compose_check_for_email_account(compose);
11230 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11231 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11232 compose_reply_set_subject(compose, msginfo);
11235 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11238 void compose_set_position(Compose *compose, gint pos)
11240 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11242 gtkut_text_view_set_position(text, pos);
11245 gboolean compose_search_string(Compose *compose,
11246 const gchar *str, gboolean case_sens)
11248 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11250 return gtkut_text_view_search_string(text, str, case_sens);
11253 gboolean compose_search_string_backward(Compose *compose,
11254 const gchar *str, gboolean case_sens)
11256 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11258 return gtkut_text_view_search_string_backward(text, str, case_sens);
11261 /* allocate a msginfo structure and populate its data from a compose data structure */
11262 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11264 MsgInfo *newmsginfo;
11266 gchar buf[BUFFSIZE];
11268 cm_return_val_if_fail( compose != NULL, NULL );
11270 newmsginfo = procmsg_msginfo_new();
11273 get_rfc822_date(buf, sizeof(buf));
11274 newmsginfo->date = g_strdup(buf);
11277 if (compose->from_name) {
11278 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11279 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11283 if (compose->subject_entry)
11284 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11286 /* to, cc, reply-to, newsgroups */
11287 for (list = compose->header_list; list; list = list->next) {
11288 gchar *header = gtk_editable_get_chars(
11290 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11291 gchar *entry = gtk_editable_get_chars(
11292 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11294 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11295 if ( newmsginfo->to == NULL ) {
11296 newmsginfo->to = g_strdup(entry);
11297 } else if (entry && *entry) {
11298 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11299 g_free(newmsginfo->to);
11300 newmsginfo->to = tmp;
11303 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11304 if ( newmsginfo->cc == NULL ) {
11305 newmsginfo->cc = g_strdup(entry);
11306 } else if (entry && *entry) {
11307 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11308 g_free(newmsginfo->cc);
11309 newmsginfo->cc = tmp;
11312 if ( strcasecmp(header,
11313 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11314 if ( newmsginfo->newsgroups == NULL ) {
11315 newmsginfo->newsgroups = g_strdup(entry);
11316 } else if (entry && *entry) {
11317 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11318 g_free(newmsginfo->newsgroups);
11319 newmsginfo->newsgroups = tmp;
11327 /* other data is unset */
11333 /* update compose's dictionaries from folder dict settings */
11334 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11335 FolderItem *folder_item)
11337 cm_return_if_fail(compose != NULL);
11339 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11340 FolderItemPrefs *prefs = folder_item->prefs;
11342 if (prefs->enable_default_dictionary)
11343 gtkaspell_change_dict(compose->gtkaspell,
11344 prefs->default_dictionary, FALSE);
11345 if (folder_item->prefs->enable_default_alt_dictionary)
11346 gtkaspell_change_alt_dict(compose->gtkaspell,
11347 prefs->default_alt_dictionary);
11348 if (prefs->enable_default_dictionary
11349 || prefs->enable_default_alt_dictionary)
11350 compose_spell_menu_changed(compose);