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 MailField mfield = NO_FIELD_PRESENT;
954 /* check if mailto defines a from */
955 if (mailto && *mailto != '\0') {
956 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
957 /* mailto defines a from, check if we can get account prefs from it,
958 if not, the account prefs will be guessed using other ways, but we'll keep
961 mailto_account = account_find_from_address(mailto_from, TRUE);
963 account = mailto_account;
966 /* if no account prefs set from mailto, set if from folder prefs (if any) */
967 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
968 account = account_find_from_id(item->prefs->default_account);
970 /* if no account prefs set, fallback to the current one */
971 if (!account) account = cur_account;
972 cm_return_val_if_fail(account != NULL, NULL);
974 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
976 /* override from name if mailto asked for it */
978 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
981 /* override from name according to folder properties */
982 if (item && item->prefs &&
983 item->prefs->compose_with_format &&
984 item->prefs->compose_override_from_format &&
985 *item->prefs->compose_override_from_format != '\0') {
990 dummyinfo = compose_msginfo_new_from_compose(compose);
992 /* decode \-escape sequences in the internal representation of the quote format */
993 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
994 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
997 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1000 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1002 quote_fmt_scan_string(tmp);
1005 buf = quote_fmt_get_buffer();
1007 alertpanel_error(_("New message From format error."));
1009 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1010 quote_fmt_reset_vartable();
1015 compose->replyinfo = NULL;
1016 compose->fwdinfo = NULL;
1018 textview = GTK_TEXT_VIEW(compose->text);
1019 textbuf = gtk_text_view_get_buffer(textview);
1020 compose_create_tags(textview, compose);
1022 undo_block(compose->undostruct);
1024 compose_set_dictionaries_from_folder_prefs(compose, item);
1027 if (account->auto_sig)
1028 compose_insert_sig(compose, FALSE);
1029 gtk_text_buffer_get_start_iter(textbuf, &iter);
1030 gtk_text_buffer_place_cursor(textbuf, &iter);
1032 if (account->protocol != A_NNTP) {
1033 if (mailto && *mailto != '\0') {
1034 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1037 compose_set_folder_prefs(compose, item, TRUE);
1039 if (item && item->ret_rcpt) {
1040 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1043 if (mailto && *mailto != '\0') {
1044 if (!strchr(mailto, '@'))
1045 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1047 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1048 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1049 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1050 mfield = TO_FIELD_PRESENT;
1053 * CLAWS: just don't allow return receipt request, even if the user
1054 * may want to send an email. simple but foolproof.
1056 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1058 compose_add_field_list( compose, listAddress );
1060 if (item && item->prefs && item->prefs->compose_with_format) {
1061 subject_format = item->prefs->compose_subject_format;
1062 body_format = item->prefs->compose_body_format;
1063 } else if (account->compose_with_format) {
1064 subject_format = account->compose_subject_format;
1065 body_format = account->compose_body_format;
1066 } else if (prefs_common.compose_with_format) {
1067 subject_format = prefs_common.compose_subject_format;
1068 body_format = prefs_common.compose_body_format;
1071 if (subject_format || body_format) {
1074 && *subject_format != '\0' )
1076 gchar *subject = NULL;
1081 dummyinfo = compose_msginfo_new_from_compose(compose);
1083 /* decode \-escape sequences in the internal representation of the quote format */
1084 tmp = g_malloc(strlen(subject_format)+1);
1085 pref_get_unescaped_pref(tmp, subject_format);
1087 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1089 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1090 compose->gtkaspell);
1092 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1094 quote_fmt_scan_string(tmp);
1097 buf = quote_fmt_get_buffer();
1099 alertpanel_error(_("New message subject format error."));
1101 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1102 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1103 quote_fmt_reset_vartable();
1107 mfield = SUBJECT_FIELD_PRESENT;
1111 && *body_format != '\0' )
1114 GtkTextBuffer *buffer;
1115 GtkTextIter start, end;
1119 dummyinfo = compose_msginfo_new_from_compose(compose);
1121 text = GTK_TEXT_VIEW(compose->text);
1122 buffer = gtk_text_view_get_buffer(text);
1123 gtk_text_buffer_get_start_iter(buffer, &start);
1124 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1125 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1127 compose_quote_fmt(compose, dummyinfo,
1129 NULL, tmp, FALSE, TRUE,
1130 _("The body of the \"New message\" template has an error at line %d."));
1131 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1132 quote_fmt_reset_vartable();
1136 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1137 gtkaspell_highlight_all(compose->gtkaspell);
1139 mfield = BODY_FIELD_PRESENT;
1143 procmsg_msginfo_free( dummyinfo );
1149 for (i = 0; i < attach_files->len; i++) {
1150 file = g_ptr_array_index(attach_files, i);
1151 compose_attach_append(compose, file, file, NULL, NULL);
1155 compose_show_first_last_header(compose, TRUE);
1157 /* Set save folder */
1158 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1159 gchar *folderidentifier;
1161 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1162 folderidentifier = folder_item_get_identifier(item);
1163 compose_set_save_to(compose, folderidentifier);
1164 g_free(folderidentifier);
1167 /* Place cursor according to provided input (mfield) */
1169 case NO_FIELD_PRESENT:
1170 if (compose->header_last)
1171 gtk_widget_grab_focus(compose->header_last->entry);
1173 case TO_FIELD_PRESENT:
1174 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1176 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1179 gtk_widget_grab_focus(compose->subject_entry);
1181 case SUBJECT_FIELD_PRESENT:
1182 textview = GTK_TEXT_VIEW(compose->text);
1185 textbuf = gtk_text_view_get_buffer(textview);
1188 mark = gtk_text_buffer_get_insert(textbuf);
1189 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1190 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1192 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1193 * only defers where it comes to the variable body
1194 * is not null. If no body is present compose->text
1195 * will be null in which case you cannot place the
1196 * cursor inside the component so. An empty component
1197 * is therefore created before placing the cursor
1199 case BODY_FIELD_PRESENT:
1200 gtk_widget_grab_focus(compose->text);
1204 undo_unblock(compose->undostruct);
1206 if (prefs_common.auto_exteditor)
1207 compose_exec_ext_editor(compose);
1209 compose->draft_timeout_tag = -1;
1210 SCROLL_TO_CURSOR(compose);
1212 compose->modified = FALSE;
1213 compose_set_title(compose);
1215 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1220 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1221 gboolean override_pref, const gchar *system)
1223 const gchar *privacy = NULL;
1225 cm_return_if_fail(compose != NULL);
1226 cm_return_if_fail(account != NULL);
1228 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1233 else if (account->default_privacy_system
1234 && strlen(account->default_privacy_system)) {
1235 privacy = account->default_privacy_system;
1237 GSList *privacy_avail = privacy_get_system_ids();
1238 if (privacy_avail && g_slist_length(privacy_avail)) {
1239 privacy = (gchar *)(privacy_avail->data);
1242 if (privacy != NULL) {
1244 g_free(compose->privacy_system);
1245 compose->privacy_system = NULL;
1247 if (compose->privacy_system == NULL)
1248 compose->privacy_system = g_strdup(privacy);
1249 else if (*(compose->privacy_system) == '\0') {
1250 g_free(compose->privacy_system);
1251 compose->privacy_system = g_strdup(privacy);
1253 compose_update_privacy_system_menu_item(compose, FALSE);
1254 compose_use_encryption(compose, TRUE);
1258 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1260 const gchar *privacy = NULL;
1264 else if (account->default_privacy_system
1265 && strlen(account->default_privacy_system)) {
1266 privacy = account->default_privacy_system;
1268 GSList *privacy_avail = privacy_get_system_ids();
1269 if (privacy_avail && g_slist_length(privacy_avail)) {
1270 privacy = (gchar *)(privacy_avail->data);
1274 if (privacy != NULL) {
1276 g_free(compose->privacy_system);
1277 compose->privacy_system = NULL;
1279 if (compose->privacy_system == NULL)
1280 compose->privacy_system = g_strdup(privacy);
1281 compose_update_privacy_system_menu_item(compose, FALSE);
1282 compose_use_signing(compose, TRUE);
1286 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1290 Compose *compose = NULL;
1292 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1294 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1295 cm_return_val_if_fail(msginfo != NULL, NULL);
1297 list_len = g_slist_length(msginfo_list);
1301 case COMPOSE_REPLY_TO_ADDRESS:
1302 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1303 FALSE, prefs_common.default_reply_list, FALSE, body);
1305 case COMPOSE_REPLY_WITH_QUOTE:
1306 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1307 FALSE, prefs_common.default_reply_list, FALSE, body);
1309 case COMPOSE_REPLY_WITHOUT_QUOTE:
1310 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1311 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1313 case COMPOSE_REPLY_TO_SENDER:
1314 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1315 FALSE, FALSE, TRUE, body);
1317 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1318 compose = compose_followup_and_reply_to(msginfo,
1319 COMPOSE_QUOTE_CHECK,
1320 FALSE, FALSE, body);
1322 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1324 FALSE, FALSE, TRUE, body);
1326 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1327 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1328 FALSE, FALSE, TRUE, NULL);
1330 case COMPOSE_REPLY_TO_ALL:
1331 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1332 TRUE, FALSE, FALSE, body);
1334 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1335 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1336 TRUE, FALSE, FALSE, body);
1338 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1339 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1340 TRUE, FALSE, FALSE, NULL);
1342 case COMPOSE_REPLY_TO_LIST:
1343 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1344 FALSE, TRUE, FALSE, body);
1346 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1347 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1348 FALSE, TRUE, FALSE, body);
1350 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1351 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1352 FALSE, TRUE, FALSE, NULL);
1354 case COMPOSE_FORWARD:
1355 if (prefs_common.forward_as_attachment) {
1356 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1359 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1363 case COMPOSE_FORWARD_INLINE:
1364 /* check if we reply to more than one Message */
1365 if (list_len == 1) {
1366 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1369 /* more messages FALL THROUGH */
1370 case COMPOSE_FORWARD_AS_ATTACH:
1371 compose = compose_forward_multiple(NULL, msginfo_list);
1373 case COMPOSE_REDIRECT:
1374 compose = compose_redirect(NULL, msginfo, FALSE);
1377 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1380 if (compose == NULL) {
1381 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1385 compose->rmode = mode;
1386 switch (compose->rmode) {
1388 case COMPOSE_REPLY_WITH_QUOTE:
1389 case COMPOSE_REPLY_WITHOUT_QUOTE:
1390 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1391 debug_print("reply mode Normal\n");
1392 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1393 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1395 case COMPOSE_REPLY_TO_SENDER:
1396 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1397 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1398 debug_print("reply mode Sender\n");
1399 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1401 case COMPOSE_REPLY_TO_ALL:
1402 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1403 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1404 debug_print("reply mode All\n");
1405 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1407 case COMPOSE_REPLY_TO_LIST:
1408 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1409 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1410 debug_print("reply mode List\n");
1411 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1413 case COMPOSE_REPLY_TO_ADDRESS:
1414 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1422 static Compose *compose_reply(MsgInfo *msginfo,
1423 ComposeQuoteMode quote_mode,
1429 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1430 to_sender, FALSE, body);
1433 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1434 ComposeQuoteMode quote_mode,
1439 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1440 to_sender, TRUE, body);
1443 static void compose_extract_original_charset(Compose *compose)
1445 MsgInfo *info = NULL;
1446 if (compose->replyinfo) {
1447 info = compose->replyinfo;
1448 } else if (compose->fwdinfo) {
1449 info = compose->fwdinfo;
1450 } else if (compose->targetinfo) {
1451 info = compose->targetinfo;
1454 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1455 MimeInfo *partinfo = mimeinfo;
1456 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1457 partinfo = procmime_mimeinfo_next(partinfo);
1459 compose->orig_charset =
1460 g_strdup(procmime_mimeinfo_get_parameter(
1461 partinfo, "charset"));
1463 procmime_mimeinfo_free_all(mimeinfo);
1467 #define SIGNAL_BLOCK(buffer) { \
1468 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1469 G_CALLBACK(compose_changed_cb), \
1471 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1472 G_CALLBACK(text_inserted), \
1476 #define SIGNAL_UNBLOCK(buffer) { \
1477 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1478 G_CALLBACK(compose_changed_cb), \
1480 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(text_inserted), \
1485 static Compose *compose_generic_reply(MsgInfo *msginfo,
1486 ComposeQuoteMode quote_mode,
1487 gboolean to_all, gboolean to_ml,
1489 gboolean followup_and_reply_to,
1493 PrefsAccount *account = NULL;
1494 GtkTextView *textview;
1495 GtkTextBuffer *textbuf;
1496 gboolean quote = FALSE;
1497 const gchar *qmark = NULL;
1498 const gchar *body_fmt = NULL;
1499 gchar *s_system = NULL;
1501 cm_return_val_if_fail(msginfo != NULL, NULL);
1502 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1504 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1506 cm_return_val_if_fail(account != NULL, NULL);
1508 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1510 compose->updating = TRUE;
1512 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1513 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1515 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1516 if (!compose->replyinfo)
1517 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1519 compose_extract_original_charset(compose);
1521 if (msginfo->folder && msginfo->folder->ret_rcpt)
1522 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1524 /* Set save folder */
1525 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1526 gchar *folderidentifier;
1528 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1529 folderidentifier = folder_item_get_identifier(msginfo->folder);
1530 compose_set_save_to(compose, folderidentifier);
1531 g_free(folderidentifier);
1534 if (compose_parse_header(compose, msginfo) < 0) {
1535 compose->updating = FALSE;
1536 compose_destroy(compose);
1540 /* override from name according to folder properties */
1541 if (msginfo->folder && msginfo->folder->prefs &&
1542 msginfo->folder->prefs->reply_with_format &&
1543 msginfo->folder->prefs->reply_override_from_format &&
1544 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1549 /* decode \-escape sequences in the internal representation of the quote format */
1550 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1551 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1554 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1555 compose->gtkaspell);
1557 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1559 quote_fmt_scan_string(tmp);
1562 buf = quote_fmt_get_buffer();
1564 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1566 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1567 quote_fmt_reset_vartable();
1572 textview = (GTK_TEXT_VIEW(compose->text));
1573 textbuf = gtk_text_view_get_buffer(textview);
1574 compose_create_tags(textview, compose);
1576 undo_block(compose->undostruct);
1578 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1581 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1582 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1583 /* use the reply format of folder (if enabled), or the account's one
1584 (if enabled) or fallback to the global reply format, which is always
1585 enabled (even if empty), and use the relevant quotemark */
1587 if (msginfo->folder && msginfo->folder->prefs &&
1588 msginfo->folder->prefs->reply_with_format) {
1589 qmark = msginfo->folder->prefs->reply_quotemark;
1590 body_fmt = msginfo->folder->prefs->reply_body_format;
1592 } else if (account->reply_with_format) {
1593 qmark = account->reply_quotemark;
1594 body_fmt = account->reply_body_format;
1597 qmark = prefs_common.quotemark;
1598 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1599 body_fmt = gettext(prefs_common.quotefmt);
1606 /* empty quotemark is not allowed */
1607 if (qmark == NULL || *qmark == '\0')
1609 compose_quote_fmt(compose, compose->replyinfo,
1610 body_fmt, qmark, body, FALSE, TRUE,
1611 _("The body of the \"Reply\" template has an error at line %d."));
1612 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1613 quote_fmt_reset_vartable();
1615 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1616 gtkaspell_highlight_all(compose->gtkaspell);
1620 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1621 compose_force_encryption(compose, account, FALSE, s_system);
1624 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1625 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1626 compose_force_signing(compose, account, s_system);
1630 SIGNAL_BLOCK(textbuf);
1632 if (account->auto_sig)
1633 compose_insert_sig(compose, FALSE);
1635 compose_wrap_all(compose);
1637 SIGNAL_UNBLOCK(textbuf);
1639 gtk_widget_grab_focus(compose->text);
1641 undo_unblock(compose->undostruct);
1643 if (prefs_common.auto_exteditor)
1644 compose_exec_ext_editor(compose);
1646 compose->modified = FALSE;
1647 compose_set_title(compose);
1649 compose->updating = FALSE;
1650 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1651 SCROLL_TO_CURSOR(compose);
1653 if (compose->deferred_destroy) {
1654 compose_destroy(compose);
1662 #define INSERT_FW_HEADER(var, hdr) \
1663 if (msginfo->var && *msginfo->var) { \
1664 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1665 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1666 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1669 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1670 gboolean as_attach, const gchar *body,
1671 gboolean no_extedit,
1675 GtkTextView *textview;
1676 GtkTextBuffer *textbuf;
1680 cm_return_val_if_fail(msginfo != NULL, NULL);
1681 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1684 !(account = compose_guess_forward_account_from_msginfo
1686 account = cur_account;
1688 if (!prefs_common.forward_as_attachment)
1689 mode = COMPOSE_FORWARD_INLINE;
1691 mode = COMPOSE_FORWARD;
1692 compose = compose_create(account, msginfo->folder, mode, batch);
1694 compose->updating = TRUE;
1695 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1696 if (!compose->fwdinfo)
1697 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1699 compose_extract_original_charset(compose);
1701 if (msginfo->subject && *msginfo->subject) {
1702 gchar *buf, *buf2, *p;
1704 buf = p = g_strdup(msginfo->subject);
1705 p += subject_get_prefix_length(p);
1706 memmove(buf, p, strlen(p) + 1);
1708 buf2 = g_strdup_printf("Fw: %s", buf);
1709 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1715 /* override from name according to folder properties */
1716 if (msginfo->folder && msginfo->folder->prefs &&
1717 msginfo->folder->prefs->forward_with_format &&
1718 msginfo->folder->prefs->forward_override_from_format &&
1719 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1723 MsgInfo *full_msginfo = NULL;
1726 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1728 full_msginfo = procmsg_msginfo_copy(msginfo);
1730 /* decode \-escape sequences in the internal representation of the quote format */
1731 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1732 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1735 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1736 compose->gtkaspell);
1738 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1740 quote_fmt_scan_string(tmp);
1743 buf = quote_fmt_get_buffer();
1745 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1747 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1748 quote_fmt_reset_vartable();
1751 procmsg_msginfo_free(full_msginfo);
1754 textview = GTK_TEXT_VIEW(compose->text);
1755 textbuf = gtk_text_view_get_buffer(textview);
1756 compose_create_tags(textview, compose);
1758 undo_block(compose->undostruct);
1762 msgfile = procmsg_get_message_file(msginfo);
1763 if (!is_file_exist(msgfile))
1764 g_warning("%s: file not exist\n", msgfile);
1766 compose_attach_append(compose, msgfile, msgfile,
1767 "message/rfc822", NULL);
1771 const gchar *qmark = NULL;
1772 const gchar *body_fmt = NULL;
1773 MsgInfo *full_msginfo;
1775 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1776 body_fmt = gettext(prefs_common.fw_quotefmt);
1780 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1782 full_msginfo = procmsg_msginfo_copy(msginfo);
1784 /* use the forward format of folder (if enabled), or the account's one
1785 (if enabled) or fallback to the global forward format, which is always
1786 enabled (even if empty), and use the relevant quotemark */
1787 if (msginfo->folder && msginfo->folder->prefs &&
1788 msginfo->folder->prefs->forward_with_format) {
1789 qmark = msginfo->folder->prefs->forward_quotemark;
1790 body_fmt = msginfo->folder->prefs->forward_body_format;
1792 } else if (account->forward_with_format) {
1793 qmark = account->forward_quotemark;
1794 body_fmt = account->forward_body_format;
1797 qmark = prefs_common.fw_quotemark;
1798 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1799 body_fmt = gettext(prefs_common.fw_quotefmt);
1804 /* empty quotemark is not allowed */
1805 if (qmark == NULL || *qmark == '\0')
1808 compose_quote_fmt(compose, full_msginfo,
1809 body_fmt, qmark, body, FALSE, TRUE,
1810 _("The body of the \"Forward\" template has an error at line %d."));
1811 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1812 quote_fmt_reset_vartable();
1813 compose_attach_parts(compose, msginfo);
1815 procmsg_msginfo_free(full_msginfo);
1817 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1818 gtkaspell_highlight_all(compose->gtkaspell);
1822 SIGNAL_BLOCK(textbuf);
1824 if (account->auto_sig)
1825 compose_insert_sig(compose, FALSE);
1827 compose_wrap_all(compose);
1829 SIGNAL_UNBLOCK(textbuf);
1831 gtk_text_buffer_get_start_iter(textbuf, &iter);
1832 gtk_text_buffer_place_cursor(textbuf, &iter);
1834 gtk_widget_grab_focus(compose->header_last->entry);
1836 if (!no_extedit && prefs_common.auto_exteditor)
1837 compose_exec_ext_editor(compose);
1840 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1841 gchar *folderidentifier;
1843 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1844 folderidentifier = folder_item_get_identifier(msginfo->folder);
1845 compose_set_save_to(compose, folderidentifier);
1846 g_free(folderidentifier);
1849 undo_unblock(compose->undostruct);
1851 compose->modified = FALSE;
1852 compose_set_title(compose);
1854 compose->updating = FALSE;
1855 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1856 SCROLL_TO_CURSOR(compose);
1858 if (compose->deferred_destroy) {
1859 compose_destroy(compose);
1863 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1868 #undef INSERT_FW_HEADER
1870 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1873 GtkTextView *textview;
1874 GtkTextBuffer *textbuf;
1878 gboolean single_mail = TRUE;
1880 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1882 if (g_slist_length(msginfo_list) > 1)
1883 single_mail = FALSE;
1885 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1886 if (((MsgInfo *)msginfo->data)->folder == NULL)
1889 /* guess account from first selected message */
1891 !(account = compose_guess_forward_account_from_msginfo
1892 (msginfo_list->data)))
1893 account = cur_account;
1895 cm_return_val_if_fail(account != NULL, NULL);
1897 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1898 if (msginfo->data) {
1899 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1900 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1904 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1905 g_warning("no msginfo_list");
1909 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1911 compose->updating = TRUE;
1913 /* override from name according to folder properties */
1914 if (msginfo_list->data) {
1915 MsgInfo *msginfo = msginfo_list->data;
1917 if (msginfo->folder && msginfo->folder->prefs &&
1918 msginfo->folder->prefs->forward_with_format &&
1919 msginfo->folder->prefs->forward_override_from_format &&
1920 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1925 /* decode \-escape sequences in the internal representation of the quote format */
1926 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1927 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1930 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1931 compose->gtkaspell);
1933 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1935 quote_fmt_scan_string(tmp);
1938 buf = quote_fmt_get_buffer();
1940 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1942 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1943 quote_fmt_reset_vartable();
1949 textview = GTK_TEXT_VIEW(compose->text);
1950 textbuf = gtk_text_view_get_buffer(textview);
1951 compose_create_tags(textview, compose);
1953 undo_block(compose->undostruct);
1954 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1955 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1957 if (!is_file_exist(msgfile))
1958 g_warning("%s: file not exist\n", msgfile);
1960 compose_attach_append(compose, msgfile, msgfile,
1961 "message/rfc822", NULL);
1966 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1967 if (info->subject && *info->subject) {
1968 gchar *buf, *buf2, *p;
1970 buf = p = g_strdup(info->subject);
1971 p += subject_get_prefix_length(p);
1972 memmove(buf, p, strlen(p) + 1);
1974 buf2 = g_strdup_printf("Fw: %s", buf);
1975 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1981 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1982 _("Fw: multiple emails"));
1985 SIGNAL_BLOCK(textbuf);
1987 if (account->auto_sig)
1988 compose_insert_sig(compose, FALSE);
1990 compose_wrap_all(compose);
1992 SIGNAL_UNBLOCK(textbuf);
1994 gtk_text_buffer_get_start_iter(textbuf, &iter);
1995 gtk_text_buffer_place_cursor(textbuf, &iter);
1997 gtk_widget_grab_focus(compose->header_last->entry);
1998 undo_unblock(compose->undostruct);
1999 compose->modified = FALSE;
2000 compose_set_title(compose);
2002 compose->updating = FALSE;
2003 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2004 SCROLL_TO_CURSOR(compose);
2006 if (compose->deferred_destroy) {
2007 compose_destroy(compose);
2011 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2016 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2018 GtkTextIter start = *iter;
2019 GtkTextIter end_iter;
2020 int start_pos = gtk_text_iter_get_offset(&start);
2022 if (!compose->account->sig_sep)
2025 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2026 start_pos+strlen(compose->account->sig_sep));
2028 /* check sig separator */
2029 str = gtk_text_iter_get_text(&start, &end_iter);
2030 if (!strcmp(str, compose->account->sig_sep)) {
2032 /* check end of line (\n) */
2033 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2034 start_pos+strlen(compose->account->sig_sep));
2035 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2036 start_pos+strlen(compose->account->sig_sep)+1);
2037 tmp = gtk_text_iter_get_text(&start, &end_iter);
2038 if (!strcmp(tmp,"\n")) {
2050 static void compose_colorize_signature(Compose *compose)
2052 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2054 GtkTextIter end_iter;
2055 gtk_text_buffer_get_start_iter(buffer, &iter);
2056 while (gtk_text_iter_forward_line(&iter))
2057 if (compose_is_sig_separator(compose, buffer, &iter)) {
2058 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2059 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2063 #define BLOCK_WRAP() { \
2064 prev_autowrap = compose->autowrap; \
2065 buffer = gtk_text_view_get_buffer( \
2066 GTK_TEXT_VIEW(compose->text)); \
2067 compose->autowrap = FALSE; \
2069 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2070 G_CALLBACK(compose_changed_cb), \
2072 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2073 G_CALLBACK(text_inserted), \
2076 #define UNBLOCK_WRAP() { \
2077 compose->autowrap = prev_autowrap; \
2078 if (compose->autowrap) { \
2079 gint old = compose->draft_timeout_tag; \
2080 compose->draft_timeout_tag = -2; \
2081 compose_wrap_all(compose); \
2082 compose->draft_timeout_tag = old; \
2085 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2086 G_CALLBACK(compose_changed_cb), \
2088 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2089 G_CALLBACK(text_inserted), \
2093 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2095 Compose *compose = NULL;
2096 PrefsAccount *account = NULL;
2097 GtkTextView *textview;
2098 GtkTextBuffer *textbuf;
2102 gchar buf[BUFFSIZE];
2103 gboolean use_signing = FALSE;
2104 gboolean use_encryption = FALSE;
2105 gchar *privacy_system = NULL;
2106 int priority = PRIORITY_NORMAL;
2107 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2108 gboolean autowrap = prefs_common.autowrap;
2109 gboolean autoindent = prefs_common.auto_indent;
2111 cm_return_val_if_fail(msginfo != NULL, NULL);
2112 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2114 if (compose_put_existing_to_front(msginfo)) {
2118 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2119 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2120 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2121 gchar queueheader_buf[BUFFSIZE];
2124 /* Select Account from queue headers */
2125 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2126 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2127 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2128 account = account_find_from_id(id);
2130 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2131 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2132 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2133 account = account_find_from_id(id);
2135 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2136 sizeof(queueheader_buf), "NAID:")) {
2137 id = atoi(&queueheader_buf[strlen("NAID:")]);
2138 account = account_find_from_id(id);
2140 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2141 sizeof(queueheader_buf), "MAID:")) {
2142 id = atoi(&queueheader_buf[strlen("MAID:")]);
2143 account = account_find_from_id(id);
2145 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2146 sizeof(queueheader_buf), "S:")) {
2147 account = account_find_from_address(queueheader_buf, FALSE);
2149 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2150 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2151 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2152 use_signing = param;
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2157 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2158 use_signing = param;
2161 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2162 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2163 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2164 use_encryption = param;
2166 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2167 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2168 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2169 use_encryption = param;
2171 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2172 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2173 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2176 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2177 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2178 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2183 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2185 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2187 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2189 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2190 sizeof(queueheader_buf), "X-Priority: ")) {
2191 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2194 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2195 sizeof(queueheader_buf), "RMID:")) {
2196 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2197 if (tokens[0] && tokens[1] && tokens[2]) {
2198 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2199 if (orig_item != NULL) {
2200 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2205 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2206 sizeof(queueheader_buf), "FMID:")) {
2207 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2208 if (tokens[0] && tokens[1] && tokens[2]) {
2209 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2210 if (orig_item != NULL) {
2211 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2217 account = msginfo->folder->folder->account;
2220 if (!account && prefs_common.reedit_account_autosel) {
2221 gchar from[BUFFSIZE];
2222 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2223 extract_address(from);
2224 account = account_find_from_address(from, FALSE);
2228 account = cur_account;
2230 cm_return_val_if_fail(account != NULL, NULL);
2232 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2234 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2235 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2236 compose->autowrap = autowrap;
2237 compose->replyinfo = replyinfo;
2238 compose->fwdinfo = fwdinfo;
2240 compose->updating = TRUE;
2241 compose->priority = priority;
2243 if (privacy_system != NULL) {
2244 compose->privacy_system = privacy_system;
2245 compose_use_signing(compose, use_signing);
2246 compose_use_encryption(compose, use_encryption);
2247 compose_update_privacy_system_menu_item(compose, FALSE);
2249 activate_privacy_system(compose, account, FALSE);
2252 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2254 compose_extract_original_charset(compose);
2256 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2257 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2258 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2259 gchar queueheader_buf[BUFFSIZE];
2261 /* Set message save folder */
2262 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2263 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2264 compose_set_save_to(compose, &queueheader_buf[4]);
2266 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2267 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2269 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2274 if (compose_parse_header(compose, msginfo) < 0) {
2275 compose->updating = FALSE;
2276 compose_destroy(compose);
2279 compose_reedit_set_entry(compose, msginfo);
2281 textview = GTK_TEXT_VIEW(compose->text);
2282 textbuf = gtk_text_view_get_buffer(textview);
2283 compose_create_tags(textview, compose);
2285 mark = gtk_text_buffer_get_insert(textbuf);
2286 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2288 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2289 G_CALLBACK(compose_changed_cb),
2292 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2293 fp = procmime_get_first_encrypted_text_content(msginfo);
2295 compose_force_encryption(compose, account, TRUE, NULL);
2298 fp = procmime_get_first_text_content(msginfo);
2301 g_warning("Can't get text part\n");
2305 gboolean prev_autowrap = compose->autowrap;
2306 GtkTextBuffer *buffer = textbuf;
2308 while (fgets(buf, sizeof(buf), fp) != NULL) {
2310 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2316 compose_attach_parts(compose, msginfo);
2318 compose_colorize_signature(compose);
2320 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2321 G_CALLBACK(compose_changed_cb),
2324 gtk_widget_grab_focus(compose->text);
2326 if (prefs_common.auto_exteditor) {
2327 compose_exec_ext_editor(compose);
2329 compose->modified = FALSE;
2330 compose_set_title(compose);
2332 compose->updating = FALSE;
2333 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2334 SCROLL_TO_CURSOR(compose);
2336 if (compose->deferred_destroy) {
2337 compose_destroy(compose);
2341 compose->sig_str = account_get_signature_str(compose->account);
2343 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2348 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2355 cm_return_val_if_fail(msginfo != NULL, NULL);
2358 account = account_get_reply_account(msginfo,
2359 prefs_common.reply_account_autosel);
2360 cm_return_val_if_fail(account != NULL, NULL);
2362 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2364 compose->updating = TRUE;
2366 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2367 compose->replyinfo = NULL;
2368 compose->fwdinfo = NULL;
2370 compose_show_first_last_header(compose, TRUE);
2372 gtk_widget_grab_focus(compose->header_last->entry);
2374 filename = procmsg_get_message_file(msginfo);
2376 if (filename == NULL) {
2377 compose->updating = FALSE;
2378 compose_destroy(compose);
2383 compose->redirect_filename = filename;
2385 /* Set save folder */
2386 item = msginfo->folder;
2387 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2388 gchar *folderidentifier;
2390 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2391 folderidentifier = folder_item_get_identifier(item);
2392 compose_set_save_to(compose, folderidentifier);
2393 g_free(folderidentifier);
2396 compose_attach_parts(compose, msginfo);
2398 if (msginfo->subject)
2399 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2401 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2403 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2404 _("The body of the \"Redirect\" template has an error at line %d."));
2405 quote_fmt_reset_vartable();
2406 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2408 compose_colorize_signature(compose);
2411 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2413 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2417 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2424 if (compose->toolbar->draft_btn)
2425 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2426 if (compose->toolbar->insert_btn)
2427 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2428 if (compose->toolbar->attach_btn)
2429 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2430 if (compose->toolbar->sig_btn)
2431 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2432 if (compose->toolbar->exteditor_btn)
2433 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2434 if (compose->toolbar->linewrap_current_btn)
2435 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2436 if (compose->toolbar->linewrap_all_btn)
2437 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2439 compose->modified = FALSE;
2440 compose_set_title(compose);
2441 compose->updating = FALSE;
2442 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2443 SCROLL_TO_CURSOR(compose);
2445 if (compose->deferred_destroy) {
2446 compose_destroy(compose);
2450 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2455 GList *compose_get_compose_list(void)
2457 return compose_list;
2460 void compose_entry_append(Compose *compose, const gchar *address,
2461 ComposeEntryType type, ComposePrefType pref_type)
2463 const gchar *header;
2465 gboolean in_quote = FALSE;
2466 if (!address || *address == '\0') return;
2473 header = N_("Bcc:");
2475 case COMPOSE_REPLYTO:
2476 header = N_("Reply-To:");
2478 case COMPOSE_NEWSGROUPS:
2479 header = N_("Newsgroups:");
2481 case COMPOSE_FOLLOWUPTO:
2482 header = N_( "Followup-To:");
2484 case COMPOSE_INREPLYTO:
2485 header = N_( "In-Reply-To:");
2492 header = prefs_common_translated_header_name(header);
2494 cur = begin = (gchar *)address;
2496 /* we separate the line by commas, but not if we're inside a quoted
2498 while (*cur != '\0') {
2500 in_quote = !in_quote;
2501 if (*cur == ',' && !in_quote) {
2502 gchar *tmp = g_strdup(begin);
2504 tmp[cur-begin]='\0';
2507 while (*tmp == ' ' || *tmp == '\t')
2509 compose_add_header_entry(compose, header, tmp, pref_type);
2516 gchar *tmp = g_strdup(begin);
2518 tmp[cur-begin]='\0';
2521 while (*tmp == ' ' || *tmp == '\t')
2523 compose_add_header_entry(compose, header, tmp, pref_type);
2528 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2530 static GdkColor yellow;
2531 static GdkColor black;
2532 static gboolean yellow_initialised = FALSE;
2536 if (!yellow_initialised) {
2537 gdk_color_parse("#f5f6be", &yellow);
2538 gdk_color_parse("#000000", &black);
2539 yellow_initialised = gdk_colormap_alloc_color(
2540 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2541 yellow_initialised &= gdk_colormap_alloc_color(
2542 gdk_colormap_get_system(), &black, FALSE, TRUE);
2545 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2546 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2547 if (gtk_entry_get_text(entry) &&
2548 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2549 if (yellow_initialised) {
2550 gtk_widget_modify_base(
2551 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2552 GTK_STATE_NORMAL, &yellow);
2553 gtk_widget_modify_text(
2554 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2555 GTK_STATE_NORMAL, &black);
2561 void compose_toolbar_cb(gint action, gpointer data)
2563 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2564 Compose *compose = (Compose*)toolbar_item->parent;
2566 cm_return_if_fail(compose != NULL);
2570 compose_send_cb(NULL, compose);
2573 compose_send_later_cb(NULL, compose);
2576 compose_draft(compose, COMPOSE_QUIT_EDITING);
2579 compose_insert_file_cb(NULL, compose);
2582 compose_attach_cb(NULL, compose);
2585 compose_insert_sig(compose, FALSE);
2588 compose_ext_editor_cb(NULL, compose);
2590 case A_LINEWRAP_CURRENT:
2591 compose_beautify_paragraph(compose, NULL, TRUE);
2593 case A_LINEWRAP_ALL:
2594 compose_wrap_all_full(compose, TRUE);
2597 compose_address_cb(NULL, compose);
2600 case A_CHECK_SPELLING:
2601 compose_check_all(NULL, compose);
2609 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2614 gchar *subject = NULL;
2618 gchar **attach = NULL;
2619 gchar *inreplyto = NULL;
2620 MailField mfield = NO_FIELD_PRESENT;
2622 /* get mailto parts but skip from */
2623 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2626 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2627 mfield = TO_FIELD_PRESENT;
2630 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2632 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2634 if (!g_utf8_validate (subject, -1, NULL)) {
2635 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2636 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2639 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2641 mfield = SUBJECT_FIELD_PRESENT;
2644 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2645 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2648 gboolean prev_autowrap = compose->autowrap;
2650 compose->autowrap = FALSE;
2652 mark = gtk_text_buffer_get_insert(buffer);
2653 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2655 if (!g_utf8_validate (body, -1, NULL)) {
2656 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2657 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2660 gtk_text_buffer_insert(buffer, &iter, body, -1);
2662 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2664 compose->autowrap = prev_autowrap;
2665 if (compose->autowrap)
2666 compose_wrap_all(compose);
2667 mfield = BODY_FIELD_PRESENT;
2671 gint i = 0, att = 0;
2672 gchar *warn_files = NULL;
2673 while (attach[i] != NULL) {
2674 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2675 if (utf8_filename) {
2676 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2677 gchar *tmp = g_strdup_printf("%s%s\n",
2678 warn_files?warn_files:"",
2684 g_free(utf8_filename);
2686 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2691 alertpanel_notice(ngettext(
2692 "The following file has been attached: \n%s",
2693 "The following files have been attached: \n%s", att), warn_files);
2698 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2711 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2713 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2714 {"Cc:", NULL, TRUE},
2715 {"References:", NULL, FALSE},
2716 {"Bcc:", NULL, TRUE},
2717 {"Newsgroups:", NULL, TRUE},
2718 {"Followup-To:", NULL, TRUE},
2719 {"List-Post:", NULL, FALSE},
2720 {"X-Priority:", NULL, FALSE},
2721 {NULL, NULL, FALSE}};
2737 cm_return_val_if_fail(msginfo != NULL, -1);
2739 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2740 procheader_get_header_fields(fp, hentry);
2743 if (hentry[H_REPLY_TO].body != NULL) {
2744 if (hentry[H_REPLY_TO].body[0] != '\0') {
2746 conv_unmime_header(hentry[H_REPLY_TO].body,
2749 g_free(hentry[H_REPLY_TO].body);
2750 hentry[H_REPLY_TO].body = NULL;
2752 if (hentry[H_CC].body != NULL) {
2753 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2754 g_free(hentry[H_CC].body);
2755 hentry[H_CC].body = NULL;
2757 if (hentry[H_REFERENCES].body != NULL) {
2758 if (compose->mode == COMPOSE_REEDIT)
2759 compose->references = hentry[H_REFERENCES].body;
2761 compose->references = compose_parse_references
2762 (hentry[H_REFERENCES].body, msginfo->msgid);
2763 g_free(hentry[H_REFERENCES].body);
2765 hentry[H_REFERENCES].body = NULL;
2767 if (hentry[H_BCC].body != NULL) {
2768 if (compose->mode == COMPOSE_REEDIT)
2770 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2771 g_free(hentry[H_BCC].body);
2772 hentry[H_BCC].body = NULL;
2774 if (hentry[H_NEWSGROUPS].body != NULL) {
2775 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2776 hentry[H_NEWSGROUPS].body = NULL;
2778 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2779 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2780 compose->followup_to =
2781 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2784 g_free(hentry[H_FOLLOWUP_TO].body);
2785 hentry[H_FOLLOWUP_TO].body = NULL;
2787 if (hentry[H_LIST_POST].body != NULL) {
2788 gchar *to = NULL, *start = NULL;
2790 extract_address(hentry[H_LIST_POST].body);
2791 if (hentry[H_LIST_POST].body[0] != '\0') {
2792 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2794 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2795 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2798 g_free(compose->ml_post);
2799 compose->ml_post = to;
2802 g_free(hentry[H_LIST_POST].body);
2803 hentry[H_LIST_POST].body = NULL;
2806 /* CLAWS - X-Priority */
2807 if (compose->mode == COMPOSE_REEDIT)
2808 if (hentry[H_X_PRIORITY].body != NULL) {
2811 priority = atoi(hentry[H_X_PRIORITY].body);
2812 g_free(hentry[H_X_PRIORITY].body);
2814 hentry[H_X_PRIORITY].body = NULL;
2816 if (priority < PRIORITY_HIGHEST ||
2817 priority > PRIORITY_LOWEST)
2818 priority = PRIORITY_NORMAL;
2820 compose->priority = priority;
2823 if (compose->mode == COMPOSE_REEDIT) {
2824 if (msginfo->inreplyto && *msginfo->inreplyto)
2825 compose->inreplyto = g_strdup(msginfo->inreplyto);
2829 if (msginfo->msgid && *msginfo->msgid)
2830 compose->inreplyto = g_strdup(msginfo->msgid);
2832 if (!compose->references) {
2833 if (msginfo->msgid && *msginfo->msgid) {
2834 if (msginfo->inreplyto && *msginfo->inreplyto)
2835 compose->references =
2836 g_strdup_printf("<%s>\n\t<%s>",
2840 compose->references =
2841 g_strconcat("<", msginfo->msgid, ">",
2843 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2844 compose->references =
2845 g_strconcat("<", msginfo->inreplyto, ">",
2853 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2855 GSList *ref_id_list, *cur;
2859 ref_id_list = references_list_append(NULL, ref);
2860 if (!ref_id_list) return NULL;
2861 if (msgid && *msgid)
2862 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2867 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2868 /* "<" + Message-ID + ">" + CR+LF+TAB */
2869 len += strlen((gchar *)cur->data) + 5;
2871 if (len > MAX_REFERENCES_LEN) {
2872 /* remove second message-ID */
2873 if (ref_id_list && ref_id_list->next &&
2874 ref_id_list->next->next) {
2875 g_free(ref_id_list->next->data);
2876 ref_id_list = g_slist_remove
2877 (ref_id_list, ref_id_list->next->data);
2879 slist_free_strings(ref_id_list);
2880 g_slist_free(ref_id_list);
2887 new_ref = g_string_new("");
2888 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2889 if (new_ref->len > 0)
2890 g_string_append(new_ref, "\n\t");
2891 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2894 slist_free_strings(ref_id_list);
2895 g_slist_free(ref_id_list);
2897 new_ref_str = new_ref->str;
2898 g_string_free(new_ref, FALSE);
2903 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2904 const gchar *fmt, const gchar *qmark,
2905 const gchar *body, gboolean rewrap,
2906 gboolean need_unescape,
2907 const gchar *err_msg)
2909 MsgInfo* dummyinfo = NULL;
2910 gchar *quote_str = NULL;
2912 gboolean prev_autowrap;
2913 const gchar *trimmed_body = body;
2914 gint cursor_pos = -1;
2915 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2916 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2921 SIGNAL_BLOCK(buffer);
2924 dummyinfo = compose_msginfo_new_from_compose(compose);
2925 msginfo = dummyinfo;
2928 if (qmark != NULL) {
2930 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2931 compose->gtkaspell);
2933 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2935 quote_fmt_scan_string(qmark);
2938 buf = quote_fmt_get_buffer();
2940 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2942 Xstrdup_a(quote_str, buf, goto error)
2945 if (fmt && *fmt != '\0') {
2948 while (*trimmed_body == '\n')
2952 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2953 compose->gtkaspell);
2955 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2957 if (need_unescape) {
2960 /* decode \-escape sequences in the internal representation of the quote format */
2961 tmp = g_malloc(strlen(fmt)+1);
2962 pref_get_unescaped_pref(tmp, fmt);
2963 quote_fmt_scan_string(tmp);
2967 quote_fmt_scan_string(fmt);
2971 buf = quote_fmt_get_buffer();
2973 gint line = quote_fmt_get_line();
2974 alertpanel_error(err_msg, line);
2980 prev_autowrap = compose->autowrap;
2981 compose->autowrap = FALSE;
2983 mark = gtk_text_buffer_get_insert(buffer);
2984 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2985 if (g_utf8_validate(buf, -1, NULL)) {
2986 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2988 gchar *tmpout = NULL;
2989 tmpout = conv_codeset_strdup
2990 (buf, conv_get_locale_charset_str_no_utf8(),
2992 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2994 tmpout = g_malloc(strlen(buf)*2+1);
2995 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2997 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3001 cursor_pos = quote_fmt_get_cursor_pos();
3002 if (cursor_pos == -1)
3003 cursor_pos = gtk_text_iter_get_offset(&iter);
3004 compose->set_cursor_pos = cursor_pos;
3006 gtk_text_buffer_get_start_iter(buffer, &iter);
3007 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3008 gtk_text_buffer_place_cursor(buffer, &iter);
3010 compose->autowrap = prev_autowrap;
3011 if (compose->autowrap && rewrap)
3012 compose_wrap_all(compose);
3019 SIGNAL_UNBLOCK(buffer);
3021 procmsg_msginfo_free( dummyinfo );
3026 /* if ml_post is of type addr@host and from is of type
3027 * addr-anything@host, return TRUE
3029 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3031 gchar *left_ml = NULL;
3032 gchar *right_ml = NULL;
3033 gchar *left_from = NULL;
3034 gchar *right_from = NULL;
3035 gboolean result = FALSE;
3037 if (!ml_post || !from)
3040 left_ml = g_strdup(ml_post);
3041 if (strstr(left_ml, "@")) {
3042 right_ml = strstr(left_ml, "@")+1;
3043 *(strstr(left_ml, "@")) = '\0';
3046 left_from = g_strdup(from);
3047 if (strstr(left_from, "@")) {
3048 right_from = strstr(left_from, "@")+1;
3049 *(strstr(left_from, "@")) = '\0';
3052 if (left_ml && left_from && right_ml && right_from
3053 && !strncmp(left_from, left_ml, strlen(left_ml))
3054 && !strcmp(right_from, right_ml)) {
3063 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3064 gboolean respect_default_to)
3068 if (!folder || !folder->prefs)
3071 if (respect_default_to && folder->prefs->enable_default_to) {
3072 compose_entry_append(compose, folder->prefs->default_to,
3073 COMPOSE_TO, PREF_FOLDER);
3074 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3076 if (folder->prefs->enable_default_cc)
3077 compose_entry_append(compose, folder->prefs->default_cc,
3078 COMPOSE_CC, PREF_FOLDER);
3079 if (folder->prefs->enable_default_bcc)
3080 compose_entry_append(compose, folder->prefs->default_bcc,
3081 COMPOSE_BCC, PREF_FOLDER);
3082 if (folder->prefs->enable_default_replyto)
3083 compose_entry_append(compose, folder->prefs->default_replyto,
3084 COMPOSE_REPLYTO, PREF_FOLDER);
3087 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3092 if (!compose || !msginfo)
3095 if (msginfo->subject && *msginfo->subject) {
3096 buf = p = g_strdup(msginfo->subject);
3097 p += subject_get_prefix_length(p);
3098 memmove(buf, p, strlen(p) + 1);
3100 buf2 = g_strdup_printf("Re: %s", buf);
3101 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3106 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3109 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3110 gboolean to_all, gboolean to_ml,
3112 gboolean followup_and_reply_to)
3114 GSList *cc_list = NULL;
3117 gchar *replyto = NULL;
3118 gchar *ac_email = NULL;
3120 gboolean reply_to_ml = FALSE;
3121 gboolean default_reply_to = FALSE;
3123 cm_return_if_fail(compose->account != NULL);
3124 cm_return_if_fail(msginfo != NULL);
3126 reply_to_ml = to_ml && compose->ml_post;
3128 default_reply_to = msginfo->folder &&
3129 msginfo->folder->prefs->enable_default_reply_to;
3131 if (compose->account->protocol != A_NNTP) {
3132 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3134 if (reply_to_ml && !default_reply_to) {
3136 gboolean is_subscr = is_subscription(compose->ml_post,
3139 /* normal answer to ml post with a reply-to */
3140 compose_entry_append(compose,
3142 COMPOSE_TO, PREF_ML);
3143 if (compose->replyto)
3144 compose_entry_append(compose,
3146 COMPOSE_CC, PREF_ML);
3148 /* answer to subscription confirmation */
3149 if (compose->replyto)
3150 compose_entry_append(compose,
3152 COMPOSE_TO, PREF_ML);
3153 else if (msginfo->from)
3154 compose_entry_append(compose,
3156 COMPOSE_TO, PREF_ML);
3159 else if (!(to_all || to_sender) && default_reply_to) {
3160 compose_entry_append(compose,
3161 msginfo->folder->prefs->default_reply_to,
3162 COMPOSE_TO, PREF_FOLDER);
3163 compose_entry_mark_default_to(compose,
3164 msginfo->folder->prefs->default_reply_to);
3169 Xstrdup_a(tmp1, msginfo->from, return);
3170 extract_address(tmp1);
3171 if (to_all || to_sender ||
3172 !account_find_from_address(tmp1, FALSE))
3173 compose_entry_append(compose,
3174 (compose->replyto && !to_sender)
3175 ? compose->replyto :
3176 msginfo->from ? msginfo->from : "",
3177 COMPOSE_TO, PREF_NONE);
3178 else if (!to_all && !to_sender) {
3179 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3180 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3181 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3182 if (compose->replyto) {
3183 compose_entry_append(compose,
3185 COMPOSE_TO, PREF_NONE);
3187 compose_entry_append(compose,
3188 msginfo->from ? msginfo->from : "",
3189 COMPOSE_TO, PREF_NONE);
3192 /* replying to own mail, use original recp */
3193 compose_entry_append(compose,
3194 msginfo->to ? msginfo->to : "",
3195 COMPOSE_TO, PREF_NONE);
3196 compose_entry_append(compose,
3197 msginfo->cc ? msginfo->cc : "",
3198 COMPOSE_CC, PREF_NONE);
3203 if (to_sender || (compose->followup_to &&
3204 !strncmp(compose->followup_to, "poster", 6)))
3205 compose_entry_append
3207 (compose->replyto ? compose->replyto :
3208 msginfo->from ? msginfo->from : ""),
3209 COMPOSE_TO, PREF_NONE);
3211 else if (followup_and_reply_to || to_all) {
3212 compose_entry_append
3214 (compose->replyto ? compose->replyto :
3215 msginfo->from ? msginfo->from : ""),
3216 COMPOSE_TO, PREF_NONE);
3218 compose_entry_append
3220 compose->followup_to ? compose->followup_to :
3221 compose->newsgroups ? compose->newsgroups : "",
3222 COMPOSE_NEWSGROUPS, PREF_NONE);
3225 compose_entry_append
3227 compose->followup_to ? compose->followup_to :
3228 compose->newsgroups ? compose->newsgroups : "",
3229 COMPOSE_NEWSGROUPS, PREF_NONE);
3231 compose_reply_set_subject(compose, msginfo);
3233 if (to_ml && compose->ml_post) return;
3234 if (!to_all || compose->account->protocol == A_NNTP) return;
3236 if (compose->replyto) {
3237 Xstrdup_a(replyto, compose->replyto, return);
3238 extract_address(replyto);
3240 if (msginfo->from) {
3241 Xstrdup_a(from, msginfo->from, return);
3242 extract_address(from);
3245 if (replyto && from)
3246 cc_list = address_list_append_with_comments(cc_list, from);
3247 if (to_all && msginfo->folder &&
3248 msginfo->folder->prefs->enable_default_reply_to)
3249 cc_list = address_list_append_with_comments(cc_list,
3250 msginfo->folder->prefs->default_reply_to);
3251 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3252 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3254 ac_email = g_utf8_strdown(compose->account->address, -1);
3257 for (cur = cc_list; cur != NULL; cur = cur->next) {
3258 gchar *addr = g_utf8_strdown(cur->data, -1);
3259 extract_address(addr);
3261 if (strcmp(ac_email, addr))
3262 compose_entry_append(compose, (gchar *)cur->data,
3263 COMPOSE_CC, PREF_NONE);
3265 debug_print("Cc address same as compose account's, ignoring\n");
3270 slist_free_strings(cc_list);
3271 g_slist_free(cc_list);
3277 #define SET_ENTRY(entry, str) \
3280 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3283 #define SET_ADDRESS(type, str) \
3286 compose_entry_append(compose, str, type, PREF_NONE); \
3289 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3291 cm_return_if_fail(msginfo != NULL);
3293 SET_ENTRY(subject_entry, msginfo->subject);
3294 SET_ENTRY(from_name, msginfo->from);
3295 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3296 SET_ADDRESS(COMPOSE_CC, compose->cc);
3297 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3298 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3299 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3300 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3302 compose_update_priority_menu_item(compose);
3303 compose_update_privacy_system_menu_item(compose, FALSE);
3304 compose_show_first_last_header(compose, TRUE);
3310 static void compose_insert_sig(Compose *compose, gboolean replace)
3312 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3313 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3315 GtkTextIter iter, iter_end;
3316 gint cur_pos, ins_pos;
3317 gboolean prev_autowrap;
3318 gboolean found = FALSE;
3319 gboolean exists = FALSE;
3321 cm_return_if_fail(compose->account != NULL);
3325 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3326 G_CALLBACK(compose_changed_cb),
3329 mark = gtk_text_buffer_get_insert(buffer);
3330 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3331 cur_pos = gtk_text_iter_get_offset (&iter);
3334 gtk_text_buffer_get_end_iter(buffer, &iter);
3336 exists = (compose->sig_str != NULL);
3339 GtkTextIter first_iter, start_iter, end_iter;
3341 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3343 if (!exists || compose->sig_str[0] == '\0')
3346 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3347 compose->signature_tag);
3350 /* include previous \n\n */
3351 gtk_text_iter_backward_chars(&first_iter, 1);
3352 start_iter = first_iter;
3353 end_iter = first_iter;
3355 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3356 compose->signature_tag);
3357 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3358 compose->signature_tag);
3360 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3366 g_free(compose->sig_str);
3367 compose->sig_str = account_get_signature_str(compose->account);
3369 cur_pos = gtk_text_iter_get_offset(&iter);
3371 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3372 g_free(compose->sig_str);
3373 compose->sig_str = NULL;
3375 if (compose->sig_inserted == FALSE)
3376 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3377 compose->sig_inserted = TRUE;
3379 cur_pos = gtk_text_iter_get_offset(&iter);
3380 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3382 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3383 gtk_text_iter_forward_chars(&iter, 1);
3384 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3385 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3387 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3388 cur_pos = gtk_text_buffer_get_char_count (buffer);
3391 /* put the cursor where it should be
3392 * either where the quote_fmt says, either where it was */
3393 if (compose->set_cursor_pos < 0)
3394 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3396 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3397 compose->set_cursor_pos);
3399 compose->set_cursor_pos = -1;
3400 gtk_text_buffer_place_cursor(buffer, &iter);
3401 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3402 G_CALLBACK(compose_changed_cb),
3408 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3411 GtkTextBuffer *buffer;
3414 const gchar *cur_encoding;
3415 gchar buf[BUFFSIZE];
3418 gboolean prev_autowrap;
3419 gboolean badtxt = FALSE;
3420 struct stat file_stat;
3423 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3425 /* get the size of the file we are about to insert */
3426 ret = g_stat(file, &file_stat);
3428 gchar *shortfile = g_path_get_basename(file);
3429 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3431 return COMPOSE_INSERT_NO_FILE;
3432 } else if (prefs_common.warn_large_insert == TRUE) {
3434 /* ask user for confirmation if the file is large */
3435 if (prefs_common.warn_large_insert_size < 0 ||
3436 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3440 msg = g_strdup_printf(_("You are about to insert a file of %s "
3441 "in the message body. Are you sure you want to do that?"),
3442 to_human_readable(file_stat.st_size));
3443 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3444 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3447 /* do we ask for confirmation next time? */
3448 if (aval & G_ALERTDISABLE) {
3449 /* no confirmation next time, disable feature in preferences */
3450 aval &= ~G_ALERTDISABLE;
3451 prefs_common.warn_large_insert = FALSE;
3454 /* abort file insertion if user canceled action */
3455 if (aval != G_ALERTALTERNATE) {
3456 return COMPOSE_INSERT_NO_FILE;
3462 if ((fp = g_fopen(file, "rb")) == NULL) {
3463 FILE_OP_ERROR(file, "fopen");
3464 return COMPOSE_INSERT_READ_ERROR;
3467 prev_autowrap = compose->autowrap;
3468 compose->autowrap = FALSE;
3470 text = GTK_TEXT_VIEW(compose->text);
3471 buffer = gtk_text_view_get_buffer(text);
3472 mark = gtk_text_buffer_get_insert(buffer);
3473 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3475 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3476 G_CALLBACK(text_inserted),
3479 cur_encoding = conv_get_locale_charset_str_no_utf8();
3481 while (fgets(buf, sizeof(buf), fp) != NULL) {
3484 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3485 str = g_strdup(buf);
3487 str = conv_codeset_strdup
3488 (buf, cur_encoding, CS_INTERNAL);
3491 /* strip <CR> if DOS/Windows file,
3492 replace <CR> with <LF> if Macintosh file. */
3495 if (len > 0 && str[len - 1] != '\n') {
3497 if (str[len] == '\r') str[len] = '\n';
3500 gtk_text_buffer_insert(buffer, &iter, str, -1);
3504 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3505 G_CALLBACK(text_inserted),
3507 compose->autowrap = prev_autowrap;
3508 if (compose->autowrap)
3509 compose_wrap_all(compose);
3514 return COMPOSE_INSERT_INVALID_CHARACTER;
3516 return COMPOSE_INSERT_SUCCESS;
3519 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3520 const gchar *filename,
3521 const gchar *content_type,
3522 const gchar *charset)
3530 GtkListStore *store;
3532 gboolean has_binary = FALSE;
3534 if (!is_file_exist(file)) {
3535 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3536 gboolean result = FALSE;
3537 if (file_from_uri && is_file_exist(file_from_uri)) {
3538 result = compose_attach_append(
3539 compose, file_from_uri,
3540 filename, content_type,
3543 g_free(file_from_uri);
3546 alertpanel_error("File %s doesn't exist\n", filename);
3549 if ((size = get_file_size(file)) < 0) {
3550 alertpanel_error("Can't get file size of %s\n", filename);
3554 alertpanel_error(_("File %s is empty."), filename);
3557 if ((fp = g_fopen(file, "rb")) == NULL) {
3558 alertpanel_error(_("Can't read %s."), filename);
3563 ainfo = g_new0(AttachInfo, 1);
3564 auto_ainfo = g_auto_pointer_new_with_free
3565 (ainfo, (GFreeFunc) compose_attach_info_free);
3566 ainfo->file = g_strdup(file);
3569 ainfo->content_type = g_strdup(content_type);
3570 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3572 MsgFlags flags = {0, 0};
3574 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3575 ainfo->encoding = ENC_7BIT;
3577 ainfo->encoding = ENC_8BIT;
3579 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3580 if (msginfo && msginfo->subject)
3581 name = g_strdup(msginfo->subject);
3583 name = g_path_get_basename(filename ? filename : file);
3585 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3587 procmsg_msginfo_free(msginfo);
3589 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3590 ainfo->charset = g_strdup(charset);
3591 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3593 ainfo->encoding = ENC_BASE64;
3595 name = g_path_get_basename(filename ? filename : file);
3596 ainfo->name = g_strdup(name);
3600 ainfo->content_type = procmime_get_mime_type(file);
3601 if (!ainfo->content_type) {
3602 ainfo->content_type =
3603 g_strdup("application/octet-stream");
3604 ainfo->encoding = ENC_BASE64;
3605 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3607 procmime_get_encoding_for_text_file(file, &has_binary);
3609 ainfo->encoding = ENC_BASE64;
3610 name = g_path_get_basename(filename ? filename : file);
3611 ainfo->name = g_strdup(name);
3615 if (ainfo->name != NULL
3616 && !strcmp(ainfo->name, ".")) {
3617 g_free(ainfo->name);
3621 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3622 g_free(ainfo->content_type);
3623 ainfo->content_type = g_strdup("application/octet-stream");
3624 g_free(ainfo->charset);
3625 ainfo->charset = NULL;
3628 ainfo->size = (goffset)size;
3629 size_text = to_human_readable((goffset)size);
3631 store = GTK_LIST_STORE(gtk_tree_view_get_model
3632 (GTK_TREE_VIEW(compose->attach_clist)));
3634 gtk_list_store_append(store, &iter);
3635 gtk_list_store_set(store, &iter,
3636 COL_MIMETYPE, ainfo->content_type,
3637 COL_SIZE, size_text,
3638 COL_NAME, ainfo->name,
3639 COL_CHARSET, ainfo->charset,
3641 COL_AUTODATA, auto_ainfo,
3644 g_auto_pointer_free(auto_ainfo);
3645 compose_attach_update_label(compose);
3649 static void compose_use_signing(Compose *compose, gboolean use_signing)
3651 compose->use_signing = use_signing;
3652 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3655 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3657 compose->use_encryption = use_encryption;
3658 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3661 #define NEXT_PART_NOT_CHILD(info) \
3663 node = info->node; \
3664 while (node->children) \
3665 node = g_node_last_child(node); \
3666 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3669 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3673 MimeInfo *firsttext = NULL;
3674 MimeInfo *encrypted = NULL;
3677 const gchar *partname = NULL;
3679 mimeinfo = procmime_scan_message(msginfo);
3680 if (!mimeinfo) return;
3682 if (mimeinfo->node->children == NULL) {
3683 procmime_mimeinfo_free_all(mimeinfo);
3687 /* find first content part */
3688 child = (MimeInfo *) mimeinfo->node->children->data;
3689 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3690 child = (MimeInfo *)child->node->children->data;
3693 if (child->type == MIMETYPE_TEXT) {
3695 debug_print("First text part found\n");
3696 } else if (compose->mode == COMPOSE_REEDIT &&
3697 child->type == MIMETYPE_APPLICATION &&
3698 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3699 encrypted = (MimeInfo *)child->node->parent->data;
3702 child = (MimeInfo *) mimeinfo->node->children->data;
3703 while (child != NULL) {
3706 if (child == encrypted) {
3707 /* skip this part of tree */
3708 NEXT_PART_NOT_CHILD(child);
3712 if (child->type == MIMETYPE_MULTIPART) {
3713 /* get the actual content */
3714 child = procmime_mimeinfo_next(child);
3718 if (child == firsttext) {
3719 child = procmime_mimeinfo_next(child);
3723 outfile = procmime_get_tmp_file_name(child);
3724 if ((err = procmime_get_part(outfile, child)) < 0)
3725 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3727 gchar *content_type;
3729 content_type = procmime_get_content_type_str(child->type, child->subtype);
3731 /* if we meet a pgp signature, we don't attach it, but
3732 * we force signing. */
3733 if ((strcmp(content_type, "application/pgp-signature") &&
3734 strcmp(content_type, "application/pkcs7-signature") &&
3735 strcmp(content_type, "application/x-pkcs7-signature"))
3736 || compose->mode == COMPOSE_REDIRECT) {
3737 partname = procmime_mimeinfo_get_parameter(child, "filename");
3738 if (partname == NULL)
3739 partname = procmime_mimeinfo_get_parameter(child, "name");
3740 if (partname == NULL)
3742 compose_attach_append(compose, outfile,
3743 partname, content_type,
3744 procmime_mimeinfo_get_parameter(child, "charset"));
3746 compose_force_signing(compose, compose->account, NULL);
3748 g_free(content_type);
3751 NEXT_PART_NOT_CHILD(child);
3753 procmime_mimeinfo_free_all(mimeinfo);
3756 #undef NEXT_PART_NOT_CHILD
3761 WAIT_FOR_INDENT_CHAR,
3762 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3765 /* return indent length, we allow:
3766 indent characters followed by indent characters or spaces/tabs,
3767 alphabets and numbers immediately followed by indent characters,
3768 and the repeating sequences of the above
3769 If quote ends with multiple spaces, only the first one is included. */
3770 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3771 const GtkTextIter *start, gint *len)
3773 GtkTextIter iter = *start;
3777 IndentState state = WAIT_FOR_INDENT_CHAR;
3780 gint alnum_count = 0;
3781 gint space_count = 0;
3784 if (prefs_common.quote_chars == NULL) {
3788 while (!gtk_text_iter_ends_line(&iter)) {
3789 wc = gtk_text_iter_get_char(&iter);
3790 if (g_unichar_iswide(wc))
3792 clen = g_unichar_to_utf8(wc, ch);
3796 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3797 is_space = g_unichar_isspace(wc);
3799 if (state == WAIT_FOR_INDENT_CHAR) {
3800 if (!is_indent && !g_unichar_isalnum(wc))
3803 quote_len += alnum_count + space_count + 1;
3804 alnum_count = space_count = 0;
3805 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3808 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3809 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3813 else if (is_indent) {
3814 quote_len += alnum_count + space_count + 1;
3815 alnum_count = space_count = 0;
3818 state = WAIT_FOR_INDENT_CHAR;
3822 gtk_text_iter_forward_char(&iter);
3825 if (quote_len > 0 && space_count > 0)
3831 if (quote_len > 0) {
3833 gtk_text_iter_forward_chars(&iter, quote_len);
3834 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3840 /* return >0 if the line is itemized */
3841 static int compose_itemized_length(GtkTextBuffer *buffer,
3842 const GtkTextIter *start)
3844 GtkTextIter iter = *start;
3849 if (gtk_text_iter_ends_line(&iter))
3854 wc = gtk_text_iter_get_char(&iter);
3855 if (!g_unichar_isspace(wc))
3857 gtk_text_iter_forward_char(&iter);
3858 if (gtk_text_iter_ends_line(&iter))
3862 clen = g_unichar_to_utf8(wc, ch);
3866 if (!strchr("*-+", ch[0]))
3869 gtk_text_iter_forward_char(&iter);
3870 if (gtk_text_iter_ends_line(&iter))
3872 wc = gtk_text_iter_get_char(&iter);
3873 if (g_unichar_isspace(wc)) {
3879 /* return the string at the start of the itemization */
3880 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3881 const GtkTextIter *start)
3883 GtkTextIter iter = *start;
3886 GString *item_chars = g_string_new("");
3889 if (gtk_text_iter_ends_line(&iter))
3894 wc = gtk_text_iter_get_char(&iter);
3895 if (!g_unichar_isspace(wc))
3897 gtk_text_iter_forward_char(&iter);
3898 if (gtk_text_iter_ends_line(&iter))
3900 g_string_append_unichar(item_chars, wc);
3903 str = item_chars->str;
3904 g_string_free(item_chars, FALSE);
3908 /* return the number of spaces at a line's start */
3909 static int compose_left_offset_length(GtkTextBuffer *buffer,
3910 const GtkTextIter *start)
3912 GtkTextIter iter = *start;
3915 if (gtk_text_iter_ends_line(&iter))
3919 wc = gtk_text_iter_get_char(&iter);
3920 if (!g_unichar_isspace(wc))
3923 gtk_text_iter_forward_char(&iter);
3924 if (gtk_text_iter_ends_line(&iter))
3928 gtk_text_iter_forward_char(&iter);
3929 if (gtk_text_iter_ends_line(&iter))
3934 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3935 const GtkTextIter *start,
3936 GtkTextIter *break_pos,
3940 GtkTextIter iter = *start, line_end = *start;
3941 PangoLogAttr *attrs;
3948 gboolean can_break = FALSE;
3949 gboolean do_break = FALSE;
3950 gboolean was_white = FALSE;
3951 gboolean prev_dont_break = FALSE;
3953 gtk_text_iter_forward_to_line_end(&line_end);
3954 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3955 len = g_utf8_strlen(str, -1);
3959 g_warning("compose_get_line_break_pos: len = 0!\n");
3963 /* g_print("breaking line: %d: %s (len = %d)\n",
3964 gtk_text_iter_get_line(&iter), str, len); */
3966 attrs = g_new(PangoLogAttr, len + 1);
3968 pango_default_break(str, -1, NULL, attrs, len + 1);
3972 /* skip quote and leading spaces */
3973 for (i = 0; *p != '\0' && i < len; i++) {
3976 wc = g_utf8_get_char(p);
3977 if (i >= quote_len && !g_unichar_isspace(wc))
3979 if (g_unichar_iswide(wc))
3981 else if (*p == '\t')
3985 p = g_utf8_next_char(p);
3988 for (; *p != '\0' && i < len; i++) {
3989 PangoLogAttr *attr = attrs + i;
3993 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3996 was_white = attr->is_white;
3998 /* don't wrap URI */
3999 if ((uri_len = get_uri_len(p)) > 0) {
4001 if (pos > 0 && col > max_col) {
4011 wc = g_utf8_get_char(p);
4012 if (g_unichar_iswide(wc)) {
4014 if (prev_dont_break && can_break && attr->is_line_break)
4016 } else if (*p == '\t')
4020 if (pos > 0 && col > max_col) {
4025 if (*p == '-' || *p == '/')
4026 prev_dont_break = TRUE;
4028 prev_dont_break = FALSE;
4030 p = g_utf8_next_char(p);
4034 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4039 *break_pos = *start;
4040 gtk_text_iter_set_line_offset(break_pos, pos);
4045 static gboolean compose_join_next_line(Compose *compose,
4046 GtkTextBuffer *buffer,
4048 const gchar *quote_str)
4050 GtkTextIter iter_ = *iter, cur, prev, next, end;
4051 PangoLogAttr attrs[3];
4053 gchar *next_quote_str;
4056 gboolean keep_cursor = FALSE;
4058 if (!gtk_text_iter_forward_line(&iter_) ||
4059 gtk_text_iter_ends_line(&iter_)) {
4062 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4064 if ((quote_str || next_quote_str) &&
4065 strcmp2(quote_str, next_quote_str) != 0) {
4066 g_free(next_quote_str);
4069 g_free(next_quote_str);
4072 if (quote_len > 0) {
4073 gtk_text_iter_forward_chars(&end, quote_len);
4074 if (gtk_text_iter_ends_line(&end)) {
4079 /* don't join itemized lines */
4080 if (compose_itemized_length(buffer, &end) > 0) {
4084 /* don't join signature separator */
4085 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4088 /* delete quote str */
4090 gtk_text_buffer_delete(buffer, &iter_, &end);
4092 /* don't join line breaks put by the user */
4094 gtk_text_iter_backward_char(&cur);
4095 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4096 gtk_text_iter_forward_char(&cur);
4100 gtk_text_iter_forward_char(&cur);
4101 /* delete linebreak and extra spaces */
4102 while (gtk_text_iter_backward_char(&cur)) {
4103 wc1 = gtk_text_iter_get_char(&cur);
4104 if (!g_unichar_isspace(wc1))
4109 while (!gtk_text_iter_ends_line(&cur)) {
4110 wc1 = gtk_text_iter_get_char(&cur);
4111 if (!g_unichar_isspace(wc1))
4113 gtk_text_iter_forward_char(&cur);
4116 if (!gtk_text_iter_equal(&prev, &next)) {
4119 mark = gtk_text_buffer_get_insert(buffer);
4120 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4121 if (gtk_text_iter_equal(&prev, &cur))
4123 gtk_text_buffer_delete(buffer, &prev, &next);
4127 /* insert space if required */
4128 gtk_text_iter_backward_char(&prev);
4129 wc1 = gtk_text_iter_get_char(&prev);
4130 wc2 = gtk_text_iter_get_char(&next);
4131 gtk_text_iter_forward_char(&next);
4132 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4133 pango_default_break(str, -1, NULL, attrs, 3);
4134 if (!attrs[1].is_line_break ||
4135 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4136 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4138 gtk_text_iter_backward_char(&iter_);
4139 gtk_text_buffer_place_cursor(buffer, &iter_);
4148 #define ADD_TXT_POS(bp_, ep_, pti_) \
4149 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4150 last = last->next; \
4151 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4152 last->next = NULL; \
4154 g_warning("alloc error scanning URIs\n"); \
4157 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4159 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4160 GtkTextBuffer *buffer;
4161 GtkTextIter iter, break_pos, end_of_line;
4162 gchar *quote_str = NULL;
4164 gboolean wrap_quote = prefs_common.linewrap_quote;
4165 gboolean prev_autowrap = compose->autowrap;
4166 gint startq_offset = -1, noq_offset = -1;
4167 gint uri_start = -1, uri_stop = -1;
4168 gint nouri_start = -1, nouri_stop = -1;
4169 gint num_blocks = 0;
4170 gint quotelevel = -1;
4171 gboolean modified = force;
4172 gboolean removed = FALSE;
4173 gboolean modified_before_remove = FALSE;
4175 gboolean start = TRUE;
4176 gint itemized_len = 0, rem_item_len = 0;
4177 gchar *itemized_chars = NULL;
4178 gboolean item_continuation = FALSE;
4183 if (compose->draft_timeout_tag == -2) {
4187 compose->autowrap = FALSE;
4189 buffer = gtk_text_view_get_buffer(text);
4190 undo_wrapping(compose->undostruct, TRUE);
4195 mark = gtk_text_buffer_get_insert(buffer);
4196 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4200 if (compose->draft_timeout_tag == -2) {
4201 if (gtk_text_iter_ends_line(&iter)) {
4202 while (gtk_text_iter_ends_line(&iter) &&
4203 gtk_text_iter_forward_line(&iter))
4206 while (gtk_text_iter_backward_line(&iter)) {
4207 if (gtk_text_iter_ends_line(&iter)) {
4208 gtk_text_iter_forward_line(&iter);
4214 /* move to line start */
4215 gtk_text_iter_set_line_offset(&iter, 0);
4218 itemized_len = compose_itemized_length(buffer, &iter);
4220 if (!itemized_len) {
4221 itemized_len = compose_left_offset_length(buffer, &iter);
4222 item_continuation = TRUE;
4226 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4228 /* go until paragraph end (empty line) */
4229 while (start || !gtk_text_iter_ends_line(&iter)) {
4230 gchar *scanpos = NULL;
4231 /* parse table - in order of priority */
4233 const gchar *needle; /* token */
4235 /* token search function */
4236 gchar *(*search) (const gchar *haystack,
4237 const gchar *needle);
4238 /* part parsing function */
4239 gboolean (*parse) (const gchar *start,
4240 const gchar *scanpos,
4244 /* part to URI function */
4245 gchar *(*build_uri) (const gchar *bp,
4249 static struct table parser[] = {
4250 {"http://", strcasestr, get_uri_part, make_uri_string},
4251 {"https://", strcasestr, get_uri_part, make_uri_string},
4252 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4253 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4254 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4255 {"www.", strcasestr, get_uri_part, make_http_string},
4256 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4257 {"@", strcasestr, get_email_part, make_email_string}
4259 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4260 gint last_index = PARSE_ELEMS;
4262 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4266 if (!prev_autowrap && num_blocks == 0) {
4268 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4269 G_CALLBACK(text_inserted),
4272 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4275 uri_start = uri_stop = -1;
4277 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4280 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4281 if (startq_offset == -1)
4282 startq_offset = gtk_text_iter_get_offset(&iter);
4283 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4284 if (quotelevel > 2) {
4285 /* recycle colors */
4286 if (prefs_common.recycle_quote_colors)
4295 if (startq_offset == -1)
4296 noq_offset = gtk_text_iter_get_offset(&iter);
4300 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4303 if (gtk_text_iter_ends_line(&iter)) {
4305 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4306 prefs_common.linewrap_len,
4308 GtkTextIter prev, next, cur;
4309 if (prev_autowrap != FALSE || force) {
4310 compose->automatic_break = TRUE;
4312 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4313 compose->automatic_break = FALSE;
4314 if (itemized_len && compose->autoindent) {
4315 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4316 if (!item_continuation)
4317 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4319 } else if (quote_str && wrap_quote) {
4320 compose->automatic_break = TRUE;
4322 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4323 compose->automatic_break = FALSE;
4324 if (itemized_len && compose->autoindent) {
4325 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4326 if (!item_continuation)
4327 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4331 /* remove trailing spaces */
4333 rem_item_len = itemized_len;
4334 while (compose->autoindent && rem_item_len-- > 0)
4335 gtk_text_iter_backward_char(&cur);
4336 gtk_text_iter_backward_char(&cur);
4339 while (!gtk_text_iter_starts_line(&cur)) {
4342 gtk_text_iter_backward_char(&cur);
4343 wc = gtk_text_iter_get_char(&cur);
4344 if (!g_unichar_isspace(wc))
4348 if (!gtk_text_iter_equal(&prev, &next)) {
4349 gtk_text_buffer_delete(buffer, &prev, &next);
4351 gtk_text_iter_forward_char(&break_pos);
4355 gtk_text_buffer_insert(buffer, &break_pos,
4359 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4361 /* move iter to current line start */
4362 gtk_text_iter_set_line_offset(&iter, 0);
4369 /* move iter to next line start */
4375 if (!prev_autowrap && num_blocks > 0) {
4377 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4378 G_CALLBACK(text_inserted),
4382 while (!gtk_text_iter_ends_line(&end_of_line)) {
4383 gtk_text_iter_forward_char(&end_of_line);
4385 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4387 nouri_start = gtk_text_iter_get_offset(&iter);
4388 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4390 walk_pos = gtk_text_iter_get_offset(&iter);
4391 /* FIXME: this looks phony. scanning for anything in the parse table */
4392 for (n = 0; n < PARSE_ELEMS; n++) {
4395 tmp = parser[n].search(walk, parser[n].needle);
4397 if (scanpos == NULL || tmp < scanpos) {
4406 /* check if URI can be parsed */
4407 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4408 (const gchar **)&ep, FALSE)
4409 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4413 strlen(parser[last_index].needle);
4416 uri_start = walk_pos + (bp - o_walk);
4417 uri_stop = walk_pos + (ep - o_walk);
4421 gtk_text_iter_forward_line(&iter);
4424 if (startq_offset != -1) {
4425 GtkTextIter startquote, endquote;
4426 gtk_text_buffer_get_iter_at_offset(
4427 buffer, &startquote, startq_offset);
4430 switch (quotelevel) {
4432 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4433 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4434 gtk_text_buffer_apply_tag_by_name(
4435 buffer, "quote0", &startquote, &endquote);
4436 gtk_text_buffer_remove_tag_by_name(
4437 buffer, "quote1", &startquote, &endquote);
4438 gtk_text_buffer_remove_tag_by_name(
4439 buffer, "quote2", &startquote, &endquote);
4444 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4445 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4446 gtk_text_buffer_apply_tag_by_name(
4447 buffer, "quote1", &startquote, &endquote);
4448 gtk_text_buffer_remove_tag_by_name(
4449 buffer, "quote0", &startquote, &endquote);
4450 gtk_text_buffer_remove_tag_by_name(
4451 buffer, "quote2", &startquote, &endquote);
4456 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4457 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4458 gtk_text_buffer_apply_tag_by_name(
4459 buffer, "quote2", &startquote, &endquote);
4460 gtk_text_buffer_remove_tag_by_name(
4461 buffer, "quote0", &startquote, &endquote);
4462 gtk_text_buffer_remove_tag_by_name(
4463 buffer, "quote1", &startquote, &endquote);
4469 } else if (noq_offset != -1) {
4470 GtkTextIter startnoquote, endnoquote;
4471 gtk_text_buffer_get_iter_at_offset(
4472 buffer, &startnoquote, noq_offset);
4475 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4476 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4477 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4478 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4479 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4480 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4481 gtk_text_buffer_remove_tag_by_name(
4482 buffer, "quote0", &startnoquote, &endnoquote);
4483 gtk_text_buffer_remove_tag_by_name(
4484 buffer, "quote1", &startnoquote, &endnoquote);
4485 gtk_text_buffer_remove_tag_by_name(
4486 buffer, "quote2", &startnoquote, &endnoquote);
4492 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4493 GtkTextIter nouri_start_iter, nouri_end_iter;
4494 gtk_text_buffer_get_iter_at_offset(
4495 buffer, &nouri_start_iter, nouri_start);
4496 gtk_text_buffer_get_iter_at_offset(
4497 buffer, &nouri_end_iter, nouri_stop);
4498 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4499 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4500 gtk_text_buffer_remove_tag_by_name(
4501 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4502 modified_before_remove = modified;
4507 if (uri_start >= 0 && uri_stop > 0) {
4508 GtkTextIter uri_start_iter, uri_end_iter, back;
4509 gtk_text_buffer_get_iter_at_offset(
4510 buffer, &uri_start_iter, uri_start);
4511 gtk_text_buffer_get_iter_at_offset(
4512 buffer, &uri_end_iter, uri_stop);
4513 back = uri_end_iter;
4514 gtk_text_iter_backward_char(&back);
4515 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4516 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4517 gtk_text_buffer_apply_tag_by_name(
4518 buffer, "link", &uri_start_iter, &uri_end_iter);
4520 if (removed && !modified_before_remove) {
4526 // debug_print("not modified, out after %d lines\n", lines);
4530 // debug_print("modified, out after %d lines\n", lines);
4532 g_free(itemized_chars);
4535 undo_wrapping(compose->undostruct, FALSE);
4536 compose->autowrap = prev_autowrap;
4541 void compose_action_cb(void *data)
4543 Compose *compose = (Compose *)data;
4544 compose_wrap_all(compose);
4547 static void compose_wrap_all(Compose *compose)
4549 compose_wrap_all_full(compose, FALSE);
4552 static void compose_wrap_all_full(Compose *compose, gboolean force)
4554 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4555 GtkTextBuffer *buffer;
4557 gboolean modified = TRUE;
4559 buffer = gtk_text_view_get_buffer(text);
4561 gtk_text_buffer_get_start_iter(buffer, &iter);
4562 while (!gtk_text_iter_is_end(&iter) && modified)
4563 modified = compose_beautify_paragraph(compose, &iter, force);
4567 static void compose_set_title(Compose *compose)
4573 edited = compose->modified ? _(" [Edited]") : "";
4575 subject = gtk_editable_get_chars(
4576 GTK_EDITABLE(compose->subject_entry), 0, -1);
4578 #ifndef GENERIC_UMPC
4579 if (subject && strlen(subject))
4580 str = g_strdup_printf(_("%s - Compose message%s"),
4583 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4585 str = g_strdup(_("Compose message"));
4588 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4594 * compose_current_mail_account:
4596 * Find a current mail account (the currently selected account, or the
4597 * default account, if a news account is currently selected). If a
4598 * mail account cannot be found, display an error message.
4600 * Return value: Mail account, or NULL if not found.
4602 static PrefsAccount *
4603 compose_current_mail_account(void)
4607 if (cur_account && cur_account->protocol != A_NNTP)
4610 ac = account_get_default();
4611 if (!ac || ac->protocol == A_NNTP) {
4612 alertpanel_error(_("Account for sending mail is not specified.\n"
4613 "Please select a mail account before sending."));
4620 #define QUOTE_IF_REQUIRED(out, str) \
4622 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4626 len = strlen(str) + 3; \
4627 if ((__tmp = alloca(len)) == NULL) { \
4628 g_warning("can't allocate memory\n"); \
4629 g_string_free(header, TRUE); \
4632 g_snprintf(__tmp, len, "\"%s\"", str); \
4637 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4638 g_warning("can't allocate memory\n"); \
4639 g_string_free(header, TRUE); \
4642 strcpy(__tmp, str); \
4648 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4650 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4654 len = strlen(str) + 3; \
4655 if ((__tmp = alloca(len)) == NULL) { \
4656 g_warning("can't allocate memory\n"); \
4659 g_snprintf(__tmp, len, "\"%s\"", str); \
4664 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4665 g_warning("can't allocate memory\n"); \
4668 strcpy(__tmp, str); \
4674 static void compose_select_account(Compose *compose, PrefsAccount *account,
4677 gchar *from = NULL, *header;
4678 ComposeHeaderEntry *header_entry;
4680 cm_return_if_fail(account != NULL);
4682 compose->account = account;
4683 if (account->name && *account->name) {
4685 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4686 from = g_strdup_printf("%s <%s>",
4687 buf, account->address);
4688 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4690 from = g_strdup_printf("<%s>",
4692 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4697 compose_set_title(compose);
4699 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4700 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4702 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4703 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4704 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4706 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4708 activate_privacy_system(compose, account, FALSE);
4710 if (!init && compose->mode != COMPOSE_REDIRECT) {
4711 undo_block(compose->undostruct);
4712 compose_insert_sig(compose, TRUE);
4713 undo_unblock(compose->undostruct);
4716 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4717 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4719 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4720 if (account->protocol == A_NNTP) {
4721 if (!strcmp(header, _("To:")))
4722 combobox_select_by_text(
4723 GTK_COMBO_BOX(header_entry->combo),
4726 if (!strcmp(header, _("Newsgroups:")))
4727 combobox_select_by_text(
4728 GTK_COMBO_BOX(header_entry->combo),
4736 /* use account's dict info if set */
4737 if (compose->gtkaspell) {
4738 if (account->enable_default_dictionary)
4739 gtkaspell_change_dict(compose->gtkaspell,
4740 account->default_dictionary, FALSE);
4741 if (account->enable_default_alt_dictionary)
4742 gtkaspell_change_alt_dict(compose->gtkaspell,
4743 account->default_alt_dictionary);
4744 if (account->enable_default_dictionary
4745 || account->enable_default_alt_dictionary)
4746 compose_spell_menu_changed(compose);
4751 gboolean compose_check_for_valid_recipient(Compose *compose) {
4752 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4753 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4754 gboolean recipient_found = FALSE;
4758 /* free to and newsgroup list */
4759 slist_free_strings(compose->to_list);
4760 g_slist_free(compose->to_list);
4761 compose->to_list = NULL;
4763 slist_free_strings(compose->newsgroup_list);
4764 g_slist_free(compose->newsgroup_list);
4765 compose->newsgroup_list = NULL;
4767 /* search header entries for to and newsgroup entries */
4768 for (list = compose->header_list; list; list = list->next) {
4771 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4772 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4775 if (entry[0] != '\0') {
4776 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4777 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4778 compose->to_list = address_list_append(compose->to_list, entry);
4779 recipient_found = TRUE;
4782 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4783 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4784 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4785 recipient_found = TRUE;
4792 return recipient_found;
4795 static gboolean compose_check_for_set_recipients(Compose *compose)
4797 if (compose->account->set_autocc && compose->account->auto_cc) {
4798 gboolean found_other = FALSE;
4800 /* search header entries for to and newsgroup entries */
4801 for (list = compose->header_list; list; list = list->next) {
4804 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4805 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4808 if (strcmp(entry, compose->account->auto_cc)
4809 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4819 if (compose->batch) {
4820 gtk_widget_show_all(compose->window);
4822 aval = alertpanel(_("Send"),
4823 _("The only recipient is the default CC address. Send anyway?"),
4824 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4825 if (aval != G_ALERTALTERNATE)
4829 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4830 gboolean found_other = FALSE;
4832 /* search header entries for to and newsgroup entries */
4833 for (list = compose->header_list; list; list = list->next) {
4836 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4837 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4840 if (strcmp(entry, compose->account->auto_bcc)
4841 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4851 if (compose->batch) {
4852 gtk_widget_show_all(compose->window);
4854 aval = alertpanel(_("Send"),
4855 _("The only recipient is the default BCC address. Send anyway?"),
4856 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4857 if (aval != G_ALERTALTERNATE)
4864 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4868 if (compose_check_for_valid_recipient(compose) == FALSE) {
4869 if (compose->batch) {
4870 gtk_widget_show_all(compose->window);
4872 alertpanel_error(_("Recipient is not specified."));
4876 if (compose_check_for_set_recipients(compose) == FALSE) {
4880 if (!compose->batch) {
4881 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4882 if (*str == '\0' && check_everything == TRUE &&
4883 compose->mode != COMPOSE_REDIRECT) {
4885 gchar *button_label;
4888 if (compose->sending)
4889 button_label = _("+_Send");
4891 button_label = _("+_Queue");
4892 message = g_strdup_printf(_("Subject is empty. %s"),
4893 compose->sending?_("Send it anyway?"):
4894 _("Queue it anyway?"));
4896 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4897 GTK_STOCK_CANCEL, button_label, NULL);
4899 if (aval != G_ALERTALTERNATE)
4904 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4910 gint compose_send(Compose *compose)
4913 FolderItem *folder = NULL;
4915 gchar *msgpath = NULL;
4916 gboolean discard_window = FALSE;
4917 gchar *errstr = NULL;
4918 gchar *tmsgid = NULL;
4919 MainWindow *mainwin = mainwindow_get_mainwindow();
4920 gboolean queued_removed = FALSE;
4922 if (prefs_common.send_dialog_invisible
4923 || compose->batch == TRUE)
4924 discard_window = TRUE;
4926 compose_allow_user_actions (compose, FALSE);
4927 compose->sending = TRUE;
4929 if (compose_check_entries(compose, TRUE) == FALSE) {
4930 if (compose->batch) {
4931 gtk_widget_show_all(compose->window);
4937 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4940 if (compose->batch) {
4941 gtk_widget_show_all(compose->window);
4944 alertpanel_error(_("Could not queue message for sending:\n\n"
4945 "Charset conversion failed."));
4946 } else if (val == -5) {
4947 alertpanel_error(_("Could not queue message for sending:\n\n"
4948 "Couldn't get recipient encryption key."));
4949 } else if (val == -6) {
4951 } else if (val == -3) {
4952 if (privacy_peek_error())
4953 alertpanel_error(_("Could not queue message for sending:\n\n"
4954 "Signature failed: %s"), privacy_get_error());
4955 } else if (val == -2 && errno != 0) {
4956 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4958 alertpanel_error(_("Could not queue message for sending."));
4963 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4964 if (discard_window) {
4965 compose->sending = FALSE;
4966 compose_close(compose);
4967 /* No more compose access in the normal codepath
4968 * after this point! */
4973 alertpanel_error(_("The message was queued but could not be "
4974 "sent.\nUse \"Send queued messages\" from "
4975 "the main window to retry."));
4976 if (!discard_window) {
4983 if (msgpath == NULL) {
4984 msgpath = folder_item_fetch_msg(folder, msgnum);
4985 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4988 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4989 claws_unlink(msgpath);
4992 if (!discard_window) {
4994 if (!queued_removed)
4995 folder_item_remove_msg(folder, msgnum);
4996 folder_item_scan(folder);
4998 /* make sure we delete that */
4999 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5001 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5002 folder_item_remove_msg(folder, tmp->msgnum);
5003 procmsg_msginfo_free(tmp);
5010 if (!queued_removed)
5011 folder_item_remove_msg(folder, msgnum);
5012 folder_item_scan(folder);
5014 /* make sure we delete that */
5015 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5017 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5018 folder_item_remove_msg(folder, tmp->msgnum);
5019 procmsg_msginfo_free(tmp);
5022 if (!discard_window) {
5023 compose->sending = FALSE;
5024 compose_allow_user_actions (compose, TRUE);
5025 compose_close(compose);
5029 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5030 "the main window to retry."), errstr);
5033 alertpanel_error_log(_("The message was queued but could not be "
5034 "sent.\nUse \"Send queued messages\" from "
5035 "the main window to retry."));
5037 if (!discard_window) {
5046 toolbar_main_set_sensitive(mainwin);
5047 main_window_set_menu_sensitive(mainwin);
5053 compose_allow_user_actions (compose, TRUE);
5054 compose->sending = FALSE;
5055 compose->modified = TRUE;
5056 toolbar_main_set_sensitive(mainwin);
5057 main_window_set_menu_sensitive(mainwin);
5062 static gboolean compose_use_attach(Compose *compose)
5064 GtkTreeModel *model = gtk_tree_view_get_model
5065 (GTK_TREE_VIEW(compose->attach_clist));
5066 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5069 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5072 gchar buf[BUFFSIZE];
5074 gboolean first_to_address;
5075 gboolean first_cc_address;
5077 ComposeHeaderEntry *headerentry;
5078 const gchar *headerentryname;
5079 const gchar *cc_hdr;
5080 const gchar *to_hdr;
5081 gboolean err = FALSE;
5083 debug_print("Writing redirect header\n");
5085 cc_hdr = prefs_common_translated_header_name("Cc:");
5086 to_hdr = prefs_common_translated_header_name("To:");
5088 first_to_address = TRUE;
5089 for (list = compose->header_list; list; list = list->next) {
5090 headerentry = ((ComposeHeaderEntry *)list->data);
5091 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5093 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5094 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5095 Xstrdup_a(str, entstr, return -1);
5097 if (str[0] != '\0') {
5098 compose_convert_header
5099 (compose, buf, sizeof(buf), str,
5100 strlen("Resent-To") + 2, TRUE);
5102 if (first_to_address) {
5103 err |= (fprintf(fp, "Resent-To: ") < 0);
5104 first_to_address = FALSE;
5106 err |= (fprintf(fp, ",") < 0);
5108 err |= (fprintf(fp, "%s", buf) < 0);
5112 if (!first_to_address) {
5113 err |= (fprintf(fp, "\n") < 0);
5116 first_cc_address = TRUE;
5117 for (list = compose->header_list; list; list = list->next) {
5118 headerentry = ((ComposeHeaderEntry *)list->data);
5119 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5121 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5122 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5123 Xstrdup_a(str, strg, return -1);
5125 if (str[0] != '\0') {
5126 compose_convert_header
5127 (compose, buf, sizeof(buf), str,
5128 strlen("Resent-Cc") + 2, TRUE);
5130 if (first_cc_address) {
5131 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5132 first_cc_address = FALSE;
5134 err |= (fprintf(fp, ",") < 0);
5136 err |= (fprintf(fp, "%s", buf) < 0);
5140 if (!first_cc_address) {
5141 err |= (fprintf(fp, "\n") < 0);
5144 return (err ? -1:0);
5147 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5149 gchar buf[BUFFSIZE];
5151 const gchar *entstr;
5152 /* struct utsname utsbuf; */
5153 gboolean err = FALSE;
5155 cm_return_val_if_fail(fp != NULL, -1);
5156 cm_return_val_if_fail(compose->account != NULL, -1);
5157 cm_return_val_if_fail(compose->account->address != NULL, -1);
5160 get_rfc822_date(buf, sizeof(buf));
5161 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5164 if (compose->account->name && *compose->account->name) {
5165 compose_convert_header
5166 (compose, buf, sizeof(buf), compose->account->name,
5167 strlen("From: "), TRUE);
5168 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5169 buf, compose->account->address) < 0);
5171 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5174 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5175 if (*entstr != '\0') {
5176 Xstrdup_a(str, entstr, return -1);
5179 compose_convert_header(compose, buf, sizeof(buf), str,
5180 strlen("Subject: "), FALSE);
5181 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5185 /* Resent-Message-ID */
5186 if (compose->account->set_domain && compose->account->domain) {
5187 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5188 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5189 g_snprintf(buf, sizeof(buf), "%s",
5190 strchr(compose->account->address, '@') ?
5191 strchr(compose->account->address, '@')+1 :
5192 compose->account->address);
5194 g_snprintf(buf, sizeof(buf), "%s", "");
5197 if (compose->account->gen_msgid) {
5199 if (compose->account->msgid_with_addr) {
5200 addr = compose->account->address;
5202 generate_msgid(buf, sizeof(buf), addr);
5203 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5204 compose->msgid = g_strdup(buf);
5206 compose->msgid = NULL;
5209 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5212 /* separator between header and body */
5213 err |= (fputs("\n", fp) == EOF);
5215 return (err ? -1:0);
5218 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5222 gchar buf[BUFFSIZE];
5224 gboolean skip = FALSE;
5225 gboolean err = FALSE;
5226 gchar *not_included[]={
5227 "Return-Path:", "Delivered-To:", "Received:",
5228 "Subject:", "X-UIDL:", "AF:",
5229 "NF:", "PS:", "SRH:",
5230 "SFN:", "DSR:", "MID:",
5231 "CFG:", "PT:", "S:",
5232 "RQ:", "SSV:", "NSV:",
5233 "SSH:", "R:", "MAID:",
5234 "NAID:", "RMID:", "FMID:",
5235 "SCF:", "RRCPT:", "NG:",
5236 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5237 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5238 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5239 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5240 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5243 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5244 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5248 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5250 for (i = 0; not_included[i] != NULL; i++) {
5251 if (g_ascii_strncasecmp(buf, not_included[i],
5252 strlen(not_included[i])) == 0) {
5259 if (fputs(buf, fdest) == -1)
5262 if (!prefs_common.redirect_keep_from) {
5263 if (g_ascii_strncasecmp(buf, "From:",
5264 strlen("From:")) == 0) {
5265 err |= (fputs(" (by way of ", fdest) == EOF);
5266 if (compose->account->name
5267 && *compose->account->name) {
5268 compose_convert_header
5269 (compose, buf, sizeof(buf),
5270 compose->account->name,
5273 err |= (fprintf(fdest, "%s <%s>",
5275 compose->account->address) < 0);
5277 err |= (fprintf(fdest, "%s",
5278 compose->account->address) < 0);
5279 err |= (fputs(")", fdest) == EOF);
5283 if (fputs("\n", fdest) == -1)
5290 if (compose_redirect_write_headers(compose, fdest))
5293 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5294 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5307 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5309 GtkTextBuffer *buffer;
5310 GtkTextIter start, end;
5313 const gchar *out_codeset;
5314 EncodingType encoding = ENC_UNKNOWN;
5315 MimeInfo *mimemsg, *mimetext;
5317 const gchar *src_codeset = CS_INTERNAL;
5318 gchar *from_addr = NULL;
5319 gchar *from_name = NULL;
5321 if (action == COMPOSE_WRITE_FOR_SEND)
5322 attach_parts = TRUE;
5324 /* create message MimeInfo */
5325 mimemsg = procmime_mimeinfo_new();
5326 mimemsg->type = MIMETYPE_MESSAGE;
5327 mimemsg->subtype = g_strdup("rfc822");
5328 mimemsg->content = MIMECONTENT_MEM;
5329 mimemsg->tmp = TRUE; /* must free content later */
5330 mimemsg->data.mem = compose_get_header(compose);
5332 /* Create text part MimeInfo */
5333 /* get all composed text */
5334 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5335 gtk_text_buffer_get_start_iter(buffer, &start);
5336 gtk_text_buffer_get_end_iter(buffer, &end);
5337 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5339 out_codeset = conv_get_charset_str(compose->out_encoding);
5341 if (!out_codeset && is_ascii_str(chars)) {
5342 out_codeset = CS_US_ASCII;
5343 } else if (prefs_common.outgoing_fallback_to_ascii &&
5344 is_ascii_str(chars)) {
5345 out_codeset = CS_US_ASCII;
5346 encoding = ENC_7BIT;
5350 gchar *test_conv_global_out = NULL;
5351 gchar *test_conv_reply = NULL;
5353 /* automatic mode. be automatic. */
5354 codeconv_set_strict(TRUE);
5356 out_codeset = conv_get_outgoing_charset_str();
5358 debug_print("trying to convert to %s\n", out_codeset);
5359 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5362 if (!test_conv_global_out && compose->orig_charset
5363 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5364 out_codeset = compose->orig_charset;
5365 debug_print("failure; trying to convert to %s\n", out_codeset);
5366 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5369 if (!test_conv_global_out && !test_conv_reply) {
5371 out_codeset = CS_INTERNAL;
5372 debug_print("failure; finally using %s\n", out_codeset);
5374 g_free(test_conv_global_out);
5375 g_free(test_conv_reply);
5376 codeconv_set_strict(FALSE);
5379 if (encoding == ENC_UNKNOWN) {
5380 if (prefs_common.encoding_method == CTE_BASE64)
5381 encoding = ENC_BASE64;
5382 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5383 encoding = ENC_QUOTED_PRINTABLE;
5384 else if (prefs_common.encoding_method == CTE_8BIT)
5385 encoding = ENC_8BIT;
5387 encoding = procmime_get_encoding_for_charset(out_codeset);
5390 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5391 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5393 if (action == COMPOSE_WRITE_FOR_SEND) {
5394 codeconv_set_strict(TRUE);
5395 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5396 codeconv_set_strict(FALSE);
5402 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5403 "to the specified %s charset.\n"
5404 "Send it as %s?"), out_codeset, src_codeset);
5405 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5406 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5409 if (aval != G_ALERTALTERNATE) {
5414 out_codeset = src_codeset;
5420 out_codeset = src_codeset;
5425 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5426 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5427 strstr(buf, "\nFrom ") != NULL) {
5428 encoding = ENC_QUOTED_PRINTABLE;
5432 mimetext = procmime_mimeinfo_new();
5433 mimetext->content = MIMECONTENT_MEM;
5434 mimetext->tmp = TRUE; /* must free content later */
5435 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5436 * and free the data, which we need later. */
5437 mimetext->data.mem = g_strdup(buf);
5438 mimetext->type = MIMETYPE_TEXT;
5439 mimetext->subtype = g_strdup("plain");
5440 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5441 g_strdup(out_codeset));
5443 /* protect trailing spaces when signing message */
5444 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5445 privacy_system_can_sign(compose->privacy_system)) {
5446 encoding = ENC_QUOTED_PRINTABLE;
5449 debug_print("main text: %zd bytes encoded as %s in %d\n",
5450 strlen(buf), out_codeset, encoding);
5452 /* check for line length limit */
5453 if (action == COMPOSE_WRITE_FOR_SEND &&
5454 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5455 check_line_length(buf, 1000, &line) < 0) {
5459 msg = g_strdup_printf
5460 (_("Line %d exceeds the line length limit (998 bytes).\n"
5461 "The contents of the message might be broken on the way to the delivery.\n"
5463 "Send it anyway?"), line + 1);
5464 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5466 if (aval != G_ALERTALTERNATE) {
5472 if (encoding != ENC_UNKNOWN)
5473 procmime_encode_content(mimetext, encoding);
5475 /* append attachment parts */
5476 if (compose_use_attach(compose) && attach_parts) {
5477 MimeInfo *mimempart;
5478 gchar *boundary = NULL;
5479 mimempart = procmime_mimeinfo_new();
5480 mimempart->content = MIMECONTENT_EMPTY;
5481 mimempart->type = MIMETYPE_MULTIPART;
5482 mimempart->subtype = g_strdup("mixed");
5486 boundary = generate_mime_boundary(NULL);
5487 } while (strstr(buf, boundary) != NULL);
5489 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5492 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5494 g_node_append(mimempart->node, mimetext->node);
5495 g_node_append(mimemsg->node, mimempart->node);
5497 if (compose_add_attachments(compose, mimempart) < 0)
5500 g_node_append(mimemsg->node, mimetext->node);
5504 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5505 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5506 /* extract name and address */
5507 if (strstr(spec, " <") && strstr(spec, ">")) {
5508 from_addr = g_strdup(strrchr(spec, '<')+1);
5509 *(strrchr(from_addr, '>')) = '\0';
5510 from_name = g_strdup(spec);
5511 *(strrchr(from_name, '<')) = '\0';
5518 /* sign message if sending */
5519 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5520 privacy_system_can_sign(compose->privacy_system))
5521 if (!privacy_sign(compose->privacy_system, mimemsg,
5522 compose->account, from_addr)) {
5529 procmime_write_mimeinfo(mimemsg, fp);
5531 procmime_mimeinfo_free_all(mimemsg);
5536 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5538 GtkTextBuffer *buffer;
5539 GtkTextIter start, end;
5544 if ((fp = g_fopen(file, "wb")) == NULL) {
5545 FILE_OP_ERROR(file, "fopen");
5549 /* chmod for security */
5550 if (change_file_mode_rw(fp, file) < 0) {
5551 FILE_OP_ERROR(file, "chmod");
5552 g_warning("can't change file mode\n");
5555 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5556 gtk_text_buffer_get_start_iter(buffer, &start);
5557 gtk_text_buffer_get_end_iter(buffer, &end);
5558 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5560 chars = conv_codeset_strdup
5561 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5564 if (!chars) return -1;
5567 len = strlen(chars);
5568 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5569 FILE_OP_ERROR(file, "fwrite");
5578 if (fclose(fp) == EOF) {
5579 FILE_OP_ERROR(file, "fclose");
5586 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5589 MsgInfo *msginfo = compose->targetinfo;
5591 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5592 if (!msginfo) return -1;
5594 if (!force && MSG_IS_LOCKED(msginfo->flags))
5597 item = msginfo->folder;
5598 cm_return_val_if_fail(item != NULL, -1);
5600 if (procmsg_msg_exist(msginfo) &&
5601 (folder_has_parent_of_type(item, F_QUEUE) ||
5602 folder_has_parent_of_type(item, F_DRAFT)
5603 || msginfo == compose->autosaved_draft)) {
5604 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5605 g_warning("can't remove the old message\n");
5608 debug_print("removed reedit target %d\n", msginfo->msgnum);
5615 static void compose_remove_draft(Compose *compose)
5618 MsgInfo *msginfo = compose->targetinfo;
5619 drafts = account_get_special_folder(compose->account, F_DRAFT);
5621 if (procmsg_msg_exist(msginfo)) {
5622 folder_item_remove_msg(drafts, msginfo->msgnum);
5627 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5628 gboolean remove_reedit_target)
5630 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5633 static gboolean compose_warn_encryption(Compose *compose)
5635 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5636 AlertValue val = G_ALERTALTERNATE;
5638 if (warning == NULL)
5641 val = alertpanel_full(_("Encryption warning"), warning,
5642 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5643 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5644 if (val & G_ALERTDISABLE) {
5645 val &= ~G_ALERTDISABLE;
5646 if (val == G_ALERTALTERNATE)
5647 privacy_inhibit_encrypt_warning(compose->privacy_system,
5651 if (val == G_ALERTALTERNATE) {
5658 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5659 gchar **msgpath, gboolean check_subject,
5660 gboolean remove_reedit_target)
5667 static gboolean lock = FALSE;
5668 PrefsAccount *mailac = NULL, *newsac = NULL;
5669 gboolean err = FALSE;
5671 debug_print("queueing message...\n");
5672 cm_return_val_if_fail(compose->account != NULL, -1);
5676 if (compose_check_entries(compose, check_subject) == FALSE) {
5678 if (compose->batch) {
5679 gtk_widget_show_all(compose->window);
5684 if (!compose->to_list && !compose->newsgroup_list) {
5685 g_warning("can't get recipient list.");
5690 if (compose->to_list) {
5691 if (compose->account->protocol != A_NNTP)
5692 mailac = compose->account;
5693 else if (cur_account && cur_account->protocol != A_NNTP)
5694 mailac = cur_account;
5695 else if (!(mailac = compose_current_mail_account())) {
5697 alertpanel_error(_("No account for sending mails available!"));
5702 if (compose->newsgroup_list) {
5703 if (compose->account->protocol == A_NNTP)
5704 newsac = compose->account;
5707 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5712 /* write queue header */
5713 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5714 G_DIR_SEPARATOR, compose, (guint) rand());
5715 debug_print("queuing to %s\n", tmp);
5716 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5717 FILE_OP_ERROR(tmp, "fopen");
5723 if (change_file_mode_rw(fp, tmp) < 0) {
5724 FILE_OP_ERROR(tmp, "chmod");
5725 g_warning("can't change file mode\n");
5728 /* queueing variables */
5729 err |= (fprintf(fp, "AF:\n") < 0);
5730 err |= (fprintf(fp, "NF:0\n") < 0);
5731 err |= (fprintf(fp, "PS:10\n") < 0);
5732 err |= (fprintf(fp, "SRH:1\n") < 0);
5733 err |= (fprintf(fp, "SFN:\n") < 0);
5734 err |= (fprintf(fp, "DSR:\n") < 0);
5736 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5738 err |= (fprintf(fp, "MID:\n") < 0);
5739 err |= (fprintf(fp, "CFG:\n") < 0);
5740 err |= (fprintf(fp, "PT:0\n") < 0);
5741 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5742 err |= (fprintf(fp, "RQ:\n") < 0);
5744 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5746 err |= (fprintf(fp, "SSV:\n") < 0);
5748 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5750 err |= (fprintf(fp, "NSV:\n") < 0);
5751 err |= (fprintf(fp, "SSH:\n") < 0);
5752 /* write recepient list */
5753 if (compose->to_list) {
5754 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5755 for (cur = compose->to_list->next; cur != NULL;
5757 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5758 err |= (fprintf(fp, "\n") < 0);
5760 /* write newsgroup list */
5761 if (compose->newsgroup_list) {
5762 err |= (fprintf(fp, "NG:") < 0);
5763 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5764 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5765 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5766 err |= (fprintf(fp, "\n") < 0);
5768 /* Sylpheed account IDs */
5770 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5772 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5775 if (compose->privacy_system != NULL) {
5776 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5777 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5778 if (compose->use_encryption) {
5780 if (!compose_warn_encryption(compose)) {
5787 if (mailac && mailac->encrypt_to_self) {
5788 GSList *tmp_list = g_slist_copy(compose->to_list);
5789 tmp_list = g_slist_append(tmp_list, compose->account->address);
5790 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5791 g_slist_free(tmp_list);
5793 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5795 if (encdata != NULL) {
5796 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5797 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5798 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5800 } /* else we finally dont want to encrypt */
5802 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5803 /* and if encdata was null, it means there's been a problem in
5815 /* Save copy folder */
5816 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5817 gchar *savefolderid;
5819 savefolderid = compose_get_save_to(compose);
5820 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5821 g_free(savefolderid);
5823 /* Save copy folder */
5824 if (compose->return_receipt) {
5825 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5827 /* Message-ID of message replying to */
5828 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5831 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5832 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5835 /* Message-ID of message forwarding to */
5836 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5839 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5840 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5844 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5845 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5847 /* end of headers */
5848 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5850 if (compose->redirect_filename != NULL) {
5851 if (compose_redirect_write_to_file(compose, fp) < 0) {
5860 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5865 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5869 g_warning("failed to write queue message\n");
5876 if (fclose(fp) == EOF) {
5877 FILE_OP_ERROR(tmp, "fclose");
5884 if (item && *item) {
5887 queue = account_get_special_folder(compose->account, F_QUEUE);
5890 g_warning("can't find queue folder\n");
5896 folder_item_scan(queue);
5897 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5898 g_warning("can't queue the message\n");
5905 if (msgpath == NULL) {
5911 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5912 compose_remove_reedit_target(compose, FALSE);
5915 if ((msgnum != NULL) && (item != NULL)) {
5923 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5926 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5928 struct stat statbuf;
5929 gchar *type, *subtype;
5930 GtkTreeModel *model;
5933 model = gtk_tree_view_get_model(tree_view);
5935 if (!gtk_tree_model_get_iter_first(model, &iter))
5938 gtk_tree_model_get(model, &iter,
5942 if (!is_file_exist(ainfo->file)) {
5943 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5944 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5945 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5947 if (val == G_ALERTDEFAULT) {
5952 mimepart = procmime_mimeinfo_new();
5953 mimepart->content = MIMECONTENT_FILE;
5954 mimepart->data.filename = g_strdup(ainfo->file);
5955 mimepart->tmp = FALSE; /* or we destroy our attachment */
5956 mimepart->offset = 0;
5958 g_stat(ainfo->file, &statbuf);
5959 mimepart->length = statbuf.st_size;
5961 type = g_strdup(ainfo->content_type);
5963 if (!strchr(type, '/')) {
5965 type = g_strdup("application/octet-stream");
5968 subtype = strchr(type, '/') + 1;
5969 *(subtype - 1) = '\0';
5970 mimepart->type = procmime_get_media_type(type);
5971 mimepart->subtype = g_strdup(subtype);
5974 if (mimepart->type == MIMETYPE_MESSAGE &&
5975 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5976 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5977 } else if (mimepart->type == MIMETYPE_TEXT) {
5978 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
5979 /* Text parts with no name come from multipart/alternative
5980 * forwards. Make sure the recipient won't look at the
5981 * original HTML part by mistake. */
5982 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5983 ainfo->name = g_strdup_printf(_("Original %s part"),
5987 g_hash_table_insert(mimepart->typeparameters,
5988 g_strdup("charset"), g_strdup(ainfo->charset));
5990 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
5991 if (mimepart->type == MIMETYPE_APPLICATION &&
5992 !strcmp2(mimepart->subtype, "octet-stream"))
5993 g_hash_table_insert(mimepart->typeparameters,
5994 g_strdup("name"), g_strdup(ainfo->name));
5995 g_hash_table_insert(mimepart->dispositionparameters,
5996 g_strdup("filename"), g_strdup(ainfo->name));
5997 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6000 if (mimepart->type == MIMETYPE_MESSAGE
6001 || mimepart->type == MIMETYPE_MULTIPART)
6002 ainfo->encoding = ENC_BINARY;
6003 else if (compose->use_signing) {
6004 if (ainfo->encoding == ENC_7BIT)
6005 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6006 else if (ainfo->encoding == ENC_8BIT)
6007 ainfo->encoding = ENC_BASE64;
6012 procmime_encode_content(mimepart, ainfo->encoding);
6014 g_node_append(parent->node, mimepart->node);
6015 } while (gtk_tree_model_iter_next(model, &iter));
6020 #define IS_IN_CUSTOM_HEADER(header) \
6021 (compose->account->add_customhdr && \
6022 custom_header_find(compose->account->customhdr_list, header) != NULL)
6024 static void compose_add_headerfield_from_headerlist(Compose *compose,
6026 const gchar *fieldname,
6027 const gchar *seperator)
6029 gchar *str, *fieldname_w_colon;
6030 gboolean add_field = FALSE;
6032 ComposeHeaderEntry *headerentry;
6033 const gchar *headerentryname;
6034 const gchar *trans_fieldname;
6037 if (IS_IN_CUSTOM_HEADER(fieldname))
6040 debug_print("Adding %s-fields\n", fieldname);
6042 fieldstr = g_string_sized_new(64);
6044 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6045 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6047 for (list = compose->header_list; list; list = list->next) {
6048 headerentry = ((ComposeHeaderEntry *)list->data);
6049 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6051 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6052 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6054 if (str[0] != '\0') {
6056 g_string_append(fieldstr, seperator);
6057 g_string_append(fieldstr, str);
6066 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6067 compose_convert_header
6068 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6069 strlen(fieldname) + 2, TRUE);
6070 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6074 g_free(fieldname_w_colon);
6075 g_string_free(fieldstr, TRUE);
6080 static gchar *compose_get_header(Compose *compose)
6082 gchar buf[BUFFSIZE];
6083 const gchar *entry_str;
6087 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6089 gchar *from_name = NULL, *from_address = NULL;
6092 cm_return_val_if_fail(compose->account != NULL, NULL);
6093 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6095 header = g_string_sized_new(64);
6098 get_rfc822_date(buf, sizeof(buf));
6099 g_string_append_printf(header, "Date: %s\n", buf);
6103 if (compose->account->name && *compose->account->name) {
6105 QUOTE_IF_REQUIRED(buf, compose->account->name);
6106 tmp = g_strdup_printf("%s <%s>",
6107 buf, compose->account->address);
6109 tmp = g_strdup_printf("%s",
6110 compose->account->address);
6112 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6113 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6115 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6116 from_address = g_strdup(compose->account->address);
6118 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6119 /* extract name and address */
6120 if (strstr(spec, " <") && strstr(spec, ">")) {
6121 from_address = g_strdup(strrchr(spec, '<')+1);
6122 *(strrchr(from_address, '>')) = '\0';
6123 from_name = g_strdup(spec);
6124 *(strrchr(from_name, '<')) = '\0';
6127 from_address = g_strdup(spec);
6134 if (from_name && *from_name) {
6135 compose_convert_header
6136 (compose, buf, sizeof(buf), from_name,
6137 strlen("From: "), TRUE);
6138 QUOTE_IF_REQUIRED(name, buf);
6140 g_string_append_printf(header, "From: %s <%s>\n",
6141 name, from_address);
6143 g_string_append_printf(header, "From: %s\n", from_address);
6146 g_free(from_address);
6149 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6152 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6155 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6159 * If this account is a NNTP account remove Bcc header from
6160 * message body since it otherwise will be publicly shown
6162 if (compose->account->protocol != A_NNTP)
6163 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6166 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6168 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6171 compose_convert_header(compose, buf, sizeof(buf), str,
6172 strlen("Subject: "), FALSE);
6173 g_string_append_printf(header, "Subject: %s\n", buf);
6179 if (compose->account->set_domain && compose->account->domain) {
6180 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6181 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6182 g_snprintf(buf, sizeof(buf), "%s",
6183 strchr(compose->account->address, '@') ?
6184 strchr(compose->account->address, '@')+1 :
6185 compose->account->address);
6187 g_snprintf(buf, sizeof(buf), "%s", "");
6190 if (compose->account->gen_msgid) {
6192 if (compose->account->msgid_with_addr) {
6193 addr = compose->account->address;
6195 generate_msgid(buf, sizeof(buf), addr);
6196 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6197 compose->msgid = g_strdup(buf);
6199 compose->msgid = NULL;
6202 if (compose->remove_references == FALSE) {
6204 if (compose->inreplyto && compose->to_list)
6205 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6208 if (compose->references)
6209 g_string_append_printf(header, "References: %s\n", compose->references);
6213 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6216 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6219 if (compose->account->organization &&
6220 strlen(compose->account->organization) &&
6221 !IS_IN_CUSTOM_HEADER("Organization")) {
6222 compose_convert_header(compose, buf, sizeof(buf),
6223 compose->account->organization,
6224 strlen("Organization: "), FALSE);
6225 g_string_append_printf(header, "Organization: %s\n", buf);
6228 /* Program version and system info */
6229 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6230 !compose->newsgroup_list) {
6231 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6233 gtk_major_version, gtk_minor_version, gtk_micro_version,
6236 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6237 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6239 gtk_major_version, gtk_minor_version, gtk_micro_version,
6243 /* custom headers */
6244 if (compose->account->add_customhdr) {
6247 for (cur = compose->account->customhdr_list; cur != NULL;
6249 CustomHeader *chdr = (CustomHeader *)cur->data;
6251 if (custom_header_is_allowed(chdr->name)) {
6252 compose_convert_header
6253 (compose, buf, sizeof(buf),
6254 chdr->value ? chdr->value : "",
6255 strlen(chdr->name) + 2, FALSE);
6256 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6261 /* Automatic Faces and X-Faces */
6262 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6263 g_string_append_printf(header, "X-Face: %s\n", buf);
6265 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6266 g_string_append_printf(header, "X-Face: %s\n", buf);
6268 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6269 g_string_append_printf(header, "Face: %s\n", buf);
6271 else if (get_default_face (buf, sizeof(buf)) == 0) {
6272 g_string_append_printf(header, "Face: %s\n", buf);
6276 switch (compose->priority) {
6277 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6278 "X-Priority: 1 (Highest)\n");
6280 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6281 "X-Priority: 2 (High)\n");
6283 case PRIORITY_NORMAL: break;
6284 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6285 "X-Priority: 4 (Low)\n");
6287 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6288 "X-Priority: 5 (Lowest)\n");
6290 default: debug_print("compose: priority unknown : %d\n",
6294 /* Request Return Receipt */
6295 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6296 if (compose->return_receipt) {
6297 if (compose->account->name
6298 && *compose->account->name) {
6299 compose_convert_header(compose, buf, sizeof(buf),
6300 compose->account->name,
6301 strlen("Disposition-Notification-To: "),
6303 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6305 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6309 /* get special headers */
6310 for (list = compose->header_list; list; list = list->next) {
6311 ComposeHeaderEntry *headerentry;
6314 gchar *headername_wcolon;
6315 const gchar *headername_trans;
6318 gboolean standard_header = FALSE;
6320 headerentry = ((ComposeHeaderEntry *)list->data);
6322 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6324 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6329 if (!strstr(tmp, ":")) {
6330 headername_wcolon = g_strconcat(tmp, ":", NULL);
6331 headername = g_strdup(tmp);
6333 headername_wcolon = g_strdup(tmp);
6334 headername = g_strdup(strtok(tmp, ":"));
6338 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6339 Xstrdup_a(headervalue, entry_str, return NULL);
6340 subst_char(headervalue, '\r', ' ');
6341 subst_char(headervalue, '\n', ' ');
6342 string = std_headers;
6343 while (*string != NULL) {
6344 headername_trans = prefs_common_translated_header_name(*string);
6345 if (!strcmp(headername_trans, headername_wcolon))
6346 standard_header = TRUE;
6349 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6350 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6353 g_free(headername_wcolon);
6357 g_string_free(header, FALSE);
6362 #undef IS_IN_CUSTOM_HEADER
6364 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6365 gint header_len, gboolean addr_field)
6367 gchar *tmpstr = NULL;
6368 const gchar *out_codeset = NULL;
6370 cm_return_if_fail(src != NULL);
6371 cm_return_if_fail(dest != NULL);
6373 if (len < 1) return;
6375 tmpstr = g_strdup(src);
6377 subst_char(tmpstr, '\n', ' ');
6378 subst_char(tmpstr, '\r', ' ');
6381 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6382 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6383 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6388 codeconv_set_strict(TRUE);
6389 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6390 conv_get_charset_str(compose->out_encoding));
6391 codeconv_set_strict(FALSE);
6393 if (!dest || *dest == '\0') {
6394 gchar *test_conv_global_out = NULL;
6395 gchar *test_conv_reply = NULL;
6397 /* automatic mode. be automatic. */
6398 codeconv_set_strict(TRUE);
6400 out_codeset = conv_get_outgoing_charset_str();
6402 debug_print("trying to convert to %s\n", out_codeset);
6403 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6406 if (!test_conv_global_out && compose->orig_charset
6407 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6408 out_codeset = compose->orig_charset;
6409 debug_print("failure; trying to convert to %s\n", out_codeset);
6410 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6413 if (!test_conv_global_out && !test_conv_reply) {
6415 out_codeset = CS_INTERNAL;
6416 debug_print("finally using %s\n", out_codeset);
6418 g_free(test_conv_global_out);
6419 g_free(test_conv_reply);
6420 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6422 codeconv_set_strict(FALSE);
6427 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6431 cm_return_if_fail(user_data != NULL);
6433 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6434 g_strstrip(address);
6435 if (*address != '\0') {
6436 gchar *name = procheader_get_fromname(address);
6437 extract_address(address);
6438 addressbook_add_contact(name, address, NULL, NULL);
6443 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6445 GtkWidget *menuitem;
6448 cm_return_if_fail(menu != NULL);
6449 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6451 menuitem = gtk_separator_menu_item_new();
6452 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6453 gtk_widget_show(menuitem);
6455 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6456 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6458 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6459 g_strstrip(address);
6460 if (*address == '\0') {
6461 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6464 g_signal_connect(G_OBJECT(menuitem), "activate",
6465 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6466 gtk_widget_show(menuitem);
6469 static void compose_create_header_entry(Compose *compose)
6471 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6478 const gchar *header = NULL;
6479 ComposeHeaderEntry *headerentry;
6480 gboolean standard_header = FALSE;
6481 GtkListStore *model;
6483 #if !(GTK_CHECK_VERSION(2,12,0))
6484 GtkTooltips *tips = compose->tooltips;
6487 headerentry = g_new0(ComposeHeaderEntry, 1);
6490 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6491 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6492 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6494 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6496 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6498 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6499 COMPOSE_NEWSGROUPS);
6500 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6502 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6503 COMPOSE_FOLLOWUPTO);
6505 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6506 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6507 G_CALLBACK(compose_grab_focus_cb), compose);
6508 gtk_widget_show(combo);
6509 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6510 compose->header_nextrow, compose->header_nextrow+1,
6511 GTK_SHRINK, GTK_FILL, 0, 0);
6512 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6513 const gchar *last_header_entry = gtk_entry_get_text(
6514 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6516 while (*string != NULL) {
6517 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6518 standard_header = TRUE;
6521 if (standard_header)
6522 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6524 if (!compose->header_last || !standard_header) {
6525 switch(compose->account->protocol) {
6527 header = prefs_common_translated_header_name("Newsgroups:");
6530 header = prefs_common_translated_header_name("To:");
6535 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6537 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6538 G_CALLBACK(compose_grab_focus_cb), compose);
6540 /* Entry field with cleanup button */
6541 button = gtk_button_new();
6542 gtk_button_set_image(GTK_BUTTON(button),
6543 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6544 gtk_widget_show(button);
6545 CLAWS_SET_TIP(button,
6546 _("Delete entry contents"));
6547 entry = gtk_entry_new();
6548 gtk_widget_show(entry);
6549 CLAWS_SET_TIP(entry,
6550 _("Use <tab> to autocomplete from addressbook"));
6551 hbox = gtk_hbox_new (FALSE, 0);
6552 gtk_widget_show(hbox);
6553 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6554 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6555 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6556 compose->header_nextrow, compose->header_nextrow+1,
6557 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6559 g_signal_connect(G_OBJECT(entry), "key-press-event",
6560 G_CALLBACK(compose_headerentry_key_press_event_cb),
6562 g_signal_connect(G_OBJECT(entry), "changed",
6563 G_CALLBACK(compose_headerentry_changed_cb),
6565 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6566 G_CALLBACK(compose_grab_focus_cb), compose);
6568 g_signal_connect(G_OBJECT(button), "clicked",
6569 G_CALLBACK(compose_headerentry_button_clicked_cb),
6573 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6574 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6575 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6576 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6577 G_CALLBACK(compose_header_drag_received_cb),
6579 g_signal_connect(G_OBJECT(entry), "drag-drop",
6580 G_CALLBACK(compose_drag_drop),
6582 g_signal_connect(G_OBJECT(entry), "populate-popup",
6583 G_CALLBACK(compose_entry_popup_extend),
6586 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6588 headerentry->compose = compose;
6589 headerentry->combo = combo;
6590 headerentry->entry = entry;
6591 headerentry->button = button;
6592 headerentry->hbox = hbox;
6593 headerentry->headernum = compose->header_nextrow;
6594 headerentry->type = PREF_NONE;
6596 compose->header_nextrow++;
6597 compose->header_last = headerentry;
6598 compose->header_list =
6599 g_slist_append(compose->header_list,
6603 static void compose_add_header_entry(Compose *compose, const gchar *header,
6604 gchar *text, ComposePrefType pref_type)
6606 ComposeHeaderEntry *last_header = compose->header_last;
6607 gchar *tmp = g_strdup(text), *email;
6608 gboolean replyto_hdr;
6610 replyto_hdr = (!strcasecmp(header,
6611 prefs_common_translated_header_name("Reply-To:")) ||
6613 prefs_common_translated_header_name("Followup-To:")) ||
6615 prefs_common_translated_header_name("In-Reply-To:")));
6617 extract_address(tmp);
6618 email = g_utf8_strdown(tmp, -1);
6620 if (replyto_hdr == FALSE &&
6621 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6623 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6624 header, text, (gint) pref_type);
6630 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6631 gtk_entry_set_text(GTK_ENTRY(
6632 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6634 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6635 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6636 last_header->type = pref_type;
6638 if (replyto_hdr == FALSE)
6639 g_hash_table_insert(compose->email_hashtable, email,
6640 GUINT_TO_POINTER(1));
6647 static void compose_destroy_headerentry(Compose *compose,
6648 ComposeHeaderEntry *headerentry)
6650 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6653 extract_address(text);
6654 email = g_utf8_strdown(text, -1);
6655 g_hash_table_remove(compose->email_hashtable, email);
6659 gtk_widget_destroy(headerentry->combo);
6660 gtk_widget_destroy(headerentry->entry);
6661 gtk_widget_destroy(headerentry->button);
6662 gtk_widget_destroy(headerentry->hbox);
6663 g_free(headerentry);
6666 static void compose_remove_header_entries(Compose *compose)
6669 for (list = compose->header_list; list; list = list->next)
6670 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6672 compose->header_last = NULL;
6673 g_slist_free(compose->header_list);
6674 compose->header_list = NULL;
6675 compose->header_nextrow = 1;
6676 compose_create_header_entry(compose);
6679 static GtkWidget *compose_create_header(Compose *compose)
6681 GtkWidget *from_optmenu_hbox;
6682 GtkWidget *header_scrolledwin;
6683 GtkWidget *header_table;
6687 /* header labels and entries */
6688 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6689 gtk_widget_show(header_scrolledwin);
6690 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6692 header_table = gtk_table_new(2, 2, FALSE);
6693 gtk_widget_show(header_table);
6694 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6695 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6696 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6699 /* option menu for selecting accounts */
6700 from_optmenu_hbox = compose_account_option_menu_create(compose);
6701 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6702 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6705 compose->header_table = header_table;
6706 compose->header_list = NULL;
6707 compose->header_nextrow = count;
6709 compose_create_header_entry(compose);
6711 compose->table = NULL;
6713 return header_scrolledwin ;
6716 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6718 Compose *compose = (Compose *)data;
6719 GdkEventButton event;
6722 event.time = gtk_get_current_event_time();
6724 return attach_button_pressed(compose->attach_clist, &event, compose);
6727 static GtkWidget *compose_create_attach(Compose *compose)
6729 GtkWidget *attach_scrwin;
6730 GtkWidget *attach_clist;
6732 GtkListStore *store;
6733 GtkCellRenderer *renderer;
6734 GtkTreeViewColumn *column;
6735 GtkTreeSelection *selection;
6737 /* attachment list */
6738 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6739 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6740 GTK_POLICY_AUTOMATIC,
6741 GTK_POLICY_AUTOMATIC);
6742 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6744 store = gtk_list_store_new(N_ATTACH_COLS,
6750 G_TYPE_AUTO_POINTER,
6752 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6753 (GTK_TREE_MODEL(store)));
6754 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6755 g_object_unref(store);
6757 renderer = gtk_cell_renderer_text_new();
6758 column = gtk_tree_view_column_new_with_attributes
6759 (_("Mime type"), renderer, "text",
6760 COL_MIMETYPE, NULL);
6761 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6763 renderer = gtk_cell_renderer_text_new();
6764 column = gtk_tree_view_column_new_with_attributes
6765 (_("Size"), renderer, "text",
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 (_("Name"), renderer, "text",
6773 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6775 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6776 prefs_common.use_stripes_everywhere);
6777 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6778 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6780 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6781 G_CALLBACK(attach_selected), compose);
6782 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6783 G_CALLBACK(attach_button_pressed), compose);
6785 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6786 G_CALLBACK(popup_attach_button_pressed), compose);
6788 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6789 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6790 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6791 G_CALLBACK(popup_attach_button_pressed), compose);
6793 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6794 G_CALLBACK(attach_key_pressed), compose);
6797 gtk_drag_dest_set(attach_clist,
6798 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6799 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6800 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6801 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6802 G_CALLBACK(compose_attach_drag_received_cb),
6804 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6805 G_CALLBACK(compose_drag_drop),
6808 compose->attach_scrwin = attach_scrwin;
6809 compose->attach_clist = attach_clist;
6811 return attach_scrwin;
6814 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6815 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6817 static GtkWidget *compose_create_others(Compose *compose)
6820 GtkWidget *savemsg_checkbtn;
6821 GtkWidget *savemsg_combo;
6822 GtkWidget *savemsg_select;
6825 gchar *folderidentifier;
6827 /* Table for settings */
6828 table = gtk_table_new(3, 1, FALSE);
6829 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6830 gtk_widget_show(table);
6831 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6834 /* Save Message to folder */
6835 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6836 gtk_widget_show(savemsg_checkbtn);
6837 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6838 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6839 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6841 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6842 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6844 savemsg_combo = gtk_combo_box_entry_new_text();
6845 compose->savemsg_checkbtn = savemsg_checkbtn;
6846 compose->savemsg_combo = savemsg_combo;
6847 gtk_widget_show(savemsg_combo);
6849 if (prefs_common.compose_save_to_history)
6850 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6851 prefs_common.compose_save_to_history);
6853 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6854 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6855 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6856 G_CALLBACK(compose_grab_focus_cb), compose);
6857 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6858 folderidentifier = folder_item_get_identifier(account_get_special_folder
6859 (compose->account, F_OUTBOX));
6860 compose_set_save_to(compose, folderidentifier);
6861 g_free(folderidentifier);
6864 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6865 gtk_widget_show(savemsg_select);
6866 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6867 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6868 G_CALLBACK(compose_savemsg_select_cb),
6876 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6878 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6879 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6882 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6887 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6890 path = folder_item_get_identifier(dest);
6892 compose_set_save_to(compose, path);
6896 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6897 GdkAtom clip, GtkTextIter *insert_place);
6900 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6904 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6906 if (event->button == 3) {
6908 GtkTextIter sel_start, sel_end;
6909 gboolean stuff_selected;
6911 /* move the cursor to allow GtkAspell to check the word
6912 * under the mouse */
6913 if (event->x && event->y) {
6914 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6915 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6917 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6920 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6921 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6924 stuff_selected = gtk_text_buffer_get_selection_bounds(
6926 &sel_start, &sel_end);
6928 gtk_text_buffer_place_cursor (buffer, &iter);
6929 /* reselect stuff */
6931 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6932 gtk_text_buffer_select_range(buffer,
6933 &sel_start, &sel_end);
6935 return FALSE; /* pass the event so that the right-click goes through */
6938 if (event->button == 2) {
6943 /* get the middle-click position to paste at the correct place */
6944 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6945 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6947 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6950 entry_paste_clipboard(compose, text,
6951 prefs_common.linewrap_pastes,
6952 GDK_SELECTION_PRIMARY, &iter);
6960 static void compose_spell_menu_changed(void *data)
6962 Compose *compose = (Compose *)data;
6964 GtkWidget *menuitem;
6965 GtkWidget *parent_item;
6966 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6969 if (compose->gtkaspell == NULL)
6972 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6973 "/Menu/Spelling/Options");
6975 /* setting the submenu removes /Spelling/Options from the factory
6976 * so we need to save it */
6978 if (parent_item == NULL) {
6979 parent_item = compose->aspell_options_menu;
6980 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6982 compose->aspell_options_menu = parent_item;
6984 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6986 spell_menu = g_slist_reverse(spell_menu);
6987 for (items = spell_menu;
6988 items; items = items->next) {
6989 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6990 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6991 gtk_widget_show(GTK_WIDGET(menuitem));
6993 g_slist_free(spell_menu);
6995 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6996 gtk_widget_show(parent_item);
6999 static void compose_dict_changed(void *data)
7001 Compose *compose = (Compose *) data;
7003 if(compose->gtkaspell &&
7004 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7007 gtkaspell_highlight_all(compose->gtkaspell);
7008 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7012 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7014 Compose *compose = (Compose *)data;
7015 GdkEventButton event;
7018 event.time = gtk_get_current_event_time();
7022 return text_clicked(compose->text, &event, compose);
7025 static gboolean compose_force_window_origin = TRUE;
7026 static Compose *compose_create(PrefsAccount *account,
7035 GtkWidget *handlebox;
7037 GtkWidget *notebook;
7039 GtkWidget *attach_hbox;
7040 GtkWidget *attach_lab1;
7041 GtkWidget *attach_lab2;
7046 GtkWidget *subject_hbox;
7047 GtkWidget *subject_frame;
7048 GtkWidget *subject_entry;
7052 GtkWidget *edit_vbox;
7053 GtkWidget *ruler_hbox;
7055 GtkWidget *scrolledwin;
7057 GtkTextBuffer *buffer;
7058 GtkClipboard *clipboard;
7061 UndoMain *undostruct;
7063 gchar *titles[N_ATTACH_COLS];
7064 GtkWidget *popupmenu;
7065 GtkWidget *tmpl_menu;
7066 GtkActionGroup *action_group = NULL;
7069 GtkAspell * gtkaspell = NULL;
7072 static GdkGeometry geometry;
7074 cm_return_val_if_fail(account != NULL, NULL);
7076 debug_print("Creating compose window...\n");
7077 compose = g_new0(Compose, 1);
7079 titles[COL_MIMETYPE] = _("MIME type");
7080 titles[COL_SIZE] = _("Size");
7081 titles[COL_NAME] = _("Name");
7082 titles[COL_CHARSET] = _("Charset");
7084 compose->batch = batch;
7085 compose->account = account;
7086 compose->folder = folder;
7088 compose->mutex = g_mutex_new();
7089 compose->set_cursor_pos = -1;
7091 #if !(GTK_CHECK_VERSION(2,12,0))
7092 compose->tooltips = tips;
7095 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7097 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7098 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7100 if (!geometry.max_width) {
7101 geometry.max_width = gdk_screen_width();
7102 geometry.max_height = gdk_screen_height();
7105 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7106 &geometry, GDK_HINT_MAX_SIZE);
7107 if (!geometry.min_width) {
7108 geometry.min_width = 600;
7109 geometry.min_height = 440;
7111 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7112 &geometry, GDK_HINT_MIN_SIZE);
7114 #ifndef GENERIC_UMPC
7115 if (compose_force_window_origin)
7116 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7117 prefs_common.compose_y);
7119 g_signal_connect(G_OBJECT(window), "delete_event",
7120 G_CALLBACK(compose_delete_cb), compose);
7121 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7122 gtk_widget_realize(window);
7124 gtkut_widget_set_composer_icon(window);
7126 vbox = gtk_vbox_new(FALSE, 0);
7127 gtk_container_add(GTK_CONTAINER(window), vbox);
7129 compose->ui_manager = gtk_ui_manager_new();
7130 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7131 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7132 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7133 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7134 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7135 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7136 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7137 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7138 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7139 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7150 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7177 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7200 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7229 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7250 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7254 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7259 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)
7260 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)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7266 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)
7267 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)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7272 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)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7276 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)
7277 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7282 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)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7287 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7288 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)
7289 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)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7300 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7302 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)
7304 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7310 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7320 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7321 gtk_widget_show_all(menubar);
7323 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7325 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7327 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7330 if (prefs_common.toolbar_detachable) {
7331 handlebox = gtk_handle_box_new();
7333 handlebox = gtk_hbox_new(FALSE, 0);
7335 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7337 gtk_widget_realize(handlebox);
7339 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7342 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7346 vbox2 = gtk_vbox_new(FALSE, 2);
7347 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7348 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7351 notebook = gtk_notebook_new();
7352 gtk_widget_set_size_request(notebook, -1, 130);
7353 gtk_widget_show(notebook);
7355 /* header labels and entries */
7356 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7357 compose_create_header(compose),
7358 gtk_label_new_with_mnemonic(_("Hea_der")));
7359 /* attachment list */
7360 attach_hbox = gtk_hbox_new(FALSE, 0);
7361 gtk_widget_show(attach_hbox);
7363 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7364 gtk_widget_show(attach_lab1);
7365 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7367 attach_lab2 = gtk_label_new("");
7368 gtk_widget_show(attach_lab2);
7369 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7371 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7372 compose_create_attach(compose),
7375 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7376 compose_create_others(compose),
7377 gtk_label_new_with_mnemonic(_("Othe_rs")));
7380 subject_hbox = gtk_hbox_new(FALSE, 0);
7381 gtk_widget_show(subject_hbox);
7383 subject_frame = gtk_frame_new(NULL);
7384 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7385 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7386 gtk_widget_show(subject_frame);
7388 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7389 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7390 gtk_widget_show(subject);
7392 label = gtk_label_new(_("Subject:"));
7393 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7394 gtk_widget_show(label);
7397 subject_entry = claws_spell_entry_new();
7399 subject_entry = gtk_entry_new();
7401 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7402 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7403 G_CALLBACK(compose_grab_focus_cb), compose);
7404 gtk_widget_show(subject_entry);
7405 compose->subject_entry = subject_entry;
7406 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7408 edit_vbox = gtk_vbox_new(FALSE, 0);
7410 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7413 ruler_hbox = gtk_hbox_new(FALSE, 0);
7414 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7416 ruler = gtk_shruler_new();
7417 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7418 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7422 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7423 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7424 GTK_POLICY_AUTOMATIC,
7425 GTK_POLICY_AUTOMATIC);
7426 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7428 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7429 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7431 text = gtk_text_view_new();
7432 if (prefs_common.show_compose_margin) {
7433 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7434 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7436 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7437 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7438 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7439 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7440 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7442 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7444 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7445 G_CALLBACK(compose_edit_size_alloc),
7447 g_signal_connect(G_OBJECT(buffer), "changed",
7448 G_CALLBACK(compose_changed_cb), compose);
7449 g_signal_connect(G_OBJECT(text), "grab_focus",
7450 G_CALLBACK(compose_grab_focus_cb), compose);
7451 g_signal_connect(G_OBJECT(buffer), "insert_text",
7452 G_CALLBACK(text_inserted), compose);
7453 g_signal_connect(G_OBJECT(text), "button_press_event",
7454 G_CALLBACK(text_clicked), compose);
7456 g_signal_connect(G_OBJECT(text), "popup-menu",
7457 G_CALLBACK(compose_popup_menu), compose);
7459 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7460 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7461 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7462 G_CALLBACK(compose_popup_menu), compose);
7464 g_signal_connect(G_OBJECT(subject_entry), "changed",
7465 G_CALLBACK(compose_changed_cb), compose);
7468 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7469 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7470 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7471 g_signal_connect(G_OBJECT(text), "drag_data_received",
7472 G_CALLBACK(compose_insert_drag_received_cb),
7474 g_signal_connect(G_OBJECT(text), "drag-drop",
7475 G_CALLBACK(compose_drag_drop),
7477 gtk_widget_show_all(vbox);
7479 /* pane between attach clist and text */
7480 paned = gtk_vpaned_new();
7481 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7483 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7484 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7486 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7488 gtk_paned_add1(GTK_PANED(paned), notebook);
7489 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7490 gtk_widget_show_all(paned);
7493 if (prefs_common.textfont) {
7494 PangoFontDescription *font_desc;
7496 font_desc = pango_font_description_from_string
7497 (prefs_common.textfont);
7499 gtk_widget_modify_font(text, font_desc);
7500 pango_font_description_free(font_desc);
7504 gtk_action_group_add_actions(action_group, compose_popup_entries,
7505 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7506 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7507 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7508 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7509 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7510 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7511 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7513 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7515 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7516 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7517 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7519 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7521 undostruct = undo_init(text);
7522 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7525 address_completion_start(window);
7527 compose->window = window;
7528 compose->vbox = vbox;
7529 compose->menubar = menubar;
7530 compose->handlebox = handlebox;
7532 compose->vbox2 = vbox2;
7534 compose->paned = paned;
7536 compose->attach_label = attach_lab2;
7538 compose->notebook = notebook;
7539 compose->edit_vbox = edit_vbox;
7540 compose->ruler_hbox = ruler_hbox;
7541 compose->ruler = ruler;
7542 compose->scrolledwin = scrolledwin;
7543 compose->text = text;
7545 compose->focused_editable = NULL;
7547 compose->popupmenu = popupmenu;
7549 compose->tmpl_menu = tmpl_menu;
7551 compose->mode = mode;
7552 compose->rmode = mode;
7554 compose->targetinfo = NULL;
7555 compose->replyinfo = NULL;
7556 compose->fwdinfo = NULL;
7558 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7559 g_str_equal, (GDestroyNotify) g_free, NULL);
7561 compose->replyto = NULL;
7563 compose->bcc = NULL;
7564 compose->followup_to = NULL;
7566 compose->ml_post = NULL;
7568 compose->inreplyto = NULL;
7569 compose->references = NULL;
7570 compose->msgid = NULL;
7571 compose->boundary = NULL;
7573 compose->autowrap = prefs_common.autowrap;
7574 compose->autoindent = prefs_common.auto_indent;
7575 compose->use_signing = FALSE;
7576 compose->use_encryption = FALSE;
7577 compose->privacy_system = NULL;
7579 compose->modified = FALSE;
7581 compose->return_receipt = FALSE;
7583 compose->to_list = NULL;
7584 compose->newsgroup_list = NULL;
7586 compose->undostruct = undostruct;
7588 compose->sig_str = NULL;
7590 compose->exteditor_file = NULL;
7591 compose->exteditor_pid = -1;
7592 compose->exteditor_tag = -1;
7593 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7596 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7597 if (mode != COMPOSE_REDIRECT) {
7598 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7599 strcmp(prefs_common.dictionary, "")) {
7600 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7601 prefs_common.alt_dictionary,
7602 conv_get_locale_charset_str(),
7603 prefs_common.misspelled_col,
7604 prefs_common.check_while_typing,
7605 prefs_common.recheck_when_changing_dict,
7606 prefs_common.use_alternate,
7607 prefs_common.use_both_dicts,
7608 GTK_TEXT_VIEW(text),
7609 GTK_WINDOW(compose->window),
7610 compose_dict_changed,
7611 compose_spell_menu_changed,
7614 alertpanel_error(_("Spell checker could not "
7616 gtkaspell_checkers_strerror());
7617 gtkaspell_checkers_reset_error();
7619 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7623 compose->gtkaspell = gtkaspell;
7624 compose_spell_menu_changed(compose);
7625 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7628 compose_select_account(compose, account, TRUE);
7630 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7631 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7633 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7634 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7636 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7637 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7639 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7640 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7642 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7643 if (account->protocol != A_NNTP)
7644 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7645 prefs_common_translated_header_name("To:"));
7647 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7648 prefs_common_translated_header_name("Newsgroups:"));
7650 addressbook_set_target_compose(compose);
7652 if (mode != COMPOSE_REDIRECT)
7653 compose_set_template_menu(compose);
7655 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7658 compose_list = g_list_append(compose_list, compose);
7660 if (!prefs_common.show_ruler)
7661 gtk_widget_hide(ruler_hbox);
7663 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7666 compose->priority = PRIORITY_NORMAL;
7667 compose_update_priority_menu_item(compose);
7669 compose_set_out_encoding(compose);
7672 compose_update_actions_menu(compose);
7674 /* Privacy Systems menu */
7675 compose_update_privacy_systems_menu(compose);
7677 activate_privacy_system(compose, account, TRUE);
7678 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7680 gtk_widget_realize(window);
7682 gtk_widget_show(window);
7684 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7685 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7692 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7697 GtkWidget *optmenubox;
7700 GtkWidget *from_name = NULL;
7701 #if !(GTK_CHECK_VERSION(2,12,0))
7702 GtkTooltips *tips = compose->tooltips;
7705 gint num = 0, def_menu = 0;
7707 accounts = account_get_list();
7708 cm_return_val_if_fail(accounts != NULL, NULL);
7710 optmenubox = gtk_event_box_new();
7711 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7712 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7714 hbox = gtk_hbox_new(FALSE, 6);
7715 from_name = gtk_entry_new();
7717 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7718 G_CALLBACK(compose_grab_focus_cb), compose);
7720 for (; accounts != NULL; accounts = accounts->next, num++) {
7721 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7722 gchar *name, *from = NULL;
7724 if (ac == compose->account) def_menu = num;
7726 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7729 if (ac == compose->account) {
7730 if (ac->name && *ac->name) {
7732 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7733 from = g_strdup_printf("%s <%s>",
7735 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7737 from = g_strdup_printf("%s",
7739 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7742 COMBOBOX_ADD(menu, name, ac->account_id);
7747 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7749 g_signal_connect(G_OBJECT(optmenu), "changed",
7750 G_CALLBACK(account_activated),
7752 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7753 G_CALLBACK(compose_entry_popup_extend),
7756 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7757 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7759 CLAWS_SET_TIP(optmenubox,
7760 _("Account to use for this email"));
7761 CLAWS_SET_TIP(from_name,
7762 _("Sender address to be used"));
7764 compose->account_combo = optmenu;
7765 compose->from_name = from_name;
7770 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7772 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7773 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7774 Compose *compose = (Compose *) data;
7776 compose->priority = value;
7780 static void compose_reply_change_mode(Compose *compose,
7783 gboolean was_modified = compose->modified;
7785 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7787 cm_return_if_fail(compose->replyinfo != NULL);
7789 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7791 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7793 if (action == COMPOSE_REPLY_TO_ALL)
7795 if (action == COMPOSE_REPLY_TO_SENDER)
7797 if (action == COMPOSE_REPLY_TO_LIST)
7800 compose_remove_header_entries(compose);
7801 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7802 if (compose->account->set_autocc && compose->account->auto_cc)
7803 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7805 if (compose->account->set_autobcc && compose->account->auto_bcc)
7806 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7808 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7809 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7810 compose_show_first_last_header(compose, TRUE);
7811 compose->modified = was_modified;
7812 compose_set_title(compose);
7815 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7817 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7818 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7819 Compose *compose = (Compose *) data;
7822 compose_reply_change_mode(compose, value);
7825 static void compose_update_priority_menu_item(Compose * compose)
7827 GtkWidget *menuitem = NULL;
7828 switch (compose->priority) {
7829 case PRIORITY_HIGHEST:
7830 menuitem = gtk_ui_manager_get_widget
7831 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7834 menuitem = gtk_ui_manager_get_widget
7835 (compose->ui_manager, "/Menu/Options/Priority/High");
7837 case PRIORITY_NORMAL:
7838 menuitem = gtk_ui_manager_get_widget
7839 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7842 menuitem = gtk_ui_manager_get_widget
7843 (compose->ui_manager, "/Menu/Options/Priority/Low");
7845 case PRIORITY_LOWEST:
7846 menuitem = gtk_ui_manager_get_widget
7847 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7850 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7853 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7855 Compose *compose = (Compose *) data;
7857 gboolean can_sign = FALSE, can_encrypt = FALSE;
7859 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7861 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7864 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7865 g_free(compose->privacy_system);
7866 compose->privacy_system = NULL;
7867 if (systemid != NULL) {
7868 compose->privacy_system = g_strdup(systemid);
7870 can_sign = privacy_system_can_sign(systemid);
7871 can_encrypt = privacy_system_can_encrypt(systemid);
7874 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7876 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7877 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7880 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7882 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7883 GtkWidget *menuitem = NULL;
7885 gboolean can_sign = FALSE, can_encrypt = FALSE;
7886 gboolean found = FALSE;
7888 if (compose->privacy_system != NULL) {
7890 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7891 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7892 cm_return_if_fail(menuitem != NULL);
7894 amenu = GTK_MENU_SHELL(menuitem)->children;
7896 while (amenu != NULL) {
7897 GList *alist = amenu->next;
7899 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7900 if (systemid != NULL) {
7901 if (strcmp(systemid, compose->privacy_system) == 0 &&
7902 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7903 menuitem = GTK_WIDGET(amenu->data);
7905 can_sign = privacy_system_can_sign(systemid);
7906 can_encrypt = privacy_system_can_encrypt(systemid);
7910 } else if (strlen(compose->privacy_system) == 0 &&
7911 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7912 menuitem = GTK_WIDGET(amenu->data);
7915 can_encrypt = FALSE;
7922 if (menuitem != NULL)
7923 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7925 if (warn && !found && strlen(compose->privacy_system)) {
7926 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7927 "will not be able to sign or encrypt this message."),
7928 compose->privacy_system);
7932 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7933 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7936 static void compose_set_out_encoding(Compose *compose)
7938 CharSet out_encoding;
7939 const gchar *branch = NULL;
7940 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7942 switch(out_encoding) {
7943 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7944 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7945 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7946 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7947 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7948 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7949 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7950 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7951 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7952 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7953 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7954 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7955 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7956 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7957 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7958 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7959 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7960 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7961 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7962 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7963 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7964 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7965 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7966 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7967 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7968 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7969 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7970 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7971 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7972 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7973 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7974 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7975 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7977 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7980 static void compose_set_template_menu(Compose *compose)
7982 GSList *tmpl_list, *cur;
7986 tmpl_list = template_get_config();
7988 menu = gtk_menu_new();
7990 gtk_menu_set_accel_group (GTK_MENU (menu),
7991 gtk_ui_manager_get_accel_group(compose->ui_manager));
7992 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7993 Template *tmpl = (Template *)cur->data;
7994 gchar *accel_path = NULL;
7995 item = gtk_menu_item_new_with_label(tmpl->name);
7996 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7997 g_signal_connect(G_OBJECT(item), "activate",
7998 G_CALLBACK(compose_template_activate_cb),
8000 g_object_set_data(G_OBJECT(item), "template", tmpl);
8001 gtk_widget_show(item);
8002 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8003 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8007 gtk_widget_show(menu);
8008 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8011 void compose_update_actions_menu(Compose *compose)
8013 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8016 static void compose_update_privacy_systems_menu(Compose *compose)
8018 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8019 GSList *systems, *cur;
8021 GtkWidget *system_none;
8023 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8024 GtkWidget *privacy_menu = gtk_menu_new();
8026 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8027 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8029 g_signal_connect(G_OBJECT(system_none), "activate",
8030 G_CALLBACK(compose_set_privacy_system_cb), compose);
8032 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8033 gtk_widget_show(system_none);
8035 systems = privacy_get_system_ids();
8036 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8037 gchar *systemid = cur->data;
8039 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8040 widget = gtk_radio_menu_item_new_with_label(group,
8041 privacy_system_get_name(systemid));
8042 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8043 g_strdup(systemid), g_free);
8044 g_signal_connect(G_OBJECT(widget), "activate",
8045 G_CALLBACK(compose_set_privacy_system_cb), compose);
8047 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8048 gtk_widget_show(widget);
8051 g_slist_free(systems);
8052 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8053 gtk_widget_show_all(privacy_menu);
8054 gtk_widget_show_all(privacy_menuitem);
8057 void compose_reflect_prefs_all(void)
8062 for (cur = compose_list; cur != NULL; cur = cur->next) {
8063 compose = (Compose *)cur->data;
8064 compose_set_template_menu(compose);
8068 void compose_reflect_prefs_pixmap_theme(void)
8073 for (cur = compose_list; cur != NULL; cur = cur->next) {
8074 compose = (Compose *)cur->data;
8075 toolbar_update(TOOLBAR_COMPOSE, compose);
8079 static const gchar *compose_quote_char_from_context(Compose *compose)
8081 const gchar *qmark = NULL;
8083 cm_return_val_if_fail(compose != NULL, NULL);
8085 switch (compose->mode) {
8086 /* use forward-specific quote char */
8087 case COMPOSE_FORWARD:
8088 case COMPOSE_FORWARD_AS_ATTACH:
8089 case COMPOSE_FORWARD_INLINE:
8090 if (compose->folder && compose->folder->prefs &&
8091 compose->folder->prefs->forward_with_format)
8092 qmark = compose->folder->prefs->forward_quotemark;
8093 else if (compose->account->forward_with_format)
8094 qmark = compose->account->forward_quotemark;
8096 qmark = prefs_common.fw_quotemark;
8099 /* use reply-specific quote char in all other modes */
8101 if (compose->folder && compose->folder->prefs &&
8102 compose->folder->prefs->reply_with_format)
8103 qmark = compose->folder->prefs->reply_quotemark;
8104 else if (compose->account->reply_with_format)
8105 qmark = compose->account->reply_quotemark;
8107 qmark = prefs_common.quotemark;
8111 if (qmark == NULL || *qmark == '\0')
8117 static void compose_template_apply(Compose *compose, Template *tmpl,
8121 GtkTextBuffer *buffer;
8125 gchar *parsed_str = NULL;
8126 gint cursor_pos = 0;
8127 const gchar *err_msg = _("The body of the template has an error at line %d.");
8130 /* process the body */
8132 text = GTK_TEXT_VIEW(compose->text);
8133 buffer = gtk_text_view_get_buffer(text);
8136 qmark = compose_quote_char_from_context(compose);
8138 if (compose->replyinfo != NULL) {
8141 gtk_text_buffer_set_text(buffer, "", -1);
8142 mark = gtk_text_buffer_get_insert(buffer);
8143 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8145 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8146 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8148 } else if (compose->fwdinfo != NULL) {
8151 gtk_text_buffer_set_text(buffer, "", -1);
8152 mark = gtk_text_buffer_get_insert(buffer);
8153 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8155 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8156 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8159 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8161 GtkTextIter start, end;
8164 gtk_text_buffer_get_start_iter(buffer, &start);
8165 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8166 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8168 /* clear the buffer now */
8170 gtk_text_buffer_set_text(buffer, "", -1);
8172 parsed_str = compose_quote_fmt(compose, dummyinfo,
8173 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8174 procmsg_msginfo_free( dummyinfo );
8180 gtk_text_buffer_set_text(buffer, "", -1);
8181 mark = gtk_text_buffer_get_insert(buffer);
8182 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8185 if (replace && parsed_str && compose->account->auto_sig)
8186 compose_insert_sig(compose, FALSE);
8188 if (replace && parsed_str) {
8189 gtk_text_buffer_get_start_iter(buffer, &iter);
8190 gtk_text_buffer_place_cursor(buffer, &iter);
8194 cursor_pos = quote_fmt_get_cursor_pos();
8195 compose->set_cursor_pos = cursor_pos;
8196 if (cursor_pos == -1)
8198 gtk_text_buffer_get_start_iter(buffer, &iter);
8199 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8200 gtk_text_buffer_place_cursor(buffer, &iter);
8203 /* process the other fields */
8205 compose_template_apply_fields(compose, tmpl);
8206 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8207 quote_fmt_reset_vartable();
8208 compose_changed_cb(NULL, compose);
8211 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8212 gtkaspell_highlight_all(compose->gtkaspell);
8216 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8218 MsgInfo* dummyinfo = NULL;
8219 MsgInfo *msginfo = NULL;
8222 if (compose->replyinfo != NULL)
8223 msginfo = compose->replyinfo;
8224 else if (compose->fwdinfo != NULL)
8225 msginfo = compose->fwdinfo;
8227 dummyinfo = compose_msginfo_new_from_compose(compose);
8228 msginfo = dummyinfo;
8231 if (tmpl->from && *tmpl->from != '\0') {
8233 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8234 compose->gtkaspell);
8236 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8238 quote_fmt_scan_string(tmpl->from);
8241 buf = quote_fmt_get_buffer();
8243 alertpanel_error(_("Template From format error."));
8245 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8249 if (tmpl->to && *tmpl->to != '\0') {
8251 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8252 compose->gtkaspell);
8254 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8256 quote_fmt_scan_string(tmpl->to);
8259 buf = quote_fmt_get_buffer();
8261 alertpanel_error(_("Template To format error."));
8263 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8267 if (tmpl->cc && *tmpl->cc != '\0') {
8269 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8270 compose->gtkaspell);
8272 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8274 quote_fmt_scan_string(tmpl->cc);
8277 buf = quote_fmt_get_buffer();
8279 alertpanel_error(_("Template Cc format error."));
8281 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8285 if (tmpl->bcc && *tmpl->bcc != '\0') {
8287 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8288 compose->gtkaspell);
8290 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8292 quote_fmt_scan_string(tmpl->bcc);
8295 buf = quote_fmt_get_buffer();
8297 alertpanel_error(_("Template Bcc format error."));
8299 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8303 /* process the subject */
8304 if (tmpl->subject && *tmpl->subject != '\0') {
8306 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8307 compose->gtkaspell);
8309 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8311 quote_fmt_scan_string(tmpl->subject);
8314 buf = quote_fmt_get_buffer();
8316 alertpanel_error(_("Template subject format error."));
8318 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8322 procmsg_msginfo_free( dummyinfo );
8325 static void compose_destroy(Compose *compose)
8327 GtkTextBuffer *buffer;
8328 GtkClipboard *clipboard;
8330 compose_list = g_list_remove(compose_list, compose);
8332 if (compose->updating) {
8333 debug_print("danger, not destroying anything now\n");
8334 compose->deferred_destroy = TRUE;
8337 /* NOTE: address_completion_end() does nothing with the window
8338 * however this may change. */
8339 address_completion_end(compose->window);
8341 slist_free_strings(compose->to_list);
8342 g_slist_free(compose->to_list);
8343 slist_free_strings(compose->newsgroup_list);
8344 g_slist_free(compose->newsgroup_list);
8345 slist_free_strings(compose->header_list);
8346 g_slist_free(compose->header_list);
8348 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8350 g_hash_table_destroy(compose->email_hashtable);
8352 procmsg_msginfo_free(compose->targetinfo);
8353 procmsg_msginfo_free(compose->replyinfo);
8354 procmsg_msginfo_free(compose->fwdinfo);
8356 g_free(compose->replyto);
8357 g_free(compose->cc);
8358 g_free(compose->bcc);
8359 g_free(compose->newsgroups);
8360 g_free(compose->followup_to);
8362 g_free(compose->ml_post);
8364 g_free(compose->inreplyto);
8365 g_free(compose->references);
8366 g_free(compose->msgid);
8367 g_free(compose->boundary);
8369 g_free(compose->redirect_filename);
8370 if (compose->undostruct)
8371 undo_destroy(compose->undostruct);
8373 g_free(compose->sig_str);
8375 g_free(compose->exteditor_file);
8377 g_free(compose->orig_charset);
8379 g_free(compose->privacy_system);
8381 if (addressbook_get_target_compose() == compose)
8382 addressbook_set_target_compose(NULL);
8385 if (compose->gtkaspell) {
8386 gtkaspell_delete(compose->gtkaspell);
8387 compose->gtkaspell = NULL;
8391 if (!compose->batch) {
8392 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8393 prefs_common.compose_height = compose->window->allocation.height;
8396 if (!gtk_widget_get_parent(compose->paned))
8397 gtk_widget_destroy(compose->paned);
8398 gtk_widget_destroy(compose->popupmenu);
8400 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8401 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8402 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8404 gtk_widget_destroy(compose->window);
8405 toolbar_destroy(compose->toolbar);
8406 g_free(compose->toolbar);
8407 g_mutex_free(compose->mutex);
8411 static void compose_attach_info_free(AttachInfo *ainfo)
8413 g_free(ainfo->file);
8414 g_free(ainfo->content_type);
8415 g_free(ainfo->name);
8416 g_free(ainfo->charset);
8420 static void compose_attach_update_label(Compose *compose)
8425 GtkTreeModel *model;
8430 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8431 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8432 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8436 while(gtk_tree_model_iter_next(model, &iter))
8439 text = g_strdup_printf("(%d)", i);
8440 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8444 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8446 Compose *compose = (Compose *)data;
8447 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8448 GtkTreeSelection *selection;
8450 GtkTreeModel *model;
8452 selection = gtk_tree_view_get_selection(tree_view);
8453 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8458 for (cur = sel; cur != NULL; cur = cur->next) {
8459 GtkTreePath *path = cur->data;
8460 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8463 gtk_tree_path_free(path);
8466 for (cur = sel; cur != NULL; cur = cur->next) {
8467 GtkTreeRowReference *ref = cur->data;
8468 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8471 if (gtk_tree_model_get_iter(model, &iter, path))
8472 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8474 gtk_tree_path_free(path);
8475 gtk_tree_row_reference_free(ref);
8479 compose_attach_update_label(compose);
8482 static struct _AttachProperty
8485 GtkWidget *mimetype_entry;
8486 GtkWidget *encoding_optmenu;
8487 GtkWidget *path_entry;
8488 GtkWidget *filename_entry;
8490 GtkWidget *cancel_btn;
8493 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8495 gtk_tree_path_free((GtkTreePath *)ptr);
8498 static void compose_attach_property(GtkAction *action, gpointer data)
8500 Compose *compose = (Compose *)data;
8501 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8503 GtkComboBox *optmenu;
8504 GtkTreeSelection *selection;
8506 GtkTreeModel *model;
8509 static gboolean cancelled;
8511 /* only if one selected */
8512 selection = gtk_tree_view_get_selection(tree_view);
8513 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8516 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8520 path = (GtkTreePath *) sel->data;
8521 gtk_tree_model_get_iter(model, &iter, path);
8522 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8525 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8531 if (!attach_prop.window)
8532 compose_attach_property_create(&cancelled);
8533 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8534 gtk_widget_grab_focus(attach_prop.ok_btn);
8535 gtk_widget_show(attach_prop.window);
8536 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8538 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8539 if (ainfo->encoding == ENC_UNKNOWN)
8540 combobox_select_by_data(optmenu, ENC_BASE64);
8542 combobox_select_by_data(optmenu, ainfo->encoding);
8544 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8545 ainfo->content_type ? ainfo->content_type : "");
8546 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8547 ainfo->file ? ainfo->file : "");
8548 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8549 ainfo->name ? ainfo->name : "");
8552 const gchar *entry_text;
8554 gchar *cnttype = NULL;
8561 gtk_widget_hide(attach_prop.window);
8562 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8567 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8568 if (*entry_text != '\0') {
8571 text = g_strstrip(g_strdup(entry_text));
8572 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8573 cnttype = g_strdup(text);
8576 alertpanel_error(_("Invalid MIME type."));
8582 ainfo->encoding = combobox_get_active_data(optmenu);
8584 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8585 if (*entry_text != '\0') {
8586 if (is_file_exist(entry_text) &&
8587 (size = get_file_size(entry_text)) > 0)
8588 file = g_strdup(entry_text);
8591 (_("File doesn't exist or is empty."));
8597 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8598 if (*entry_text != '\0') {
8599 g_free(ainfo->name);
8600 ainfo->name = g_strdup(entry_text);
8604 g_free(ainfo->content_type);
8605 ainfo->content_type = cnttype;
8608 g_free(ainfo->file);
8612 ainfo->size = (goffset)size;
8614 /* update tree store */
8615 text = to_human_readable(ainfo->size);
8616 gtk_tree_model_get_iter(model, &iter, path);
8617 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8618 COL_MIMETYPE, ainfo->content_type,
8620 COL_NAME, ainfo->name,
8621 COL_CHARSET, ainfo->charset,
8627 gtk_tree_path_free(path);
8630 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8632 label = gtk_label_new(str); \
8633 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8634 GTK_FILL, 0, 0, 0); \
8635 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8637 entry = gtk_entry_new(); \
8638 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8639 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8642 static void compose_attach_property_create(gboolean *cancelled)
8648 GtkWidget *mimetype_entry;
8651 GtkListStore *optmenu_menu;
8652 GtkWidget *path_entry;
8653 GtkWidget *filename_entry;
8656 GtkWidget *cancel_btn;
8657 GList *mime_type_list, *strlist;
8660 debug_print("Creating attach_property window...\n");
8662 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8663 gtk_widget_set_size_request(window, 480, -1);
8664 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8665 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8666 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8667 g_signal_connect(G_OBJECT(window), "delete_event",
8668 G_CALLBACK(attach_property_delete_event),
8670 g_signal_connect(G_OBJECT(window), "key_press_event",
8671 G_CALLBACK(attach_property_key_pressed),
8674 vbox = gtk_vbox_new(FALSE, 8);
8675 gtk_container_add(GTK_CONTAINER(window), vbox);
8677 table = gtk_table_new(4, 2, FALSE);
8678 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8679 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8680 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8682 label = gtk_label_new(_("MIME type"));
8683 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8685 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8686 mimetype_entry = gtk_combo_box_entry_new_text();
8687 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8688 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8690 /* stuff with list */
8691 mime_type_list = procmime_get_mime_type_list();
8693 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8694 MimeType *type = (MimeType *) mime_type_list->data;
8697 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8699 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8702 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8703 (GCompareFunc)strcmp2);
8706 for (mime_type_list = strlist; mime_type_list != NULL;
8707 mime_type_list = mime_type_list->next) {
8708 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8709 g_free(mime_type_list->data);
8711 g_list_free(strlist);
8712 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8713 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8715 label = gtk_label_new(_("Encoding"));
8716 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8718 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8720 hbox = gtk_hbox_new(FALSE, 0);
8721 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8722 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8724 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8725 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8727 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8728 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8729 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8730 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8731 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8733 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8735 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8736 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8738 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8739 &ok_btn, GTK_STOCK_OK,
8741 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8742 gtk_widget_grab_default(ok_btn);
8744 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8745 G_CALLBACK(attach_property_ok),
8747 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8748 G_CALLBACK(attach_property_cancel),
8751 gtk_widget_show_all(vbox);
8753 attach_prop.window = window;
8754 attach_prop.mimetype_entry = mimetype_entry;
8755 attach_prop.encoding_optmenu = optmenu;
8756 attach_prop.path_entry = path_entry;
8757 attach_prop.filename_entry = filename_entry;
8758 attach_prop.ok_btn = ok_btn;
8759 attach_prop.cancel_btn = cancel_btn;
8762 #undef SET_LABEL_AND_ENTRY
8764 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8770 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8776 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8777 gboolean *cancelled)
8785 static gboolean attach_property_key_pressed(GtkWidget *widget,
8787 gboolean *cancelled)
8789 if (event && event->keyval == GDK_Escape) {
8793 if (event && event->keyval == GDK_Return) {
8801 static void compose_exec_ext_editor(Compose *compose)
8808 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8809 G_DIR_SEPARATOR, compose);
8811 if (pipe(pipe_fds) < 0) {
8817 if ((pid = fork()) < 0) {
8824 /* close the write side of the pipe */
8827 compose->exteditor_file = g_strdup(tmp);
8828 compose->exteditor_pid = pid;
8830 compose_set_ext_editor_sensitive(compose, FALSE);
8833 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8835 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8837 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8841 } else { /* process-monitoring process */
8847 /* close the read side of the pipe */
8850 if (compose_write_body_to_file(compose, tmp) < 0) {
8851 fd_write_all(pipe_fds[1], "2\n", 2);
8855 pid_ed = compose_exec_ext_editor_real(tmp);
8857 fd_write_all(pipe_fds[1], "1\n", 2);
8861 /* wait until editor is terminated */
8862 waitpid(pid_ed, NULL, 0);
8864 fd_write_all(pipe_fds[1], "0\n", 2);
8871 #endif /* G_OS_UNIX */
8875 static gint compose_exec_ext_editor_real(const gchar *file)
8882 cm_return_val_if_fail(file != NULL, -1);
8884 if ((pid = fork()) < 0) {
8889 if (pid != 0) return pid;
8891 /* grandchild process */
8893 if (setpgid(0, getppid()))
8896 if (prefs_common_get_ext_editor_cmd() &&
8897 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8898 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8899 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8901 if (prefs_common_get_ext_editor_cmd())
8902 g_warning("External editor command-line is invalid: '%s'\n",
8903 prefs_common_get_ext_editor_cmd());
8904 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8907 cmdline = strsplit_with_quote(buf, " ", 1024);
8908 execvp(cmdline[0], cmdline);
8911 g_strfreev(cmdline);
8916 static gboolean compose_ext_editor_kill(Compose *compose)
8918 pid_t pgid = compose->exteditor_pid * -1;
8921 ret = kill(pgid, 0);
8923 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8927 msg = g_strdup_printf
8928 (_("The external editor is still working.\n"
8929 "Force terminating the process?\n"
8930 "process group id: %d"), -pgid);
8931 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8932 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8936 if (val == G_ALERTALTERNATE) {
8937 g_source_remove(compose->exteditor_tag);
8938 g_io_channel_shutdown(compose->exteditor_ch,
8940 g_io_channel_unref(compose->exteditor_ch);
8942 if (kill(pgid, SIGTERM) < 0) perror("kill");
8943 waitpid(compose->exteditor_pid, NULL, 0);
8945 g_warning("Terminated process group id: %d", -pgid);
8946 g_warning("Temporary file: %s",
8947 compose->exteditor_file);
8949 compose_set_ext_editor_sensitive(compose, TRUE);
8951 g_free(compose->exteditor_file);
8952 compose->exteditor_file = NULL;
8953 compose->exteditor_pid = -1;
8954 compose->exteditor_ch = NULL;
8955 compose->exteditor_tag = -1;
8963 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8967 Compose *compose = (Compose *)data;
8970 debug_print("Compose: input from monitoring process\n");
8972 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8974 g_io_channel_shutdown(source, FALSE, NULL);
8975 g_io_channel_unref(source);
8977 waitpid(compose->exteditor_pid, NULL, 0);
8979 if (buf[0] == '0') { /* success */
8980 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8981 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8983 gtk_text_buffer_set_text(buffer, "", -1);
8984 compose_insert_file(compose, compose->exteditor_file);
8985 compose_changed_cb(NULL, compose);
8986 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8988 if (claws_unlink(compose->exteditor_file) < 0)
8989 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8990 } else if (buf[0] == '1') { /* failed */
8991 g_warning("Couldn't exec external editor\n");
8992 if (claws_unlink(compose->exteditor_file) < 0)
8993 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8994 } else if (buf[0] == '2') {
8995 g_warning("Couldn't write to file\n");
8996 } else if (buf[0] == '3') {
8997 g_warning("Pipe read failed\n");
9000 compose_set_ext_editor_sensitive(compose, TRUE);
9002 g_free(compose->exteditor_file);
9003 compose->exteditor_file = NULL;
9004 compose->exteditor_pid = -1;
9005 compose->exteditor_ch = NULL;
9006 compose->exteditor_tag = -1;
9011 static void compose_set_ext_editor_sensitive(Compose *compose,
9014 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9015 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9016 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9017 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9018 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9019 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9020 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9022 gtk_widget_set_sensitive(compose->text, sensitive);
9023 if (compose->toolbar->send_btn)
9024 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9025 if (compose->toolbar->sendl_btn)
9026 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9027 if (compose->toolbar->draft_btn)
9028 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9029 if (compose->toolbar->insert_btn)
9030 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9031 if (compose->toolbar->sig_btn)
9032 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9033 if (compose->toolbar->exteditor_btn)
9034 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9035 if (compose->toolbar->linewrap_current_btn)
9036 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9037 if (compose->toolbar->linewrap_all_btn)
9038 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9040 #endif /* G_OS_UNIX */
9043 * compose_undo_state_changed:
9045 * Change the sensivity of the menuentries undo and redo
9047 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9048 gint redo_state, gpointer data)
9050 Compose *compose = (Compose *)data;
9052 switch (undo_state) {
9053 case UNDO_STATE_TRUE:
9054 if (!undostruct->undo_state) {
9055 undostruct->undo_state = TRUE;
9056 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9059 case UNDO_STATE_FALSE:
9060 if (undostruct->undo_state) {
9061 undostruct->undo_state = FALSE;
9062 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9065 case UNDO_STATE_UNCHANGED:
9067 case UNDO_STATE_REFRESH:
9068 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9071 g_warning("Undo state not recognized");
9075 switch (redo_state) {
9076 case UNDO_STATE_TRUE:
9077 if (!undostruct->redo_state) {
9078 undostruct->redo_state = TRUE;
9079 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9082 case UNDO_STATE_FALSE:
9083 if (undostruct->redo_state) {
9084 undostruct->redo_state = FALSE;
9085 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9088 case UNDO_STATE_UNCHANGED:
9090 case UNDO_STATE_REFRESH:
9091 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9094 g_warning("Redo state not recognized");
9099 /* callback functions */
9101 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9102 * includes "non-client" (windows-izm) in calculation, so this calculation
9103 * may not be accurate.
9105 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9106 GtkAllocation *allocation,
9107 GtkSHRuler *shruler)
9109 if (prefs_common.show_ruler) {
9110 gint char_width = 0, char_height = 0;
9111 gint line_width_in_chars;
9113 gtkut_get_font_size(GTK_WIDGET(widget),
9114 &char_width, &char_height);
9115 line_width_in_chars =
9116 (allocation->width - allocation->x) / char_width;
9118 /* got the maximum */
9119 gtk_ruler_set_range(GTK_RULER(shruler),
9120 0.0, line_width_in_chars, 0,
9121 /*line_width_in_chars*/ char_width);
9130 ComposePrefType type;
9131 gboolean entry_marked;
9134 static void account_activated(GtkComboBox *optmenu, gpointer data)
9136 Compose *compose = (Compose *)data;
9139 gchar *folderidentifier;
9140 gint account_id = 0;
9143 GSList *list, *saved_list = NULL;
9144 HeaderEntryState *state;
9145 GtkRcStyle *style = NULL;
9146 static GdkColor yellow;
9147 static gboolean color_set = FALSE;
9149 /* Get ID of active account in the combo box */
9150 menu = gtk_combo_box_get_model(optmenu);
9151 gtk_combo_box_get_active_iter(optmenu, &iter);
9152 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9154 ac = account_find_from_id(account_id);
9155 cm_return_if_fail(ac != NULL);
9157 if (ac != compose->account) {
9158 compose_select_account(compose, ac, FALSE);
9160 for (list = compose->header_list; list; list = list->next) {
9161 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9163 if (hentry->type == PREF_ACCOUNT || !list->next) {
9164 compose_destroy_headerentry(compose, hentry);
9168 state = g_malloc0(sizeof(HeaderEntryState));
9169 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9170 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9171 state->entry = gtk_editable_get_chars(
9172 GTK_EDITABLE(hentry->entry), 0, -1);
9173 state->type = hentry->type;
9176 gdk_color_parse("#f5f6be", &yellow);
9177 color_set = gdk_colormap_alloc_color(
9178 gdk_colormap_get_system(),
9179 &yellow, FALSE, TRUE);
9182 style = gtk_widget_get_modifier_style(hentry->entry);
9183 state->entry_marked = gdk_color_equal(&yellow,
9184 &style->base[GTK_STATE_NORMAL]);
9186 saved_list = g_slist_append(saved_list, state);
9187 compose_destroy_headerentry(compose, hentry);
9190 compose->header_last = NULL;
9191 g_slist_free(compose->header_list);
9192 compose->header_list = NULL;
9193 compose->header_nextrow = 1;
9194 compose_create_header_entry(compose);
9196 if (ac->set_autocc && ac->auto_cc)
9197 compose_entry_append(compose, ac->auto_cc,
9198 COMPOSE_CC, PREF_ACCOUNT);
9200 if (ac->set_autobcc && ac->auto_bcc)
9201 compose_entry_append(compose, ac->auto_bcc,
9202 COMPOSE_BCC, PREF_ACCOUNT);
9204 if (ac->set_autoreplyto && ac->auto_replyto)
9205 compose_entry_append(compose, ac->auto_replyto,
9206 COMPOSE_REPLYTO, PREF_ACCOUNT);
9208 for (list = saved_list; list; list = list->next) {
9209 state = (HeaderEntryState *) list->data;
9211 compose_add_header_entry(compose, state->header,
9212 state->entry, state->type);
9213 if (state->entry_marked)
9214 compose_entry_mark_default_to(compose, state->entry);
9216 g_free(state->header);
9217 g_free(state->entry);
9220 g_slist_free(saved_list);
9222 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9223 (ac->protocol == A_NNTP) ?
9224 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9227 /* Set message save folder */
9228 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9229 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9231 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9232 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9234 compose_set_save_to(compose, NULL);
9235 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9236 folderidentifier = folder_item_get_identifier(account_get_special_folder
9237 (compose->account, F_OUTBOX));
9238 compose_set_save_to(compose, folderidentifier);
9239 g_free(folderidentifier);
9243 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9244 GtkTreeViewColumn *column, Compose *compose)
9246 compose_attach_property(NULL, compose);
9249 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9252 Compose *compose = (Compose *)data;
9253 GtkTreeSelection *attach_selection;
9254 gint attach_nr_selected;
9256 if (!event) return FALSE;
9258 if (event->button == 3) {
9259 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9260 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9262 if (attach_nr_selected > 0)
9264 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9265 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9267 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9268 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9271 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9272 NULL, NULL, event->button, event->time);
9279 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9282 Compose *compose = (Compose *)data;
9284 if (!event) return FALSE;
9286 switch (event->keyval) {
9288 compose_attach_remove_selected(NULL, compose);
9294 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9296 toolbar_comp_set_sensitive(compose, allow);
9297 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9298 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9300 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9302 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9303 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9304 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9306 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9310 static void compose_send_cb(GtkAction *action, gpointer data)
9312 Compose *compose = (Compose *)data;
9314 if (prefs_common.work_offline &&
9315 !inc_offline_should_override(TRUE,
9316 _("Claws Mail needs network access in order "
9317 "to send this email.")))
9320 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9321 g_source_remove(compose->draft_timeout_tag);
9322 compose->draft_timeout_tag = -1;
9325 compose_send(compose);
9328 static void compose_send_later_cb(GtkAction *action, gpointer data)
9330 Compose *compose = (Compose *)data;
9334 compose_allow_user_actions(compose, FALSE);
9335 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9336 compose_allow_user_actions(compose, TRUE);
9340 compose_close(compose);
9341 } else if (val == -1) {
9342 alertpanel_error(_("Could not queue message."));
9343 } else if (val == -2) {
9344 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9345 } else if (val == -3) {
9346 if (privacy_peek_error())
9347 alertpanel_error(_("Could not queue message for sending:\n\n"
9348 "Signature failed: %s"), privacy_get_error());
9349 } else if (val == -4) {
9350 alertpanel_error(_("Could not queue message for sending:\n\n"
9351 "Charset conversion failed."));
9352 } else if (val == -5) {
9353 alertpanel_error(_("Could not queue message for sending:\n\n"
9354 "Couldn't get recipient encryption key."));
9355 } else if (val == -6) {
9358 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9361 #define DRAFTED_AT_EXIT "drafted_at_exit"
9362 static void compose_register_draft(MsgInfo *info)
9364 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9365 DRAFTED_AT_EXIT, NULL);
9366 FILE *fp = g_fopen(filepath, "ab");
9369 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9377 gboolean compose_draft (gpointer data, guint action)
9379 Compose *compose = (Compose *)data;
9383 MsgFlags flag = {0, 0};
9384 static gboolean lock = FALSE;
9385 MsgInfo *newmsginfo;
9387 gboolean target_locked = FALSE;
9388 gboolean err = FALSE;
9390 if (lock) return FALSE;
9392 if (compose->sending)
9395 draft = account_get_special_folder(compose->account, F_DRAFT);
9396 cm_return_val_if_fail(draft != NULL, FALSE);
9398 if (!g_mutex_trylock(compose->mutex)) {
9399 /* we don't want to lock the mutex once it's available,
9400 * because as the only other part of compose.c locking
9401 * it is compose_close - which means once unlocked,
9402 * the compose struct will be freed */
9403 debug_print("couldn't lock mutex, probably sending\n");
9409 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9410 G_DIR_SEPARATOR, compose);
9411 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9412 FILE_OP_ERROR(tmp, "fopen");
9416 /* chmod for security */
9417 if (change_file_mode_rw(fp, tmp) < 0) {
9418 FILE_OP_ERROR(tmp, "chmod");
9419 g_warning("can't change file mode\n");
9422 /* Save draft infos */
9423 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9424 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9426 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9427 gchar *savefolderid;
9429 savefolderid = compose_get_save_to(compose);
9430 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9431 g_free(savefolderid);
9433 if (compose->return_receipt) {
9434 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9436 if (compose->privacy_system) {
9437 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9438 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9439 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9442 /* Message-ID of message replying to */
9443 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9446 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9447 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9450 /* Message-ID of message forwarding to */
9451 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9454 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9455 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9459 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9460 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9462 /* end of headers */
9463 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9470 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9474 if (fclose(fp) == EOF) {
9478 if (compose->targetinfo) {
9479 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9480 flag.perm_flags = target_locked?MSG_LOCKED:0;
9482 flag.tmp_flags = MSG_DRAFT;
9484 folder_item_scan(draft);
9485 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9486 MsgInfo *tmpinfo = NULL;
9487 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9488 if (compose->msgid) {
9489 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9492 msgnum = tmpinfo->msgnum;
9493 procmsg_msginfo_free(tmpinfo);
9494 debug_print("got draft msgnum %d from scanning\n", msgnum);
9496 debug_print("didn't get draft msgnum after scanning\n");
9499 debug_print("got draft msgnum %d from adding\n", msgnum);
9505 if (action != COMPOSE_AUTO_SAVE) {
9506 if (action != COMPOSE_DRAFT_FOR_EXIT)
9507 alertpanel_error(_("Could not save draft."));
9510 gtkut_window_popup(compose->window);
9511 val = alertpanel_full(_("Could not save draft"),
9512 _("Could not save draft.\n"
9513 "Do you want to cancel exit or discard this email?"),
9514 _("_Cancel exit"), _("_Discard email"), NULL,
9515 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9516 if (val == G_ALERTALTERNATE) {
9518 g_mutex_unlock(compose->mutex); /* must be done before closing */
9519 compose_close(compose);
9523 g_mutex_unlock(compose->mutex); /* must be done before closing */
9532 if (compose->mode == COMPOSE_REEDIT) {
9533 compose_remove_reedit_target(compose, TRUE);
9536 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9539 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9541 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9543 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9544 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9545 procmsg_msginfo_set_flags(newmsginfo, 0,
9546 MSG_HAS_ATTACHMENT);
9548 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9549 compose_register_draft(newmsginfo);
9551 procmsg_msginfo_free(newmsginfo);
9554 folder_item_scan(draft);
9556 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9558 g_mutex_unlock(compose->mutex); /* must be done before closing */
9559 compose_close(compose);
9565 path = folder_item_fetch_msg(draft, msgnum);
9567 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9570 if (g_stat(path, &s) < 0) {
9571 FILE_OP_ERROR(path, "stat");
9577 procmsg_msginfo_free(compose->targetinfo);
9578 compose->targetinfo = procmsg_msginfo_new();
9579 compose->targetinfo->msgnum = msgnum;
9580 compose->targetinfo->size = (goffset)s.st_size;
9581 compose->targetinfo->mtime = s.st_mtime;
9582 compose->targetinfo->folder = draft;
9584 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9585 compose->mode = COMPOSE_REEDIT;
9587 if (action == COMPOSE_AUTO_SAVE) {
9588 compose->autosaved_draft = compose->targetinfo;
9590 compose->modified = FALSE;
9591 compose_set_title(compose);
9595 g_mutex_unlock(compose->mutex);
9599 void compose_clear_exit_drafts(void)
9601 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9602 DRAFTED_AT_EXIT, NULL);
9603 if (is_file_exist(filepath))
9604 claws_unlink(filepath);
9609 void compose_reopen_exit_drafts(void)
9611 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9612 DRAFTED_AT_EXIT, NULL);
9613 FILE *fp = g_fopen(filepath, "rb");
9617 while (fgets(buf, sizeof(buf), fp)) {
9618 gchar **parts = g_strsplit(buf, "\t", 2);
9619 const gchar *folder = parts[0];
9620 int msgnum = parts[1] ? atoi(parts[1]):-1;
9622 if (folder && *folder && msgnum > -1) {
9623 FolderItem *item = folder_find_item_from_identifier(folder);
9624 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9626 compose_reedit(info, FALSE);
9633 compose_clear_exit_drafts();
9636 static void compose_save_cb(GtkAction *action, gpointer data)
9638 Compose *compose = (Compose *)data;
9639 compose_draft(compose, COMPOSE_KEEP_EDITING);
9640 compose->rmode = COMPOSE_REEDIT;
9643 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9645 if (compose && file_list) {
9648 for ( tmp = file_list; tmp; tmp = tmp->next) {
9649 gchar *file = (gchar *) tmp->data;
9650 gchar *utf8_filename = conv_filename_to_utf8(file);
9651 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9652 compose_changed_cb(NULL, compose);
9657 g_free(utf8_filename);
9662 static void compose_attach_cb(GtkAction *action, gpointer data)
9664 Compose *compose = (Compose *)data;
9667 if (compose->redirect_filename != NULL)
9670 file_list = filesel_select_multiple_files_open(_("Select file"));
9673 compose_attach_from_list(compose, file_list, TRUE);
9674 g_list_free(file_list);
9678 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9680 Compose *compose = (Compose *)data;
9682 gint files_inserted = 0;
9684 file_list = filesel_select_multiple_files_open(_("Select file"));
9689 for ( tmp = file_list; tmp; tmp = tmp->next) {
9690 gchar *file = (gchar *) tmp->data;
9691 gchar *filedup = g_strdup(file);
9692 gchar *shortfile = g_path_get_basename(filedup);
9693 ComposeInsertResult res;
9694 /* insert the file if the file is short or if the user confirmed that
9695 he/she wants to insert the large file */
9696 res = compose_insert_file(compose, file);
9697 if (res == COMPOSE_INSERT_READ_ERROR) {
9698 alertpanel_error(_("File '%s' could not be read."), shortfile);
9699 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9700 alertpanel_error(_("File '%s' contained invalid characters\n"
9701 "for the current encoding, insertion may be incorrect."),
9703 } else if (res == COMPOSE_INSERT_SUCCESS)
9710 g_list_free(file_list);
9714 if (files_inserted > 0 && compose->gtkaspell &&
9715 compose->gtkaspell->check_while_typing)
9716 gtkaspell_highlight_all(compose->gtkaspell);
9720 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9722 Compose *compose = (Compose *)data;
9724 compose_insert_sig(compose, FALSE);
9727 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9731 Compose *compose = (Compose *)data;
9733 gtkut_widget_get_uposition(widget, &x, &y);
9734 if (!compose->batch) {
9735 prefs_common.compose_x = x;
9736 prefs_common.compose_y = y;
9738 if (compose->sending || compose->updating)
9740 compose_close_cb(NULL, compose);
9744 void compose_close_toolbar(Compose *compose)
9746 compose_close_cb(NULL, compose);
9749 static void compose_close_cb(GtkAction *action, gpointer data)
9751 Compose *compose = (Compose *)data;
9755 if (compose->exteditor_tag != -1) {
9756 if (!compose_ext_editor_kill(compose))
9761 if (compose->modified) {
9762 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9763 if (!g_mutex_trylock(compose->mutex)) {
9764 /* we don't want to lock the mutex once it's available,
9765 * because as the only other part of compose.c locking
9766 * it is compose_close - which means once unlocked,
9767 * the compose struct will be freed */
9768 debug_print("couldn't lock mutex, probably sending\n");
9772 val = alertpanel(_("Discard message"),
9773 _("This message has been modified. Discard it?"),
9774 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9776 val = alertpanel(_("Save changes"),
9777 _("This message has been modified. Save the latest changes?"),
9778 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9780 g_mutex_unlock(compose->mutex);
9782 case G_ALERTDEFAULT:
9783 if (prefs_common.autosave && !reedit)
9784 compose_remove_draft(compose);
9786 case G_ALERTALTERNATE:
9787 compose_draft(data, COMPOSE_QUIT_EDITING);
9794 compose_close(compose);
9797 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9799 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9800 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9801 Compose *compose = (Compose *) data;
9804 compose->out_encoding = (CharSet)value;
9807 static void compose_address_cb(GtkAction *action, gpointer data)
9809 Compose *compose = (Compose *)data;
9811 addressbook_open(compose);
9814 static void about_show_cb(GtkAction *action, gpointer data)
9819 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9821 Compose *compose = (Compose *)data;
9826 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9827 cm_return_if_fail(tmpl != NULL);
9829 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9831 val = alertpanel(_("Apply template"), msg,
9832 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9835 if (val == G_ALERTDEFAULT)
9836 compose_template_apply(compose, tmpl, TRUE);
9837 else if (val == G_ALERTALTERNATE)
9838 compose_template_apply(compose, tmpl, FALSE);
9841 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9843 Compose *compose = (Compose *)data;
9845 compose_exec_ext_editor(compose);
9848 static void compose_undo_cb(GtkAction *action, gpointer data)
9850 Compose *compose = (Compose *)data;
9851 gboolean prev_autowrap = compose->autowrap;
9853 compose->autowrap = FALSE;
9854 undo_undo(compose->undostruct);
9855 compose->autowrap = prev_autowrap;
9858 static void compose_redo_cb(GtkAction *action, gpointer data)
9860 Compose *compose = (Compose *)data;
9861 gboolean prev_autowrap = compose->autowrap;
9863 compose->autowrap = FALSE;
9864 undo_redo(compose->undostruct);
9865 compose->autowrap = prev_autowrap;
9868 static void entry_cut_clipboard(GtkWidget *entry)
9870 if (GTK_IS_EDITABLE(entry))
9871 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9872 else if (GTK_IS_TEXT_VIEW(entry))
9873 gtk_text_buffer_cut_clipboard(
9874 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9875 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9879 static void entry_copy_clipboard(GtkWidget *entry)
9881 if (GTK_IS_EDITABLE(entry))
9882 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9883 else if (GTK_IS_TEXT_VIEW(entry))
9884 gtk_text_buffer_copy_clipboard(
9885 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9886 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9889 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9890 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9892 if (GTK_IS_TEXT_VIEW(entry)) {
9893 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9894 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9895 GtkTextIter start_iter, end_iter;
9897 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9899 if (contents == NULL)
9902 /* we shouldn't delete the selection when middle-click-pasting, or we
9903 * can't mid-click-paste our own selection */
9904 if (clip != GDK_SELECTION_PRIMARY) {
9905 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9906 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9909 if (insert_place == NULL) {
9910 /* if insert_place isn't specified, insert at the cursor.
9911 * used for Ctrl-V pasting */
9912 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9913 start = gtk_text_iter_get_offset(&start_iter);
9914 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9916 /* if insert_place is specified, paste here.
9917 * used for mid-click-pasting */
9918 start = gtk_text_iter_get_offset(insert_place);
9919 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9920 if (prefs_common.primary_paste_unselects)
9921 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9925 /* paste unwrapped: mark the paste so it's not wrapped later */
9926 end = start + strlen(contents);
9927 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9928 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9929 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9930 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9931 /* rewrap paragraph now (after a mid-click-paste) */
9932 mark_start = gtk_text_buffer_get_insert(buffer);
9933 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9934 gtk_text_iter_backward_char(&start_iter);
9935 compose_beautify_paragraph(compose, &start_iter, TRUE);
9937 } else if (GTK_IS_EDITABLE(entry))
9938 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9940 compose->modified = TRUE;
9943 static void entry_allsel(GtkWidget *entry)
9945 if (GTK_IS_EDITABLE(entry))
9946 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9947 else if (GTK_IS_TEXT_VIEW(entry)) {
9948 GtkTextIter startiter, enditer;
9949 GtkTextBuffer *textbuf;
9951 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9952 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9953 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9955 gtk_text_buffer_move_mark_by_name(textbuf,
9956 "selection_bound", &startiter);
9957 gtk_text_buffer_move_mark_by_name(textbuf,
9958 "insert", &enditer);
9962 static void compose_cut_cb(GtkAction *action, gpointer data)
9964 Compose *compose = (Compose *)data;
9965 if (compose->focused_editable
9966 #ifndef GENERIC_UMPC
9967 && gtkut_widget_has_focus(compose->focused_editable)
9970 entry_cut_clipboard(compose->focused_editable);
9973 static void compose_copy_cb(GtkAction *action, gpointer data)
9975 Compose *compose = (Compose *)data;
9976 if (compose->focused_editable
9977 #ifndef GENERIC_UMPC
9978 && gtkut_widget_has_focus(compose->focused_editable)
9981 entry_copy_clipboard(compose->focused_editable);
9984 static void compose_paste_cb(GtkAction *action, gpointer data)
9986 Compose *compose = (Compose *)data;
9988 GtkTextBuffer *buffer;
9990 if (compose->focused_editable &&
9991 gtkut_widget_has_focus(compose->focused_editable))
9992 entry_paste_clipboard(compose, compose->focused_editable,
9993 prefs_common.linewrap_pastes,
9994 GDK_SELECTION_CLIPBOARD, NULL);
9998 if (gtkut_widget_has_focus(compose->text) &&
9999 compose->gtkaspell &&
10000 compose->gtkaspell->check_while_typing)
10001 gtkaspell_highlight_all(compose->gtkaspell);
10005 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10007 Compose *compose = (Compose *)data;
10008 gint wrap_quote = prefs_common.linewrap_quote;
10009 if (compose->focused_editable
10010 #ifndef GENERIC_UMPC
10011 && gtkut_widget_has_focus(compose->focused_editable)
10014 /* let text_insert() (called directly or at a later time
10015 * after the gtk_editable_paste_clipboard) know that
10016 * text is to be inserted as a quotation. implemented
10017 * by using a simple refcount... */
10018 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10019 G_OBJECT(compose->focused_editable),
10020 "paste_as_quotation"));
10021 g_object_set_data(G_OBJECT(compose->focused_editable),
10022 "paste_as_quotation",
10023 GINT_TO_POINTER(paste_as_quotation + 1));
10024 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10025 entry_paste_clipboard(compose, compose->focused_editable,
10026 prefs_common.linewrap_pastes,
10027 GDK_SELECTION_CLIPBOARD, NULL);
10028 prefs_common.linewrap_quote = wrap_quote;
10032 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10034 Compose *compose = (Compose *)data;
10035 gint prev_autowrap;
10036 GtkTextBuffer *buffer;
10038 if (compose->focused_editable
10039 #ifndef GENERIC_UMPC
10040 && gtkut_widget_has_focus(compose->focused_editable)
10043 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10044 GDK_SELECTION_CLIPBOARD, NULL);
10048 if (gtkut_widget_has_focus(compose->text) &&
10049 compose->gtkaspell &&
10050 compose->gtkaspell->check_while_typing)
10051 gtkaspell_highlight_all(compose->gtkaspell);
10055 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10057 Compose *compose = (Compose *)data;
10058 gint prev_autowrap;
10059 GtkTextBuffer *buffer;
10061 if (compose->focused_editable
10062 #ifndef GENERIC_UMPC
10063 && gtkut_widget_has_focus(compose->focused_editable)
10066 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10067 GDK_SELECTION_CLIPBOARD, NULL);
10071 if (gtkut_widget_has_focus(compose->text) &&
10072 compose->gtkaspell &&
10073 compose->gtkaspell->check_while_typing)
10074 gtkaspell_highlight_all(compose->gtkaspell);
10078 static void compose_allsel_cb(GtkAction *action, gpointer data)
10080 Compose *compose = (Compose *)data;
10081 if (compose->focused_editable
10082 #ifndef GENERIC_UMPC
10083 && gtkut_widget_has_focus(compose->focused_editable)
10086 entry_allsel(compose->focused_editable);
10089 static void textview_move_beginning_of_line (GtkTextView *text)
10091 GtkTextBuffer *buffer;
10095 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10097 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10098 mark = gtk_text_buffer_get_insert(buffer);
10099 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10100 gtk_text_iter_set_line_offset(&ins, 0);
10101 gtk_text_buffer_place_cursor(buffer, &ins);
10104 static void textview_move_forward_character (GtkTextView *text)
10106 GtkTextBuffer *buffer;
10110 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10112 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10113 mark = gtk_text_buffer_get_insert(buffer);
10114 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10115 if (gtk_text_iter_forward_cursor_position(&ins))
10116 gtk_text_buffer_place_cursor(buffer, &ins);
10119 static void textview_move_backward_character (GtkTextView *text)
10121 GtkTextBuffer *buffer;
10125 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10127 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10128 mark = gtk_text_buffer_get_insert(buffer);
10129 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10130 if (gtk_text_iter_backward_cursor_position(&ins))
10131 gtk_text_buffer_place_cursor(buffer, &ins);
10134 static void textview_move_forward_word (GtkTextView *text)
10136 GtkTextBuffer *buffer;
10141 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10143 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10144 mark = gtk_text_buffer_get_insert(buffer);
10145 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10146 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10147 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10148 gtk_text_iter_backward_word_start(&ins);
10149 gtk_text_buffer_place_cursor(buffer, &ins);
10153 static void textview_move_backward_word (GtkTextView *text)
10155 GtkTextBuffer *buffer;
10160 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10162 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10163 mark = gtk_text_buffer_get_insert(buffer);
10164 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10165 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10166 if (gtk_text_iter_backward_word_starts(&ins, 1))
10167 gtk_text_buffer_place_cursor(buffer, &ins);
10170 static void textview_move_end_of_line (GtkTextView *text)
10172 GtkTextBuffer *buffer;
10176 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10178 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10179 mark = gtk_text_buffer_get_insert(buffer);
10180 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10181 if (gtk_text_iter_forward_to_line_end(&ins))
10182 gtk_text_buffer_place_cursor(buffer, &ins);
10185 static void textview_move_next_line (GtkTextView *text)
10187 GtkTextBuffer *buffer;
10192 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10194 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10195 mark = gtk_text_buffer_get_insert(buffer);
10196 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10197 offset = gtk_text_iter_get_line_offset(&ins);
10198 if (gtk_text_iter_forward_line(&ins)) {
10199 gtk_text_iter_set_line_offset(&ins, offset);
10200 gtk_text_buffer_place_cursor(buffer, &ins);
10204 static void textview_move_previous_line (GtkTextView *text)
10206 GtkTextBuffer *buffer;
10211 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10213 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10214 mark = gtk_text_buffer_get_insert(buffer);
10215 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10216 offset = gtk_text_iter_get_line_offset(&ins);
10217 if (gtk_text_iter_backward_line(&ins)) {
10218 gtk_text_iter_set_line_offset(&ins, offset);
10219 gtk_text_buffer_place_cursor(buffer, &ins);
10223 static void textview_delete_forward_character (GtkTextView *text)
10225 GtkTextBuffer *buffer;
10227 GtkTextIter ins, end_iter;
10229 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10231 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10232 mark = gtk_text_buffer_get_insert(buffer);
10233 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10235 if (gtk_text_iter_forward_char(&end_iter)) {
10236 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10240 static void textview_delete_backward_character (GtkTextView *text)
10242 GtkTextBuffer *buffer;
10244 GtkTextIter ins, end_iter;
10246 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10248 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10249 mark = gtk_text_buffer_get_insert(buffer);
10250 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10252 if (gtk_text_iter_backward_char(&end_iter)) {
10253 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10257 static void textview_delete_forward_word (GtkTextView *text)
10259 GtkTextBuffer *buffer;
10261 GtkTextIter ins, end_iter;
10263 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10265 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10266 mark = gtk_text_buffer_get_insert(buffer);
10267 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10269 if (gtk_text_iter_forward_word_end(&end_iter)) {
10270 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10274 static void textview_delete_backward_word (GtkTextView *text)
10276 GtkTextBuffer *buffer;
10278 GtkTextIter ins, end_iter;
10280 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10282 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10283 mark = gtk_text_buffer_get_insert(buffer);
10284 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10286 if (gtk_text_iter_backward_word_start(&end_iter)) {
10287 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10291 static void textview_delete_line (GtkTextView *text)
10293 GtkTextBuffer *buffer;
10295 GtkTextIter ins, start_iter, end_iter;
10297 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10299 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10300 mark = gtk_text_buffer_get_insert(buffer);
10301 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10304 gtk_text_iter_set_line_offset(&start_iter, 0);
10307 if (gtk_text_iter_ends_line(&end_iter)){
10308 if (!gtk_text_iter_forward_char(&end_iter))
10309 gtk_text_iter_backward_char(&start_iter);
10312 gtk_text_iter_forward_to_line_end(&end_iter);
10313 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10316 static void textview_delete_to_line_end (GtkTextView *text)
10318 GtkTextBuffer *buffer;
10320 GtkTextIter ins, end_iter;
10322 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10324 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10325 mark = gtk_text_buffer_get_insert(buffer);
10326 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10328 if (gtk_text_iter_ends_line(&end_iter))
10329 gtk_text_iter_forward_char(&end_iter);
10331 gtk_text_iter_forward_to_line_end(&end_iter);
10332 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10335 #define DO_ACTION(name, act) { \
10336 if(!strcmp(name, a_name)) { \
10340 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10342 const gchar *a_name = gtk_action_get_name(action);
10343 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10344 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10345 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10346 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10347 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10348 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10349 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10350 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10351 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10352 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10353 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10354 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10355 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10356 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10360 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10362 Compose *compose = (Compose *)data;
10363 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10364 ComposeCallAdvancedAction action = -1;
10366 action = compose_call_advanced_action_from_path(gaction);
10369 void (*do_action) (GtkTextView *text);
10370 } action_table[] = {
10371 {textview_move_beginning_of_line},
10372 {textview_move_forward_character},
10373 {textview_move_backward_character},
10374 {textview_move_forward_word},
10375 {textview_move_backward_word},
10376 {textview_move_end_of_line},
10377 {textview_move_next_line},
10378 {textview_move_previous_line},
10379 {textview_delete_forward_character},
10380 {textview_delete_backward_character},
10381 {textview_delete_forward_word},
10382 {textview_delete_backward_word},
10383 {textview_delete_line},
10384 {textview_delete_to_line_end}
10387 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10389 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10390 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10391 if (action_table[action].do_action)
10392 action_table[action].do_action(text);
10394 g_warning("Not implemented yet.");
10398 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10402 if (GTK_IS_EDITABLE(widget)) {
10403 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10404 gtk_editable_set_position(GTK_EDITABLE(widget),
10407 if (widget->parent && widget->parent->parent
10408 && widget->parent->parent->parent) {
10409 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10410 gint y = widget->allocation.y;
10411 gint height = widget->allocation.height;
10412 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10413 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10415 if (y < (int)shown->value) {
10416 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10418 if (y + height > (int)shown->value + (int)shown->page_size) {
10419 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10420 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10421 y + height - (int)shown->page_size - 1);
10423 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10424 (int)shown->upper - (int)shown->page_size - 1);
10431 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10432 compose->focused_editable = widget;
10434 #ifdef GENERIC_UMPC
10435 if (GTK_IS_TEXT_VIEW(widget)
10436 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10437 g_object_ref(compose->notebook);
10438 g_object_ref(compose->edit_vbox);
10439 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10440 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10441 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10442 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10443 g_object_unref(compose->notebook);
10444 g_object_unref(compose->edit_vbox);
10445 g_signal_handlers_block_by_func(G_OBJECT(widget),
10446 G_CALLBACK(compose_grab_focus_cb),
10448 gtk_widget_grab_focus(widget);
10449 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10450 G_CALLBACK(compose_grab_focus_cb),
10452 } else if (!GTK_IS_TEXT_VIEW(widget)
10453 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10454 g_object_ref(compose->notebook);
10455 g_object_ref(compose->edit_vbox);
10456 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10457 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10458 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10459 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10460 g_object_unref(compose->notebook);
10461 g_object_unref(compose->edit_vbox);
10462 g_signal_handlers_block_by_func(G_OBJECT(widget),
10463 G_CALLBACK(compose_grab_focus_cb),
10465 gtk_widget_grab_focus(widget);
10466 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10467 G_CALLBACK(compose_grab_focus_cb),
10473 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10475 compose->modified = TRUE;
10476 // compose_beautify_paragraph(compose, NULL, TRUE);
10477 #ifndef GENERIC_UMPC
10478 compose_set_title(compose);
10482 static void compose_wrap_cb(GtkAction *action, gpointer data)
10484 Compose *compose = (Compose *)data;
10485 compose_beautify_paragraph(compose, NULL, TRUE);
10488 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10490 Compose *compose = (Compose *)data;
10491 compose_wrap_all_full(compose, TRUE);
10494 static void compose_find_cb(GtkAction *action, gpointer data)
10496 Compose *compose = (Compose *)data;
10498 message_search_compose(compose);
10501 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10504 Compose *compose = (Compose *)data;
10505 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10506 if (compose->autowrap)
10507 compose_wrap_all_full(compose, TRUE);
10508 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10511 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10514 Compose *compose = (Compose *)data;
10515 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10518 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10520 Compose *compose = (Compose *)data;
10522 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10525 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10527 Compose *compose = (Compose *)data;
10529 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10532 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10534 g_free(compose->privacy_system);
10536 compose->privacy_system = g_strdup(account->default_privacy_system);
10537 compose_update_privacy_system_menu_item(compose, warn);
10540 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10542 Compose *compose = (Compose *)data;
10544 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10545 gtk_widget_show(compose->ruler_hbox);
10546 prefs_common.show_ruler = TRUE;
10548 gtk_widget_hide(compose->ruler_hbox);
10549 gtk_widget_queue_resize(compose->edit_vbox);
10550 prefs_common.show_ruler = FALSE;
10554 static void compose_attach_drag_received_cb (GtkWidget *widget,
10555 GdkDragContext *context,
10558 GtkSelectionData *data,
10561 gpointer user_data)
10563 Compose *compose = (Compose *)user_data;
10566 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10568 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10570 ) && gtk_drag_get_source_widget(context) !=
10571 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10572 list = uri_list_extract_filenames((const gchar *)data->data);
10573 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10574 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10575 compose_attach_append
10576 (compose, (const gchar *)tmp->data,
10577 utf8_filename, NULL, NULL);
10578 g_free(utf8_filename);
10580 if (list) compose_changed_cb(NULL, compose);
10581 list_free_strings(list);
10583 } else if (gtk_drag_get_source_widget(context)
10584 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10585 /* comes from our summaryview */
10586 SummaryView * summaryview = NULL;
10587 GSList * list = NULL, *cur = NULL;
10589 if (mainwindow_get_mainwindow())
10590 summaryview = mainwindow_get_mainwindow()->summaryview;
10593 list = summary_get_selected_msg_list(summaryview);
10595 for (cur = list; cur; cur = cur->next) {
10596 MsgInfo *msginfo = (MsgInfo *)cur->data;
10597 gchar *file = NULL;
10599 file = procmsg_get_message_file_full(msginfo,
10602 compose_attach_append(compose, (const gchar *)file,
10603 (const gchar *)file, "message/rfc822", NULL);
10607 g_slist_free(list);
10611 static gboolean compose_drag_drop(GtkWidget *widget,
10612 GdkDragContext *drag_context,
10614 guint time, gpointer user_data)
10616 /* not handling this signal makes compose_insert_drag_received_cb
10621 static void compose_insert_drag_received_cb (GtkWidget *widget,
10622 GdkDragContext *drag_context,
10625 GtkSelectionData *data,
10628 gpointer user_data)
10630 Compose *compose = (Compose *)user_data;
10633 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10636 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10638 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10640 AlertValue val = G_ALERTDEFAULT;
10642 list = uri_list_extract_filenames((const gchar *)data->data);
10643 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10644 /* Assume a list of no files, and data has ://, is a remote link */
10645 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10646 gchar *tmpfile = get_tmp_file();
10647 str_write_to_file(tmpdata, tmpfile);
10649 compose_insert_file(compose, tmpfile);
10650 claws_unlink(tmpfile);
10652 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10653 compose_beautify_paragraph(compose, NULL, TRUE);
10656 switch (prefs_common.compose_dnd_mode) {
10657 case COMPOSE_DND_ASK:
10658 val = alertpanel_full(_("Insert or attach?"),
10659 _("Do you want to insert the contents of the file(s) "
10660 "into the message body, or attach it to the email?"),
10661 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10662 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10664 case COMPOSE_DND_INSERT:
10665 val = G_ALERTALTERNATE;
10667 case COMPOSE_DND_ATTACH:
10668 val = G_ALERTOTHER;
10671 /* unexpected case */
10672 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10675 if (val & G_ALERTDISABLE) {
10676 val &= ~G_ALERTDISABLE;
10677 /* remember what action to perform by default, only if we don't click Cancel */
10678 if (val == G_ALERTALTERNATE)
10679 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10680 else if (val == G_ALERTOTHER)
10681 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10684 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10685 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10686 list_free_strings(list);
10689 } else if (val == G_ALERTOTHER) {
10690 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10691 list_free_strings(list);
10696 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10697 compose_insert_file(compose, (const gchar *)tmp->data);
10699 list_free_strings(list);
10701 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10706 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10709 static void compose_header_drag_received_cb (GtkWidget *widget,
10710 GdkDragContext *drag_context,
10713 GtkSelectionData *data,
10716 gpointer user_data)
10718 GtkEditable *entry = (GtkEditable *)user_data;
10719 gchar *email = (gchar *)data->data;
10721 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10724 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10725 gchar *decoded=g_new(gchar, strlen(email));
10728 email += strlen("mailto:");
10729 decode_uri(decoded, email); /* will fit */
10730 gtk_editable_delete_text(entry, 0, -1);
10731 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10732 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10736 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10739 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10741 Compose *compose = (Compose *)data;
10743 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10744 compose->return_receipt = TRUE;
10746 compose->return_receipt = FALSE;
10749 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10751 Compose *compose = (Compose *)data;
10753 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10754 compose->remove_references = TRUE;
10756 compose->remove_references = FALSE;
10759 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10760 ComposeHeaderEntry *headerentry)
10762 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10766 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10767 GdkEventKey *event,
10768 ComposeHeaderEntry *headerentry)
10770 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10771 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10772 !(event->state & GDK_MODIFIER_MASK) &&
10773 (event->keyval == GDK_BackSpace) &&
10774 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10775 gtk_container_remove
10776 (GTK_CONTAINER(headerentry->compose->header_table),
10777 headerentry->combo);
10778 gtk_container_remove
10779 (GTK_CONTAINER(headerentry->compose->header_table),
10780 headerentry->entry);
10781 headerentry->compose->header_list =
10782 g_slist_remove(headerentry->compose->header_list,
10784 g_free(headerentry);
10785 } else if (event->keyval == GDK_Tab) {
10786 if (headerentry->compose->header_last == headerentry) {
10787 /* Override default next focus, and give it to subject_entry
10788 * instead of notebook tabs
10790 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10791 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10798 static gboolean scroll_postpone(gpointer data)
10800 Compose *compose = (Compose *)data;
10802 cm_return_val_if_fail(!compose->batch, FALSE);
10804 GTK_EVENTS_FLUSH();
10805 compose_show_first_last_header(compose, FALSE);
10809 static void compose_headerentry_changed_cb(GtkWidget *entry,
10810 ComposeHeaderEntry *headerentry)
10812 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10813 compose_create_header_entry(headerentry->compose);
10814 g_signal_handlers_disconnect_matched
10815 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10816 0, 0, NULL, NULL, headerentry);
10818 if (!headerentry->compose->batch)
10819 g_timeout_add(0, scroll_postpone, headerentry->compose);
10823 static gboolean compose_defer_auto_save_draft(Compose *compose)
10825 compose->draft_timeout_tag = -1;
10826 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10830 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10832 GtkAdjustment *vadj;
10834 cm_return_if_fail(compose);
10835 cm_return_if_fail(!compose->batch);
10836 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10837 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10838 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10839 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10840 gtk_adjustment_changed(vadj);
10843 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10844 const gchar *text, gint len, Compose *compose)
10846 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10847 (G_OBJECT(compose->text), "paste_as_quotation"));
10850 cm_return_if_fail(text != NULL);
10852 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10853 G_CALLBACK(text_inserted),
10855 if (paste_as_quotation) {
10857 const gchar *qmark;
10859 GtkTextIter start_iter;
10862 len = strlen(text);
10864 new_text = g_strndup(text, len);
10866 qmark = compose_quote_char_from_context(compose);
10868 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10869 gtk_text_buffer_place_cursor(buffer, iter);
10871 pos = gtk_text_iter_get_offset(iter);
10873 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10874 _("Quote format error at line %d."));
10875 quote_fmt_reset_vartable();
10877 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10878 GINT_TO_POINTER(paste_as_quotation - 1));
10880 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10881 gtk_text_buffer_place_cursor(buffer, iter);
10882 gtk_text_buffer_delete_mark(buffer, mark);
10884 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10885 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10886 compose_beautify_paragraph(compose, &start_iter, FALSE);
10887 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10888 gtk_text_buffer_delete_mark(buffer, mark);
10890 if (strcmp(text, "\n") || compose->automatic_break
10891 || gtk_text_iter_starts_line(iter)) {
10892 GtkTextIter before_ins;
10893 gtk_text_buffer_insert(buffer, iter, text, len);
10894 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10895 before_ins = *iter;
10896 gtk_text_iter_backward_chars(&before_ins, len);
10897 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10900 /* check if the preceding is just whitespace or quote */
10901 GtkTextIter start_line;
10902 gchar *tmp = NULL, *quote = NULL;
10903 gint quote_len = 0, is_normal = 0;
10904 start_line = *iter;
10905 gtk_text_iter_set_line_offset(&start_line, 0);
10906 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10909 if (*tmp == '\0') {
10912 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10920 gtk_text_buffer_insert(buffer, iter, text, len);
10922 gtk_text_buffer_insert_with_tags_by_name(buffer,
10923 iter, text, len, "no_join", NULL);
10928 if (!paste_as_quotation) {
10929 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10930 compose_beautify_paragraph(compose, iter, FALSE);
10931 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10932 gtk_text_buffer_delete_mark(buffer, mark);
10935 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10936 G_CALLBACK(text_inserted),
10938 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10940 if (prefs_common.autosave &&
10941 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10942 compose->draft_timeout_tag != -2 /* disabled while loading */)
10943 compose->draft_timeout_tag = g_timeout_add
10944 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
10948 static void compose_check_all(GtkAction *action, gpointer data)
10950 Compose *compose = (Compose *)data;
10951 if (!compose->gtkaspell)
10954 if (gtkut_widget_has_focus(compose->subject_entry))
10955 claws_spell_entry_check_all(
10956 CLAWS_SPELL_ENTRY(compose->subject_entry));
10958 gtkaspell_check_all(compose->gtkaspell);
10961 static void compose_highlight_all(GtkAction *action, gpointer data)
10963 Compose *compose = (Compose *)data;
10964 if (compose->gtkaspell) {
10965 claws_spell_entry_recheck_all(
10966 CLAWS_SPELL_ENTRY(compose->subject_entry));
10967 gtkaspell_highlight_all(compose->gtkaspell);
10971 static void compose_check_backwards(GtkAction *action, gpointer data)
10973 Compose *compose = (Compose *)data;
10974 if (!compose->gtkaspell) {
10975 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10979 if (gtkut_widget_has_focus(compose->subject_entry))
10980 claws_spell_entry_check_backwards(
10981 CLAWS_SPELL_ENTRY(compose->subject_entry));
10983 gtkaspell_check_backwards(compose->gtkaspell);
10986 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10988 Compose *compose = (Compose *)data;
10989 if (!compose->gtkaspell) {
10990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10994 if (gtkut_widget_has_focus(compose->subject_entry))
10995 claws_spell_entry_check_forwards_go(
10996 CLAWS_SPELL_ENTRY(compose->subject_entry));
10998 gtkaspell_check_forwards_go(compose->gtkaspell);
11003 *\brief Guess originating forward account from MsgInfo and several
11004 * "common preference" settings. Return NULL if no guess.
11006 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11008 PrefsAccount *account = NULL;
11010 cm_return_val_if_fail(msginfo, NULL);
11011 cm_return_val_if_fail(msginfo->folder, NULL);
11012 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11014 if (msginfo->folder->prefs->enable_default_account)
11015 account = account_find_from_id(msginfo->folder->prefs->default_account);
11018 account = msginfo->folder->folder->account;
11020 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11022 Xstrdup_a(to, msginfo->to, return NULL);
11023 extract_address(to);
11024 account = account_find_from_address(to, FALSE);
11027 if (!account && prefs_common.forward_account_autosel) {
11028 gchar cc[BUFFSIZE];
11029 if (!procheader_get_header_from_msginfo
11030 (msginfo, cc,sizeof cc , "Cc:")) {
11031 gchar *buf = cc + strlen("Cc:");
11032 extract_address(buf);
11033 account = account_find_from_address(buf, FALSE);
11037 if (!account && prefs_common.forward_account_autosel) {
11038 gchar deliveredto[BUFFSIZE];
11039 if (!procheader_get_header_from_msginfo
11040 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11041 gchar *buf = deliveredto + strlen("Delivered-To:");
11042 extract_address(buf);
11043 account = account_find_from_address(buf, FALSE);
11050 gboolean compose_close(Compose *compose)
11054 if (!g_mutex_trylock(compose->mutex)) {
11055 /* we have to wait for the (possibly deferred by auto-save)
11056 * drafting to be done, before destroying the compose under
11058 debug_print("waiting for drafting to finish...\n");
11059 compose_allow_user_actions(compose, FALSE);
11060 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11063 cm_return_val_if_fail(compose, FALSE);
11064 gtkut_widget_get_uposition(compose->window, &x, &y);
11065 if (!compose->batch) {
11066 prefs_common.compose_x = x;
11067 prefs_common.compose_y = y;
11069 g_mutex_unlock(compose->mutex);
11070 compose_destroy(compose);
11075 * Add entry field for each address in list.
11076 * \param compose E-Mail composition object.
11077 * \param listAddress List of (formatted) E-Mail addresses.
11079 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11082 node = listAddress;
11084 addr = ( gchar * ) node->data;
11085 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11086 node = g_list_next( node );
11090 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11091 guint action, gboolean opening_multiple)
11093 gchar *body = NULL;
11094 GSList *new_msglist = NULL;
11095 MsgInfo *tmp_msginfo = NULL;
11096 gboolean originally_enc = FALSE;
11097 gboolean originally_sig = FALSE;
11098 Compose *compose = NULL;
11099 gchar *s_system = NULL;
11101 cm_return_if_fail(msgview != NULL);
11103 cm_return_if_fail(msginfo_list != NULL);
11105 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11106 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11107 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11109 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11110 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11111 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11112 orig_msginfo, mimeinfo);
11113 if (tmp_msginfo != NULL) {
11114 new_msglist = g_slist_append(NULL, tmp_msginfo);
11116 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11117 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11118 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11120 tmp_msginfo->folder = orig_msginfo->folder;
11121 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11122 if (orig_msginfo->tags) {
11123 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11124 tmp_msginfo->folder->tags_dirty = TRUE;
11130 if (!opening_multiple)
11131 body = messageview_get_selection(msgview);
11134 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11135 procmsg_msginfo_free(tmp_msginfo);
11136 g_slist_free(new_msglist);
11138 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11140 if (compose && originally_enc) {
11141 compose_force_encryption(compose, compose->account, FALSE, s_system);
11144 if (compose && originally_sig && compose->account->default_sign_reply) {
11145 compose_force_signing(compose, compose->account, s_system);
11149 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11152 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11155 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11156 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11157 GSList *cur = msginfo_list;
11158 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11159 "messages. Opening the windows "
11160 "could take some time. Do you "
11161 "want to continue?"),
11162 g_slist_length(msginfo_list));
11163 if (g_slist_length(msginfo_list) > 9
11164 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11165 != G_ALERTALTERNATE) {
11170 /* We'll open multiple compose windows */
11171 /* let the WM place the next windows */
11172 compose_force_window_origin = FALSE;
11173 for (; cur; cur = cur->next) {
11175 tmplist.data = cur->data;
11176 tmplist.next = NULL;
11177 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11179 compose_force_window_origin = TRUE;
11181 /* forwarding multiple mails as attachments is done via a
11182 * single compose window */
11183 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11187 void compose_check_for_email_account(Compose *compose)
11189 PrefsAccount *ac = NULL, *curr = NULL;
11195 if (compose->account && compose->account->protocol == A_NNTP) {
11196 ac = account_get_cur_account();
11197 if (ac->protocol == A_NNTP) {
11198 list = account_get_list();
11200 for( ; list != NULL ; list = g_list_next(list)) {
11201 curr = (PrefsAccount *) list->data;
11202 if (curr->protocol != A_NNTP) {
11208 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11213 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11214 const gchar *address)
11216 GSList *msginfo_list = NULL;
11217 gchar *body = messageview_get_selection(msgview);
11220 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11222 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11223 compose_check_for_email_account(compose);
11224 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11225 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11226 compose_reply_set_subject(compose, msginfo);
11229 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11232 void compose_set_position(Compose *compose, gint pos)
11234 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11236 gtkut_text_view_set_position(text, pos);
11239 gboolean compose_search_string(Compose *compose,
11240 const gchar *str, gboolean case_sens)
11242 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11244 return gtkut_text_view_search_string(text, str, case_sens);
11247 gboolean compose_search_string_backward(Compose *compose,
11248 const gchar *str, gboolean case_sens)
11250 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11252 return gtkut_text_view_search_string_backward(text, str, case_sens);
11255 /* allocate a msginfo structure and populate its data from a compose data structure */
11256 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11258 MsgInfo *newmsginfo;
11260 gchar buf[BUFFSIZE];
11262 cm_return_val_if_fail( compose != NULL, NULL );
11264 newmsginfo = procmsg_msginfo_new();
11267 get_rfc822_date(buf, sizeof(buf));
11268 newmsginfo->date = g_strdup(buf);
11271 if (compose->from_name) {
11272 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11273 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11277 if (compose->subject_entry)
11278 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11280 /* to, cc, reply-to, newsgroups */
11281 for (list = compose->header_list; list; list = list->next) {
11282 gchar *header = gtk_editable_get_chars(
11284 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11285 gchar *entry = gtk_editable_get_chars(
11286 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11288 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11289 if ( newmsginfo->to == NULL ) {
11290 newmsginfo->to = g_strdup(entry);
11291 } else if (entry && *entry) {
11292 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11293 g_free(newmsginfo->to);
11294 newmsginfo->to = tmp;
11297 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11298 if ( newmsginfo->cc == NULL ) {
11299 newmsginfo->cc = g_strdup(entry);
11300 } else if (entry && *entry) {
11301 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11302 g_free(newmsginfo->cc);
11303 newmsginfo->cc = tmp;
11306 if ( strcasecmp(header,
11307 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11308 if ( newmsginfo->newsgroups == NULL ) {
11309 newmsginfo->newsgroups = g_strdup(entry);
11310 } else if (entry && *entry) {
11311 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11312 g_free(newmsginfo->newsgroups);
11313 newmsginfo->newsgroups = tmp;
11321 /* other data is unset */
11327 /* update compose's dictionaries from folder dict settings */
11328 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11329 FolderItem *folder_item)
11331 cm_return_if_fail(compose != NULL);
11333 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11334 FolderItemPrefs *prefs = folder_item->prefs;
11336 if (prefs->enable_default_dictionary)
11337 gtkaspell_change_dict(compose->gtkaspell,
11338 prefs->default_dictionary, FALSE);
11339 if (folder_item->prefs->enable_default_alt_dictionary)
11340 gtkaspell_change_alt_dict(compose->gtkaspell,
11341 prefs->default_alt_dictionary);
11342 if (prefs->enable_default_dictionary
11343 || prefs->enable_default_alt_dictionary)
11344 compose_spell_menu_changed(compose);