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_text_get_active_text(
4718 GTK_COMBO_BOX_TEXT(header_entry->combo));
4720 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4721 if (account->protocol == A_NNTP) {
4722 if (!strcmp(header, _("To:")))
4723 combobox_select_by_text(
4724 GTK_COMBO_BOX(header_entry->combo),
4727 if (!strcmp(header, _("Newsgroups:")))
4728 combobox_select_by_text(
4729 GTK_COMBO_BOX(header_entry->combo),
4737 /* use account's dict info if set */
4738 if (compose->gtkaspell) {
4739 if (account->enable_default_dictionary)
4740 gtkaspell_change_dict(compose->gtkaspell,
4741 account->default_dictionary, FALSE);
4742 if (account->enable_default_alt_dictionary)
4743 gtkaspell_change_alt_dict(compose->gtkaspell,
4744 account->default_alt_dictionary);
4745 if (account->enable_default_dictionary
4746 || account->enable_default_alt_dictionary)
4747 compose_spell_menu_changed(compose);
4752 gboolean compose_check_for_valid_recipient(Compose *compose) {
4753 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4754 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4755 gboolean recipient_found = FALSE;
4759 /* free to and newsgroup list */
4760 slist_free_strings(compose->to_list);
4761 g_slist_free(compose->to_list);
4762 compose->to_list = NULL;
4764 slist_free_strings(compose->newsgroup_list);
4765 g_slist_free(compose->newsgroup_list);
4766 compose->newsgroup_list = NULL;
4768 /* search header entries for to and newsgroup entries */
4769 for (list = compose->header_list; list; list = list->next) {
4772 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4773 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4776 if (entry[0] != '\0') {
4777 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4778 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4779 compose->to_list = address_list_append(compose->to_list, entry);
4780 recipient_found = TRUE;
4783 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4784 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4785 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4786 recipient_found = TRUE;
4793 return recipient_found;
4796 static gboolean compose_check_for_set_recipients(Compose *compose)
4798 if (compose->account->set_autocc && compose->account->auto_cc) {
4799 gboolean found_other = FALSE;
4801 /* search header entries for to and newsgroup entries */
4802 for (list = compose->header_list; list; list = list->next) {
4805 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4806 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4809 if (strcmp(entry, compose->account->auto_cc)
4810 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4820 if (compose->batch) {
4821 gtk_widget_show_all(compose->window);
4823 aval = alertpanel(_("Send"),
4824 _("The only recipient is the default CC address. Send anyway?"),
4825 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4826 if (aval != G_ALERTALTERNATE)
4830 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4831 gboolean found_other = FALSE;
4833 /* search header entries for to and newsgroup entries */
4834 for (list = compose->header_list; list; list = list->next) {
4837 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4838 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4841 if (strcmp(entry, compose->account->auto_bcc)
4842 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4852 if (compose->batch) {
4853 gtk_widget_show_all(compose->window);
4855 aval = alertpanel(_("Send"),
4856 _("The only recipient is the default BCC address. Send anyway?"),
4857 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4858 if (aval != G_ALERTALTERNATE)
4865 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4869 if (compose_check_for_valid_recipient(compose) == FALSE) {
4870 if (compose->batch) {
4871 gtk_widget_show_all(compose->window);
4873 alertpanel_error(_("Recipient is not specified."));
4877 if (compose_check_for_set_recipients(compose) == FALSE) {
4881 if (!compose->batch) {
4882 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4883 if (*str == '\0' && check_everything == TRUE &&
4884 compose->mode != COMPOSE_REDIRECT) {
4886 gchar *button_label;
4889 if (compose->sending)
4890 button_label = _("+_Send");
4892 button_label = _("+_Queue");
4893 message = g_strdup_printf(_("Subject is empty. %s"),
4894 compose->sending?_("Send it anyway?"):
4895 _("Queue it anyway?"));
4897 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4898 GTK_STOCK_CANCEL, button_label, NULL);
4900 if (aval != G_ALERTALTERNATE)
4905 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4911 gint compose_send(Compose *compose)
4914 FolderItem *folder = NULL;
4916 gchar *msgpath = NULL;
4917 gboolean discard_window = FALSE;
4918 gchar *errstr = NULL;
4919 gchar *tmsgid = NULL;
4920 MainWindow *mainwin = mainwindow_get_mainwindow();
4921 gboolean queued_removed = FALSE;
4923 if (prefs_common.send_dialog_invisible
4924 || compose->batch == TRUE)
4925 discard_window = TRUE;
4927 compose_allow_user_actions (compose, FALSE);
4928 compose->sending = TRUE;
4930 if (compose_check_entries(compose, TRUE) == FALSE) {
4931 if (compose->batch) {
4932 gtk_widget_show_all(compose->window);
4938 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4941 if (compose->batch) {
4942 gtk_widget_show_all(compose->window);
4945 alertpanel_error(_("Could not queue message for sending:\n\n"
4946 "Charset conversion failed."));
4947 } else if (val == -5) {
4948 alertpanel_error(_("Could not queue message for sending:\n\n"
4949 "Couldn't get recipient encryption key."));
4950 } else if (val == -6) {
4952 } else if (val == -3) {
4953 if (privacy_peek_error())
4954 alertpanel_error(_("Could not queue message for sending:\n\n"
4955 "Signature failed: %s"), privacy_get_error());
4956 } else if (val == -2 && errno != 0) {
4957 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4959 alertpanel_error(_("Could not queue message for sending."));
4964 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4965 if (discard_window) {
4966 compose->sending = FALSE;
4967 compose_close(compose);
4968 /* No more compose access in the normal codepath
4969 * after this point! */
4974 alertpanel_error(_("The message was queued but could not be "
4975 "sent.\nUse \"Send queued messages\" from "
4976 "the main window to retry."));
4977 if (!discard_window) {
4984 if (msgpath == NULL) {
4985 msgpath = folder_item_fetch_msg(folder, msgnum);
4986 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4989 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4990 claws_unlink(msgpath);
4993 if (!discard_window) {
4995 if (!queued_removed)
4996 folder_item_remove_msg(folder, msgnum);
4997 folder_item_scan(folder);
4999 /* make sure we delete that */
5000 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5002 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5003 folder_item_remove_msg(folder, tmp->msgnum);
5004 procmsg_msginfo_free(tmp);
5011 if (!queued_removed)
5012 folder_item_remove_msg(folder, msgnum);
5013 folder_item_scan(folder);
5015 /* make sure we delete that */
5016 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5018 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5019 folder_item_remove_msg(folder, tmp->msgnum);
5020 procmsg_msginfo_free(tmp);
5023 if (!discard_window) {
5024 compose->sending = FALSE;
5025 compose_allow_user_actions (compose, TRUE);
5026 compose_close(compose);
5030 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5031 "the main window to retry."), errstr);
5034 alertpanel_error_log(_("The message was queued but could not be "
5035 "sent.\nUse \"Send queued messages\" from "
5036 "the main window to retry."));
5038 if (!discard_window) {
5047 toolbar_main_set_sensitive(mainwin);
5048 main_window_set_menu_sensitive(mainwin);
5054 compose_allow_user_actions (compose, TRUE);
5055 compose->sending = FALSE;
5056 compose->modified = TRUE;
5057 toolbar_main_set_sensitive(mainwin);
5058 main_window_set_menu_sensitive(mainwin);
5063 static gboolean compose_use_attach(Compose *compose)
5065 GtkTreeModel *model = gtk_tree_view_get_model
5066 (GTK_TREE_VIEW(compose->attach_clist));
5067 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5070 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5073 gchar buf[BUFFSIZE];
5075 gboolean first_to_address;
5076 gboolean first_cc_address;
5078 ComposeHeaderEntry *headerentry;
5079 const gchar *headerentryname;
5080 const gchar *cc_hdr;
5081 const gchar *to_hdr;
5082 gboolean err = FALSE;
5084 debug_print("Writing redirect header\n");
5086 cc_hdr = prefs_common_translated_header_name("Cc:");
5087 to_hdr = prefs_common_translated_header_name("To:");
5089 first_to_address = TRUE;
5090 for (list = compose->header_list; list; list = list->next) {
5091 headerentry = ((ComposeHeaderEntry *)list->data);
5092 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5094 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5095 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5096 Xstrdup_a(str, entstr, return -1);
5098 if (str[0] != '\0') {
5099 compose_convert_header
5100 (compose, buf, sizeof(buf), str,
5101 strlen("Resent-To") + 2, TRUE);
5103 if (first_to_address) {
5104 err |= (fprintf(fp, "Resent-To: ") < 0);
5105 first_to_address = FALSE;
5107 err |= (fprintf(fp, ",") < 0);
5109 err |= (fprintf(fp, "%s", buf) < 0);
5113 if (!first_to_address) {
5114 err |= (fprintf(fp, "\n") < 0);
5117 first_cc_address = TRUE;
5118 for (list = compose->header_list; list; list = list->next) {
5119 headerentry = ((ComposeHeaderEntry *)list->data);
5120 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5122 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5123 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5124 Xstrdup_a(str, strg, return -1);
5126 if (str[0] != '\0') {
5127 compose_convert_header
5128 (compose, buf, sizeof(buf), str,
5129 strlen("Resent-Cc") + 2, TRUE);
5131 if (first_cc_address) {
5132 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5133 first_cc_address = FALSE;
5135 err |= (fprintf(fp, ",") < 0);
5137 err |= (fprintf(fp, "%s", buf) < 0);
5141 if (!first_cc_address) {
5142 err |= (fprintf(fp, "\n") < 0);
5145 return (err ? -1:0);
5148 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5150 gchar buf[BUFFSIZE];
5152 const gchar *entstr;
5153 /* struct utsname utsbuf; */
5154 gboolean err = FALSE;
5156 cm_return_val_if_fail(fp != NULL, -1);
5157 cm_return_val_if_fail(compose->account != NULL, -1);
5158 cm_return_val_if_fail(compose->account->address != NULL, -1);
5161 get_rfc822_date(buf, sizeof(buf));
5162 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5165 if (compose->account->name && *compose->account->name) {
5166 compose_convert_header
5167 (compose, buf, sizeof(buf), compose->account->name,
5168 strlen("From: "), TRUE);
5169 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5170 buf, compose->account->address) < 0);
5172 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5175 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5176 if (*entstr != '\0') {
5177 Xstrdup_a(str, entstr, return -1);
5180 compose_convert_header(compose, buf, sizeof(buf), str,
5181 strlen("Subject: "), FALSE);
5182 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5186 /* Resent-Message-ID */
5187 if (compose->account->set_domain && compose->account->domain) {
5188 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5189 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5190 g_snprintf(buf, sizeof(buf), "%s",
5191 strchr(compose->account->address, '@') ?
5192 strchr(compose->account->address, '@')+1 :
5193 compose->account->address);
5195 g_snprintf(buf, sizeof(buf), "%s", "");
5198 if (compose->account->gen_msgid) {
5200 if (compose->account->msgid_with_addr) {
5201 addr = compose->account->address;
5203 generate_msgid(buf, sizeof(buf), addr);
5204 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5205 compose->msgid = g_strdup(buf);
5207 compose->msgid = NULL;
5210 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5213 /* separator between header and body */
5214 err |= (fputs("\n", fp) == EOF);
5216 return (err ? -1:0);
5219 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5223 gchar buf[BUFFSIZE];
5225 gboolean skip = FALSE;
5226 gboolean err = FALSE;
5227 gchar *not_included[]={
5228 "Return-Path:", "Delivered-To:", "Received:",
5229 "Subject:", "X-UIDL:", "AF:",
5230 "NF:", "PS:", "SRH:",
5231 "SFN:", "DSR:", "MID:",
5232 "CFG:", "PT:", "S:",
5233 "RQ:", "SSV:", "NSV:",
5234 "SSH:", "R:", "MAID:",
5235 "NAID:", "RMID:", "FMID:",
5236 "SCF:", "RRCPT:", "NG:",
5237 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5238 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5239 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5240 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5241 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5244 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5245 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5249 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5251 for (i = 0; not_included[i] != NULL; i++) {
5252 if (g_ascii_strncasecmp(buf, not_included[i],
5253 strlen(not_included[i])) == 0) {
5260 if (fputs(buf, fdest) == -1)
5263 if (!prefs_common.redirect_keep_from) {
5264 if (g_ascii_strncasecmp(buf, "From:",
5265 strlen("From:")) == 0) {
5266 err |= (fputs(" (by way of ", fdest) == EOF);
5267 if (compose->account->name
5268 && *compose->account->name) {
5269 compose_convert_header
5270 (compose, buf, sizeof(buf),
5271 compose->account->name,
5274 err |= (fprintf(fdest, "%s <%s>",
5276 compose->account->address) < 0);
5278 err |= (fprintf(fdest, "%s",
5279 compose->account->address) < 0);
5280 err |= (fputs(")", fdest) == EOF);
5284 if (fputs("\n", fdest) == -1)
5291 if (compose_redirect_write_headers(compose, fdest))
5294 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5295 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5308 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5310 GtkTextBuffer *buffer;
5311 GtkTextIter start, end;
5314 const gchar *out_codeset;
5315 EncodingType encoding = ENC_UNKNOWN;
5316 MimeInfo *mimemsg, *mimetext;
5318 const gchar *src_codeset = CS_INTERNAL;
5319 gchar *from_addr = NULL;
5320 gchar *from_name = NULL;
5322 if (action == COMPOSE_WRITE_FOR_SEND)
5323 attach_parts = TRUE;
5325 /* create message MimeInfo */
5326 mimemsg = procmime_mimeinfo_new();
5327 mimemsg->type = MIMETYPE_MESSAGE;
5328 mimemsg->subtype = g_strdup("rfc822");
5329 mimemsg->content = MIMECONTENT_MEM;
5330 mimemsg->tmp = TRUE; /* must free content later */
5331 mimemsg->data.mem = compose_get_header(compose);
5333 /* Create text part MimeInfo */
5334 /* get all composed text */
5335 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5336 gtk_text_buffer_get_start_iter(buffer, &start);
5337 gtk_text_buffer_get_end_iter(buffer, &end);
5338 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5340 out_codeset = conv_get_charset_str(compose->out_encoding);
5342 if (!out_codeset && is_ascii_str(chars)) {
5343 out_codeset = CS_US_ASCII;
5344 } else if (prefs_common.outgoing_fallback_to_ascii &&
5345 is_ascii_str(chars)) {
5346 out_codeset = CS_US_ASCII;
5347 encoding = ENC_7BIT;
5351 gchar *test_conv_global_out = NULL;
5352 gchar *test_conv_reply = NULL;
5354 /* automatic mode. be automatic. */
5355 codeconv_set_strict(TRUE);
5357 out_codeset = conv_get_outgoing_charset_str();
5359 debug_print("trying to convert to %s\n", out_codeset);
5360 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5363 if (!test_conv_global_out && compose->orig_charset
5364 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5365 out_codeset = compose->orig_charset;
5366 debug_print("failure; trying to convert to %s\n", out_codeset);
5367 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5370 if (!test_conv_global_out && !test_conv_reply) {
5372 out_codeset = CS_INTERNAL;
5373 debug_print("failure; finally using %s\n", out_codeset);
5375 g_free(test_conv_global_out);
5376 g_free(test_conv_reply);
5377 codeconv_set_strict(FALSE);
5380 if (encoding == ENC_UNKNOWN) {
5381 if (prefs_common.encoding_method == CTE_BASE64)
5382 encoding = ENC_BASE64;
5383 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5384 encoding = ENC_QUOTED_PRINTABLE;
5385 else if (prefs_common.encoding_method == CTE_8BIT)
5386 encoding = ENC_8BIT;
5388 encoding = procmime_get_encoding_for_charset(out_codeset);
5391 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5392 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5394 if (action == COMPOSE_WRITE_FOR_SEND) {
5395 codeconv_set_strict(TRUE);
5396 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5397 codeconv_set_strict(FALSE);
5403 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5404 "to the specified %s charset.\n"
5405 "Send it as %s?"), out_codeset, src_codeset);
5406 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5407 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5410 if (aval != G_ALERTALTERNATE) {
5415 out_codeset = src_codeset;
5421 out_codeset = src_codeset;
5426 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5427 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5428 strstr(buf, "\nFrom ") != NULL) {
5429 encoding = ENC_QUOTED_PRINTABLE;
5433 mimetext = procmime_mimeinfo_new();
5434 mimetext->content = MIMECONTENT_MEM;
5435 mimetext->tmp = TRUE; /* must free content later */
5436 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5437 * and free the data, which we need later. */
5438 mimetext->data.mem = g_strdup(buf);
5439 mimetext->type = MIMETYPE_TEXT;
5440 mimetext->subtype = g_strdup("plain");
5441 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5442 g_strdup(out_codeset));
5444 /* protect trailing spaces when signing message */
5445 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5446 privacy_system_can_sign(compose->privacy_system)) {
5447 encoding = ENC_QUOTED_PRINTABLE;
5450 debug_print("main text: %zd bytes encoded as %s in %d\n",
5451 strlen(buf), out_codeset, encoding);
5453 /* check for line length limit */
5454 if (action == COMPOSE_WRITE_FOR_SEND &&
5455 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5456 check_line_length(buf, 1000, &line) < 0) {
5460 msg = g_strdup_printf
5461 (_("Line %d exceeds the line length limit (998 bytes).\n"
5462 "The contents of the message might be broken on the way to the delivery.\n"
5464 "Send it anyway?"), line + 1);
5465 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5467 if (aval != G_ALERTALTERNATE) {
5473 if (encoding != ENC_UNKNOWN)
5474 procmime_encode_content(mimetext, encoding);
5476 /* append attachment parts */
5477 if (compose_use_attach(compose) && attach_parts) {
5478 MimeInfo *mimempart;
5479 gchar *boundary = NULL;
5480 mimempart = procmime_mimeinfo_new();
5481 mimempart->content = MIMECONTENT_EMPTY;
5482 mimempart->type = MIMETYPE_MULTIPART;
5483 mimempart->subtype = g_strdup("mixed");
5487 boundary = generate_mime_boundary(NULL);
5488 } while (strstr(buf, boundary) != NULL);
5490 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5493 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5495 g_node_append(mimempart->node, mimetext->node);
5496 g_node_append(mimemsg->node, mimempart->node);
5498 if (compose_add_attachments(compose, mimempart) < 0)
5501 g_node_append(mimemsg->node, mimetext->node);
5505 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5506 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5507 /* extract name and address */
5508 if (strstr(spec, " <") && strstr(spec, ">")) {
5509 from_addr = g_strdup(strrchr(spec, '<')+1);
5510 *(strrchr(from_addr, '>')) = '\0';
5511 from_name = g_strdup(spec);
5512 *(strrchr(from_name, '<')) = '\0';
5519 /* sign message if sending */
5520 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5521 privacy_system_can_sign(compose->privacy_system))
5522 if (!privacy_sign(compose->privacy_system, mimemsg,
5523 compose->account, from_addr)) {
5530 procmime_write_mimeinfo(mimemsg, fp);
5532 procmime_mimeinfo_free_all(mimemsg);
5537 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5539 GtkTextBuffer *buffer;
5540 GtkTextIter start, end;
5545 if ((fp = g_fopen(file, "wb")) == NULL) {
5546 FILE_OP_ERROR(file, "fopen");
5550 /* chmod for security */
5551 if (change_file_mode_rw(fp, file) < 0) {
5552 FILE_OP_ERROR(file, "chmod");
5553 g_warning("can't change file mode\n");
5556 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5557 gtk_text_buffer_get_start_iter(buffer, &start);
5558 gtk_text_buffer_get_end_iter(buffer, &end);
5559 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5561 chars = conv_codeset_strdup
5562 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5565 if (!chars) return -1;
5568 len = strlen(chars);
5569 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5570 FILE_OP_ERROR(file, "fwrite");
5579 if (fclose(fp) == EOF) {
5580 FILE_OP_ERROR(file, "fclose");
5587 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5590 MsgInfo *msginfo = compose->targetinfo;
5592 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5593 if (!msginfo) return -1;
5595 if (!force && MSG_IS_LOCKED(msginfo->flags))
5598 item = msginfo->folder;
5599 cm_return_val_if_fail(item != NULL, -1);
5601 if (procmsg_msg_exist(msginfo) &&
5602 (folder_has_parent_of_type(item, F_QUEUE) ||
5603 folder_has_parent_of_type(item, F_DRAFT)
5604 || msginfo == compose->autosaved_draft)) {
5605 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5606 g_warning("can't remove the old message\n");
5609 debug_print("removed reedit target %d\n", msginfo->msgnum);
5616 static void compose_remove_draft(Compose *compose)
5619 MsgInfo *msginfo = compose->targetinfo;
5620 drafts = account_get_special_folder(compose->account, F_DRAFT);
5622 if (procmsg_msg_exist(msginfo)) {
5623 folder_item_remove_msg(drafts, msginfo->msgnum);
5628 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5629 gboolean remove_reedit_target)
5631 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5634 static gboolean compose_warn_encryption(Compose *compose)
5636 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5637 AlertValue val = G_ALERTALTERNATE;
5639 if (warning == NULL)
5642 val = alertpanel_full(_("Encryption warning"), warning,
5643 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5644 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5645 if (val & G_ALERTDISABLE) {
5646 val &= ~G_ALERTDISABLE;
5647 if (val == G_ALERTALTERNATE)
5648 privacy_inhibit_encrypt_warning(compose->privacy_system,
5652 if (val == G_ALERTALTERNATE) {
5659 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5660 gchar **msgpath, gboolean check_subject,
5661 gboolean remove_reedit_target)
5668 static gboolean lock = FALSE;
5669 PrefsAccount *mailac = NULL, *newsac = NULL;
5670 gboolean err = FALSE;
5672 debug_print("queueing message...\n");
5673 cm_return_val_if_fail(compose->account != NULL, -1);
5677 if (compose_check_entries(compose, check_subject) == FALSE) {
5679 if (compose->batch) {
5680 gtk_widget_show_all(compose->window);
5685 if (!compose->to_list && !compose->newsgroup_list) {
5686 g_warning("can't get recipient list.");
5691 if (compose->to_list) {
5692 if (compose->account->protocol != A_NNTP)
5693 mailac = compose->account;
5694 else if (cur_account && cur_account->protocol != A_NNTP)
5695 mailac = cur_account;
5696 else if (!(mailac = compose_current_mail_account())) {
5698 alertpanel_error(_("No account for sending mails available!"));
5703 if (compose->newsgroup_list) {
5704 if (compose->account->protocol == A_NNTP)
5705 newsac = compose->account;
5708 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5713 /* write queue header */
5714 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5715 G_DIR_SEPARATOR, compose, (guint) rand());
5716 debug_print("queuing to %s\n", tmp);
5717 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5718 FILE_OP_ERROR(tmp, "fopen");
5724 if (change_file_mode_rw(fp, tmp) < 0) {
5725 FILE_OP_ERROR(tmp, "chmod");
5726 g_warning("can't change file mode\n");
5729 /* queueing variables */
5730 err |= (fprintf(fp, "AF:\n") < 0);
5731 err |= (fprintf(fp, "NF:0\n") < 0);
5732 err |= (fprintf(fp, "PS:10\n") < 0);
5733 err |= (fprintf(fp, "SRH:1\n") < 0);
5734 err |= (fprintf(fp, "SFN:\n") < 0);
5735 err |= (fprintf(fp, "DSR:\n") < 0);
5737 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5739 err |= (fprintf(fp, "MID:\n") < 0);
5740 err |= (fprintf(fp, "CFG:\n") < 0);
5741 err |= (fprintf(fp, "PT:0\n") < 0);
5742 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5743 err |= (fprintf(fp, "RQ:\n") < 0);
5745 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5747 err |= (fprintf(fp, "SSV:\n") < 0);
5749 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5751 err |= (fprintf(fp, "NSV:\n") < 0);
5752 err |= (fprintf(fp, "SSH:\n") < 0);
5753 /* write recepient list */
5754 if (compose->to_list) {
5755 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5756 for (cur = compose->to_list->next; cur != NULL;
5758 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5759 err |= (fprintf(fp, "\n") < 0);
5761 /* write newsgroup list */
5762 if (compose->newsgroup_list) {
5763 err |= (fprintf(fp, "NG:") < 0);
5764 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5765 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5766 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5767 err |= (fprintf(fp, "\n") < 0);
5769 /* Sylpheed account IDs */
5771 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5773 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5776 if (compose->privacy_system != NULL) {
5777 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5778 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5779 if (compose->use_encryption) {
5781 if (!compose_warn_encryption(compose)) {
5788 if (mailac && mailac->encrypt_to_self) {
5789 GSList *tmp_list = g_slist_copy(compose->to_list);
5790 tmp_list = g_slist_append(tmp_list, compose->account->address);
5791 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5792 g_slist_free(tmp_list);
5794 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5796 if (encdata != NULL) {
5797 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5798 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5799 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5801 } /* else we finally dont want to encrypt */
5803 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5804 /* and if encdata was null, it means there's been a problem in
5816 /* Save copy folder */
5817 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5818 gchar *savefolderid;
5820 savefolderid = compose_get_save_to(compose);
5821 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5822 g_free(savefolderid);
5824 /* Save copy folder */
5825 if (compose->return_receipt) {
5826 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5828 /* Message-ID of message replying to */
5829 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5832 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5833 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5836 /* Message-ID of message forwarding to */
5837 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5840 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5841 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5845 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5846 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5848 /* end of headers */
5849 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5851 if (compose->redirect_filename != NULL) {
5852 if (compose_redirect_write_to_file(compose, fp) < 0) {
5861 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5866 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5870 g_warning("failed to write queue message\n");
5877 if (fclose(fp) == EOF) {
5878 FILE_OP_ERROR(tmp, "fclose");
5885 if (item && *item) {
5888 queue = account_get_special_folder(compose->account, F_QUEUE);
5891 g_warning("can't find queue folder\n");
5897 folder_item_scan(queue);
5898 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5899 g_warning("can't queue the message\n");
5906 if (msgpath == NULL) {
5912 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5913 compose_remove_reedit_target(compose, FALSE);
5916 if ((msgnum != NULL) && (item != NULL)) {
5924 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5927 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5929 struct stat statbuf;
5930 gchar *type, *subtype;
5931 GtkTreeModel *model;
5934 model = gtk_tree_view_get_model(tree_view);
5936 if (!gtk_tree_model_get_iter_first(model, &iter))
5939 gtk_tree_model_get(model, &iter,
5943 if (!is_file_exist(ainfo->file)) {
5944 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5945 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5946 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5948 if (val == G_ALERTDEFAULT) {
5953 mimepart = procmime_mimeinfo_new();
5954 mimepart->content = MIMECONTENT_FILE;
5955 mimepart->data.filename = g_strdup(ainfo->file);
5956 mimepart->tmp = FALSE; /* or we destroy our attachment */
5957 mimepart->offset = 0;
5959 g_stat(ainfo->file, &statbuf);
5960 mimepart->length = statbuf.st_size;
5962 type = g_strdup(ainfo->content_type);
5964 if (!strchr(type, '/')) {
5966 type = g_strdup("application/octet-stream");
5969 subtype = strchr(type, '/') + 1;
5970 *(subtype - 1) = '\0';
5971 mimepart->type = procmime_get_media_type(type);
5972 mimepart->subtype = g_strdup(subtype);
5975 if (mimepart->type == MIMETYPE_MESSAGE &&
5976 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5977 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5978 } else if (mimepart->type == MIMETYPE_TEXT) {
5979 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
5980 /* Text parts with no name come from multipart/alternative
5981 * forwards. Make sure the recipient won't look at the
5982 * original HTML part by mistake. */
5983 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5984 ainfo->name = g_strdup_printf(_("Original %s part"),
5988 g_hash_table_insert(mimepart->typeparameters,
5989 g_strdup("charset"), g_strdup(ainfo->charset));
5991 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
5992 if (mimepart->type == MIMETYPE_APPLICATION &&
5993 !strcmp2(mimepart->subtype, "octet-stream"))
5994 g_hash_table_insert(mimepart->typeparameters,
5995 g_strdup("name"), g_strdup(ainfo->name));
5996 g_hash_table_insert(mimepart->dispositionparameters,
5997 g_strdup("filename"), g_strdup(ainfo->name));
5998 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6001 if (mimepart->type == MIMETYPE_MESSAGE
6002 || mimepart->type == MIMETYPE_MULTIPART)
6003 ainfo->encoding = ENC_BINARY;
6004 else if (compose->use_signing) {
6005 if (ainfo->encoding == ENC_7BIT)
6006 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6007 else if (ainfo->encoding == ENC_8BIT)
6008 ainfo->encoding = ENC_BASE64;
6013 procmime_encode_content(mimepart, ainfo->encoding);
6015 g_node_append(parent->node, mimepart->node);
6016 } while (gtk_tree_model_iter_next(model, &iter));
6021 #define IS_IN_CUSTOM_HEADER(header) \
6022 (compose->account->add_customhdr && \
6023 custom_header_find(compose->account->customhdr_list, header) != NULL)
6025 static void compose_add_headerfield_from_headerlist(Compose *compose,
6027 const gchar *fieldname,
6028 const gchar *seperator)
6030 gchar *str, *fieldname_w_colon;
6031 gboolean add_field = FALSE;
6033 ComposeHeaderEntry *headerentry;
6034 const gchar *headerentryname;
6035 const gchar *trans_fieldname;
6038 if (IS_IN_CUSTOM_HEADER(fieldname))
6041 debug_print("Adding %s-fields\n", fieldname);
6043 fieldstr = g_string_sized_new(64);
6045 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6046 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6048 for (list = compose->header_list; list; list = list->next) {
6049 headerentry = ((ComposeHeaderEntry *)list->data);
6050 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6052 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6053 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6055 if (str[0] != '\0') {
6057 g_string_append(fieldstr, seperator);
6058 g_string_append(fieldstr, str);
6067 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6068 compose_convert_header
6069 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6070 strlen(fieldname) + 2, TRUE);
6071 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6075 g_free(fieldname_w_colon);
6076 g_string_free(fieldstr, TRUE);
6081 static gchar *compose_get_header(Compose *compose)
6083 gchar buf[BUFFSIZE];
6084 const gchar *entry_str;
6088 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6090 gchar *from_name = NULL, *from_address = NULL;
6093 cm_return_val_if_fail(compose->account != NULL, NULL);
6094 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6096 header = g_string_sized_new(64);
6099 get_rfc822_date(buf, sizeof(buf));
6100 g_string_append_printf(header, "Date: %s\n", buf);
6104 if (compose->account->name && *compose->account->name) {
6106 QUOTE_IF_REQUIRED(buf, compose->account->name);
6107 tmp = g_strdup_printf("%s <%s>",
6108 buf, compose->account->address);
6110 tmp = g_strdup_printf("%s",
6111 compose->account->address);
6113 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6114 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6116 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6117 from_address = g_strdup(compose->account->address);
6119 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6120 /* extract name and address */
6121 if (strstr(spec, " <") && strstr(spec, ">")) {
6122 from_address = g_strdup(strrchr(spec, '<')+1);
6123 *(strrchr(from_address, '>')) = '\0';
6124 from_name = g_strdup(spec);
6125 *(strrchr(from_name, '<')) = '\0';
6128 from_address = g_strdup(spec);
6135 if (from_name && *from_name) {
6136 compose_convert_header
6137 (compose, buf, sizeof(buf), from_name,
6138 strlen("From: "), TRUE);
6139 QUOTE_IF_REQUIRED(name, buf);
6141 g_string_append_printf(header, "From: %s <%s>\n",
6142 name, from_address);
6144 g_string_append_printf(header, "From: %s\n", from_address);
6147 g_free(from_address);
6150 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6153 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6156 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6160 * If this account is a NNTP account remove Bcc header from
6161 * message body since it otherwise will be publicly shown
6163 if (compose->account->protocol != A_NNTP)
6164 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6167 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6169 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6172 compose_convert_header(compose, buf, sizeof(buf), str,
6173 strlen("Subject: "), FALSE);
6174 g_string_append_printf(header, "Subject: %s\n", buf);
6180 if (compose->account->set_domain && compose->account->domain) {
6181 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6182 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6183 g_snprintf(buf, sizeof(buf), "%s",
6184 strchr(compose->account->address, '@') ?
6185 strchr(compose->account->address, '@')+1 :
6186 compose->account->address);
6188 g_snprintf(buf, sizeof(buf), "%s", "");
6191 if (compose->account->gen_msgid) {
6193 if (compose->account->msgid_with_addr) {
6194 addr = compose->account->address;
6196 generate_msgid(buf, sizeof(buf), addr);
6197 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6198 compose->msgid = g_strdup(buf);
6200 compose->msgid = NULL;
6203 if (compose->remove_references == FALSE) {
6205 if (compose->inreplyto && compose->to_list)
6206 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6209 if (compose->references)
6210 g_string_append_printf(header, "References: %s\n", compose->references);
6214 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6217 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6220 if (compose->account->organization &&
6221 strlen(compose->account->organization) &&
6222 !IS_IN_CUSTOM_HEADER("Organization")) {
6223 compose_convert_header(compose, buf, sizeof(buf),
6224 compose->account->organization,
6225 strlen("Organization: "), FALSE);
6226 g_string_append_printf(header, "Organization: %s\n", buf);
6229 /* Program version and system info */
6230 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6231 !compose->newsgroup_list) {
6232 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6234 gtk_major_version, gtk_minor_version, gtk_micro_version,
6237 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6238 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6240 gtk_major_version, gtk_minor_version, gtk_micro_version,
6244 /* custom headers */
6245 if (compose->account->add_customhdr) {
6248 for (cur = compose->account->customhdr_list; cur != NULL;
6250 CustomHeader *chdr = (CustomHeader *)cur->data;
6252 if (custom_header_is_allowed(chdr->name)) {
6253 compose_convert_header
6254 (compose, buf, sizeof(buf),
6255 chdr->value ? chdr->value : "",
6256 strlen(chdr->name) + 2, FALSE);
6257 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6262 /* Automatic Faces and X-Faces */
6263 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6264 g_string_append_printf(header, "X-Face: %s\n", buf);
6266 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6267 g_string_append_printf(header, "X-Face: %s\n", buf);
6269 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6270 g_string_append_printf(header, "Face: %s\n", buf);
6272 else if (get_default_face (buf, sizeof(buf)) == 0) {
6273 g_string_append_printf(header, "Face: %s\n", buf);
6277 switch (compose->priority) {
6278 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6279 "X-Priority: 1 (Highest)\n");
6281 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6282 "X-Priority: 2 (High)\n");
6284 case PRIORITY_NORMAL: break;
6285 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6286 "X-Priority: 4 (Low)\n");
6288 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6289 "X-Priority: 5 (Lowest)\n");
6291 default: debug_print("compose: priority unknown : %d\n",
6295 /* Request Return Receipt */
6296 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6297 if (compose->return_receipt) {
6298 if (compose->account->name
6299 && *compose->account->name) {
6300 compose_convert_header(compose, buf, sizeof(buf),
6301 compose->account->name,
6302 strlen("Disposition-Notification-To: "),
6304 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6306 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6310 /* get special headers */
6311 for (list = compose->header_list; list; list = list->next) {
6312 ComposeHeaderEntry *headerentry;
6315 gchar *headername_wcolon;
6316 const gchar *headername_trans;
6319 gboolean standard_header = FALSE;
6321 headerentry = ((ComposeHeaderEntry *)list->data);
6323 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6325 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6330 if (!strstr(tmp, ":")) {
6331 headername_wcolon = g_strconcat(tmp, ":", NULL);
6332 headername = g_strdup(tmp);
6334 headername_wcolon = g_strdup(tmp);
6335 headername = g_strdup(strtok(tmp, ":"));
6339 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6340 Xstrdup_a(headervalue, entry_str, return NULL);
6341 subst_char(headervalue, '\r', ' ');
6342 subst_char(headervalue, '\n', ' ');
6343 string = std_headers;
6344 while (*string != NULL) {
6345 headername_trans = prefs_common_translated_header_name(*string);
6346 if (!strcmp(headername_trans, headername_wcolon))
6347 standard_header = TRUE;
6350 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6351 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6354 g_free(headername_wcolon);
6358 g_string_free(header, FALSE);
6363 #undef IS_IN_CUSTOM_HEADER
6365 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6366 gint header_len, gboolean addr_field)
6368 gchar *tmpstr = NULL;
6369 const gchar *out_codeset = NULL;
6371 cm_return_if_fail(src != NULL);
6372 cm_return_if_fail(dest != NULL);
6374 if (len < 1) return;
6376 tmpstr = g_strdup(src);
6378 subst_char(tmpstr, '\n', ' ');
6379 subst_char(tmpstr, '\r', ' ');
6382 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6383 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6384 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6389 codeconv_set_strict(TRUE);
6390 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6391 conv_get_charset_str(compose->out_encoding));
6392 codeconv_set_strict(FALSE);
6394 if (!dest || *dest == '\0') {
6395 gchar *test_conv_global_out = NULL;
6396 gchar *test_conv_reply = NULL;
6398 /* automatic mode. be automatic. */
6399 codeconv_set_strict(TRUE);
6401 out_codeset = conv_get_outgoing_charset_str();
6403 debug_print("trying to convert to %s\n", out_codeset);
6404 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6407 if (!test_conv_global_out && compose->orig_charset
6408 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6409 out_codeset = compose->orig_charset;
6410 debug_print("failure; trying to convert to %s\n", out_codeset);
6411 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6414 if (!test_conv_global_out && !test_conv_reply) {
6416 out_codeset = CS_INTERNAL;
6417 debug_print("finally using %s\n", out_codeset);
6419 g_free(test_conv_global_out);
6420 g_free(test_conv_reply);
6421 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6423 codeconv_set_strict(FALSE);
6428 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6432 cm_return_if_fail(user_data != NULL);
6434 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6435 g_strstrip(address);
6436 if (*address != '\0') {
6437 gchar *name = procheader_get_fromname(address);
6438 extract_address(address);
6439 addressbook_add_contact(name, address, NULL, NULL);
6444 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6446 GtkWidget *menuitem;
6449 cm_return_if_fail(menu != NULL);
6450 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6452 menuitem = gtk_separator_menu_item_new();
6453 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6454 gtk_widget_show(menuitem);
6456 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6457 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6459 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6460 g_strstrip(address);
6461 if (*address == '\0') {
6462 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6465 g_signal_connect(G_OBJECT(menuitem), "activate",
6466 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6467 gtk_widget_show(menuitem);
6470 static void compose_create_header_entry(Compose *compose)
6472 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6479 const gchar *header = NULL;
6480 ComposeHeaderEntry *headerentry;
6481 gboolean standard_header = FALSE;
6482 GtkListStore *model;
6484 #if !(GTK_CHECK_VERSION(2,12,0))
6485 GtkTooltips *tips = compose->tooltips;
6488 headerentry = g_new0(ComposeHeaderEntry, 1);
6491 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6492 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6493 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6495 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6497 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6499 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6500 COMPOSE_NEWSGROUPS);
6501 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6503 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6504 COMPOSE_FOLLOWUPTO);
6506 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6507 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6508 G_CALLBACK(compose_grab_focus_cb), compose);
6509 gtk_widget_show(combo);
6510 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6511 compose->header_nextrow, compose->header_nextrow+1,
6512 GTK_SHRINK, GTK_FILL, 0, 0);
6513 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6514 const gchar *last_header_entry = gtk_entry_get_text(
6515 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6517 while (*string != NULL) {
6518 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6519 standard_header = TRUE;
6522 if (standard_header)
6523 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6525 if (!compose->header_last || !standard_header) {
6526 switch(compose->account->protocol) {
6528 header = prefs_common_translated_header_name("Newsgroups:");
6531 header = prefs_common_translated_header_name("To:");
6536 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6538 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6539 G_CALLBACK(compose_grab_focus_cb), compose);
6541 /* Entry field with cleanup button */
6542 button = gtk_button_new();
6543 gtk_button_set_image(GTK_BUTTON(button),
6544 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6545 gtk_widget_show(button);
6546 CLAWS_SET_TIP(button,
6547 _("Delete entry contents"));
6548 entry = gtk_entry_new();
6549 gtk_widget_show(entry);
6550 CLAWS_SET_TIP(entry,
6551 _("Use <tab> to autocomplete from addressbook"));
6552 hbox = gtk_hbox_new (FALSE, 0);
6553 gtk_widget_show(hbox);
6554 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6555 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6556 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6557 compose->header_nextrow, compose->header_nextrow+1,
6558 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6560 g_signal_connect(G_OBJECT(entry), "key-press-event",
6561 G_CALLBACK(compose_headerentry_key_press_event_cb),
6563 g_signal_connect(G_OBJECT(entry), "changed",
6564 G_CALLBACK(compose_headerentry_changed_cb),
6566 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6567 G_CALLBACK(compose_grab_focus_cb), compose);
6569 g_signal_connect(G_OBJECT(button), "clicked",
6570 G_CALLBACK(compose_headerentry_button_clicked_cb),
6574 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6575 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6576 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6577 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6578 G_CALLBACK(compose_header_drag_received_cb),
6580 g_signal_connect(G_OBJECT(entry), "drag-drop",
6581 G_CALLBACK(compose_drag_drop),
6583 g_signal_connect(G_OBJECT(entry), "populate-popup",
6584 G_CALLBACK(compose_entry_popup_extend),
6587 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6589 headerentry->compose = compose;
6590 headerentry->combo = combo;
6591 headerentry->entry = entry;
6592 headerentry->button = button;
6593 headerentry->hbox = hbox;
6594 headerentry->headernum = compose->header_nextrow;
6595 headerentry->type = PREF_NONE;
6597 compose->header_nextrow++;
6598 compose->header_last = headerentry;
6599 compose->header_list =
6600 g_slist_append(compose->header_list,
6604 static void compose_add_header_entry(Compose *compose, const gchar *header,
6605 gchar *text, ComposePrefType pref_type)
6607 ComposeHeaderEntry *last_header = compose->header_last;
6608 gchar *tmp = g_strdup(text), *email;
6609 gboolean replyto_hdr;
6611 replyto_hdr = (!strcasecmp(header,
6612 prefs_common_translated_header_name("Reply-To:")) ||
6614 prefs_common_translated_header_name("Followup-To:")) ||
6616 prefs_common_translated_header_name("In-Reply-To:")));
6618 extract_address(tmp);
6619 email = g_utf8_strdown(tmp, -1);
6621 if (replyto_hdr == FALSE &&
6622 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6624 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6625 header, text, (gint) pref_type);
6631 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6632 gtk_entry_set_text(GTK_ENTRY(
6633 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6635 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6636 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6637 last_header->type = pref_type;
6639 if (replyto_hdr == FALSE)
6640 g_hash_table_insert(compose->email_hashtable, email,
6641 GUINT_TO_POINTER(1));
6648 static void compose_destroy_headerentry(Compose *compose,
6649 ComposeHeaderEntry *headerentry)
6651 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6654 extract_address(text);
6655 email = g_utf8_strdown(text, -1);
6656 g_hash_table_remove(compose->email_hashtable, email);
6660 gtk_widget_destroy(headerentry->combo);
6661 gtk_widget_destroy(headerentry->entry);
6662 gtk_widget_destroy(headerentry->button);
6663 gtk_widget_destroy(headerentry->hbox);
6664 g_free(headerentry);
6667 static void compose_remove_header_entries(Compose *compose)
6670 for (list = compose->header_list; list; list = list->next)
6671 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6673 compose->header_last = NULL;
6674 g_slist_free(compose->header_list);
6675 compose->header_list = NULL;
6676 compose->header_nextrow = 1;
6677 compose_create_header_entry(compose);
6680 static GtkWidget *compose_create_header(Compose *compose)
6682 GtkWidget *from_optmenu_hbox;
6683 GtkWidget *header_scrolledwin;
6684 GtkWidget *header_table;
6688 /* header labels and entries */
6689 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6690 gtk_widget_show(header_scrolledwin);
6691 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6693 header_table = gtk_table_new(2, 2, FALSE);
6694 gtk_widget_show(header_table);
6695 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6696 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6697 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6700 /* option menu for selecting accounts */
6701 from_optmenu_hbox = compose_account_option_menu_create(compose);
6702 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6703 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6706 compose->header_table = header_table;
6707 compose->header_list = NULL;
6708 compose->header_nextrow = count;
6710 compose_create_header_entry(compose);
6712 compose->table = NULL;
6714 return header_scrolledwin ;
6717 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6719 Compose *compose = (Compose *)data;
6720 GdkEventButton event;
6723 event.time = gtk_get_current_event_time();
6725 return attach_button_pressed(compose->attach_clist, &event, compose);
6728 static GtkWidget *compose_create_attach(Compose *compose)
6730 GtkWidget *attach_scrwin;
6731 GtkWidget *attach_clist;
6733 GtkListStore *store;
6734 GtkCellRenderer *renderer;
6735 GtkTreeViewColumn *column;
6736 GtkTreeSelection *selection;
6738 /* attachment list */
6739 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6740 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6741 GTK_POLICY_AUTOMATIC,
6742 GTK_POLICY_AUTOMATIC);
6743 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6745 store = gtk_list_store_new(N_ATTACH_COLS,
6751 G_TYPE_AUTO_POINTER,
6753 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6754 (GTK_TREE_MODEL(store)));
6755 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6756 g_object_unref(store);
6758 renderer = gtk_cell_renderer_text_new();
6759 column = gtk_tree_view_column_new_with_attributes
6760 (_("Mime type"), renderer, "text",
6761 COL_MIMETYPE, NULL);
6762 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6764 renderer = gtk_cell_renderer_text_new();
6765 column = gtk_tree_view_column_new_with_attributes
6766 (_("Size"), renderer, "text",
6768 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6770 renderer = gtk_cell_renderer_text_new();
6771 column = gtk_tree_view_column_new_with_attributes
6772 (_("Name"), renderer, "text",
6774 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6776 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6777 prefs_common.use_stripes_everywhere);
6778 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6779 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6781 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6782 G_CALLBACK(attach_selected), compose);
6783 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6784 G_CALLBACK(attach_button_pressed), compose);
6786 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6787 G_CALLBACK(popup_attach_button_pressed), compose);
6789 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6790 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6791 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6792 G_CALLBACK(popup_attach_button_pressed), compose);
6794 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6795 G_CALLBACK(attach_key_pressed), compose);
6798 gtk_drag_dest_set(attach_clist,
6799 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6800 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6801 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6802 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6803 G_CALLBACK(compose_attach_drag_received_cb),
6805 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6806 G_CALLBACK(compose_drag_drop),
6809 compose->attach_scrwin = attach_scrwin;
6810 compose->attach_clist = attach_clist;
6812 return attach_scrwin;
6815 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6816 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6818 static GtkWidget *compose_create_others(Compose *compose)
6821 GtkWidget *savemsg_checkbtn;
6822 GtkWidget *savemsg_combo;
6823 GtkWidget *savemsg_select;
6826 gchar *folderidentifier;
6828 /* Table for settings */
6829 table = gtk_table_new(3, 1, FALSE);
6830 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6831 gtk_widget_show(table);
6832 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6835 /* Save Message to folder */
6836 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6837 gtk_widget_show(savemsg_checkbtn);
6838 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6839 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6840 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6842 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6843 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6845 savemsg_combo = gtk_combo_box_entry_new_text();
6846 compose->savemsg_checkbtn = savemsg_checkbtn;
6847 compose->savemsg_combo = savemsg_combo;
6848 gtk_widget_show(savemsg_combo);
6850 if (prefs_common.compose_save_to_history)
6851 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6852 prefs_common.compose_save_to_history);
6854 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6855 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6856 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6857 G_CALLBACK(compose_grab_focus_cb), compose);
6858 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6859 folderidentifier = folder_item_get_identifier(account_get_special_folder
6860 (compose->account, F_OUTBOX));
6861 compose_set_save_to(compose, folderidentifier);
6862 g_free(folderidentifier);
6865 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6866 gtk_widget_show(savemsg_select);
6867 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6868 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6869 G_CALLBACK(compose_savemsg_select_cb),
6877 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6879 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6880 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6883 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6888 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6891 path = folder_item_get_identifier(dest);
6893 compose_set_save_to(compose, path);
6897 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6898 GdkAtom clip, GtkTextIter *insert_place);
6901 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6905 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6907 if (event->button == 3) {
6909 GtkTextIter sel_start, sel_end;
6910 gboolean stuff_selected;
6912 /* move the cursor to allow GtkAspell to check the word
6913 * under the mouse */
6914 if (event->x && event->y) {
6915 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6916 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6918 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6921 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6922 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6925 stuff_selected = gtk_text_buffer_get_selection_bounds(
6927 &sel_start, &sel_end);
6929 gtk_text_buffer_place_cursor (buffer, &iter);
6930 /* reselect stuff */
6932 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6933 gtk_text_buffer_select_range(buffer,
6934 &sel_start, &sel_end);
6936 return FALSE; /* pass the event so that the right-click goes through */
6939 if (event->button == 2) {
6944 /* get the middle-click position to paste at the correct place */
6945 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6946 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6948 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6951 entry_paste_clipboard(compose, text,
6952 prefs_common.linewrap_pastes,
6953 GDK_SELECTION_PRIMARY, &iter);
6961 static void compose_spell_menu_changed(void *data)
6963 Compose *compose = (Compose *)data;
6965 GtkWidget *menuitem;
6966 GtkWidget *parent_item;
6967 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6970 if (compose->gtkaspell == NULL)
6973 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6974 "/Menu/Spelling/Options");
6976 /* setting the submenu removes /Spelling/Options from the factory
6977 * so we need to save it */
6979 if (parent_item == NULL) {
6980 parent_item = compose->aspell_options_menu;
6981 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6983 compose->aspell_options_menu = parent_item;
6985 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6987 spell_menu = g_slist_reverse(spell_menu);
6988 for (items = spell_menu;
6989 items; items = items->next) {
6990 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6991 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6992 gtk_widget_show(GTK_WIDGET(menuitem));
6994 g_slist_free(spell_menu);
6996 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6997 gtk_widget_show(parent_item);
7000 static void compose_dict_changed(void *data)
7002 Compose *compose = (Compose *) data;
7004 if(compose->gtkaspell &&
7005 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7008 gtkaspell_highlight_all(compose->gtkaspell);
7009 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7013 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7015 Compose *compose = (Compose *)data;
7016 GdkEventButton event;
7019 event.time = gtk_get_current_event_time();
7023 return text_clicked(compose->text, &event, compose);
7026 static gboolean compose_force_window_origin = TRUE;
7027 static Compose *compose_create(PrefsAccount *account,
7036 GtkWidget *handlebox;
7038 GtkWidget *notebook;
7040 GtkWidget *attach_hbox;
7041 GtkWidget *attach_lab1;
7042 GtkWidget *attach_lab2;
7047 GtkWidget *subject_hbox;
7048 GtkWidget *subject_frame;
7049 GtkWidget *subject_entry;
7053 GtkWidget *edit_vbox;
7054 GtkWidget *ruler_hbox;
7056 GtkWidget *scrolledwin;
7058 GtkTextBuffer *buffer;
7059 GtkClipboard *clipboard;
7062 UndoMain *undostruct;
7064 gchar *titles[N_ATTACH_COLS];
7065 GtkWidget *popupmenu;
7066 GtkWidget *tmpl_menu;
7067 GtkActionGroup *action_group = NULL;
7070 GtkAspell * gtkaspell = NULL;
7073 static GdkGeometry geometry;
7075 cm_return_val_if_fail(account != NULL, NULL);
7077 debug_print("Creating compose window...\n");
7078 compose = g_new0(Compose, 1);
7080 titles[COL_MIMETYPE] = _("MIME type");
7081 titles[COL_SIZE] = _("Size");
7082 titles[COL_NAME] = _("Name");
7083 titles[COL_CHARSET] = _("Charset");
7085 compose->batch = batch;
7086 compose->account = account;
7087 compose->folder = folder;
7089 compose->mutex = g_mutex_new();
7090 compose->set_cursor_pos = -1;
7092 #if !(GTK_CHECK_VERSION(2,12,0))
7093 compose->tooltips = tips;
7096 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7098 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7099 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7101 if (!geometry.max_width) {
7102 geometry.max_width = gdk_screen_width();
7103 geometry.max_height = gdk_screen_height();
7106 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7107 &geometry, GDK_HINT_MAX_SIZE);
7108 if (!geometry.min_width) {
7109 geometry.min_width = 600;
7110 geometry.min_height = 440;
7112 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7113 &geometry, GDK_HINT_MIN_SIZE);
7115 #ifndef GENERIC_UMPC
7116 if (compose_force_window_origin)
7117 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7118 prefs_common.compose_y);
7120 g_signal_connect(G_OBJECT(window), "delete_event",
7121 G_CALLBACK(compose_delete_cb), compose);
7122 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7123 gtk_widget_realize(window);
7125 gtkut_widget_set_composer_icon(window);
7127 vbox = gtk_vbox_new(FALSE, 0);
7128 gtk_container_add(GTK_CONTAINER(window), vbox);
7130 compose->ui_manager = gtk_ui_manager_new();
7131 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7132 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7133 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7134 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7135 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7136 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7137 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7138 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7139 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7140 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7145 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7154 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7159 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7161 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7227 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7237 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7240 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7248 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7254 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7256 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7260 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)
7261 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)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7266 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7267 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)
7268 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)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7273 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)
7274 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7277 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)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7280 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7283 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)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7285 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7289 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)
7290 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)
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7294 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7295 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7298 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7302 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7303 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)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7306 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7314 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7316 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7321 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7322 gtk_widget_show_all(menubar);
7324 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7326 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7328 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7331 if (prefs_common.toolbar_detachable) {
7332 handlebox = gtk_handle_box_new();
7334 handlebox = gtk_hbox_new(FALSE, 0);
7336 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7338 gtk_widget_realize(handlebox);
7340 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7343 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7347 vbox2 = gtk_vbox_new(FALSE, 2);
7348 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7349 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7352 notebook = gtk_notebook_new();
7353 gtk_widget_set_size_request(notebook, -1, 130);
7354 gtk_widget_show(notebook);
7356 /* header labels and entries */
7357 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7358 compose_create_header(compose),
7359 gtk_label_new_with_mnemonic(_("Hea_der")));
7360 /* attachment list */
7361 attach_hbox = gtk_hbox_new(FALSE, 0);
7362 gtk_widget_show(attach_hbox);
7364 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7365 gtk_widget_show(attach_lab1);
7366 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7368 attach_lab2 = gtk_label_new("");
7369 gtk_widget_show(attach_lab2);
7370 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7372 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7373 compose_create_attach(compose),
7376 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7377 compose_create_others(compose),
7378 gtk_label_new_with_mnemonic(_("Othe_rs")));
7381 subject_hbox = gtk_hbox_new(FALSE, 0);
7382 gtk_widget_show(subject_hbox);
7384 subject_frame = gtk_frame_new(NULL);
7385 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7386 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7387 gtk_widget_show(subject_frame);
7389 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7390 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7391 gtk_widget_show(subject);
7393 label = gtk_label_new(_("Subject:"));
7394 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7395 gtk_widget_show(label);
7398 subject_entry = claws_spell_entry_new();
7400 subject_entry = gtk_entry_new();
7402 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7403 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7404 G_CALLBACK(compose_grab_focus_cb), compose);
7405 gtk_widget_show(subject_entry);
7406 compose->subject_entry = subject_entry;
7407 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7409 edit_vbox = gtk_vbox_new(FALSE, 0);
7411 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7414 ruler_hbox = gtk_hbox_new(FALSE, 0);
7415 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7417 ruler = gtk_shruler_new();
7418 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7419 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7423 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7424 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7425 GTK_POLICY_AUTOMATIC,
7426 GTK_POLICY_AUTOMATIC);
7427 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7429 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7430 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7432 text = gtk_text_view_new();
7433 if (prefs_common.show_compose_margin) {
7434 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7435 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7437 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7438 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7439 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7440 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7441 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7443 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7445 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7446 G_CALLBACK(compose_edit_size_alloc),
7448 g_signal_connect(G_OBJECT(buffer), "changed",
7449 G_CALLBACK(compose_changed_cb), compose);
7450 g_signal_connect(G_OBJECT(text), "grab_focus",
7451 G_CALLBACK(compose_grab_focus_cb), compose);
7452 g_signal_connect(G_OBJECT(buffer), "insert_text",
7453 G_CALLBACK(text_inserted), compose);
7454 g_signal_connect(G_OBJECT(text), "button_press_event",
7455 G_CALLBACK(text_clicked), compose);
7457 g_signal_connect(G_OBJECT(text), "popup-menu",
7458 G_CALLBACK(compose_popup_menu), compose);
7460 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7461 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7462 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7463 G_CALLBACK(compose_popup_menu), compose);
7465 g_signal_connect(G_OBJECT(subject_entry), "changed",
7466 G_CALLBACK(compose_changed_cb), compose);
7469 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7470 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7471 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7472 g_signal_connect(G_OBJECT(text), "drag_data_received",
7473 G_CALLBACK(compose_insert_drag_received_cb),
7475 g_signal_connect(G_OBJECT(text), "drag-drop",
7476 G_CALLBACK(compose_drag_drop),
7478 gtk_widget_show_all(vbox);
7480 /* pane between attach clist and text */
7481 paned = gtk_vpaned_new();
7482 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7484 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7485 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7487 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7489 gtk_paned_add1(GTK_PANED(paned), notebook);
7490 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7491 gtk_widget_show_all(paned);
7494 if (prefs_common.textfont) {
7495 PangoFontDescription *font_desc;
7497 font_desc = pango_font_description_from_string
7498 (prefs_common.textfont);
7500 gtk_widget_modify_font(text, font_desc);
7501 pango_font_description_free(font_desc);
7505 gtk_action_group_add_actions(action_group, compose_popup_entries,
7506 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7507 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7508 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7509 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7510 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7511 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7512 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7514 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7516 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7517 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7518 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7520 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7522 undostruct = undo_init(text);
7523 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7526 address_completion_start(window);
7528 compose->window = window;
7529 compose->vbox = vbox;
7530 compose->menubar = menubar;
7531 compose->handlebox = handlebox;
7533 compose->vbox2 = vbox2;
7535 compose->paned = paned;
7537 compose->attach_label = attach_lab2;
7539 compose->notebook = notebook;
7540 compose->edit_vbox = edit_vbox;
7541 compose->ruler_hbox = ruler_hbox;
7542 compose->ruler = ruler;
7543 compose->scrolledwin = scrolledwin;
7544 compose->text = text;
7546 compose->focused_editable = NULL;
7548 compose->popupmenu = popupmenu;
7550 compose->tmpl_menu = tmpl_menu;
7552 compose->mode = mode;
7553 compose->rmode = mode;
7555 compose->targetinfo = NULL;
7556 compose->replyinfo = NULL;
7557 compose->fwdinfo = NULL;
7559 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7560 g_str_equal, (GDestroyNotify) g_free, NULL);
7562 compose->replyto = NULL;
7564 compose->bcc = NULL;
7565 compose->followup_to = NULL;
7567 compose->ml_post = NULL;
7569 compose->inreplyto = NULL;
7570 compose->references = NULL;
7571 compose->msgid = NULL;
7572 compose->boundary = NULL;
7574 compose->autowrap = prefs_common.autowrap;
7575 compose->autoindent = prefs_common.auto_indent;
7576 compose->use_signing = FALSE;
7577 compose->use_encryption = FALSE;
7578 compose->privacy_system = NULL;
7580 compose->modified = FALSE;
7582 compose->return_receipt = FALSE;
7584 compose->to_list = NULL;
7585 compose->newsgroup_list = NULL;
7587 compose->undostruct = undostruct;
7589 compose->sig_str = NULL;
7591 compose->exteditor_file = NULL;
7592 compose->exteditor_pid = -1;
7593 compose->exteditor_tag = -1;
7594 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7597 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7598 if (mode != COMPOSE_REDIRECT) {
7599 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7600 strcmp(prefs_common.dictionary, "")) {
7601 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7602 prefs_common.alt_dictionary,
7603 conv_get_locale_charset_str(),
7604 prefs_common.misspelled_col,
7605 prefs_common.check_while_typing,
7606 prefs_common.recheck_when_changing_dict,
7607 prefs_common.use_alternate,
7608 prefs_common.use_both_dicts,
7609 GTK_TEXT_VIEW(text),
7610 GTK_WINDOW(compose->window),
7611 compose_dict_changed,
7612 compose_spell_menu_changed,
7615 alertpanel_error(_("Spell checker could not "
7617 gtkaspell_checkers_strerror());
7618 gtkaspell_checkers_reset_error();
7620 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7624 compose->gtkaspell = gtkaspell;
7625 compose_spell_menu_changed(compose);
7626 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7629 compose_select_account(compose, account, TRUE);
7631 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7632 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7634 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7635 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7637 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7638 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7640 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7641 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7643 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7644 if (account->protocol != A_NNTP)
7645 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7646 prefs_common_translated_header_name("To:"));
7648 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7649 prefs_common_translated_header_name("Newsgroups:"));
7651 addressbook_set_target_compose(compose);
7653 if (mode != COMPOSE_REDIRECT)
7654 compose_set_template_menu(compose);
7656 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7659 compose_list = g_list_append(compose_list, compose);
7661 if (!prefs_common.show_ruler)
7662 gtk_widget_hide(ruler_hbox);
7664 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7667 compose->priority = PRIORITY_NORMAL;
7668 compose_update_priority_menu_item(compose);
7670 compose_set_out_encoding(compose);
7673 compose_update_actions_menu(compose);
7675 /* Privacy Systems menu */
7676 compose_update_privacy_systems_menu(compose);
7678 activate_privacy_system(compose, account, TRUE);
7679 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7681 gtk_widget_realize(window);
7683 gtk_widget_show(window);
7685 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7686 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7693 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7698 GtkWidget *optmenubox;
7701 GtkWidget *from_name = NULL;
7702 #if !(GTK_CHECK_VERSION(2,12,0))
7703 GtkTooltips *tips = compose->tooltips;
7706 gint num = 0, def_menu = 0;
7708 accounts = account_get_list();
7709 cm_return_val_if_fail(accounts != NULL, NULL);
7711 optmenubox = gtk_event_box_new();
7712 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7713 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7715 hbox = gtk_hbox_new(FALSE, 6);
7716 from_name = gtk_entry_new();
7718 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7719 G_CALLBACK(compose_grab_focus_cb), compose);
7721 for (; accounts != NULL; accounts = accounts->next, num++) {
7722 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7723 gchar *name, *from = NULL;
7725 if (ac == compose->account) def_menu = num;
7727 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7730 if (ac == compose->account) {
7731 if (ac->name && *ac->name) {
7733 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7734 from = g_strdup_printf("%s <%s>",
7736 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7738 from = g_strdup_printf("%s",
7740 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7743 COMBOBOX_ADD(menu, name, ac->account_id);
7748 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7750 g_signal_connect(G_OBJECT(optmenu), "changed",
7751 G_CALLBACK(account_activated),
7753 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7754 G_CALLBACK(compose_entry_popup_extend),
7757 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7758 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7760 CLAWS_SET_TIP(optmenubox,
7761 _("Account to use for this email"));
7762 CLAWS_SET_TIP(from_name,
7763 _("Sender address to be used"));
7765 compose->account_combo = optmenu;
7766 compose->from_name = from_name;
7771 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7773 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7774 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7775 Compose *compose = (Compose *) data;
7777 compose->priority = value;
7781 static void compose_reply_change_mode(Compose *compose,
7784 gboolean was_modified = compose->modified;
7786 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7788 cm_return_if_fail(compose->replyinfo != NULL);
7790 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7792 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7794 if (action == COMPOSE_REPLY_TO_ALL)
7796 if (action == COMPOSE_REPLY_TO_SENDER)
7798 if (action == COMPOSE_REPLY_TO_LIST)
7801 compose_remove_header_entries(compose);
7802 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7803 if (compose->account->set_autocc && compose->account->auto_cc)
7804 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7806 if (compose->account->set_autobcc && compose->account->auto_bcc)
7807 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7809 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7810 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7811 compose_show_first_last_header(compose, TRUE);
7812 compose->modified = was_modified;
7813 compose_set_title(compose);
7816 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7818 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7819 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7820 Compose *compose = (Compose *) data;
7823 compose_reply_change_mode(compose, value);
7826 static void compose_update_priority_menu_item(Compose * compose)
7828 GtkWidget *menuitem = NULL;
7829 switch (compose->priority) {
7830 case PRIORITY_HIGHEST:
7831 menuitem = gtk_ui_manager_get_widget
7832 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7835 menuitem = gtk_ui_manager_get_widget
7836 (compose->ui_manager, "/Menu/Options/Priority/High");
7838 case PRIORITY_NORMAL:
7839 menuitem = gtk_ui_manager_get_widget
7840 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7843 menuitem = gtk_ui_manager_get_widget
7844 (compose->ui_manager, "/Menu/Options/Priority/Low");
7846 case PRIORITY_LOWEST:
7847 menuitem = gtk_ui_manager_get_widget
7848 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7851 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7854 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7856 Compose *compose = (Compose *) data;
7858 gboolean can_sign = FALSE, can_encrypt = FALSE;
7860 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7862 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7865 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7866 g_free(compose->privacy_system);
7867 compose->privacy_system = NULL;
7868 if (systemid != NULL) {
7869 compose->privacy_system = g_strdup(systemid);
7871 can_sign = privacy_system_can_sign(systemid);
7872 can_encrypt = privacy_system_can_encrypt(systemid);
7875 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7877 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7878 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7881 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7883 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7884 GtkWidget *menuitem = NULL;
7886 gboolean can_sign = FALSE, can_encrypt = FALSE;
7887 gboolean found = FALSE;
7889 if (compose->privacy_system != NULL) {
7891 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7892 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7893 cm_return_if_fail(menuitem != NULL);
7895 amenu = GTK_MENU_SHELL(menuitem)->children;
7897 while (amenu != NULL) {
7898 GList *alist = amenu->next;
7900 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7901 if (systemid != NULL) {
7902 if (strcmp(systemid, compose->privacy_system) == 0 &&
7903 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7904 menuitem = GTK_WIDGET(amenu->data);
7906 can_sign = privacy_system_can_sign(systemid);
7907 can_encrypt = privacy_system_can_encrypt(systemid);
7911 } else if (strlen(compose->privacy_system) == 0 &&
7912 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7913 menuitem = GTK_WIDGET(amenu->data);
7916 can_encrypt = FALSE;
7923 if (menuitem != NULL)
7924 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7926 if (warn && !found && strlen(compose->privacy_system)) {
7927 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7928 "will not be able to sign or encrypt this message."),
7929 compose->privacy_system);
7933 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7934 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7937 static void compose_set_out_encoding(Compose *compose)
7939 CharSet out_encoding;
7940 const gchar *branch = NULL;
7941 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7943 switch(out_encoding) {
7944 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7945 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7946 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7947 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7948 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7949 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7950 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7951 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7952 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7953 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7954 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7955 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7956 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7957 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7958 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7959 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7960 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7961 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7962 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7963 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7964 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7965 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7966 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7967 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7968 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7969 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7970 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7971 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7972 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7973 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7974 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7975 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7976 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7978 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7981 static void compose_set_template_menu(Compose *compose)
7983 GSList *tmpl_list, *cur;
7987 tmpl_list = template_get_config();
7989 menu = gtk_menu_new();
7991 gtk_menu_set_accel_group (GTK_MENU (menu),
7992 gtk_ui_manager_get_accel_group(compose->ui_manager));
7993 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7994 Template *tmpl = (Template *)cur->data;
7995 gchar *accel_path = NULL;
7996 item = gtk_menu_item_new_with_label(tmpl->name);
7997 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7998 g_signal_connect(G_OBJECT(item), "activate",
7999 G_CALLBACK(compose_template_activate_cb),
8001 g_object_set_data(G_OBJECT(item), "template", tmpl);
8002 gtk_widget_show(item);
8003 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8004 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8008 gtk_widget_show(menu);
8009 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8012 void compose_update_actions_menu(Compose *compose)
8014 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8017 static void compose_update_privacy_systems_menu(Compose *compose)
8019 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8020 GSList *systems, *cur;
8022 GtkWidget *system_none;
8024 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8025 GtkWidget *privacy_menu = gtk_menu_new();
8027 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8028 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8030 g_signal_connect(G_OBJECT(system_none), "activate",
8031 G_CALLBACK(compose_set_privacy_system_cb), compose);
8033 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8034 gtk_widget_show(system_none);
8036 systems = privacy_get_system_ids();
8037 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8038 gchar *systemid = cur->data;
8040 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8041 widget = gtk_radio_menu_item_new_with_label(group,
8042 privacy_system_get_name(systemid));
8043 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8044 g_strdup(systemid), g_free);
8045 g_signal_connect(G_OBJECT(widget), "activate",
8046 G_CALLBACK(compose_set_privacy_system_cb), compose);
8048 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8049 gtk_widget_show(widget);
8052 g_slist_free(systems);
8053 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8054 gtk_widget_show_all(privacy_menu);
8055 gtk_widget_show_all(privacy_menuitem);
8058 void compose_reflect_prefs_all(void)
8063 for (cur = compose_list; cur != NULL; cur = cur->next) {
8064 compose = (Compose *)cur->data;
8065 compose_set_template_menu(compose);
8069 void compose_reflect_prefs_pixmap_theme(void)
8074 for (cur = compose_list; cur != NULL; cur = cur->next) {
8075 compose = (Compose *)cur->data;
8076 toolbar_update(TOOLBAR_COMPOSE, compose);
8080 static const gchar *compose_quote_char_from_context(Compose *compose)
8082 const gchar *qmark = NULL;
8084 cm_return_val_if_fail(compose != NULL, NULL);
8086 switch (compose->mode) {
8087 /* use forward-specific quote char */
8088 case COMPOSE_FORWARD:
8089 case COMPOSE_FORWARD_AS_ATTACH:
8090 case COMPOSE_FORWARD_INLINE:
8091 if (compose->folder && compose->folder->prefs &&
8092 compose->folder->prefs->forward_with_format)
8093 qmark = compose->folder->prefs->forward_quotemark;
8094 else if (compose->account->forward_with_format)
8095 qmark = compose->account->forward_quotemark;
8097 qmark = prefs_common.fw_quotemark;
8100 /* use reply-specific quote char in all other modes */
8102 if (compose->folder && compose->folder->prefs &&
8103 compose->folder->prefs->reply_with_format)
8104 qmark = compose->folder->prefs->reply_quotemark;
8105 else if (compose->account->reply_with_format)
8106 qmark = compose->account->reply_quotemark;
8108 qmark = prefs_common.quotemark;
8112 if (qmark == NULL || *qmark == '\0')
8118 static void compose_template_apply(Compose *compose, Template *tmpl,
8122 GtkTextBuffer *buffer;
8126 gchar *parsed_str = NULL;
8127 gint cursor_pos = 0;
8128 const gchar *err_msg = _("The body of the template has an error at line %d.");
8131 /* process the body */
8133 text = GTK_TEXT_VIEW(compose->text);
8134 buffer = gtk_text_view_get_buffer(text);
8137 qmark = compose_quote_char_from_context(compose);
8139 if (compose->replyinfo != NULL) {
8142 gtk_text_buffer_set_text(buffer, "", -1);
8143 mark = gtk_text_buffer_get_insert(buffer);
8144 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8146 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8147 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8149 } else if (compose->fwdinfo != NULL) {
8152 gtk_text_buffer_set_text(buffer, "", -1);
8153 mark = gtk_text_buffer_get_insert(buffer);
8154 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8156 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8157 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8160 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8162 GtkTextIter start, end;
8165 gtk_text_buffer_get_start_iter(buffer, &start);
8166 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8167 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8169 /* clear the buffer now */
8171 gtk_text_buffer_set_text(buffer, "", -1);
8173 parsed_str = compose_quote_fmt(compose, dummyinfo,
8174 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8175 procmsg_msginfo_free( dummyinfo );
8181 gtk_text_buffer_set_text(buffer, "", -1);
8182 mark = gtk_text_buffer_get_insert(buffer);
8183 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8186 if (replace && parsed_str && compose->account->auto_sig)
8187 compose_insert_sig(compose, FALSE);
8189 if (replace && parsed_str) {
8190 gtk_text_buffer_get_start_iter(buffer, &iter);
8191 gtk_text_buffer_place_cursor(buffer, &iter);
8195 cursor_pos = quote_fmt_get_cursor_pos();
8196 compose->set_cursor_pos = cursor_pos;
8197 if (cursor_pos == -1)
8199 gtk_text_buffer_get_start_iter(buffer, &iter);
8200 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8201 gtk_text_buffer_place_cursor(buffer, &iter);
8204 /* process the other fields */
8206 compose_template_apply_fields(compose, tmpl);
8207 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8208 quote_fmt_reset_vartable();
8209 compose_changed_cb(NULL, compose);
8212 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8213 gtkaspell_highlight_all(compose->gtkaspell);
8217 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8219 MsgInfo* dummyinfo = NULL;
8220 MsgInfo *msginfo = NULL;
8223 if (compose->replyinfo != NULL)
8224 msginfo = compose->replyinfo;
8225 else if (compose->fwdinfo != NULL)
8226 msginfo = compose->fwdinfo;
8228 dummyinfo = compose_msginfo_new_from_compose(compose);
8229 msginfo = dummyinfo;
8232 if (tmpl->from && *tmpl->from != '\0') {
8234 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8235 compose->gtkaspell);
8237 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8239 quote_fmt_scan_string(tmpl->from);
8242 buf = quote_fmt_get_buffer();
8244 alertpanel_error(_("Template From format error."));
8246 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8250 if (tmpl->to && *tmpl->to != '\0') {
8252 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8253 compose->gtkaspell);
8255 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8257 quote_fmt_scan_string(tmpl->to);
8260 buf = quote_fmt_get_buffer();
8262 alertpanel_error(_("Template To format error."));
8264 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8268 if (tmpl->cc && *tmpl->cc != '\0') {
8270 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8271 compose->gtkaspell);
8273 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8275 quote_fmt_scan_string(tmpl->cc);
8278 buf = quote_fmt_get_buffer();
8280 alertpanel_error(_("Template Cc format error."));
8282 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8286 if (tmpl->bcc && *tmpl->bcc != '\0') {
8288 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8289 compose->gtkaspell);
8291 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8293 quote_fmt_scan_string(tmpl->bcc);
8296 buf = quote_fmt_get_buffer();
8298 alertpanel_error(_("Template Bcc format error."));
8300 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8304 /* process the subject */
8305 if (tmpl->subject && *tmpl->subject != '\0') {
8307 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8308 compose->gtkaspell);
8310 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8312 quote_fmt_scan_string(tmpl->subject);
8315 buf = quote_fmt_get_buffer();
8317 alertpanel_error(_("Template subject format error."));
8319 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8323 procmsg_msginfo_free( dummyinfo );
8326 static void compose_destroy(Compose *compose)
8328 GtkTextBuffer *buffer;
8329 GtkClipboard *clipboard;
8331 compose_list = g_list_remove(compose_list, compose);
8333 if (compose->updating) {
8334 debug_print("danger, not destroying anything now\n");
8335 compose->deferred_destroy = TRUE;
8338 /* NOTE: address_completion_end() does nothing with the window
8339 * however this may change. */
8340 address_completion_end(compose->window);
8342 slist_free_strings(compose->to_list);
8343 g_slist_free(compose->to_list);
8344 slist_free_strings(compose->newsgroup_list);
8345 g_slist_free(compose->newsgroup_list);
8346 slist_free_strings(compose->header_list);
8347 g_slist_free(compose->header_list);
8349 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8351 g_hash_table_destroy(compose->email_hashtable);
8353 procmsg_msginfo_free(compose->targetinfo);
8354 procmsg_msginfo_free(compose->replyinfo);
8355 procmsg_msginfo_free(compose->fwdinfo);
8357 g_free(compose->replyto);
8358 g_free(compose->cc);
8359 g_free(compose->bcc);
8360 g_free(compose->newsgroups);
8361 g_free(compose->followup_to);
8363 g_free(compose->ml_post);
8365 g_free(compose->inreplyto);
8366 g_free(compose->references);
8367 g_free(compose->msgid);
8368 g_free(compose->boundary);
8370 g_free(compose->redirect_filename);
8371 if (compose->undostruct)
8372 undo_destroy(compose->undostruct);
8374 g_free(compose->sig_str);
8376 g_free(compose->exteditor_file);
8378 g_free(compose->orig_charset);
8380 g_free(compose->privacy_system);
8382 if (addressbook_get_target_compose() == compose)
8383 addressbook_set_target_compose(NULL);
8386 if (compose->gtkaspell) {
8387 gtkaspell_delete(compose->gtkaspell);
8388 compose->gtkaspell = NULL;
8392 if (!compose->batch) {
8393 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8394 prefs_common.compose_height = compose->window->allocation.height;
8397 if (!gtk_widget_get_parent(compose->paned))
8398 gtk_widget_destroy(compose->paned);
8399 gtk_widget_destroy(compose->popupmenu);
8401 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8402 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8403 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8405 gtk_widget_destroy(compose->window);
8406 toolbar_destroy(compose->toolbar);
8407 g_free(compose->toolbar);
8408 g_mutex_free(compose->mutex);
8412 static void compose_attach_info_free(AttachInfo *ainfo)
8414 g_free(ainfo->file);
8415 g_free(ainfo->content_type);
8416 g_free(ainfo->name);
8417 g_free(ainfo->charset);
8421 static void compose_attach_update_label(Compose *compose)
8426 GtkTreeModel *model;
8431 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8432 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8433 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8437 while(gtk_tree_model_iter_next(model, &iter))
8440 text = g_strdup_printf("(%d)", i);
8441 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8445 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8447 Compose *compose = (Compose *)data;
8448 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8449 GtkTreeSelection *selection;
8451 GtkTreeModel *model;
8453 selection = gtk_tree_view_get_selection(tree_view);
8454 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8459 for (cur = sel; cur != NULL; cur = cur->next) {
8460 GtkTreePath *path = cur->data;
8461 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8464 gtk_tree_path_free(path);
8467 for (cur = sel; cur != NULL; cur = cur->next) {
8468 GtkTreeRowReference *ref = cur->data;
8469 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8472 if (gtk_tree_model_get_iter(model, &iter, path))
8473 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8475 gtk_tree_path_free(path);
8476 gtk_tree_row_reference_free(ref);
8480 compose_attach_update_label(compose);
8483 static struct _AttachProperty
8486 GtkWidget *mimetype_entry;
8487 GtkWidget *encoding_optmenu;
8488 GtkWidget *path_entry;
8489 GtkWidget *filename_entry;
8491 GtkWidget *cancel_btn;
8494 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8496 gtk_tree_path_free((GtkTreePath *)ptr);
8499 static void compose_attach_property(GtkAction *action, gpointer data)
8501 Compose *compose = (Compose *)data;
8502 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8504 GtkComboBox *optmenu;
8505 GtkTreeSelection *selection;
8507 GtkTreeModel *model;
8510 static gboolean cancelled;
8512 /* only if one selected */
8513 selection = gtk_tree_view_get_selection(tree_view);
8514 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8517 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8521 path = (GtkTreePath *) sel->data;
8522 gtk_tree_model_get_iter(model, &iter, path);
8523 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8526 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8532 if (!attach_prop.window)
8533 compose_attach_property_create(&cancelled);
8534 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8535 gtk_widget_grab_focus(attach_prop.ok_btn);
8536 gtk_widget_show(attach_prop.window);
8537 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8539 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8540 if (ainfo->encoding == ENC_UNKNOWN)
8541 combobox_select_by_data(optmenu, ENC_BASE64);
8543 combobox_select_by_data(optmenu, ainfo->encoding);
8545 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8546 ainfo->content_type ? ainfo->content_type : "");
8547 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8548 ainfo->file ? ainfo->file : "");
8549 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8550 ainfo->name ? ainfo->name : "");
8553 const gchar *entry_text;
8555 gchar *cnttype = NULL;
8562 gtk_widget_hide(attach_prop.window);
8563 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8568 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8569 if (*entry_text != '\0') {
8572 text = g_strstrip(g_strdup(entry_text));
8573 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8574 cnttype = g_strdup(text);
8577 alertpanel_error(_("Invalid MIME type."));
8583 ainfo->encoding = combobox_get_active_data(optmenu);
8585 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8586 if (*entry_text != '\0') {
8587 if (is_file_exist(entry_text) &&
8588 (size = get_file_size(entry_text)) > 0)
8589 file = g_strdup(entry_text);
8592 (_("File doesn't exist or is empty."));
8598 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8599 if (*entry_text != '\0') {
8600 g_free(ainfo->name);
8601 ainfo->name = g_strdup(entry_text);
8605 g_free(ainfo->content_type);
8606 ainfo->content_type = cnttype;
8609 g_free(ainfo->file);
8613 ainfo->size = (goffset)size;
8615 /* update tree store */
8616 text = to_human_readable(ainfo->size);
8617 gtk_tree_model_get_iter(model, &iter, path);
8618 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8619 COL_MIMETYPE, ainfo->content_type,
8621 COL_NAME, ainfo->name,
8622 COL_CHARSET, ainfo->charset,
8628 gtk_tree_path_free(path);
8631 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8633 label = gtk_label_new(str); \
8634 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8635 GTK_FILL, 0, 0, 0); \
8636 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8638 entry = gtk_entry_new(); \
8639 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8640 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8643 static void compose_attach_property_create(gboolean *cancelled)
8649 GtkWidget *mimetype_entry;
8652 GtkListStore *optmenu_menu;
8653 GtkWidget *path_entry;
8654 GtkWidget *filename_entry;
8657 GtkWidget *cancel_btn;
8658 GList *mime_type_list, *strlist;
8661 debug_print("Creating attach_property window...\n");
8663 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8664 gtk_widget_set_size_request(window, 480, -1);
8665 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8666 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8667 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8668 g_signal_connect(G_OBJECT(window), "delete_event",
8669 G_CALLBACK(attach_property_delete_event),
8671 g_signal_connect(G_OBJECT(window), "key_press_event",
8672 G_CALLBACK(attach_property_key_pressed),
8675 vbox = gtk_vbox_new(FALSE, 8);
8676 gtk_container_add(GTK_CONTAINER(window), vbox);
8678 table = gtk_table_new(4, 2, FALSE);
8679 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8680 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8681 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8683 label = gtk_label_new(_("MIME type"));
8684 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8686 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8687 mimetype_entry = gtk_combo_box_entry_new_text();
8688 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8689 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8691 /* stuff with list */
8692 mime_type_list = procmime_get_mime_type_list();
8694 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8695 MimeType *type = (MimeType *) mime_type_list->data;
8698 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8700 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8703 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8704 (GCompareFunc)strcmp2);
8707 for (mime_type_list = strlist; mime_type_list != NULL;
8708 mime_type_list = mime_type_list->next) {
8709 gtk_combo_box_text_append_text(
8710 GTK_COMBO_BOX_TEXT(mimetype_entry),
8711 mime_type_list->data);
8712 g_free(mime_type_list->data);
8714 g_list_free(strlist);
8715 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8716 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8718 label = gtk_label_new(_("Encoding"));
8719 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8721 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8723 hbox = gtk_hbox_new(FALSE, 0);
8724 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8725 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8727 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8728 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8730 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8731 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8732 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8733 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8734 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8736 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8738 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8739 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8741 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8742 &ok_btn, GTK_STOCK_OK,
8744 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8745 gtk_widget_grab_default(ok_btn);
8747 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8748 G_CALLBACK(attach_property_ok),
8750 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8751 G_CALLBACK(attach_property_cancel),
8754 gtk_widget_show_all(vbox);
8756 attach_prop.window = window;
8757 attach_prop.mimetype_entry = mimetype_entry;
8758 attach_prop.encoding_optmenu = optmenu;
8759 attach_prop.path_entry = path_entry;
8760 attach_prop.filename_entry = filename_entry;
8761 attach_prop.ok_btn = ok_btn;
8762 attach_prop.cancel_btn = cancel_btn;
8765 #undef SET_LABEL_AND_ENTRY
8767 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8773 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8779 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8780 gboolean *cancelled)
8788 static gboolean attach_property_key_pressed(GtkWidget *widget,
8790 gboolean *cancelled)
8792 if (event && event->keyval == GDK_Escape) {
8796 if (event && event->keyval == GDK_Return) {
8804 static void compose_exec_ext_editor(Compose *compose)
8811 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8812 G_DIR_SEPARATOR, compose);
8814 if (pipe(pipe_fds) < 0) {
8820 if ((pid = fork()) < 0) {
8827 /* close the write side of the pipe */
8830 compose->exteditor_file = g_strdup(tmp);
8831 compose->exteditor_pid = pid;
8833 compose_set_ext_editor_sensitive(compose, FALSE);
8836 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8838 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8840 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8844 } else { /* process-monitoring process */
8850 /* close the read side of the pipe */
8853 if (compose_write_body_to_file(compose, tmp) < 0) {
8854 fd_write_all(pipe_fds[1], "2\n", 2);
8858 pid_ed = compose_exec_ext_editor_real(tmp);
8860 fd_write_all(pipe_fds[1], "1\n", 2);
8864 /* wait until editor is terminated */
8865 waitpid(pid_ed, NULL, 0);
8867 fd_write_all(pipe_fds[1], "0\n", 2);
8874 #endif /* G_OS_UNIX */
8878 static gint compose_exec_ext_editor_real(const gchar *file)
8885 cm_return_val_if_fail(file != NULL, -1);
8887 if ((pid = fork()) < 0) {
8892 if (pid != 0) return pid;
8894 /* grandchild process */
8896 if (setpgid(0, getppid()))
8899 if (prefs_common_get_ext_editor_cmd() &&
8900 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8901 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8902 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8904 if (prefs_common_get_ext_editor_cmd())
8905 g_warning("External editor command-line is invalid: '%s'\n",
8906 prefs_common_get_ext_editor_cmd());
8907 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8910 cmdline = strsplit_with_quote(buf, " ", 1024);
8911 execvp(cmdline[0], cmdline);
8914 g_strfreev(cmdline);
8919 static gboolean compose_ext_editor_kill(Compose *compose)
8921 pid_t pgid = compose->exteditor_pid * -1;
8924 ret = kill(pgid, 0);
8926 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8930 msg = g_strdup_printf
8931 (_("The external editor is still working.\n"
8932 "Force terminating the process?\n"
8933 "process group id: %d"), -pgid);
8934 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8935 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8939 if (val == G_ALERTALTERNATE) {
8940 g_source_remove(compose->exteditor_tag);
8941 g_io_channel_shutdown(compose->exteditor_ch,
8943 g_io_channel_unref(compose->exteditor_ch);
8945 if (kill(pgid, SIGTERM) < 0) perror("kill");
8946 waitpid(compose->exteditor_pid, NULL, 0);
8948 g_warning("Terminated process group id: %d", -pgid);
8949 g_warning("Temporary file: %s",
8950 compose->exteditor_file);
8952 compose_set_ext_editor_sensitive(compose, TRUE);
8954 g_free(compose->exteditor_file);
8955 compose->exteditor_file = NULL;
8956 compose->exteditor_pid = -1;
8957 compose->exteditor_ch = NULL;
8958 compose->exteditor_tag = -1;
8966 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8970 Compose *compose = (Compose *)data;
8973 debug_print("Compose: input from monitoring process\n");
8975 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8977 g_io_channel_shutdown(source, FALSE, NULL);
8978 g_io_channel_unref(source);
8980 waitpid(compose->exteditor_pid, NULL, 0);
8982 if (buf[0] == '0') { /* success */
8983 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8984 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8986 gtk_text_buffer_set_text(buffer, "", -1);
8987 compose_insert_file(compose, compose->exteditor_file);
8988 compose_changed_cb(NULL, compose);
8989 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8991 if (claws_unlink(compose->exteditor_file) < 0)
8992 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8993 } else if (buf[0] == '1') { /* failed */
8994 g_warning("Couldn't exec external editor\n");
8995 if (claws_unlink(compose->exteditor_file) < 0)
8996 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8997 } else if (buf[0] == '2') {
8998 g_warning("Couldn't write to file\n");
8999 } else if (buf[0] == '3') {
9000 g_warning("Pipe read failed\n");
9003 compose_set_ext_editor_sensitive(compose, TRUE);
9005 g_free(compose->exteditor_file);
9006 compose->exteditor_file = NULL;
9007 compose->exteditor_pid = -1;
9008 compose->exteditor_ch = NULL;
9009 compose->exteditor_tag = -1;
9014 static void compose_set_ext_editor_sensitive(Compose *compose,
9017 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9018 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9019 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9020 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9021 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9022 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9023 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9025 gtk_widget_set_sensitive(compose->text, sensitive);
9026 if (compose->toolbar->send_btn)
9027 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9028 if (compose->toolbar->sendl_btn)
9029 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9030 if (compose->toolbar->draft_btn)
9031 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9032 if (compose->toolbar->insert_btn)
9033 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9034 if (compose->toolbar->sig_btn)
9035 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9036 if (compose->toolbar->exteditor_btn)
9037 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9038 if (compose->toolbar->linewrap_current_btn)
9039 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9040 if (compose->toolbar->linewrap_all_btn)
9041 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9043 #endif /* G_OS_UNIX */
9046 * compose_undo_state_changed:
9048 * Change the sensivity of the menuentries undo and redo
9050 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9051 gint redo_state, gpointer data)
9053 Compose *compose = (Compose *)data;
9055 switch (undo_state) {
9056 case UNDO_STATE_TRUE:
9057 if (!undostruct->undo_state) {
9058 undostruct->undo_state = TRUE;
9059 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9062 case UNDO_STATE_FALSE:
9063 if (undostruct->undo_state) {
9064 undostruct->undo_state = FALSE;
9065 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9068 case UNDO_STATE_UNCHANGED:
9070 case UNDO_STATE_REFRESH:
9071 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9074 g_warning("Undo state not recognized");
9078 switch (redo_state) {
9079 case UNDO_STATE_TRUE:
9080 if (!undostruct->redo_state) {
9081 undostruct->redo_state = TRUE;
9082 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9085 case UNDO_STATE_FALSE:
9086 if (undostruct->redo_state) {
9087 undostruct->redo_state = FALSE;
9088 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9091 case UNDO_STATE_UNCHANGED:
9093 case UNDO_STATE_REFRESH:
9094 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9097 g_warning("Redo state not recognized");
9102 /* callback functions */
9104 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9105 * includes "non-client" (windows-izm) in calculation, so this calculation
9106 * may not be accurate.
9108 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9109 GtkAllocation *allocation,
9110 GtkSHRuler *shruler)
9112 if (prefs_common.show_ruler) {
9113 gint char_width = 0, char_height = 0;
9114 gint line_width_in_chars;
9116 gtkut_get_font_size(GTK_WIDGET(widget),
9117 &char_width, &char_height);
9118 line_width_in_chars =
9119 (allocation->width - allocation->x) / char_width;
9121 /* got the maximum */
9122 gtk_ruler_set_range(GTK_RULER(shruler),
9123 0.0, line_width_in_chars, 0,
9124 /*line_width_in_chars*/ char_width);
9133 ComposePrefType type;
9134 gboolean entry_marked;
9137 static void account_activated(GtkComboBox *optmenu, gpointer data)
9139 Compose *compose = (Compose *)data;
9142 gchar *folderidentifier;
9143 gint account_id = 0;
9146 GSList *list, *saved_list = NULL;
9147 HeaderEntryState *state;
9148 GtkRcStyle *style = NULL;
9149 static GdkColor yellow;
9150 static gboolean color_set = FALSE;
9152 /* Get ID of active account in the combo box */
9153 menu = gtk_combo_box_get_model(optmenu);
9154 gtk_combo_box_get_active_iter(optmenu, &iter);
9155 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9157 ac = account_find_from_id(account_id);
9158 cm_return_if_fail(ac != NULL);
9160 if (ac != compose->account) {
9161 compose_select_account(compose, ac, FALSE);
9163 for (list = compose->header_list; list; list = list->next) {
9164 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9166 if (hentry->type == PREF_ACCOUNT || !list->next) {
9167 compose_destroy_headerentry(compose, hentry);
9171 state = g_malloc0(sizeof(HeaderEntryState));
9172 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9173 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9174 state->entry = gtk_editable_get_chars(
9175 GTK_EDITABLE(hentry->entry), 0, -1);
9176 state->type = hentry->type;
9179 gdk_color_parse("#f5f6be", &yellow);
9180 color_set = gdk_colormap_alloc_color(
9181 gdk_colormap_get_system(),
9182 &yellow, FALSE, TRUE);
9185 style = gtk_widget_get_modifier_style(hentry->entry);
9186 state->entry_marked = gdk_color_equal(&yellow,
9187 &style->base[GTK_STATE_NORMAL]);
9189 saved_list = g_slist_append(saved_list, state);
9190 compose_destroy_headerentry(compose, hentry);
9193 compose->header_last = NULL;
9194 g_slist_free(compose->header_list);
9195 compose->header_list = NULL;
9196 compose->header_nextrow = 1;
9197 compose_create_header_entry(compose);
9199 if (ac->set_autocc && ac->auto_cc)
9200 compose_entry_append(compose, ac->auto_cc,
9201 COMPOSE_CC, PREF_ACCOUNT);
9203 if (ac->set_autobcc && ac->auto_bcc)
9204 compose_entry_append(compose, ac->auto_bcc,
9205 COMPOSE_BCC, PREF_ACCOUNT);
9207 if (ac->set_autoreplyto && ac->auto_replyto)
9208 compose_entry_append(compose, ac->auto_replyto,
9209 COMPOSE_REPLYTO, PREF_ACCOUNT);
9211 for (list = saved_list; list; list = list->next) {
9212 state = (HeaderEntryState *) list->data;
9214 compose_add_header_entry(compose, state->header,
9215 state->entry, state->type);
9216 if (state->entry_marked)
9217 compose_entry_mark_default_to(compose, state->entry);
9219 g_free(state->header);
9220 g_free(state->entry);
9223 g_slist_free(saved_list);
9225 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9226 (ac->protocol == A_NNTP) ?
9227 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9230 /* Set message save folder */
9231 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9234 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9235 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9237 compose_set_save_to(compose, NULL);
9238 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9239 folderidentifier = folder_item_get_identifier(account_get_special_folder
9240 (compose->account, F_OUTBOX));
9241 compose_set_save_to(compose, folderidentifier);
9242 g_free(folderidentifier);
9246 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9247 GtkTreeViewColumn *column, Compose *compose)
9249 compose_attach_property(NULL, compose);
9252 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9255 Compose *compose = (Compose *)data;
9256 GtkTreeSelection *attach_selection;
9257 gint attach_nr_selected;
9259 if (!event) return FALSE;
9261 if (event->button == 3) {
9262 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9263 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9265 if (attach_nr_selected > 0)
9267 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9268 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9270 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9274 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9275 NULL, NULL, event->button, event->time);
9282 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9285 Compose *compose = (Compose *)data;
9287 if (!event) return FALSE;
9289 switch (event->keyval) {
9291 compose_attach_remove_selected(NULL, compose);
9297 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9299 toolbar_comp_set_sensitive(compose, allow);
9300 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9301 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9303 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9305 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9306 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9307 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9309 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9313 static void compose_send_cb(GtkAction *action, gpointer data)
9315 Compose *compose = (Compose *)data;
9317 if (prefs_common.work_offline &&
9318 !inc_offline_should_override(TRUE,
9319 _("Claws Mail needs network access in order "
9320 "to send this email.")))
9323 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9324 g_source_remove(compose->draft_timeout_tag);
9325 compose->draft_timeout_tag = -1;
9328 compose_send(compose);
9331 static void compose_send_later_cb(GtkAction *action, gpointer data)
9333 Compose *compose = (Compose *)data;
9337 compose_allow_user_actions(compose, FALSE);
9338 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9339 compose_allow_user_actions(compose, TRUE);
9343 compose_close(compose);
9344 } else if (val == -1) {
9345 alertpanel_error(_("Could not queue message."));
9346 } else if (val == -2) {
9347 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9348 } else if (val == -3) {
9349 if (privacy_peek_error())
9350 alertpanel_error(_("Could not queue message for sending:\n\n"
9351 "Signature failed: %s"), privacy_get_error());
9352 } else if (val == -4) {
9353 alertpanel_error(_("Could not queue message for sending:\n\n"
9354 "Charset conversion failed."));
9355 } else if (val == -5) {
9356 alertpanel_error(_("Could not queue message for sending:\n\n"
9357 "Couldn't get recipient encryption key."));
9358 } else if (val == -6) {
9361 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9364 #define DRAFTED_AT_EXIT "drafted_at_exit"
9365 static void compose_register_draft(MsgInfo *info)
9367 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9368 DRAFTED_AT_EXIT, NULL);
9369 FILE *fp = g_fopen(filepath, "ab");
9372 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9380 gboolean compose_draft (gpointer data, guint action)
9382 Compose *compose = (Compose *)data;
9386 MsgFlags flag = {0, 0};
9387 static gboolean lock = FALSE;
9388 MsgInfo *newmsginfo;
9390 gboolean target_locked = FALSE;
9391 gboolean err = FALSE;
9393 if (lock) return FALSE;
9395 if (compose->sending)
9398 draft = account_get_special_folder(compose->account, F_DRAFT);
9399 cm_return_val_if_fail(draft != NULL, FALSE);
9401 if (!g_mutex_trylock(compose->mutex)) {
9402 /* we don't want to lock the mutex once it's available,
9403 * because as the only other part of compose.c locking
9404 * it is compose_close - which means once unlocked,
9405 * the compose struct will be freed */
9406 debug_print("couldn't lock mutex, probably sending\n");
9412 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9413 G_DIR_SEPARATOR, compose);
9414 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9415 FILE_OP_ERROR(tmp, "fopen");
9419 /* chmod for security */
9420 if (change_file_mode_rw(fp, tmp) < 0) {
9421 FILE_OP_ERROR(tmp, "chmod");
9422 g_warning("can't change file mode\n");
9425 /* Save draft infos */
9426 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9427 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9429 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9430 gchar *savefolderid;
9432 savefolderid = compose_get_save_to(compose);
9433 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9434 g_free(savefolderid);
9436 if (compose->return_receipt) {
9437 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9439 if (compose->privacy_system) {
9440 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9441 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9442 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9445 /* Message-ID of message replying to */
9446 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9449 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9450 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9453 /* Message-ID of message forwarding to */
9454 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9457 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9458 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9462 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9463 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9465 /* end of headers */
9466 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9473 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9477 if (fclose(fp) == EOF) {
9481 if (compose->targetinfo) {
9482 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9483 flag.perm_flags = target_locked?MSG_LOCKED:0;
9485 flag.tmp_flags = MSG_DRAFT;
9487 folder_item_scan(draft);
9488 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9489 MsgInfo *tmpinfo = NULL;
9490 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9491 if (compose->msgid) {
9492 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9495 msgnum = tmpinfo->msgnum;
9496 procmsg_msginfo_free(tmpinfo);
9497 debug_print("got draft msgnum %d from scanning\n", msgnum);
9499 debug_print("didn't get draft msgnum after scanning\n");
9502 debug_print("got draft msgnum %d from adding\n", msgnum);
9508 if (action != COMPOSE_AUTO_SAVE) {
9509 if (action != COMPOSE_DRAFT_FOR_EXIT)
9510 alertpanel_error(_("Could not save draft."));
9513 gtkut_window_popup(compose->window);
9514 val = alertpanel_full(_("Could not save draft"),
9515 _("Could not save draft.\n"
9516 "Do you want to cancel exit or discard this email?"),
9517 _("_Cancel exit"), _("_Discard email"), NULL,
9518 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9519 if (val == G_ALERTALTERNATE) {
9521 g_mutex_unlock(compose->mutex); /* must be done before closing */
9522 compose_close(compose);
9526 g_mutex_unlock(compose->mutex); /* must be done before closing */
9535 if (compose->mode == COMPOSE_REEDIT) {
9536 compose_remove_reedit_target(compose, TRUE);
9539 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9542 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9544 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9546 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9547 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9548 procmsg_msginfo_set_flags(newmsginfo, 0,
9549 MSG_HAS_ATTACHMENT);
9551 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9552 compose_register_draft(newmsginfo);
9554 procmsg_msginfo_free(newmsginfo);
9557 folder_item_scan(draft);
9559 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9561 g_mutex_unlock(compose->mutex); /* must be done before closing */
9562 compose_close(compose);
9568 path = folder_item_fetch_msg(draft, msgnum);
9570 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9573 if (g_stat(path, &s) < 0) {
9574 FILE_OP_ERROR(path, "stat");
9580 procmsg_msginfo_free(compose->targetinfo);
9581 compose->targetinfo = procmsg_msginfo_new();
9582 compose->targetinfo->msgnum = msgnum;
9583 compose->targetinfo->size = (goffset)s.st_size;
9584 compose->targetinfo->mtime = s.st_mtime;
9585 compose->targetinfo->folder = draft;
9587 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9588 compose->mode = COMPOSE_REEDIT;
9590 if (action == COMPOSE_AUTO_SAVE) {
9591 compose->autosaved_draft = compose->targetinfo;
9593 compose->modified = FALSE;
9594 compose_set_title(compose);
9598 g_mutex_unlock(compose->mutex);
9602 void compose_clear_exit_drafts(void)
9604 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9605 DRAFTED_AT_EXIT, NULL);
9606 if (is_file_exist(filepath))
9607 claws_unlink(filepath);
9612 void compose_reopen_exit_drafts(void)
9614 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9615 DRAFTED_AT_EXIT, NULL);
9616 FILE *fp = g_fopen(filepath, "rb");
9620 while (fgets(buf, sizeof(buf), fp)) {
9621 gchar **parts = g_strsplit(buf, "\t", 2);
9622 const gchar *folder = parts[0];
9623 int msgnum = parts[1] ? atoi(parts[1]):-1;
9625 if (folder && *folder && msgnum > -1) {
9626 FolderItem *item = folder_find_item_from_identifier(folder);
9627 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9629 compose_reedit(info, FALSE);
9636 compose_clear_exit_drafts();
9639 static void compose_save_cb(GtkAction *action, gpointer data)
9641 Compose *compose = (Compose *)data;
9642 compose_draft(compose, COMPOSE_KEEP_EDITING);
9643 compose->rmode = COMPOSE_REEDIT;
9646 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9648 if (compose && file_list) {
9651 for ( tmp = file_list; tmp; tmp = tmp->next) {
9652 gchar *file = (gchar *) tmp->data;
9653 gchar *utf8_filename = conv_filename_to_utf8(file);
9654 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9655 compose_changed_cb(NULL, compose);
9660 g_free(utf8_filename);
9665 static void compose_attach_cb(GtkAction *action, gpointer data)
9667 Compose *compose = (Compose *)data;
9670 if (compose->redirect_filename != NULL)
9673 file_list = filesel_select_multiple_files_open(_("Select file"));
9676 compose_attach_from_list(compose, file_list, TRUE);
9677 g_list_free(file_list);
9681 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9683 Compose *compose = (Compose *)data;
9685 gint files_inserted = 0;
9687 file_list = filesel_select_multiple_files_open(_("Select file"));
9692 for ( tmp = file_list; tmp; tmp = tmp->next) {
9693 gchar *file = (gchar *) tmp->data;
9694 gchar *filedup = g_strdup(file);
9695 gchar *shortfile = g_path_get_basename(filedup);
9696 ComposeInsertResult res;
9697 /* insert the file if the file is short or if the user confirmed that
9698 he/she wants to insert the large file */
9699 res = compose_insert_file(compose, file);
9700 if (res == COMPOSE_INSERT_READ_ERROR) {
9701 alertpanel_error(_("File '%s' could not be read."), shortfile);
9702 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9703 alertpanel_error(_("File '%s' contained invalid characters\n"
9704 "for the current encoding, insertion may be incorrect."),
9706 } else if (res == COMPOSE_INSERT_SUCCESS)
9713 g_list_free(file_list);
9717 if (files_inserted > 0 && compose->gtkaspell &&
9718 compose->gtkaspell->check_while_typing)
9719 gtkaspell_highlight_all(compose->gtkaspell);
9723 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9725 Compose *compose = (Compose *)data;
9727 compose_insert_sig(compose, FALSE);
9730 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9734 Compose *compose = (Compose *)data;
9736 gtkut_widget_get_uposition(widget, &x, &y);
9737 if (!compose->batch) {
9738 prefs_common.compose_x = x;
9739 prefs_common.compose_y = y;
9741 if (compose->sending || compose->updating)
9743 compose_close_cb(NULL, compose);
9747 void compose_close_toolbar(Compose *compose)
9749 compose_close_cb(NULL, compose);
9752 static void compose_close_cb(GtkAction *action, gpointer data)
9754 Compose *compose = (Compose *)data;
9758 if (compose->exteditor_tag != -1) {
9759 if (!compose_ext_editor_kill(compose))
9764 if (compose->modified) {
9765 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9766 if (!g_mutex_trylock(compose->mutex)) {
9767 /* we don't want to lock the mutex once it's available,
9768 * because as the only other part of compose.c locking
9769 * it is compose_close - which means once unlocked,
9770 * the compose struct will be freed */
9771 debug_print("couldn't lock mutex, probably sending\n");
9775 val = alertpanel(_("Discard message"),
9776 _("This message has been modified. Discard it?"),
9777 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9779 val = alertpanel(_("Save changes"),
9780 _("This message has been modified. Save the latest changes?"),
9781 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9783 g_mutex_unlock(compose->mutex);
9785 case G_ALERTDEFAULT:
9786 if (prefs_common.autosave && !reedit)
9787 compose_remove_draft(compose);
9789 case G_ALERTALTERNATE:
9790 compose_draft(data, COMPOSE_QUIT_EDITING);
9797 compose_close(compose);
9800 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9802 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9803 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9804 Compose *compose = (Compose *) data;
9807 compose->out_encoding = (CharSet)value;
9810 static void compose_address_cb(GtkAction *action, gpointer data)
9812 Compose *compose = (Compose *)data;
9814 addressbook_open(compose);
9817 static void about_show_cb(GtkAction *action, gpointer data)
9822 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9824 Compose *compose = (Compose *)data;
9829 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9830 cm_return_if_fail(tmpl != NULL);
9832 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9834 val = alertpanel(_("Apply template"), msg,
9835 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9838 if (val == G_ALERTDEFAULT)
9839 compose_template_apply(compose, tmpl, TRUE);
9840 else if (val == G_ALERTALTERNATE)
9841 compose_template_apply(compose, tmpl, FALSE);
9844 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9846 Compose *compose = (Compose *)data;
9848 compose_exec_ext_editor(compose);
9851 static void compose_undo_cb(GtkAction *action, gpointer data)
9853 Compose *compose = (Compose *)data;
9854 gboolean prev_autowrap = compose->autowrap;
9856 compose->autowrap = FALSE;
9857 undo_undo(compose->undostruct);
9858 compose->autowrap = prev_autowrap;
9861 static void compose_redo_cb(GtkAction *action, gpointer data)
9863 Compose *compose = (Compose *)data;
9864 gboolean prev_autowrap = compose->autowrap;
9866 compose->autowrap = FALSE;
9867 undo_redo(compose->undostruct);
9868 compose->autowrap = prev_autowrap;
9871 static void entry_cut_clipboard(GtkWidget *entry)
9873 if (GTK_IS_EDITABLE(entry))
9874 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9875 else if (GTK_IS_TEXT_VIEW(entry))
9876 gtk_text_buffer_cut_clipboard(
9877 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9878 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9882 static void entry_copy_clipboard(GtkWidget *entry)
9884 if (GTK_IS_EDITABLE(entry))
9885 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9886 else if (GTK_IS_TEXT_VIEW(entry))
9887 gtk_text_buffer_copy_clipboard(
9888 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9889 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9892 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9893 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9895 if (GTK_IS_TEXT_VIEW(entry)) {
9896 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9897 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9898 GtkTextIter start_iter, end_iter;
9900 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9902 if (contents == NULL)
9905 /* we shouldn't delete the selection when middle-click-pasting, or we
9906 * can't mid-click-paste our own selection */
9907 if (clip != GDK_SELECTION_PRIMARY) {
9908 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9909 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9912 if (insert_place == NULL) {
9913 /* if insert_place isn't specified, insert at the cursor.
9914 * used for Ctrl-V pasting */
9915 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9916 start = gtk_text_iter_get_offset(&start_iter);
9917 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9919 /* if insert_place is specified, paste here.
9920 * used for mid-click-pasting */
9921 start = gtk_text_iter_get_offset(insert_place);
9922 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9923 if (prefs_common.primary_paste_unselects)
9924 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9928 /* paste unwrapped: mark the paste so it's not wrapped later */
9929 end = start + strlen(contents);
9930 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9931 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9932 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9933 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9934 /* rewrap paragraph now (after a mid-click-paste) */
9935 mark_start = gtk_text_buffer_get_insert(buffer);
9936 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9937 gtk_text_iter_backward_char(&start_iter);
9938 compose_beautify_paragraph(compose, &start_iter, TRUE);
9940 } else if (GTK_IS_EDITABLE(entry))
9941 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9943 compose->modified = TRUE;
9946 static void entry_allsel(GtkWidget *entry)
9948 if (GTK_IS_EDITABLE(entry))
9949 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9950 else if (GTK_IS_TEXT_VIEW(entry)) {
9951 GtkTextIter startiter, enditer;
9952 GtkTextBuffer *textbuf;
9954 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9955 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9956 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9958 gtk_text_buffer_move_mark_by_name(textbuf,
9959 "selection_bound", &startiter);
9960 gtk_text_buffer_move_mark_by_name(textbuf,
9961 "insert", &enditer);
9965 static void compose_cut_cb(GtkAction *action, gpointer data)
9967 Compose *compose = (Compose *)data;
9968 if (compose->focused_editable
9969 #ifndef GENERIC_UMPC
9970 && gtkut_widget_has_focus(compose->focused_editable)
9973 entry_cut_clipboard(compose->focused_editable);
9976 static void compose_copy_cb(GtkAction *action, gpointer data)
9978 Compose *compose = (Compose *)data;
9979 if (compose->focused_editable
9980 #ifndef GENERIC_UMPC
9981 && gtkut_widget_has_focus(compose->focused_editable)
9984 entry_copy_clipboard(compose->focused_editable);
9987 static void compose_paste_cb(GtkAction *action, gpointer data)
9989 Compose *compose = (Compose *)data;
9991 GtkTextBuffer *buffer;
9993 if (compose->focused_editable &&
9994 gtkut_widget_has_focus(compose->focused_editable))
9995 entry_paste_clipboard(compose, compose->focused_editable,
9996 prefs_common.linewrap_pastes,
9997 GDK_SELECTION_CLIPBOARD, NULL);
10001 if (gtkut_widget_has_focus(compose->text) &&
10002 compose->gtkaspell &&
10003 compose->gtkaspell->check_while_typing)
10004 gtkaspell_highlight_all(compose->gtkaspell);
10008 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10010 Compose *compose = (Compose *)data;
10011 gint wrap_quote = prefs_common.linewrap_quote;
10012 if (compose->focused_editable
10013 #ifndef GENERIC_UMPC
10014 && gtkut_widget_has_focus(compose->focused_editable)
10017 /* let text_insert() (called directly or at a later time
10018 * after the gtk_editable_paste_clipboard) know that
10019 * text is to be inserted as a quotation. implemented
10020 * by using a simple refcount... */
10021 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10022 G_OBJECT(compose->focused_editable),
10023 "paste_as_quotation"));
10024 g_object_set_data(G_OBJECT(compose->focused_editable),
10025 "paste_as_quotation",
10026 GINT_TO_POINTER(paste_as_quotation + 1));
10027 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10028 entry_paste_clipboard(compose, compose->focused_editable,
10029 prefs_common.linewrap_pastes,
10030 GDK_SELECTION_CLIPBOARD, NULL);
10031 prefs_common.linewrap_quote = wrap_quote;
10035 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10037 Compose *compose = (Compose *)data;
10038 gint prev_autowrap;
10039 GtkTextBuffer *buffer;
10041 if (compose->focused_editable
10042 #ifndef GENERIC_UMPC
10043 && gtkut_widget_has_focus(compose->focused_editable)
10046 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10047 GDK_SELECTION_CLIPBOARD, NULL);
10051 if (gtkut_widget_has_focus(compose->text) &&
10052 compose->gtkaspell &&
10053 compose->gtkaspell->check_while_typing)
10054 gtkaspell_highlight_all(compose->gtkaspell);
10058 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10060 Compose *compose = (Compose *)data;
10061 gint prev_autowrap;
10062 GtkTextBuffer *buffer;
10064 if (compose->focused_editable
10065 #ifndef GENERIC_UMPC
10066 && gtkut_widget_has_focus(compose->focused_editable)
10069 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10070 GDK_SELECTION_CLIPBOARD, NULL);
10074 if (gtkut_widget_has_focus(compose->text) &&
10075 compose->gtkaspell &&
10076 compose->gtkaspell->check_while_typing)
10077 gtkaspell_highlight_all(compose->gtkaspell);
10081 static void compose_allsel_cb(GtkAction *action, gpointer data)
10083 Compose *compose = (Compose *)data;
10084 if (compose->focused_editable
10085 #ifndef GENERIC_UMPC
10086 && gtkut_widget_has_focus(compose->focused_editable)
10089 entry_allsel(compose->focused_editable);
10092 static void textview_move_beginning_of_line (GtkTextView *text)
10094 GtkTextBuffer *buffer;
10098 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10100 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10101 mark = gtk_text_buffer_get_insert(buffer);
10102 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10103 gtk_text_iter_set_line_offset(&ins, 0);
10104 gtk_text_buffer_place_cursor(buffer, &ins);
10107 static void textview_move_forward_character (GtkTextView *text)
10109 GtkTextBuffer *buffer;
10113 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10115 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10116 mark = gtk_text_buffer_get_insert(buffer);
10117 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10118 if (gtk_text_iter_forward_cursor_position(&ins))
10119 gtk_text_buffer_place_cursor(buffer, &ins);
10122 static void textview_move_backward_character (GtkTextView *text)
10124 GtkTextBuffer *buffer;
10128 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10130 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10131 mark = gtk_text_buffer_get_insert(buffer);
10132 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10133 if (gtk_text_iter_backward_cursor_position(&ins))
10134 gtk_text_buffer_place_cursor(buffer, &ins);
10137 static void textview_move_forward_word (GtkTextView *text)
10139 GtkTextBuffer *buffer;
10144 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10146 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10147 mark = gtk_text_buffer_get_insert(buffer);
10148 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10149 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10150 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10151 gtk_text_iter_backward_word_start(&ins);
10152 gtk_text_buffer_place_cursor(buffer, &ins);
10156 static void textview_move_backward_word (GtkTextView *text)
10158 GtkTextBuffer *buffer;
10163 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10165 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10166 mark = gtk_text_buffer_get_insert(buffer);
10167 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10168 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10169 if (gtk_text_iter_backward_word_starts(&ins, 1))
10170 gtk_text_buffer_place_cursor(buffer, &ins);
10173 static void textview_move_end_of_line (GtkTextView *text)
10175 GtkTextBuffer *buffer;
10179 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10181 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10182 mark = gtk_text_buffer_get_insert(buffer);
10183 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10184 if (gtk_text_iter_forward_to_line_end(&ins))
10185 gtk_text_buffer_place_cursor(buffer, &ins);
10188 static void textview_move_next_line (GtkTextView *text)
10190 GtkTextBuffer *buffer;
10195 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10197 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10198 mark = gtk_text_buffer_get_insert(buffer);
10199 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10200 offset = gtk_text_iter_get_line_offset(&ins);
10201 if (gtk_text_iter_forward_line(&ins)) {
10202 gtk_text_iter_set_line_offset(&ins, offset);
10203 gtk_text_buffer_place_cursor(buffer, &ins);
10207 static void textview_move_previous_line (GtkTextView *text)
10209 GtkTextBuffer *buffer;
10214 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10216 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10217 mark = gtk_text_buffer_get_insert(buffer);
10218 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10219 offset = gtk_text_iter_get_line_offset(&ins);
10220 if (gtk_text_iter_backward_line(&ins)) {
10221 gtk_text_iter_set_line_offset(&ins, offset);
10222 gtk_text_buffer_place_cursor(buffer, &ins);
10226 static void textview_delete_forward_character (GtkTextView *text)
10228 GtkTextBuffer *buffer;
10230 GtkTextIter ins, end_iter;
10232 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10234 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10235 mark = gtk_text_buffer_get_insert(buffer);
10236 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10238 if (gtk_text_iter_forward_char(&end_iter)) {
10239 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10243 static void textview_delete_backward_character (GtkTextView *text)
10245 GtkTextBuffer *buffer;
10247 GtkTextIter ins, end_iter;
10249 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10251 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10252 mark = gtk_text_buffer_get_insert(buffer);
10253 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10255 if (gtk_text_iter_backward_char(&end_iter)) {
10256 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10260 static void textview_delete_forward_word (GtkTextView *text)
10262 GtkTextBuffer *buffer;
10264 GtkTextIter ins, end_iter;
10266 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10268 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10269 mark = gtk_text_buffer_get_insert(buffer);
10270 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10272 if (gtk_text_iter_forward_word_end(&end_iter)) {
10273 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10277 static void textview_delete_backward_word (GtkTextView *text)
10279 GtkTextBuffer *buffer;
10281 GtkTextIter ins, end_iter;
10283 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10285 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10286 mark = gtk_text_buffer_get_insert(buffer);
10287 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10289 if (gtk_text_iter_backward_word_start(&end_iter)) {
10290 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10294 static void textview_delete_line (GtkTextView *text)
10296 GtkTextBuffer *buffer;
10298 GtkTextIter ins, start_iter, end_iter;
10300 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10302 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10303 mark = gtk_text_buffer_get_insert(buffer);
10304 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10307 gtk_text_iter_set_line_offset(&start_iter, 0);
10310 if (gtk_text_iter_ends_line(&end_iter)){
10311 if (!gtk_text_iter_forward_char(&end_iter))
10312 gtk_text_iter_backward_char(&start_iter);
10315 gtk_text_iter_forward_to_line_end(&end_iter);
10316 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10319 static void textview_delete_to_line_end (GtkTextView *text)
10321 GtkTextBuffer *buffer;
10323 GtkTextIter ins, end_iter;
10325 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10327 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10328 mark = gtk_text_buffer_get_insert(buffer);
10329 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10331 if (gtk_text_iter_ends_line(&end_iter))
10332 gtk_text_iter_forward_char(&end_iter);
10334 gtk_text_iter_forward_to_line_end(&end_iter);
10335 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10338 #define DO_ACTION(name, act) { \
10339 if(!strcmp(name, a_name)) { \
10343 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10345 const gchar *a_name = gtk_action_get_name(action);
10346 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10347 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10348 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10349 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10350 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10351 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10352 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10353 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10354 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10355 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10356 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10357 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10358 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10359 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10363 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10365 Compose *compose = (Compose *)data;
10366 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10367 ComposeCallAdvancedAction action = -1;
10369 action = compose_call_advanced_action_from_path(gaction);
10372 void (*do_action) (GtkTextView *text);
10373 } action_table[] = {
10374 {textview_move_beginning_of_line},
10375 {textview_move_forward_character},
10376 {textview_move_backward_character},
10377 {textview_move_forward_word},
10378 {textview_move_backward_word},
10379 {textview_move_end_of_line},
10380 {textview_move_next_line},
10381 {textview_move_previous_line},
10382 {textview_delete_forward_character},
10383 {textview_delete_backward_character},
10384 {textview_delete_forward_word},
10385 {textview_delete_backward_word},
10386 {textview_delete_line},
10387 {textview_delete_to_line_end}
10390 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10392 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10393 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10394 if (action_table[action].do_action)
10395 action_table[action].do_action(text);
10397 g_warning("Not implemented yet.");
10401 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10405 if (GTK_IS_EDITABLE(widget)) {
10406 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10407 gtk_editable_set_position(GTK_EDITABLE(widget),
10410 if (widget->parent && widget->parent->parent
10411 && widget->parent->parent->parent) {
10412 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10413 gint y = widget->allocation.y;
10414 gint height = widget->allocation.height;
10415 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10416 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10418 if (y < (int)shown->value) {
10419 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10421 if (y + height > (int)shown->value + (int)shown->page_size) {
10422 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10423 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10424 y + height - (int)shown->page_size - 1);
10426 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10427 (int)shown->upper - (int)shown->page_size - 1);
10434 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10435 compose->focused_editable = widget;
10437 #ifdef GENERIC_UMPC
10438 if (GTK_IS_TEXT_VIEW(widget)
10439 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10440 g_object_ref(compose->notebook);
10441 g_object_ref(compose->edit_vbox);
10442 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10443 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10444 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10445 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10446 g_object_unref(compose->notebook);
10447 g_object_unref(compose->edit_vbox);
10448 g_signal_handlers_block_by_func(G_OBJECT(widget),
10449 G_CALLBACK(compose_grab_focus_cb),
10451 gtk_widget_grab_focus(widget);
10452 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10453 G_CALLBACK(compose_grab_focus_cb),
10455 } else if (!GTK_IS_TEXT_VIEW(widget)
10456 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10457 g_object_ref(compose->notebook);
10458 g_object_ref(compose->edit_vbox);
10459 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10460 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10461 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10462 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10463 g_object_unref(compose->notebook);
10464 g_object_unref(compose->edit_vbox);
10465 g_signal_handlers_block_by_func(G_OBJECT(widget),
10466 G_CALLBACK(compose_grab_focus_cb),
10468 gtk_widget_grab_focus(widget);
10469 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10470 G_CALLBACK(compose_grab_focus_cb),
10476 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10478 compose->modified = TRUE;
10479 // compose_beautify_paragraph(compose, NULL, TRUE);
10480 #ifndef GENERIC_UMPC
10481 compose_set_title(compose);
10485 static void compose_wrap_cb(GtkAction *action, gpointer data)
10487 Compose *compose = (Compose *)data;
10488 compose_beautify_paragraph(compose, NULL, TRUE);
10491 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10493 Compose *compose = (Compose *)data;
10494 compose_wrap_all_full(compose, TRUE);
10497 static void compose_find_cb(GtkAction *action, gpointer data)
10499 Compose *compose = (Compose *)data;
10501 message_search_compose(compose);
10504 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10507 Compose *compose = (Compose *)data;
10508 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10509 if (compose->autowrap)
10510 compose_wrap_all_full(compose, TRUE);
10511 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10514 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10517 Compose *compose = (Compose *)data;
10518 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10521 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10523 Compose *compose = (Compose *)data;
10525 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10528 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10530 Compose *compose = (Compose *)data;
10532 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10535 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10537 g_free(compose->privacy_system);
10539 compose->privacy_system = g_strdup(account->default_privacy_system);
10540 compose_update_privacy_system_menu_item(compose, warn);
10543 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10545 Compose *compose = (Compose *)data;
10547 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10548 gtk_widget_show(compose->ruler_hbox);
10549 prefs_common.show_ruler = TRUE;
10551 gtk_widget_hide(compose->ruler_hbox);
10552 gtk_widget_queue_resize(compose->edit_vbox);
10553 prefs_common.show_ruler = FALSE;
10557 static void compose_attach_drag_received_cb (GtkWidget *widget,
10558 GdkDragContext *context,
10561 GtkSelectionData *data,
10564 gpointer user_data)
10566 Compose *compose = (Compose *)user_data;
10569 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10571 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10573 ) && gtk_drag_get_source_widget(context) !=
10574 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10575 list = uri_list_extract_filenames((const gchar *)data->data);
10576 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10577 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10578 compose_attach_append
10579 (compose, (const gchar *)tmp->data,
10580 utf8_filename, NULL, NULL);
10581 g_free(utf8_filename);
10583 if (list) compose_changed_cb(NULL, compose);
10584 list_free_strings(list);
10586 } else if (gtk_drag_get_source_widget(context)
10587 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10588 /* comes from our summaryview */
10589 SummaryView * summaryview = NULL;
10590 GSList * list = NULL, *cur = NULL;
10592 if (mainwindow_get_mainwindow())
10593 summaryview = mainwindow_get_mainwindow()->summaryview;
10596 list = summary_get_selected_msg_list(summaryview);
10598 for (cur = list; cur; cur = cur->next) {
10599 MsgInfo *msginfo = (MsgInfo *)cur->data;
10600 gchar *file = NULL;
10602 file = procmsg_get_message_file_full(msginfo,
10605 compose_attach_append(compose, (const gchar *)file,
10606 (const gchar *)file, "message/rfc822", NULL);
10610 g_slist_free(list);
10614 static gboolean compose_drag_drop(GtkWidget *widget,
10615 GdkDragContext *drag_context,
10617 guint time, gpointer user_data)
10619 /* not handling this signal makes compose_insert_drag_received_cb
10624 static void compose_insert_drag_received_cb (GtkWidget *widget,
10625 GdkDragContext *drag_context,
10628 GtkSelectionData *data,
10631 gpointer user_data)
10633 Compose *compose = (Compose *)user_data;
10636 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10639 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10641 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10643 AlertValue val = G_ALERTDEFAULT;
10645 list = uri_list_extract_filenames((const gchar *)data->data);
10646 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10647 /* Assume a list of no files, and data has ://, is a remote link */
10648 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10649 gchar *tmpfile = get_tmp_file();
10650 str_write_to_file(tmpdata, tmpfile);
10652 compose_insert_file(compose, tmpfile);
10653 claws_unlink(tmpfile);
10655 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10656 compose_beautify_paragraph(compose, NULL, TRUE);
10659 switch (prefs_common.compose_dnd_mode) {
10660 case COMPOSE_DND_ASK:
10661 val = alertpanel_full(_("Insert or attach?"),
10662 _("Do you want to insert the contents of the file(s) "
10663 "into the message body, or attach it to the email?"),
10664 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10665 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10667 case COMPOSE_DND_INSERT:
10668 val = G_ALERTALTERNATE;
10670 case COMPOSE_DND_ATTACH:
10671 val = G_ALERTOTHER;
10674 /* unexpected case */
10675 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10678 if (val & G_ALERTDISABLE) {
10679 val &= ~G_ALERTDISABLE;
10680 /* remember what action to perform by default, only if we don't click Cancel */
10681 if (val == G_ALERTALTERNATE)
10682 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10683 else if (val == G_ALERTOTHER)
10684 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10687 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10688 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10689 list_free_strings(list);
10692 } else if (val == G_ALERTOTHER) {
10693 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10694 list_free_strings(list);
10699 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10700 compose_insert_file(compose, (const gchar *)tmp->data);
10702 list_free_strings(list);
10704 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10709 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10712 static void compose_header_drag_received_cb (GtkWidget *widget,
10713 GdkDragContext *drag_context,
10716 GtkSelectionData *data,
10719 gpointer user_data)
10721 GtkEditable *entry = (GtkEditable *)user_data;
10722 gchar *email = (gchar *)data->data;
10724 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10727 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10728 gchar *decoded=g_new(gchar, strlen(email));
10731 email += strlen("mailto:");
10732 decode_uri(decoded, email); /* will fit */
10733 gtk_editable_delete_text(entry, 0, -1);
10734 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10735 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10739 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10742 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10744 Compose *compose = (Compose *)data;
10746 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10747 compose->return_receipt = TRUE;
10749 compose->return_receipt = FALSE;
10752 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10754 Compose *compose = (Compose *)data;
10756 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10757 compose->remove_references = TRUE;
10759 compose->remove_references = FALSE;
10762 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10763 ComposeHeaderEntry *headerentry)
10765 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10769 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10770 GdkEventKey *event,
10771 ComposeHeaderEntry *headerentry)
10773 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10774 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10775 !(event->state & GDK_MODIFIER_MASK) &&
10776 (event->keyval == GDK_BackSpace) &&
10777 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10778 gtk_container_remove
10779 (GTK_CONTAINER(headerentry->compose->header_table),
10780 headerentry->combo);
10781 gtk_container_remove
10782 (GTK_CONTAINER(headerentry->compose->header_table),
10783 headerentry->entry);
10784 headerentry->compose->header_list =
10785 g_slist_remove(headerentry->compose->header_list,
10787 g_free(headerentry);
10788 } else if (event->keyval == GDK_Tab) {
10789 if (headerentry->compose->header_last == headerentry) {
10790 /* Override default next focus, and give it to subject_entry
10791 * instead of notebook tabs
10793 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10794 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10801 static gboolean scroll_postpone(gpointer data)
10803 Compose *compose = (Compose *)data;
10805 cm_return_val_if_fail(!compose->batch, FALSE);
10807 GTK_EVENTS_FLUSH();
10808 compose_show_first_last_header(compose, FALSE);
10812 static void compose_headerentry_changed_cb(GtkWidget *entry,
10813 ComposeHeaderEntry *headerentry)
10815 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10816 compose_create_header_entry(headerentry->compose);
10817 g_signal_handlers_disconnect_matched
10818 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10819 0, 0, NULL, NULL, headerentry);
10821 if (!headerentry->compose->batch)
10822 g_timeout_add(0, scroll_postpone, headerentry->compose);
10826 static gboolean compose_defer_auto_save_draft(Compose *compose)
10828 compose->draft_timeout_tag = -1;
10829 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10833 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10835 GtkAdjustment *vadj;
10837 cm_return_if_fail(compose);
10838 cm_return_if_fail(!compose->batch);
10839 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10840 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10841 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10842 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10843 gtk_adjustment_changed(vadj);
10846 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10847 const gchar *text, gint len, Compose *compose)
10849 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10850 (G_OBJECT(compose->text), "paste_as_quotation"));
10853 cm_return_if_fail(text != NULL);
10855 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10856 G_CALLBACK(text_inserted),
10858 if (paste_as_quotation) {
10860 const gchar *qmark;
10862 GtkTextIter start_iter;
10865 len = strlen(text);
10867 new_text = g_strndup(text, len);
10869 qmark = compose_quote_char_from_context(compose);
10871 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10872 gtk_text_buffer_place_cursor(buffer, iter);
10874 pos = gtk_text_iter_get_offset(iter);
10876 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10877 _("Quote format error at line %d."));
10878 quote_fmt_reset_vartable();
10880 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10881 GINT_TO_POINTER(paste_as_quotation - 1));
10883 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10884 gtk_text_buffer_place_cursor(buffer, iter);
10885 gtk_text_buffer_delete_mark(buffer, mark);
10887 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10888 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10889 compose_beautify_paragraph(compose, &start_iter, FALSE);
10890 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10891 gtk_text_buffer_delete_mark(buffer, mark);
10893 if (strcmp(text, "\n") || compose->automatic_break
10894 || gtk_text_iter_starts_line(iter)) {
10895 GtkTextIter before_ins;
10896 gtk_text_buffer_insert(buffer, iter, text, len);
10897 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10898 before_ins = *iter;
10899 gtk_text_iter_backward_chars(&before_ins, len);
10900 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10903 /* check if the preceding is just whitespace or quote */
10904 GtkTextIter start_line;
10905 gchar *tmp = NULL, *quote = NULL;
10906 gint quote_len = 0, is_normal = 0;
10907 start_line = *iter;
10908 gtk_text_iter_set_line_offset(&start_line, 0);
10909 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10912 if (*tmp == '\0') {
10915 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10923 gtk_text_buffer_insert(buffer, iter, text, len);
10925 gtk_text_buffer_insert_with_tags_by_name(buffer,
10926 iter, text, len, "no_join", NULL);
10931 if (!paste_as_quotation) {
10932 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10933 compose_beautify_paragraph(compose, iter, FALSE);
10934 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10935 gtk_text_buffer_delete_mark(buffer, mark);
10938 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10939 G_CALLBACK(text_inserted),
10941 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10943 if (prefs_common.autosave &&
10944 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10945 compose->draft_timeout_tag != -2 /* disabled while loading */)
10946 compose->draft_timeout_tag = g_timeout_add
10947 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
10951 static void compose_check_all(GtkAction *action, gpointer data)
10953 Compose *compose = (Compose *)data;
10954 if (!compose->gtkaspell)
10957 if (gtkut_widget_has_focus(compose->subject_entry))
10958 claws_spell_entry_check_all(
10959 CLAWS_SPELL_ENTRY(compose->subject_entry));
10961 gtkaspell_check_all(compose->gtkaspell);
10964 static void compose_highlight_all(GtkAction *action, gpointer data)
10966 Compose *compose = (Compose *)data;
10967 if (compose->gtkaspell) {
10968 claws_spell_entry_recheck_all(
10969 CLAWS_SPELL_ENTRY(compose->subject_entry));
10970 gtkaspell_highlight_all(compose->gtkaspell);
10974 static void compose_check_backwards(GtkAction *action, gpointer data)
10976 Compose *compose = (Compose *)data;
10977 if (!compose->gtkaspell) {
10978 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10982 if (gtkut_widget_has_focus(compose->subject_entry))
10983 claws_spell_entry_check_backwards(
10984 CLAWS_SPELL_ENTRY(compose->subject_entry));
10986 gtkaspell_check_backwards(compose->gtkaspell);
10989 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10991 Compose *compose = (Compose *)data;
10992 if (!compose->gtkaspell) {
10993 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10997 if (gtkut_widget_has_focus(compose->subject_entry))
10998 claws_spell_entry_check_forwards_go(
10999 CLAWS_SPELL_ENTRY(compose->subject_entry));
11001 gtkaspell_check_forwards_go(compose->gtkaspell);
11006 *\brief Guess originating forward account from MsgInfo and several
11007 * "common preference" settings. Return NULL if no guess.
11009 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11011 PrefsAccount *account = NULL;
11013 cm_return_val_if_fail(msginfo, NULL);
11014 cm_return_val_if_fail(msginfo->folder, NULL);
11015 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11017 if (msginfo->folder->prefs->enable_default_account)
11018 account = account_find_from_id(msginfo->folder->prefs->default_account);
11021 account = msginfo->folder->folder->account;
11023 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11025 Xstrdup_a(to, msginfo->to, return NULL);
11026 extract_address(to);
11027 account = account_find_from_address(to, FALSE);
11030 if (!account && prefs_common.forward_account_autosel) {
11031 gchar cc[BUFFSIZE];
11032 if (!procheader_get_header_from_msginfo
11033 (msginfo, cc,sizeof cc , "Cc:")) {
11034 gchar *buf = cc + strlen("Cc:");
11035 extract_address(buf);
11036 account = account_find_from_address(buf, FALSE);
11040 if (!account && prefs_common.forward_account_autosel) {
11041 gchar deliveredto[BUFFSIZE];
11042 if (!procheader_get_header_from_msginfo
11043 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11044 gchar *buf = deliveredto + strlen("Delivered-To:");
11045 extract_address(buf);
11046 account = account_find_from_address(buf, FALSE);
11053 gboolean compose_close(Compose *compose)
11057 if (!g_mutex_trylock(compose->mutex)) {
11058 /* we have to wait for the (possibly deferred by auto-save)
11059 * drafting to be done, before destroying the compose under
11061 debug_print("waiting for drafting to finish...\n");
11062 compose_allow_user_actions(compose, FALSE);
11063 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11066 cm_return_val_if_fail(compose, FALSE);
11067 gtkut_widget_get_uposition(compose->window, &x, &y);
11068 if (!compose->batch) {
11069 prefs_common.compose_x = x;
11070 prefs_common.compose_y = y;
11072 g_mutex_unlock(compose->mutex);
11073 compose_destroy(compose);
11078 * Add entry field for each address in list.
11079 * \param compose E-Mail composition object.
11080 * \param listAddress List of (formatted) E-Mail addresses.
11082 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11085 node = listAddress;
11087 addr = ( gchar * ) node->data;
11088 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11089 node = g_list_next( node );
11093 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11094 guint action, gboolean opening_multiple)
11096 gchar *body = NULL;
11097 GSList *new_msglist = NULL;
11098 MsgInfo *tmp_msginfo = NULL;
11099 gboolean originally_enc = FALSE;
11100 gboolean originally_sig = FALSE;
11101 Compose *compose = NULL;
11102 gchar *s_system = NULL;
11104 cm_return_if_fail(msgview != NULL);
11106 cm_return_if_fail(msginfo_list != NULL);
11108 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11109 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11110 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11112 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11113 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11114 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11115 orig_msginfo, mimeinfo);
11116 if (tmp_msginfo != NULL) {
11117 new_msglist = g_slist_append(NULL, tmp_msginfo);
11119 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11120 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11121 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11123 tmp_msginfo->folder = orig_msginfo->folder;
11124 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11125 if (orig_msginfo->tags) {
11126 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11127 tmp_msginfo->folder->tags_dirty = TRUE;
11133 if (!opening_multiple)
11134 body = messageview_get_selection(msgview);
11137 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11138 procmsg_msginfo_free(tmp_msginfo);
11139 g_slist_free(new_msglist);
11141 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11143 if (compose && originally_enc) {
11144 compose_force_encryption(compose, compose->account, FALSE, s_system);
11147 if (compose && originally_sig && compose->account->default_sign_reply) {
11148 compose_force_signing(compose, compose->account, s_system);
11152 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11155 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11158 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11159 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11160 GSList *cur = msginfo_list;
11161 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11162 "messages. Opening the windows "
11163 "could take some time. Do you "
11164 "want to continue?"),
11165 g_slist_length(msginfo_list));
11166 if (g_slist_length(msginfo_list) > 9
11167 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11168 != G_ALERTALTERNATE) {
11173 /* We'll open multiple compose windows */
11174 /* let the WM place the next windows */
11175 compose_force_window_origin = FALSE;
11176 for (; cur; cur = cur->next) {
11178 tmplist.data = cur->data;
11179 tmplist.next = NULL;
11180 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11182 compose_force_window_origin = TRUE;
11184 /* forwarding multiple mails as attachments is done via a
11185 * single compose window */
11186 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11190 void compose_check_for_email_account(Compose *compose)
11192 PrefsAccount *ac = NULL, *curr = NULL;
11198 if (compose->account && compose->account->protocol == A_NNTP) {
11199 ac = account_get_cur_account();
11200 if (ac->protocol == A_NNTP) {
11201 list = account_get_list();
11203 for( ; list != NULL ; list = g_list_next(list)) {
11204 curr = (PrefsAccount *) list->data;
11205 if (curr->protocol != A_NNTP) {
11211 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11216 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11217 const gchar *address)
11219 GSList *msginfo_list = NULL;
11220 gchar *body = messageview_get_selection(msgview);
11223 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11225 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11226 compose_check_for_email_account(compose);
11227 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11228 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11229 compose_reply_set_subject(compose, msginfo);
11232 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11235 void compose_set_position(Compose *compose, gint pos)
11237 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11239 gtkut_text_view_set_position(text, pos);
11242 gboolean compose_search_string(Compose *compose,
11243 const gchar *str, gboolean case_sens)
11245 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11247 return gtkut_text_view_search_string(text, str, case_sens);
11250 gboolean compose_search_string_backward(Compose *compose,
11251 const gchar *str, gboolean case_sens)
11253 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11255 return gtkut_text_view_search_string_backward(text, str, case_sens);
11258 /* allocate a msginfo structure and populate its data from a compose data structure */
11259 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11261 MsgInfo *newmsginfo;
11263 gchar buf[BUFFSIZE];
11265 cm_return_val_if_fail( compose != NULL, NULL );
11267 newmsginfo = procmsg_msginfo_new();
11270 get_rfc822_date(buf, sizeof(buf));
11271 newmsginfo->date = g_strdup(buf);
11274 if (compose->from_name) {
11275 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11276 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11280 if (compose->subject_entry)
11281 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11283 /* to, cc, reply-to, newsgroups */
11284 for (list = compose->header_list; list; list = list->next) {
11285 gchar *header = gtk_editable_get_chars(
11287 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11288 gchar *entry = gtk_editable_get_chars(
11289 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11291 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11292 if ( newmsginfo->to == NULL ) {
11293 newmsginfo->to = g_strdup(entry);
11294 } else if (entry && *entry) {
11295 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11296 g_free(newmsginfo->to);
11297 newmsginfo->to = tmp;
11300 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11301 if ( newmsginfo->cc == NULL ) {
11302 newmsginfo->cc = g_strdup(entry);
11303 } else if (entry && *entry) {
11304 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11305 g_free(newmsginfo->cc);
11306 newmsginfo->cc = tmp;
11309 if ( strcasecmp(header,
11310 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11311 if ( newmsginfo->newsgroups == NULL ) {
11312 newmsginfo->newsgroups = g_strdup(entry);
11313 } else if (entry && *entry) {
11314 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11315 g_free(newmsginfo->newsgroups);
11316 newmsginfo->newsgroups = tmp;
11324 /* other data is unset */
11330 /* update compose's dictionaries from folder dict settings */
11331 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11332 FolderItem *folder_item)
11334 cm_return_if_fail(compose != NULL);
11336 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11337 FolderItemPrefs *prefs = folder_item->prefs;
11339 if (prefs->enable_default_dictionary)
11340 gtkaspell_change_dict(compose->gtkaspell,
11341 prefs->default_dictionary, FALSE);
11342 if (folder_item->prefs->enable_default_alt_dictionary)
11343 gtkaspell_change_alt_dict(compose->gtkaspell,
11344 prefs->default_alt_dictionary);
11345 if (prefs->enable_default_dictionary
11346 || prefs->enable_default_alt_dictionary)
11347 compose_spell_menu_changed(compose);