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 gint compose_defer_auto_save_draft (Compose *compose);
529 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
531 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
534 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
535 FolderItem *folder_item);
537 static void compose_attach_update_label(Compose *compose);
538 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
539 gboolean respect_default_to);
541 static GtkActionEntry compose_popup_entries[] =
543 {"Compose", NULL, "Compose" },
544 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
545 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
546 {"Compose/---", NULL, "---", NULL, NULL, NULL },
547 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
550 static GtkActionEntry compose_entries[] =
552 {"Menu", NULL, "Menu" },
554 {"Message", NULL, N_("_Message") },
555 {"Edit", NULL, N_("_Edit") },
557 {"Spelling", NULL, N_("_Spelling") },
559 {"Options", NULL, N_("_Options") },
560 {"Tools", NULL, N_("_Tools") },
561 {"Help", NULL, N_("_Help") },
563 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
564 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
565 {"Message/---", NULL, "---" },
567 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
568 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
569 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
570 /* {"Message/---", NULL, "---" }, */
571 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
572 /* {"Message/---", NULL, "---" }, */
573 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
576 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
577 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
578 {"Edit/---", NULL, "---" },
580 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
581 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
582 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
584 {"Edit/SpecialPaste", NULL, N_("Special paste") },
585 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
586 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
587 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
589 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
591 {"Edit/Advanced", NULL, N_("A_dvanced") },
592 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
593 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
594 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
595 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
596 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
597 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
598 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
599 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
600 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
601 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
602 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
603 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
604 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
605 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
607 /* {"Edit/---", NULL, "---" }, */
608 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
610 /* {"Edit/---", NULL, "---" }, */
611 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
612 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
613 /* {"Edit/---", NULL, "---" }, */
614 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
617 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
618 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
619 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
620 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
622 {"Spelling/---", NULL, "---" },
623 {"Spelling/Options", NULL, N_("_Options") },
628 {"Options/ReplyMode", NULL, N_("Reply _mode") },
629 {"Options/---", NULL, "---" },
630 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
631 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
633 /* {"Options/---", NULL, "---" }, */
635 {"Options/Priority", NULL, N_("_Priority") },
637 {"Options/Encoding", NULL, N_("Character _encoding") },
638 {"Options/Encoding/---", NULL, "---" },
639 #define ENC_ACTION(cs_char,c_char,string) \
640 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
642 {"Options/Encoding/Western", NULL, N_("Western European") },
643 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
644 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
645 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
646 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
647 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
648 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
649 {"Options/Encoding/Korean", NULL, N_("Korean") },
650 {"Options/Encoding/Thai", NULL, N_("Thai") },
653 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
655 {"Tools/Template", NULL, N_("_Template") },
656 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657 {"Tools/Actions", NULL, N_("Actio_ns") },
658 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
661 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
664 static GtkToggleActionEntry compose_toggle_entries[] =
666 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
667 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
668 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
669 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
670 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
671 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
672 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
675 static GtkRadioActionEntry compose_radio_rm_entries[] =
677 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
680 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
683 static GtkRadioActionEntry compose_radio_prio_entries[] =
685 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
689 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
692 static GtkRadioActionEntry compose_radio_enc_entries[] =
694 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
728 static GtkTargetEntry compose_mime_types[] =
730 {"text/uri-list", 0, 0},
731 {"UTF8_STRING", 0, 0},
735 static gboolean compose_put_existing_to_front(MsgInfo *info)
737 GList *compose_list = compose_get_compose_list();
741 for (elem = compose_list; elem != NULL && elem->data != NULL;
743 Compose *c = (Compose*)elem->data;
745 if (!c->targetinfo || !c->targetinfo->msgid ||
749 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
750 gtkut_window_popup(c->window);
758 static GdkColor quote_color1 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_color2 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762 static GdkColor quote_color3 =
763 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor1 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor2 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
769 static GdkColor quote_bgcolor3 =
770 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
772 static GdkColor signature_color = {
779 static GdkColor uri_color = {
786 static void compose_create_tags(GtkTextView *text, Compose *compose)
788 GtkTextBuffer *buffer;
789 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 buffer = gtk_text_view_get_buffer(text);
797 if (prefs_common.enable_color) {
798 /* grab the quote colors, converting from an int to a GdkColor */
799 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
807 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
809 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
811 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
813 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
816 signature_color = quote_color1 = quote_color2 = quote_color3 =
817 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
820 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
821 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
822 "foreground-gdk", "e_color1,
823 "paragraph-background-gdk", "e_bgcolor1,
825 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
826 "foreground-gdk", "e_color2,
827 "paragraph-background-gdk", "e_bgcolor2,
829 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
830 "foreground-gdk", "e_color3,
831 "paragraph-background-gdk", "e_bgcolor3,
834 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
835 "foreground-gdk", "e_color1,
837 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
838 "foreground-gdk", "e_color2,
840 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
841 "foreground-gdk", "e_color3,
845 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
846 "foreground-gdk", &signature_color,
849 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
850 "foreground-gdk", &uri_color,
852 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
853 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
855 color[0] = quote_color1;
856 color[1] = quote_color2;
857 color[2] = quote_color3;
858 color[3] = quote_bgcolor1;
859 color[4] = quote_bgcolor2;
860 color[5] = quote_bgcolor3;
861 color[6] = signature_color;
862 color[7] = uri_color;
863 cmap = gdk_drawable_get_colormap(compose->window->window);
864 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
866 for (i = 0; i < 8; i++) {
867 if (success[i] == FALSE) {
870 g_warning("Compose: color allocation failed.\n");
871 style = gtk_widget_get_style(GTK_WIDGET(text));
872 quote_color1 = quote_color2 = quote_color3 =
873 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
874 signature_color = uri_color = black;
879 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
880 GPtrArray *attach_files)
882 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
885 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
887 return compose_generic_new(account, mailto, item, NULL, NULL);
890 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
892 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
895 #define SCROLL_TO_CURSOR(compose) { \
896 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
897 gtk_text_view_get_buffer( \
898 GTK_TEXT_VIEW(compose->text))); \
899 gtk_text_view_scroll_mark_onscreen( \
900 GTK_TEXT_VIEW(compose->text), \
904 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
907 if (folderidentifier) {
908 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
909 prefs_common.compose_save_to_history = add_history(
910 prefs_common.compose_save_to_history, folderidentifier);
911 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
912 prefs_common.compose_save_to_history);
915 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
916 if (folderidentifier)
917 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
919 gtk_entry_set_text(GTK_ENTRY(entry), "");
922 static gchar *compose_get_save_to(Compose *compose)
925 gchar *result = NULL;
926 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
927 result = gtk_editable_get_chars(entry, 0, -1);
930 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
931 prefs_common.compose_save_to_history = add_history(
932 prefs_common.compose_save_to_history, result);
933 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
934 prefs_common.compose_save_to_history);
939 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
940 GPtrArray *attach_files, GList *listAddress )
943 GtkTextView *textview;
944 GtkTextBuffer *textbuf;
946 const gchar *subject_format = NULL;
947 const gchar *body_format = NULL;
948 gchar *mailto_from = NULL;
949 PrefsAccount *mailto_account = NULL;
950 MsgInfo* dummyinfo = NULL;
951 MailField mfield = NO_FIELD_PRESENT;
955 /* check if mailto defines a from */
956 if (mailto && *mailto != '\0') {
957 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
958 /* mailto defines a from, check if we can get account prefs from it,
959 if not, the account prefs will be guessed using other ways, but we'll keep
962 mailto_account = account_find_from_address(mailto_from, TRUE);
964 account = mailto_account;
967 /* if no account prefs set from mailto, set if from folder prefs (if any) */
968 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
969 account = account_find_from_id(item->prefs->default_account);
971 /* if no account prefs set, fallback to the current one */
972 if (!account) account = cur_account;
973 cm_return_val_if_fail(account != NULL, NULL);
975 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
977 /* override from name if mailto asked for it */
979 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
982 /* override from name according to folder properties */
983 if (item && item->prefs &&
984 item->prefs->compose_with_format &&
985 item->prefs->compose_override_from_format &&
986 *item->prefs->compose_override_from_format != '\0') {
991 dummyinfo = compose_msginfo_new_from_compose(compose);
993 /* decode \-escape sequences in the internal representation of the quote format */
994 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
995 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
998 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1001 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1003 quote_fmt_scan_string(tmp);
1006 buf = quote_fmt_get_buffer();
1008 alertpanel_error(_("New message From format error."));
1010 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1011 quote_fmt_reset_vartable();
1016 compose->replyinfo = NULL;
1017 compose->fwdinfo = NULL;
1019 textview = GTK_TEXT_VIEW(compose->text);
1020 textbuf = gtk_text_view_get_buffer(textview);
1021 compose_create_tags(textview, compose);
1023 undo_block(compose->undostruct);
1025 compose_set_dictionaries_from_folder_prefs(compose, item);
1028 if (account->auto_sig)
1029 compose_insert_sig(compose, FALSE);
1030 gtk_text_buffer_get_start_iter(textbuf, &iter);
1031 gtk_text_buffer_place_cursor(textbuf, &iter);
1033 if (account->protocol != A_NNTP) {
1034 if (mailto && *mailto != '\0') {
1035 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1038 compose_set_folder_prefs(compose, item, TRUE);
1040 if (item && item->ret_rcpt) {
1041 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1044 if (mailto && *mailto != '\0') {
1045 if (!strchr(mailto, '@'))
1046 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1048 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1049 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1050 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1051 mfield = TO_FIELD_PRESENT;
1054 * CLAWS: just don't allow return receipt request, even if the user
1055 * may want to send an email. simple but foolproof.
1057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1059 compose_add_field_list( compose, listAddress );
1061 if (item && item->prefs && item->prefs->compose_with_format) {
1062 subject_format = item->prefs->compose_subject_format;
1063 body_format = item->prefs->compose_body_format;
1064 } else if (account->compose_with_format) {
1065 subject_format = account->compose_subject_format;
1066 body_format = account->compose_body_format;
1067 } else if (prefs_common.compose_with_format) {
1068 subject_format = prefs_common.compose_subject_format;
1069 body_format = prefs_common.compose_body_format;
1072 if (subject_format || body_format) {
1075 && *subject_format != '\0' )
1077 gchar *subject = NULL;
1082 dummyinfo = compose_msginfo_new_from_compose(compose);
1084 /* decode \-escape sequences in the internal representation of the quote format */
1085 tmp = g_malloc(strlen(subject_format)+1);
1086 pref_get_unescaped_pref(tmp, subject_format);
1088 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1090 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1091 compose->gtkaspell);
1093 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1095 quote_fmt_scan_string(tmp);
1098 buf = quote_fmt_get_buffer();
1100 alertpanel_error(_("New message subject format error."));
1102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1103 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1104 quote_fmt_reset_vartable();
1108 mfield = SUBJECT_FIELD_PRESENT;
1112 && *body_format != '\0' )
1115 GtkTextBuffer *buffer;
1116 GtkTextIter start, end;
1120 dummyinfo = compose_msginfo_new_from_compose(compose);
1122 text = GTK_TEXT_VIEW(compose->text);
1123 buffer = gtk_text_view_get_buffer(text);
1124 gtk_text_buffer_get_start_iter(buffer, &start);
1125 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1126 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1128 compose_quote_fmt(compose, dummyinfo,
1130 NULL, tmp, FALSE, TRUE,
1131 _("The body of the \"New message\" template has an error at line %d."));
1132 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1133 quote_fmt_reset_vartable();
1137 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1138 gtkaspell_highlight_all(compose->gtkaspell);
1140 mfield = BODY_FIELD_PRESENT;
1144 procmsg_msginfo_free( dummyinfo );
1150 for (i = 0; i < attach_files->len; i++) {
1151 file = g_ptr_array_index(attach_files, i);
1152 compose_attach_append(compose, file, file, NULL, NULL);
1156 compose_show_first_last_header(compose, TRUE);
1158 /* Set save folder */
1159 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1160 gchar *folderidentifier;
1162 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1163 folderidentifier = folder_item_get_identifier(item);
1164 compose_set_save_to(compose, folderidentifier);
1165 g_free(folderidentifier);
1168 /* Place cursor according to provided input (mfield) */
1170 case NO_FIELD_PRESENT:
1171 if (compose->header_last)
1172 gtk_widget_grab_focus(compose->header_last->entry);
1174 case TO_FIELD_PRESENT:
1175 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1177 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 gtk_widget_grab_focus(compose->subject_entry);
1182 case SUBJECT_FIELD_PRESENT:
1183 textview = GTK_TEXT_VIEW(compose->text);
1186 textbuf = gtk_text_view_get_buffer(textview);
1189 mark = gtk_text_buffer_get_insert(textbuf);
1190 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1191 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1193 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1194 * only defers where it comes to the variable body
1195 * is not null. If no body is present compose->text
1196 * will be null in which case you cannot place the
1197 * cursor inside the component so. An empty component
1198 * is therefore created before placing the cursor
1200 case BODY_FIELD_PRESENT:
1201 gtk_widget_grab_focus(compose->text);
1205 undo_unblock(compose->undostruct);
1207 if (prefs_common.auto_exteditor)
1208 compose_exec_ext_editor(compose);
1210 compose->draft_timeout_tag = -1;
1211 SCROLL_TO_CURSOR(compose);
1213 compose->modified = FALSE;
1214 compose_set_title(compose);
1216 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1221 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1222 gboolean override_pref, const gchar *system)
1224 const gchar *privacy = NULL;
1226 cm_return_if_fail(compose != NULL);
1227 cm_return_if_fail(account != NULL);
1229 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1234 else if (account->default_privacy_system
1235 && strlen(account->default_privacy_system)) {
1236 privacy = account->default_privacy_system;
1238 GSList *privacy_avail = privacy_get_system_ids();
1239 if (privacy_avail && g_slist_length(privacy_avail)) {
1240 privacy = (gchar *)(privacy_avail->data);
1243 if (privacy != NULL) {
1245 g_free(compose->privacy_system);
1246 compose->privacy_system = NULL;
1248 if (compose->privacy_system == NULL)
1249 compose->privacy_system = g_strdup(privacy);
1250 else if (*(compose->privacy_system) == '\0') {
1251 g_free(compose->privacy_system);
1252 compose->privacy_system = g_strdup(privacy);
1254 compose_update_privacy_system_menu_item(compose, FALSE);
1255 compose_use_encryption(compose, TRUE);
1259 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1261 const gchar *privacy = NULL;
1265 else if (account->default_privacy_system
1266 && strlen(account->default_privacy_system)) {
1267 privacy = account->default_privacy_system;
1269 GSList *privacy_avail = privacy_get_system_ids();
1270 if (privacy_avail && g_slist_length(privacy_avail)) {
1271 privacy = (gchar *)(privacy_avail->data);
1275 if (privacy != NULL) {
1277 g_free(compose->privacy_system);
1278 compose->privacy_system = NULL;
1280 if (compose->privacy_system == NULL)
1281 compose->privacy_system = g_strdup(privacy);
1282 compose_update_privacy_system_menu_item(compose, FALSE);
1283 compose_use_signing(compose, TRUE);
1287 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1291 Compose *compose = NULL;
1293 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1295 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1296 cm_return_val_if_fail(msginfo != NULL, NULL);
1298 list_len = g_slist_length(msginfo_list);
1302 case COMPOSE_REPLY_TO_ADDRESS:
1303 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1304 FALSE, prefs_common.default_reply_list, FALSE, body);
1306 case COMPOSE_REPLY_WITH_QUOTE:
1307 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1308 FALSE, prefs_common.default_reply_list, FALSE, body);
1310 case COMPOSE_REPLY_WITHOUT_QUOTE:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1312 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1314 case COMPOSE_REPLY_TO_SENDER:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1316 FALSE, FALSE, TRUE, body);
1318 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1319 compose = compose_followup_and_reply_to(msginfo,
1320 COMPOSE_QUOTE_CHECK,
1321 FALSE, FALSE, body);
1323 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1324 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1325 FALSE, FALSE, TRUE, body);
1327 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1328 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1329 FALSE, FALSE, TRUE, NULL);
1331 case COMPOSE_REPLY_TO_ALL:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1333 TRUE, FALSE, FALSE, body);
1335 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1337 TRUE, FALSE, FALSE, body);
1339 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1341 TRUE, FALSE, FALSE, NULL);
1343 case COMPOSE_REPLY_TO_LIST:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345 FALSE, TRUE, FALSE, body);
1347 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1349 FALSE, TRUE, FALSE, body);
1351 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1353 FALSE, TRUE, FALSE, NULL);
1355 case COMPOSE_FORWARD:
1356 if (prefs_common.forward_as_attachment) {
1357 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1360 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1364 case COMPOSE_FORWARD_INLINE:
1365 /* check if we reply to more than one Message */
1366 if (list_len == 1) {
1367 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1370 /* more messages FALL THROUGH */
1371 case COMPOSE_FORWARD_AS_ATTACH:
1372 compose = compose_forward_multiple(NULL, msginfo_list);
1374 case COMPOSE_REDIRECT:
1375 compose = compose_redirect(NULL, msginfo, FALSE);
1378 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1381 if (compose == NULL) {
1382 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1386 compose->rmode = mode;
1387 switch (compose->rmode) {
1389 case COMPOSE_REPLY_WITH_QUOTE:
1390 case COMPOSE_REPLY_WITHOUT_QUOTE:
1391 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1392 debug_print("reply mode Normal\n");
1393 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1394 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1396 case COMPOSE_REPLY_TO_SENDER:
1397 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1398 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1399 debug_print("reply mode Sender\n");
1400 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1402 case COMPOSE_REPLY_TO_ALL:
1403 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1404 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1405 debug_print("reply mode All\n");
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1408 case COMPOSE_REPLY_TO_LIST:
1409 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1410 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1411 debug_print("reply mode List\n");
1412 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1414 case COMPOSE_REPLY_TO_ADDRESS:
1415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1423 static Compose *compose_reply(MsgInfo *msginfo,
1424 ComposeQuoteMode quote_mode,
1430 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1431 to_sender, FALSE, body);
1434 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1435 ComposeQuoteMode quote_mode,
1440 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1441 to_sender, TRUE, body);
1444 static void compose_extract_original_charset(Compose *compose)
1446 MsgInfo *info = NULL;
1447 if (compose->replyinfo) {
1448 info = compose->replyinfo;
1449 } else if (compose->fwdinfo) {
1450 info = compose->fwdinfo;
1451 } else if (compose->targetinfo) {
1452 info = compose->targetinfo;
1455 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1456 MimeInfo *partinfo = mimeinfo;
1457 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1458 partinfo = procmime_mimeinfo_next(partinfo);
1460 compose->orig_charset =
1461 g_strdup(procmime_mimeinfo_get_parameter(
1462 partinfo, "charset"));
1464 procmime_mimeinfo_free_all(mimeinfo);
1468 #define SIGNAL_BLOCK(buffer) { \
1469 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1470 G_CALLBACK(compose_changed_cb), \
1472 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1473 G_CALLBACK(text_inserted), \
1477 #define SIGNAL_UNBLOCK(buffer) { \
1478 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1479 G_CALLBACK(compose_changed_cb), \
1481 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1482 G_CALLBACK(text_inserted), \
1486 static Compose *compose_generic_reply(MsgInfo *msginfo,
1487 ComposeQuoteMode quote_mode,
1488 gboolean to_all, gboolean to_ml,
1490 gboolean followup_and_reply_to,
1494 PrefsAccount *account = NULL;
1495 GtkTextView *textview;
1496 GtkTextBuffer *textbuf;
1497 gboolean quote = FALSE;
1498 const gchar *qmark = NULL;
1499 const gchar *body_fmt = NULL;
1500 gchar *s_system = NULL;
1502 cm_return_val_if_fail(msginfo != NULL, NULL);
1503 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1505 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1507 cm_return_val_if_fail(account != NULL, NULL);
1509 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1511 compose->updating = TRUE;
1513 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1514 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1516 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1517 if (!compose->replyinfo)
1518 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1520 compose_extract_original_charset(compose);
1522 if (msginfo->folder && msginfo->folder->ret_rcpt)
1523 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1525 /* Set save folder */
1526 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1527 gchar *folderidentifier;
1529 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1530 folderidentifier = folder_item_get_identifier(msginfo->folder);
1531 compose_set_save_to(compose, folderidentifier);
1532 g_free(folderidentifier);
1535 if (compose_parse_header(compose, msginfo) < 0) {
1536 compose->updating = FALSE;
1537 compose_destroy(compose);
1541 /* override from name according to folder properties */
1542 if (msginfo->folder && msginfo->folder->prefs &&
1543 msginfo->folder->prefs->reply_with_format &&
1544 msginfo->folder->prefs->reply_override_from_format &&
1545 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1550 /* decode \-escape sequences in the internal representation of the quote format */
1551 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1552 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1555 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1556 compose->gtkaspell);
1558 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1560 quote_fmt_scan_string(tmp);
1563 buf = quote_fmt_get_buffer();
1565 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1567 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1568 quote_fmt_reset_vartable();
1573 textview = (GTK_TEXT_VIEW(compose->text));
1574 textbuf = gtk_text_view_get_buffer(textview);
1575 compose_create_tags(textview, compose);
1577 undo_block(compose->undostruct);
1579 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1582 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1583 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1584 /* use the reply format of folder (if enabled), or the account's one
1585 (if enabled) or fallback to the global reply format, which is always
1586 enabled (even if empty), and use the relevant quotemark */
1588 if (msginfo->folder && msginfo->folder->prefs &&
1589 msginfo->folder->prefs->reply_with_format) {
1590 qmark = msginfo->folder->prefs->reply_quotemark;
1591 body_fmt = msginfo->folder->prefs->reply_body_format;
1593 } else if (account->reply_with_format) {
1594 qmark = account->reply_quotemark;
1595 body_fmt = account->reply_body_format;
1598 qmark = prefs_common.quotemark;
1599 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1600 body_fmt = gettext(prefs_common.quotefmt);
1607 /* empty quotemark is not allowed */
1608 if (qmark == NULL || *qmark == '\0')
1610 compose_quote_fmt(compose, compose->replyinfo,
1611 body_fmt, qmark, body, FALSE, TRUE,
1612 _("The body of the \"Reply\" template has an error at line %d."));
1613 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1614 quote_fmt_reset_vartable();
1616 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1617 gtkaspell_highlight_all(compose->gtkaspell);
1621 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1622 compose_force_encryption(compose, account, FALSE, s_system);
1625 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1626 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1627 compose_force_signing(compose, account, s_system);
1631 SIGNAL_BLOCK(textbuf);
1633 if (account->auto_sig)
1634 compose_insert_sig(compose, FALSE);
1636 compose_wrap_all(compose);
1638 SIGNAL_UNBLOCK(textbuf);
1640 gtk_widget_grab_focus(compose->text);
1642 undo_unblock(compose->undostruct);
1644 if (prefs_common.auto_exteditor)
1645 compose_exec_ext_editor(compose);
1647 compose->modified = FALSE;
1648 compose_set_title(compose);
1650 compose->updating = FALSE;
1651 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1652 SCROLL_TO_CURSOR(compose);
1654 if (compose->deferred_destroy) {
1655 compose_destroy(compose);
1663 #define INSERT_FW_HEADER(var, hdr) \
1664 if (msginfo->var && *msginfo->var) { \
1665 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1666 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1667 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1670 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1671 gboolean as_attach, const gchar *body,
1672 gboolean no_extedit,
1676 GtkTextView *textview;
1677 GtkTextBuffer *textbuf;
1681 cm_return_val_if_fail(msginfo != NULL, NULL);
1682 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1685 !(account = compose_guess_forward_account_from_msginfo
1687 account = cur_account;
1689 if (!prefs_common.forward_as_attachment)
1690 mode = COMPOSE_FORWARD_INLINE;
1692 mode = COMPOSE_FORWARD;
1693 compose = compose_create(account, msginfo->folder, mode, batch);
1695 compose->updating = TRUE;
1696 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1697 if (!compose->fwdinfo)
1698 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1700 compose_extract_original_charset(compose);
1702 if (msginfo->subject && *msginfo->subject) {
1703 gchar *buf, *buf2, *p;
1705 buf = p = g_strdup(msginfo->subject);
1706 p += subject_get_prefix_length(p);
1707 memmove(buf, p, strlen(p) + 1);
1709 buf2 = g_strdup_printf("Fw: %s", buf);
1710 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1716 /* override from name according to folder properties */
1717 if (msginfo->folder && msginfo->folder->prefs &&
1718 msginfo->folder->prefs->forward_with_format &&
1719 msginfo->folder->prefs->forward_override_from_format &&
1720 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1724 MsgInfo *full_msginfo = NULL;
1727 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1729 full_msginfo = procmsg_msginfo_copy(msginfo);
1731 /* decode \-escape sequences in the internal representation of the quote format */
1732 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1733 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1736 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1737 compose->gtkaspell);
1739 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1741 quote_fmt_scan_string(tmp);
1744 buf = quote_fmt_get_buffer();
1746 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1748 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1749 quote_fmt_reset_vartable();
1752 procmsg_msginfo_free(full_msginfo);
1755 textview = GTK_TEXT_VIEW(compose->text);
1756 textbuf = gtk_text_view_get_buffer(textview);
1757 compose_create_tags(textview, compose);
1759 undo_block(compose->undostruct);
1763 msgfile = procmsg_get_message_file(msginfo);
1764 if (!is_file_exist(msgfile))
1765 g_warning("%s: file not exist\n", msgfile);
1767 compose_attach_append(compose, msgfile, msgfile,
1768 "message/rfc822", NULL);
1772 const gchar *qmark = NULL;
1773 const gchar *body_fmt = NULL;
1774 MsgInfo *full_msginfo;
1776 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1777 body_fmt = gettext(prefs_common.fw_quotefmt);
1781 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1783 full_msginfo = procmsg_msginfo_copy(msginfo);
1785 /* use the forward format of folder (if enabled), or the account's one
1786 (if enabled) or fallback to the global forward format, which is always
1787 enabled (even if empty), and use the relevant quotemark */
1788 if (msginfo->folder && msginfo->folder->prefs &&
1789 msginfo->folder->prefs->forward_with_format) {
1790 qmark = msginfo->folder->prefs->forward_quotemark;
1791 body_fmt = msginfo->folder->prefs->forward_body_format;
1793 } else if (account->forward_with_format) {
1794 qmark = account->forward_quotemark;
1795 body_fmt = account->forward_body_format;
1798 qmark = prefs_common.fw_quotemark;
1799 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1800 body_fmt = gettext(prefs_common.fw_quotefmt);
1805 /* empty quotemark is not allowed */
1806 if (qmark == NULL || *qmark == '\0')
1809 compose_quote_fmt(compose, full_msginfo,
1810 body_fmt, qmark, body, FALSE, TRUE,
1811 _("The body of the \"Forward\" template has an error at line %d."));
1812 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1813 quote_fmt_reset_vartable();
1814 compose_attach_parts(compose, msginfo);
1816 procmsg_msginfo_free(full_msginfo);
1818 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1819 gtkaspell_highlight_all(compose->gtkaspell);
1823 SIGNAL_BLOCK(textbuf);
1825 if (account->auto_sig)
1826 compose_insert_sig(compose, FALSE);
1828 compose_wrap_all(compose);
1830 SIGNAL_UNBLOCK(textbuf);
1832 gtk_text_buffer_get_start_iter(textbuf, &iter);
1833 gtk_text_buffer_place_cursor(textbuf, &iter);
1835 gtk_widget_grab_focus(compose->header_last->entry);
1837 if (!no_extedit && prefs_common.auto_exteditor)
1838 compose_exec_ext_editor(compose);
1841 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1842 gchar *folderidentifier;
1844 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1845 folderidentifier = folder_item_get_identifier(msginfo->folder);
1846 compose_set_save_to(compose, folderidentifier);
1847 g_free(folderidentifier);
1850 undo_unblock(compose->undostruct);
1852 compose->modified = FALSE;
1853 compose_set_title(compose);
1855 compose->updating = FALSE;
1856 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1857 SCROLL_TO_CURSOR(compose);
1859 if (compose->deferred_destroy) {
1860 compose_destroy(compose);
1864 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1869 #undef INSERT_FW_HEADER
1871 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1874 GtkTextView *textview;
1875 GtkTextBuffer *textbuf;
1879 gboolean single_mail = TRUE;
1881 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1883 if (g_slist_length(msginfo_list) > 1)
1884 single_mail = FALSE;
1886 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1887 if (((MsgInfo *)msginfo->data)->folder == NULL)
1890 /* guess account from first selected message */
1892 !(account = compose_guess_forward_account_from_msginfo
1893 (msginfo_list->data)))
1894 account = cur_account;
1896 cm_return_val_if_fail(account != NULL, NULL);
1898 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1899 if (msginfo->data) {
1900 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1901 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1905 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1906 g_warning("no msginfo_list");
1910 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1912 compose->updating = TRUE;
1914 /* override from name according to folder properties */
1915 if (msginfo_list->data) {
1916 MsgInfo *msginfo = msginfo_list->data;
1918 if (msginfo->folder && msginfo->folder->prefs &&
1919 msginfo->folder->prefs->forward_with_format &&
1920 msginfo->folder->prefs->forward_override_from_format &&
1921 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1926 /* decode \-escape sequences in the internal representation of the quote format */
1927 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1928 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1931 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1932 compose->gtkaspell);
1934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1936 quote_fmt_scan_string(tmp);
1939 buf = quote_fmt_get_buffer();
1941 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1943 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1944 quote_fmt_reset_vartable();
1950 textview = GTK_TEXT_VIEW(compose->text);
1951 textbuf = gtk_text_view_get_buffer(textview);
1952 compose_create_tags(textview, compose);
1954 undo_block(compose->undostruct);
1955 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1956 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1958 if (!is_file_exist(msgfile))
1959 g_warning("%s: file not exist\n", msgfile);
1961 compose_attach_append(compose, msgfile, msgfile,
1962 "message/rfc822", NULL);
1967 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1968 if (info->subject && *info->subject) {
1969 gchar *buf, *buf2, *p;
1971 buf = p = g_strdup(info->subject);
1972 p += subject_get_prefix_length(p);
1973 memmove(buf, p, strlen(p) + 1);
1975 buf2 = g_strdup_printf("Fw: %s", buf);
1976 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1982 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1983 _("Fw: multiple emails"));
1986 SIGNAL_BLOCK(textbuf);
1988 if (account->auto_sig)
1989 compose_insert_sig(compose, FALSE);
1991 compose_wrap_all(compose);
1993 SIGNAL_UNBLOCK(textbuf);
1995 gtk_text_buffer_get_start_iter(textbuf, &iter);
1996 gtk_text_buffer_place_cursor(textbuf, &iter);
1998 gtk_widget_grab_focus(compose->header_last->entry);
1999 undo_unblock(compose->undostruct);
2000 compose->modified = FALSE;
2001 compose_set_title(compose);
2003 compose->updating = FALSE;
2004 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2005 SCROLL_TO_CURSOR(compose);
2007 if (compose->deferred_destroy) {
2008 compose_destroy(compose);
2012 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2017 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2019 GtkTextIter start = *iter;
2020 GtkTextIter end_iter;
2021 int start_pos = gtk_text_iter_get_offset(&start);
2023 if (!compose->account->sig_sep)
2026 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2027 start_pos+strlen(compose->account->sig_sep));
2029 /* check sig separator */
2030 str = gtk_text_iter_get_text(&start, &end_iter);
2031 if (!strcmp(str, compose->account->sig_sep)) {
2033 /* check end of line (\n) */
2034 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2035 start_pos+strlen(compose->account->sig_sep));
2036 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2037 start_pos+strlen(compose->account->sig_sep)+1);
2038 tmp = gtk_text_iter_get_text(&start, &end_iter);
2039 if (!strcmp(tmp,"\n")) {
2051 static void compose_colorize_signature(Compose *compose)
2053 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2055 GtkTextIter end_iter;
2056 gtk_text_buffer_get_start_iter(buffer, &iter);
2057 while (gtk_text_iter_forward_line(&iter))
2058 if (compose_is_sig_separator(compose, buffer, &iter)) {
2059 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2060 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2064 #define BLOCK_WRAP() { \
2065 prev_autowrap = compose->autowrap; \
2066 buffer = gtk_text_view_get_buffer( \
2067 GTK_TEXT_VIEW(compose->text)); \
2068 compose->autowrap = FALSE; \
2070 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2071 G_CALLBACK(compose_changed_cb), \
2073 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2074 G_CALLBACK(text_inserted), \
2077 #define UNBLOCK_WRAP() { \
2078 compose->autowrap = prev_autowrap; \
2079 if (compose->autowrap) { \
2080 gint old = compose->draft_timeout_tag; \
2081 compose->draft_timeout_tag = -2; \
2082 compose_wrap_all(compose); \
2083 compose->draft_timeout_tag = old; \
2086 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2087 G_CALLBACK(compose_changed_cb), \
2089 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2090 G_CALLBACK(text_inserted), \
2094 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2096 Compose *compose = NULL;
2097 PrefsAccount *account = NULL;
2098 GtkTextView *textview;
2099 GtkTextBuffer *textbuf;
2103 gchar buf[BUFFSIZE];
2104 gboolean use_signing = FALSE;
2105 gboolean use_encryption = FALSE;
2106 gchar *privacy_system = NULL;
2107 int priority = PRIORITY_NORMAL;
2108 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2109 gboolean autowrap = prefs_common.autowrap;
2110 gboolean autoindent = prefs_common.auto_indent;
2112 cm_return_val_if_fail(msginfo != NULL, NULL);
2113 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2115 if (compose_put_existing_to_front(msginfo)) {
2119 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2120 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2121 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2122 gchar queueheader_buf[BUFFSIZE];
2125 /* Select Account from queue headers */
2126 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2127 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2128 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2129 account = account_find_from_id(id);
2131 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2132 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2133 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2134 account = account_find_from_id(id);
2136 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "NAID:")) {
2138 id = atoi(&queueheader_buf[strlen("NAID:")]);
2139 account = account_find_from_id(id);
2141 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2142 sizeof(queueheader_buf), "MAID:")) {
2143 id = atoi(&queueheader_buf[strlen("MAID:")]);
2144 account = account_find_from_id(id);
2146 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2147 sizeof(queueheader_buf), "S:")) {
2148 account = account_find_from_address(queueheader_buf, FALSE);
2150 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2151 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2152 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2153 use_signing = param;
2156 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2157 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2158 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2159 use_signing = param;
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2164 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2165 use_encryption = param;
2167 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2169 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2170 use_encryption = param;
2172 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2174 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2177 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2179 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2182 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2183 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2184 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2186 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2187 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2188 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2190 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2191 sizeof(queueheader_buf), "X-Priority: ")) {
2192 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "RMID:")) {
2197 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2198 if (tokens[0] && tokens[1] && tokens[2]) {
2199 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2200 if (orig_item != NULL) {
2201 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2206 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2207 sizeof(queueheader_buf), "FMID:")) {
2208 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2209 if (tokens[0] && tokens[1] && tokens[2]) {
2210 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2211 if (orig_item != NULL) {
2212 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2218 account = msginfo->folder->folder->account;
2221 if (!account && prefs_common.reedit_account_autosel) {
2222 gchar from[BUFFSIZE];
2223 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2224 extract_address(from);
2225 account = account_find_from_address(from, FALSE);
2229 account = cur_account;
2231 cm_return_val_if_fail(account != NULL, NULL);
2233 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2235 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2236 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2237 compose->autowrap = autowrap;
2238 compose->replyinfo = replyinfo;
2239 compose->fwdinfo = fwdinfo;
2241 compose->updating = TRUE;
2242 compose->priority = priority;
2244 if (privacy_system != NULL) {
2245 compose->privacy_system = privacy_system;
2246 compose_use_signing(compose, use_signing);
2247 compose_use_encryption(compose, use_encryption);
2248 compose_update_privacy_system_menu_item(compose, FALSE);
2250 activate_privacy_system(compose, account, FALSE);
2253 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2255 compose_extract_original_charset(compose);
2257 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2258 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2259 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2260 gchar queueheader_buf[BUFFSIZE];
2262 /* Set message save folder */
2263 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2264 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2265 compose_set_save_to(compose, &queueheader_buf[4]);
2267 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2268 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2270 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2275 if (compose_parse_header(compose, msginfo) < 0) {
2276 compose->updating = FALSE;
2277 compose_destroy(compose);
2280 compose_reedit_set_entry(compose, msginfo);
2282 textview = GTK_TEXT_VIEW(compose->text);
2283 textbuf = gtk_text_view_get_buffer(textview);
2284 compose_create_tags(textview, compose);
2286 mark = gtk_text_buffer_get_insert(textbuf);
2287 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2289 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2290 G_CALLBACK(compose_changed_cb),
2293 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2294 fp = procmime_get_first_encrypted_text_content(msginfo);
2296 compose_force_encryption(compose, account, TRUE, NULL);
2299 fp = procmime_get_first_text_content(msginfo);
2302 g_warning("Can't get text part\n");
2306 gboolean prev_autowrap = compose->autowrap;
2307 GtkTextBuffer *buffer = textbuf;
2309 while (fgets(buf, sizeof(buf), fp) != NULL) {
2311 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2317 compose_attach_parts(compose, msginfo);
2319 compose_colorize_signature(compose);
2321 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2322 G_CALLBACK(compose_changed_cb),
2325 gtk_widget_grab_focus(compose->text);
2327 if (prefs_common.auto_exteditor) {
2328 compose_exec_ext_editor(compose);
2330 compose->modified = FALSE;
2331 compose_set_title(compose);
2333 compose->updating = FALSE;
2334 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2335 SCROLL_TO_CURSOR(compose);
2337 if (compose->deferred_destroy) {
2338 compose_destroy(compose);
2342 compose->sig_str = account_get_signature_str(compose->account);
2344 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2349 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2356 cm_return_val_if_fail(msginfo != NULL, NULL);
2359 account = account_get_reply_account(msginfo,
2360 prefs_common.reply_account_autosel);
2361 cm_return_val_if_fail(account != NULL, NULL);
2363 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2365 compose->updating = TRUE;
2367 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2368 compose->replyinfo = NULL;
2369 compose->fwdinfo = NULL;
2371 compose_show_first_last_header(compose, TRUE);
2373 gtk_widget_grab_focus(compose->header_last->entry);
2375 filename = procmsg_get_message_file(msginfo);
2377 if (filename == NULL) {
2378 compose->updating = FALSE;
2379 compose_destroy(compose);
2384 compose->redirect_filename = filename;
2386 /* Set save folder */
2387 item = msginfo->folder;
2388 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2389 gchar *folderidentifier;
2391 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2392 folderidentifier = folder_item_get_identifier(item);
2393 compose_set_save_to(compose, folderidentifier);
2394 g_free(folderidentifier);
2397 compose_attach_parts(compose, msginfo);
2399 if (msginfo->subject)
2400 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2402 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2404 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2405 _("The body of the \"Redirect\" template has an error at line %d."));
2406 quote_fmt_reset_vartable();
2407 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2409 compose_colorize_signature(compose);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2413 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2417 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2418 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2419 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2420 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2422 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2425 if (compose->toolbar->draft_btn)
2426 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2427 if (compose->toolbar->insert_btn)
2428 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2429 if (compose->toolbar->attach_btn)
2430 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2431 if (compose->toolbar->sig_btn)
2432 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2433 if (compose->toolbar->exteditor_btn)
2434 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2435 if (compose->toolbar->linewrap_current_btn)
2436 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2437 if (compose->toolbar->linewrap_all_btn)
2438 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2440 compose->modified = FALSE;
2441 compose_set_title(compose);
2442 compose->updating = FALSE;
2443 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2444 SCROLL_TO_CURSOR(compose);
2446 if (compose->deferred_destroy) {
2447 compose_destroy(compose);
2451 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2456 GList *compose_get_compose_list(void)
2458 return compose_list;
2461 void compose_entry_append(Compose *compose, const gchar *address,
2462 ComposeEntryType type, ComposePrefType pref_type)
2464 const gchar *header;
2466 gboolean in_quote = FALSE;
2467 if (!address || *address == '\0') return;
2474 header = N_("Bcc:");
2476 case COMPOSE_REPLYTO:
2477 header = N_("Reply-To:");
2479 case COMPOSE_NEWSGROUPS:
2480 header = N_("Newsgroups:");
2482 case COMPOSE_FOLLOWUPTO:
2483 header = N_( "Followup-To:");
2485 case COMPOSE_INREPLYTO:
2486 header = N_( "In-Reply-To:");
2493 header = prefs_common_translated_header_name(header);
2495 cur = begin = (gchar *)address;
2497 /* we separate the line by commas, but not if we're inside a quoted
2499 while (*cur != '\0') {
2501 in_quote = !in_quote;
2502 if (*cur == ',' && !in_quote) {
2503 gchar *tmp = g_strdup(begin);
2505 tmp[cur-begin]='\0';
2508 while (*tmp == ' ' || *tmp == '\t')
2510 compose_add_header_entry(compose, header, tmp, pref_type);
2517 gchar *tmp = g_strdup(begin);
2519 tmp[cur-begin]='\0';
2522 while (*tmp == ' ' || *tmp == '\t')
2524 compose_add_header_entry(compose, header, tmp, pref_type);
2529 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2531 static GdkColor yellow;
2532 static GdkColor black;
2533 static gboolean yellow_initialised = FALSE;
2537 if (!yellow_initialised) {
2538 gdk_color_parse("#f5f6be", &yellow);
2539 gdk_color_parse("#000000", &black);
2540 yellow_initialised = gdk_colormap_alloc_color(
2541 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2542 yellow_initialised &= gdk_colormap_alloc_color(
2543 gdk_colormap_get_system(), &black, FALSE, TRUE);
2546 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2547 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2548 if (gtk_entry_get_text(entry) &&
2549 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2550 if (yellow_initialised) {
2551 gtk_widget_modify_base(
2552 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2553 GTK_STATE_NORMAL, &yellow);
2554 gtk_widget_modify_text(
2555 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2556 GTK_STATE_NORMAL, &black);
2562 void compose_toolbar_cb(gint action, gpointer data)
2564 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2565 Compose *compose = (Compose*)toolbar_item->parent;
2567 cm_return_if_fail(compose != NULL);
2571 compose_send_cb(NULL, compose);
2574 compose_send_later_cb(NULL, compose);
2577 compose_draft(compose, COMPOSE_QUIT_EDITING);
2580 compose_insert_file_cb(NULL, compose);
2583 compose_attach_cb(NULL, compose);
2586 compose_insert_sig(compose, FALSE);
2589 compose_ext_editor_cb(NULL, compose);
2591 case A_LINEWRAP_CURRENT:
2592 compose_beautify_paragraph(compose, NULL, TRUE);
2594 case A_LINEWRAP_ALL:
2595 compose_wrap_all_full(compose, TRUE);
2598 compose_address_cb(NULL, compose);
2601 case A_CHECK_SPELLING:
2602 compose_check_all(NULL, compose);
2610 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2615 gchar *subject = NULL;
2619 gchar **attach = NULL;
2620 gchar *inreplyto = NULL;
2621 MailField mfield = NO_FIELD_PRESENT;
2623 /* get mailto parts but skip from */
2624 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2627 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2628 mfield = TO_FIELD_PRESENT;
2631 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2633 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2635 if (!g_utf8_validate (subject, -1, NULL)) {
2636 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2637 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2640 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2642 mfield = SUBJECT_FIELD_PRESENT;
2645 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2646 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2649 gboolean prev_autowrap = compose->autowrap;
2651 compose->autowrap = FALSE;
2653 mark = gtk_text_buffer_get_insert(buffer);
2654 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2656 if (!g_utf8_validate (body, -1, NULL)) {
2657 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2658 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2661 gtk_text_buffer_insert(buffer, &iter, body, -1);
2663 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2665 compose->autowrap = prev_autowrap;
2666 if (compose->autowrap)
2667 compose_wrap_all(compose);
2668 mfield = BODY_FIELD_PRESENT;
2672 gint i = 0, att = 0;
2673 gchar *warn_files = NULL;
2674 while (attach[i] != NULL) {
2675 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2676 if (utf8_filename) {
2677 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2678 gchar *tmp = g_strdup_printf("%s%s\n",
2679 warn_files?warn_files:"",
2685 g_free(utf8_filename);
2687 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2692 alertpanel_notice(ngettext(
2693 "The following file has been attached: \n%s",
2694 "The following files have been attached: \n%s", att), warn_files);
2699 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2712 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2714 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2715 {"Cc:", NULL, TRUE},
2716 {"References:", NULL, FALSE},
2717 {"Bcc:", NULL, TRUE},
2718 {"Newsgroups:", NULL, TRUE},
2719 {"Followup-To:", NULL, TRUE},
2720 {"List-Post:", NULL, FALSE},
2721 {"X-Priority:", NULL, FALSE},
2722 {NULL, NULL, FALSE}};
2738 cm_return_val_if_fail(msginfo != NULL, -1);
2740 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2741 procheader_get_header_fields(fp, hentry);
2744 if (hentry[H_REPLY_TO].body != NULL) {
2745 if (hentry[H_REPLY_TO].body[0] != '\0') {
2747 conv_unmime_header(hentry[H_REPLY_TO].body,
2750 g_free(hentry[H_REPLY_TO].body);
2751 hentry[H_REPLY_TO].body = NULL;
2753 if (hentry[H_CC].body != NULL) {
2754 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2755 g_free(hentry[H_CC].body);
2756 hentry[H_CC].body = NULL;
2758 if (hentry[H_REFERENCES].body != NULL) {
2759 if (compose->mode == COMPOSE_REEDIT)
2760 compose->references = hentry[H_REFERENCES].body;
2762 compose->references = compose_parse_references
2763 (hentry[H_REFERENCES].body, msginfo->msgid);
2764 g_free(hentry[H_REFERENCES].body);
2766 hentry[H_REFERENCES].body = NULL;
2768 if (hentry[H_BCC].body != NULL) {
2769 if (compose->mode == COMPOSE_REEDIT)
2771 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2772 g_free(hentry[H_BCC].body);
2773 hentry[H_BCC].body = NULL;
2775 if (hentry[H_NEWSGROUPS].body != NULL) {
2776 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2777 hentry[H_NEWSGROUPS].body = NULL;
2779 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2780 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2781 compose->followup_to =
2782 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2785 g_free(hentry[H_FOLLOWUP_TO].body);
2786 hentry[H_FOLLOWUP_TO].body = NULL;
2788 if (hentry[H_LIST_POST].body != NULL) {
2789 gchar *to = NULL, *start = NULL;
2791 extract_address(hentry[H_LIST_POST].body);
2792 if (hentry[H_LIST_POST].body[0] != '\0') {
2793 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2795 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2796 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2799 g_free(compose->ml_post);
2800 compose->ml_post = to;
2803 g_free(hentry[H_LIST_POST].body);
2804 hentry[H_LIST_POST].body = NULL;
2807 /* CLAWS - X-Priority */
2808 if (compose->mode == COMPOSE_REEDIT)
2809 if (hentry[H_X_PRIORITY].body != NULL) {
2812 priority = atoi(hentry[H_X_PRIORITY].body);
2813 g_free(hentry[H_X_PRIORITY].body);
2815 hentry[H_X_PRIORITY].body = NULL;
2817 if (priority < PRIORITY_HIGHEST ||
2818 priority > PRIORITY_LOWEST)
2819 priority = PRIORITY_NORMAL;
2821 compose->priority = priority;
2824 if (compose->mode == COMPOSE_REEDIT) {
2825 if (msginfo->inreplyto && *msginfo->inreplyto)
2826 compose->inreplyto = g_strdup(msginfo->inreplyto);
2830 if (msginfo->msgid && *msginfo->msgid)
2831 compose->inreplyto = g_strdup(msginfo->msgid);
2833 if (!compose->references) {
2834 if (msginfo->msgid && *msginfo->msgid) {
2835 if (msginfo->inreplyto && *msginfo->inreplyto)
2836 compose->references =
2837 g_strdup_printf("<%s>\n\t<%s>",
2841 compose->references =
2842 g_strconcat("<", msginfo->msgid, ">",
2844 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2845 compose->references =
2846 g_strconcat("<", msginfo->inreplyto, ">",
2854 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2856 GSList *ref_id_list, *cur;
2860 ref_id_list = references_list_append(NULL, ref);
2861 if (!ref_id_list) return NULL;
2862 if (msgid && *msgid)
2863 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2868 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2869 /* "<" + Message-ID + ">" + CR+LF+TAB */
2870 len += strlen((gchar *)cur->data) + 5;
2872 if (len > MAX_REFERENCES_LEN) {
2873 /* remove second message-ID */
2874 if (ref_id_list && ref_id_list->next &&
2875 ref_id_list->next->next) {
2876 g_free(ref_id_list->next->data);
2877 ref_id_list = g_slist_remove
2878 (ref_id_list, ref_id_list->next->data);
2880 slist_free_strings(ref_id_list);
2881 g_slist_free(ref_id_list);
2888 new_ref = g_string_new("");
2889 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2890 if (new_ref->len > 0)
2891 g_string_append(new_ref, "\n\t");
2892 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2895 slist_free_strings(ref_id_list);
2896 g_slist_free(ref_id_list);
2898 new_ref_str = new_ref->str;
2899 g_string_free(new_ref, FALSE);
2904 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2905 const gchar *fmt, const gchar *qmark,
2906 const gchar *body, gboolean rewrap,
2907 gboolean need_unescape,
2908 const gchar *err_msg)
2910 MsgInfo* dummyinfo = NULL;
2911 gchar *quote_str = NULL;
2913 gboolean prev_autowrap;
2914 const gchar *trimmed_body = body;
2915 gint cursor_pos = -1;
2916 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2917 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2922 SIGNAL_BLOCK(buffer);
2925 dummyinfo = compose_msginfo_new_from_compose(compose);
2926 msginfo = dummyinfo;
2929 if (qmark != NULL) {
2931 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2932 compose->gtkaspell);
2934 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2936 quote_fmt_scan_string(qmark);
2939 buf = quote_fmt_get_buffer();
2941 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2943 Xstrdup_a(quote_str, buf, goto error)
2946 if (fmt && *fmt != '\0') {
2949 while (*trimmed_body == '\n')
2953 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2954 compose->gtkaspell);
2956 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2958 if (need_unescape) {
2961 /* decode \-escape sequences in the internal representation of the quote format */
2962 tmp = g_malloc(strlen(fmt)+1);
2963 pref_get_unescaped_pref(tmp, fmt);
2964 quote_fmt_scan_string(tmp);
2968 quote_fmt_scan_string(fmt);
2972 buf = quote_fmt_get_buffer();
2974 gint line = quote_fmt_get_line();
2975 alertpanel_error(err_msg, line);
2981 prev_autowrap = compose->autowrap;
2982 compose->autowrap = FALSE;
2984 mark = gtk_text_buffer_get_insert(buffer);
2985 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2986 if (g_utf8_validate(buf, -1, NULL)) {
2987 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2989 gchar *tmpout = NULL;
2990 tmpout = conv_codeset_strdup
2991 (buf, conv_get_locale_charset_str_no_utf8(),
2993 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2995 tmpout = g_malloc(strlen(buf)*2+1);
2996 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2998 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3002 cursor_pos = quote_fmt_get_cursor_pos();
3003 if (cursor_pos == -1)
3004 cursor_pos = gtk_text_iter_get_offset(&iter);
3005 compose->set_cursor_pos = cursor_pos;
3007 gtk_text_buffer_get_start_iter(buffer, &iter);
3008 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3009 gtk_text_buffer_place_cursor(buffer, &iter);
3011 compose->autowrap = prev_autowrap;
3012 if (compose->autowrap && rewrap)
3013 compose_wrap_all(compose);
3020 SIGNAL_UNBLOCK(buffer);
3022 procmsg_msginfo_free( dummyinfo );
3027 /* if ml_post is of type addr@host and from is of type
3028 * addr-anything@host, return TRUE
3030 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3032 gchar *left_ml = NULL;
3033 gchar *right_ml = NULL;
3034 gchar *left_from = NULL;
3035 gchar *right_from = NULL;
3036 gboolean result = FALSE;
3038 if (!ml_post || !from)
3041 left_ml = g_strdup(ml_post);
3042 if (strstr(left_ml, "@")) {
3043 right_ml = strstr(left_ml, "@")+1;
3044 *(strstr(left_ml, "@")) = '\0';
3047 left_from = g_strdup(from);
3048 if (strstr(left_from, "@")) {
3049 right_from = strstr(left_from, "@")+1;
3050 *(strstr(left_from, "@")) = '\0';
3053 if (left_ml && left_from && right_ml && right_from
3054 && !strncmp(left_from, left_ml, strlen(left_ml))
3055 && !strcmp(right_from, right_ml)) {
3064 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3065 gboolean respect_default_to)
3069 if (!folder || !folder->prefs)
3072 if (respect_default_to && folder->prefs->enable_default_to) {
3073 compose_entry_append(compose, folder->prefs->default_to,
3074 COMPOSE_TO, PREF_FOLDER);
3075 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3077 if (folder->prefs->enable_default_cc)
3078 compose_entry_append(compose, folder->prefs->default_cc,
3079 COMPOSE_CC, PREF_FOLDER);
3080 if (folder->prefs->enable_default_bcc)
3081 compose_entry_append(compose, folder->prefs->default_bcc,
3082 COMPOSE_BCC, PREF_FOLDER);
3083 if (folder->prefs->enable_default_replyto)
3084 compose_entry_append(compose, folder->prefs->default_replyto,
3085 COMPOSE_REPLYTO, PREF_FOLDER);
3088 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3093 if (!compose || !msginfo)
3096 if (msginfo->subject && *msginfo->subject) {
3097 buf = p = g_strdup(msginfo->subject);
3098 p += subject_get_prefix_length(p);
3099 memmove(buf, p, strlen(p) + 1);
3101 buf2 = g_strdup_printf("Re: %s", buf);
3102 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3107 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3110 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3111 gboolean to_all, gboolean to_ml,
3113 gboolean followup_and_reply_to)
3115 GSList *cc_list = NULL;
3118 gchar *replyto = NULL;
3119 gchar *ac_email = NULL;
3121 gboolean reply_to_ml = FALSE;
3122 gboolean default_reply_to = FALSE;
3124 cm_return_if_fail(compose->account != NULL);
3125 cm_return_if_fail(msginfo != NULL);
3127 reply_to_ml = to_ml && compose->ml_post;
3129 default_reply_to = msginfo->folder &&
3130 msginfo->folder->prefs->enable_default_reply_to;
3132 if (compose->account->protocol != A_NNTP) {
3133 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3135 if (reply_to_ml && !default_reply_to) {
3137 gboolean is_subscr = is_subscription(compose->ml_post,
3140 /* normal answer to ml post with a reply-to */
3141 compose_entry_append(compose,
3143 COMPOSE_TO, PREF_ML);
3144 if (compose->replyto)
3145 compose_entry_append(compose,
3147 COMPOSE_CC, PREF_ML);
3149 /* answer to subscription confirmation */
3150 if (compose->replyto)
3151 compose_entry_append(compose,
3153 COMPOSE_TO, PREF_ML);
3154 else if (msginfo->from)
3155 compose_entry_append(compose,
3157 COMPOSE_TO, PREF_ML);
3160 else if (!(to_all || to_sender) && default_reply_to) {
3161 compose_entry_append(compose,
3162 msginfo->folder->prefs->default_reply_to,
3163 COMPOSE_TO, PREF_FOLDER);
3164 compose_entry_mark_default_to(compose,
3165 msginfo->folder->prefs->default_reply_to);
3170 Xstrdup_a(tmp1, msginfo->from, return);
3171 extract_address(tmp1);
3172 if (to_all || to_sender ||
3173 !account_find_from_address(tmp1, FALSE))
3174 compose_entry_append(compose,
3175 (compose->replyto && !to_sender)
3176 ? compose->replyto :
3177 msginfo->from ? msginfo->from : "",
3178 COMPOSE_TO, PREF_NONE);
3179 else if (!to_all && !to_sender) {
3180 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3181 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3182 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3183 if (compose->replyto) {
3184 compose_entry_append(compose,
3186 COMPOSE_TO, PREF_NONE);
3188 compose_entry_append(compose,
3189 msginfo->from ? msginfo->from : "",
3190 COMPOSE_TO, PREF_NONE);
3193 /* replying to own mail, use original recp */
3194 compose_entry_append(compose,
3195 msginfo->to ? msginfo->to : "",
3196 COMPOSE_TO, PREF_NONE);
3197 compose_entry_append(compose,
3198 msginfo->cc ? msginfo->cc : "",
3199 COMPOSE_CC, PREF_NONE);
3204 if (to_sender || (compose->followup_to &&
3205 !strncmp(compose->followup_to, "poster", 6)))
3206 compose_entry_append
3208 (compose->replyto ? compose->replyto :
3209 msginfo->from ? msginfo->from : ""),
3210 COMPOSE_TO, PREF_NONE);
3212 else if (followup_and_reply_to || to_all) {
3213 compose_entry_append
3215 (compose->replyto ? compose->replyto :
3216 msginfo->from ? msginfo->from : ""),
3217 COMPOSE_TO, PREF_NONE);
3219 compose_entry_append
3221 compose->followup_to ? compose->followup_to :
3222 compose->newsgroups ? compose->newsgroups : "",
3223 COMPOSE_NEWSGROUPS, PREF_NONE);
3226 compose_entry_append
3228 compose->followup_to ? compose->followup_to :
3229 compose->newsgroups ? compose->newsgroups : "",
3230 COMPOSE_NEWSGROUPS, PREF_NONE);
3232 compose_reply_set_subject(compose, msginfo);
3234 if (to_ml && compose->ml_post) return;
3235 if (!to_all || compose->account->protocol == A_NNTP) return;
3237 if (compose->replyto) {
3238 Xstrdup_a(replyto, compose->replyto, return);
3239 extract_address(replyto);
3241 if (msginfo->from) {
3242 Xstrdup_a(from, msginfo->from, return);
3243 extract_address(from);
3246 if (replyto && from)
3247 cc_list = address_list_append_with_comments(cc_list, from);
3248 if (to_all && msginfo->folder &&
3249 msginfo->folder->prefs->enable_default_reply_to)
3250 cc_list = address_list_append_with_comments(cc_list,
3251 msginfo->folder->prefs->default_reply_to);
3252 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3253 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3255 ac_email = g_utf8_strdown(compose->account->address, -1);
3258 for (cur = cc_list; cur != NULL; cur = cur->next) {
3259 gchar *addr = g_utf8_strdown(cur->data, -1);
3260 extract_address(addr);
3262 if (strcmp(ac_email, addr))
3263 compose_entry_append(compose, (gchar *)cur->data,
3264 COMPOSE_CC, PREF_NONE);
3266 debug_print("Cc address same as compose account's, ignoring\n");
3271 slist_free_strings(cc_list);
3272 g_slist_free(cc_list);
3278 #define SET_ENTRY(entry, str) \
3281 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3284 #define SET_ADDRESS(type, str) \
3287 compose_entry_append(compose, str, type, PREF_NONE); \
3290 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3292 cm_return_if_fail(msginfo != NULL);
3294 SET_ENTRY(subject_entry, msginfo->subject);
3295 SET_ENTRY(from_name, msginfo->from);
3296 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3297 SET_ADDRESS(COMPOSE_CC, compose->cc);
3298 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3299 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3300 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3301 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3303 compose_update_priority_menu_item(compose);
3304 compose_update_privacy_system_menu_item(compose, FALSE);
3305 compose_show_first_last_header(compose, TRUE);
3311 static void compose_insert_sig(Compose *compose, gboolean replace)
3313 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3314 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3316 GtkTextIter iter, iter_end;
3317 gint cur_pos, ins_pos;
3318 gboolean prev_autowrap;
3319 gboolean found = FALSE;
3320 gboolean exists = FALSE;
3322 cm_return_if_fail(compose->account != NULL);
3326 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3327 G_CALLBACK(compose_changed_cb),
3330 mark = gtk_text_buffer_get_insert(buffer);
3331 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3332 cur_pos = gtk_text_iter_get_offset (&iter);
3335 gtk_text_buffer_get_end_iter(buffer, &iter);
3337 exists = (compose->sig_str != NULL);
3340 GtkTextIter first_iter, start_iter, end_iter;
3342 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3344 if (!exists || compose->sig_str[0] == '\0')
3347 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3348 compose->signature_tag);
3351 /* include previous \n\n */
3352 gtk_text_iter_backward_chars(&first_iter, 1);
3353 start_iter = first_iter;
3354 end_iter = first_iter;
3356 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3357 compose->signature_tag);
3358 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3359 compose->signature_tag);
3361 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3367 g_free(compose->sig_str);
3368 compose->sig_str = account_get_signature_str(compose->account);
3370 cur_pos = gtk_text_iter_get_offset(&iter);
3372 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3373 g_free(compose->sig_str);
3374 compose->sig_str = NULL;
3376 if (compose->sig_inserted == FALSE)
3377 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3378 compose->sig_inserted = TRUE;
3380 cur_pos = gtk_text_iter_get_offset(&iter);
3381 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3383 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3384 gtk_text_iter_forward_chars(&iter, 1);
3385 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3386 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3388 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3389 cur_pos = gtk_text_buffer_get_char_count (buffer);
3392 /* put the cursor where it should be
3393 * either where the quote_fmt says, either where it was */
3394 if (compose->set_cursor_pos < 0)
3395 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3397 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3398 compose->set_cursor_pos);
3400 compose->set_cursor_pos = -1;
3401 gtk_text_buffer_place_cursor(buffer, &iter);
3402 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3403 G_CALLBACK(compose_changed_cb),
3409 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3412 GtkTextBuffer *buffer;
3415 const gchar *cur_encoding;
3416 gchar buf[BUFFSIZE];
3419 gboolean prev_autowrap;
3420 gboolean badtxt = FALSE;
3421 struct stat file_stat;
3424 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3426 /* get the size of the file we are about to insert */
3427 ret = g_stat(file, &file_stat);
3429 gchar *shortfile = g_path_get_basename(file);
3430 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3432 return COMPOSE_INSERT_NO_FILE;
3433 } else if (prefs_common.warn_large_insert == TRUE) {
3435 /* ask user for confirmation if the file is large */
3436 if (prefs_common.warn_large_insert_size < 0 ||
3437 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3441 msg = g_strdup_printf(_("You are about to insert a file of %s "
3442 "in the message body. Are you sure you want to do that?"),
3443 to_human_readable(file_stat.st_size));
3444 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3445 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3448 /* do we ask for confirmation next time? */
3449 if (aval & G_ALERTDISABLE) {
3450 /* no confirmation next time, disable feature in preferences */
3451 aval &= ~G_ALERTDISABLE;
3452 prefs_common.warn_large_insert = FALSE;
3455 /* abort file insertion if user canceled action */
3456 if (aval != G_ALERTALTERNATE) {
3457 return COMPOSE_INSERT_NO_FILE;
3463 if ((fp = g_fopen(file, "rb")) == NULL) {
3464 FILE_OP_ERROR(file, "fopen");
3465 return COMPOSE_INSERT_READ_ERROR;
3468 prev_autowrap = compose->autowrap;
3469 compose->autowrap = FALSE;
3471 text = GTK_TEXT_VIEW(compose->text);
3472 buffer = gtk_text_view_get_buffer(text);
3473 mark = gtk_text_buffer_get_insert(buffer);
3474 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3476 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3477 G_CALLBACK(text_inserted),
3480 cur_encoding = conv_get_locale_charset_str_no_utf8();
3482 while (fgets(buf, sizeof(buf), fp) != NULL) {
3485 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3486 str = g_strdup(buf);
3488 str = conv_codeset_strdup
3489 (buf, cur_encoding, CS_INTERNAL);
3492 /* strip <CR> if DOS/Windows file,
3493 replace <CR> with <LF> if Macintosh file. */
3496 if (len > 0 && str[len - 1] != '\n') {
3498 if (str[len] == '\r') str[len] = '\n';
3501 gtk_text_buffer_insert(buffer, &iter, str, -1);
3505 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3506 G_CALLBACK(text_inserted),
3508 compose->autowrap = prev_autowrap;
3509 if (compose->autowrap)
3510 compose_wrap_all(compose);
3515 return COMPOSE_INSERT_INVALID_CHARACTER;
3517 return COMPOSE_INSERT_SUCCESS;
3520 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3521 const gchar *filename,
3522 const gchar *content_type,
3523 const gchar *charset)
3531 GtkListStore *store;
3533 gboolean has_binary = FALSE;
3535 if (!is_file_exist(file)) {
3536 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3537 gboolean result = FALSE;
3538 if (file_from_uri && is_file_exist(file_from_uri)) {
3539 result = compose_attach_append(
3540 compose, file_from_uri,
3541 filename, content_type,
3544 g_free(file_from_uri);
3547 alertpanel_error("File %s doesn't exist\n", filename);
3550 if ((size = get_file_size(file)) < 0) {
3551 alertpanel_error("Can't get file size of %s\n", filename);
3555 alertpanel_error(_("File %s is empty."), filename);
3558 if ((fp = g_fopen(file, "rb")) == NULL) {
3559 alertpanel_error(_("Can't read %s."), filename);
3564 ainfo = g_new0(AttachInfo, 1);
3565 auto_ainfo = g_auto_pointer_new_with_free
3566 (ainfo, (GFreeFunc) compose_attach_info_free);
3567 ainfo->file = g_strdup(file);
3570 ainfo->content_type = g_strdup(content_type);
3571 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3573 MsgFlags flags = {0, 0};
3575 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3576 ainfo->encoding = ENC_7BIT;
3578 ainfo->encoding = ENC_8BIT;
3580 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3581 if (msginfo && msginfo->subject)
3582 name = g_strdup(msginfo->subject);
3584 name = g_path_get_basename(filename ? filename : file);
3586 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3588 procmsg_msginfo_free(msginfo);
3590 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3591 ainfo->charset = g_strdup(charset);
3592 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3594 ainfo->encoding = ENC_BASE64;
3596 name = g_path_get_basename(filename ? filename : file);
3597 ainfo->name = g_strdup(name);
3601 ainfo->content_type = procmime_get_mime_type(file);
3602 if (!ainfo->content_type) {
3603 ainfo->content_type =
3604 g_strdup("application/octet-stream");
3605 ainfo->encoding = ENC_BASE64;
3606 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3608 procmime_get_encoding_for_text_file(file, &has_binary);
3610 ainfo->encoding = ENC_BASE64;
3611 name = g_path_get_basename(filename ? filename : file);
3612 ainfo->name = g_strdup(name);
3616 if (ainfo->name != NULL
3617 && !strcmp(ainfo->name, ".")) {
3618 g_free(ainfo->name);
3622 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3623 g_free(ainfo->content_type);
3624 ainfo->content_type = g_strdup("application/octet-stream");
3625 g_free(ainfo->charset);
3626 ainfo->charset = NULL;
3629 ainfo->size = (goffset)size;
3630 size_text = to_human_readable((goffset)size);
3632 store = GTK_LIST_STORE(gtk_tree_view_get_model
3633 (GTK_TREE_VIEW(compose->attach_clist)));
3635 gtk_list_store_append(store, &iter);
3636 gtk_list_store_set(store, &iter,
3637 COL_MIMETYPE, ainfo->content_type,
3638 COL_SIZE, size_text,
3639 COL_NAME, ainfo->name,
3640 COL_CHARSET, ainfo->charset,
3642 COL_AUTODATA, auto_ainfo,
3645 g_auto_pointer_free(auto_ainfo);
3646 compose_attach_update_label(compose);
3650 static void compose_use_signing(Compose *compose, gboolean use_signing)
3652 compose->use_signing = use_signing;
3653 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3656 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3658 compose->use_encryption = use_encryption;
3659 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3662 #define NEXT_PART_NOT_CHILD(info) \
3664 node = info->node; \
3665 while (node->children) \
3666 node = g_node_last_child(node); \
3667 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3670 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3674 MimeInfo *firsttext = NULL;
3675 MimeInfo *encrypted = NULL;
3678 const gchar *partname = NULL;
3680 mimeinfo = procmime_scan_message(msginfo);
3681 if (!mimeinfo) return;
3683 if (mimeinfo->node->children == NULL) {
3684 procmime_mimeinfo_free_all(mimeinfo);
3688 /* find first content part */
3689 child = (MimeInfo *) mimeinfo->node->children->data;
3690 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3691 child = (MimeInfo *)child->node->children->data;
3694 if (child->type == MIMETYPE_TEXT) {
3696 debug_print("First text part found\n");
3697 } else if (compose->mode == COMPOSE_REEDIT &&
3698 child->type == MIMETYPE_APPLICATION &&
3699 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3700 encrypted = (MimeInfo *)child->node->parent->data;
3703 child = (MimeInfo *) mimeinfo->node->children->data;
3704 while (child != NULL) {
3707 if (child == encrypted) {
3708 /* skip this part of tree */
3709 NEXT_PART_NOT_CHILD(child);
3713 if (child->type == MIMETYPE_MULTIPART) {
3714 /* get the actual content */
3715 child = procmime_mimeinfo_next(child);
3719 if (child == firsttext) {
3720 child = procmime_mimeinfo_next(child);
3724 outfile = procmime_get_tmp_file_name(child);
3725 if ((err = procmime_get_part(outfile, child)) < 0)
3726 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3728 gchar *content_type;
3730 content_type = procmime_get_content_type_str(child->type, child->subtype);
3732 /* if we meet a pgp signature, we don't attach it, but
3733 * we force signing. */
3734 if ((strcmp(content_type, "application/pgp-signature") &&
3735 strcmp(content_type, "application/pkcs7-signature") &&
3736 strcmp(content_type, "application/x-pkcs7-signature"))
3737 || compose->mode == COMPOSE_REDIRECT) {
3738 partname = procmime_mimeinfo_get_parameter(child, "filename");
3739 if (partname == NULL)
3740 partname = procmime_mimeinfo_get_parameter(child, "name");
3741 if (partname == NULL)
3743 compose_attach_append(compose, outfile,
3744 partname, content_type,
3745 procmime_mimeinfo_get_parameter(child, "charset"));
3747 compose_force_signing(compose, compose->account, NULL);
3749 g_free(content_type);
3752 NEXT_PART_NOT_CHILD(child);
3754 procmime_mimeinfo_free_all(mimeinfo);
3757 #undef NEXT_PART_NOT_CHILD
3762 WAIT_FOR_INDENT_CHAR,
3763 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3766 /* return indent length, we allow:
3767 indent characters followed by indent characters or spaces/tabs,
3768 alphabets and numbers immediately followed by indent characters,
3769 and the repeating sequences of the above
3770 If quote ends with multiple spaces, only the first one is included. */
3771 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3772 const GtkTextIter *start, gint *len)
3774 GtkTextIter iter = *start;
3778 IndentState state = WAIT_FOR_INDENT_CHAR;
3781 gint alnum_count = 0;
3782 gint space_count = 0;
3785 if (prefs_common.quote_chars == NULL) {
3789 while (!gtk_text_iter_ends_line(&iter)) {
3790 wc = gtk_text_iter_get_char(&iter);
3791 if (g_unichar_iswide(wc))
3793 clen = g_unichar_to_utf8(wc, ch);
3797 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3798 is_space = g_unichar_isspace(wc);
3800 if (state == WAIT_FOR_INDENT_CHAR) {
3801 if (!is_indent && !g_unichar_isalnum(wc))
3804 quote_len += alnum_count + space_count + 1;
3805 alnum_count = space_count = 0;
3806 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3809 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3810 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3814 else if (is_indent) {
3815 quote_len += alnum_count + space_count + 1;
3816 alnum_count = space_count = 0;
3819 state = WAIT_FOR_INDENT_CHAR;
3823 gtk_text_iter_forward_char(&iter);
3826 if (quote_len > 0 && space_count > 0)
3832 if (quote_len > 0) {
3834 gtk_text_iter_forward_chars(&iter, quote_len);
3835 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3841 /* return >0 if the line is itemized */
3842 static int compose_itemized_length(GtkTextBuffer *buffer,
3843 const GtkTextIter *start)
3845 GtkTextIter iter = *start;
3850 if (gtk_text_iter_ends_line(&iter))
3855 wc = gtk_text_iter_get_char(&iter);
3856 if (!g_unichar_isspace(wc))
3858 gtk_text_iter_forward_char(&iter);
3859 if (gtk_text_iter_ends_line(&iter))
3863 clen = g_unichar_to_utf8(wc, ch);
3867 if (!strchr("*-+", ch[0]))
3870 gtk_text_iter_forward_char(&iter);
3871 if (gtk_text_iter_ends_line(&iter))
3873 wc = gtk_text_iter_get_char(&iter);
3874 if (g_unichar_isspace(wc)) {
3880 /* return the string at the start of the itemization */
3881 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3882 const GtkTextIter *start)
3884 GtkTextIter iter = *start;
3887 GString *item_chars = g_string_new("");
3890 if (gtk_text_iter_ends_line(&iter))
3895 wc = gtk_text_iter_get_char(&iter);
3896 if (!g_unichar_isspace(wc))
3898 gtk_text_iter_forward_char(&iter);
3899 if (gtk_text_iter_ends_line(&iter))
3901 g_string_append_unichar(item_chars, wc);
3904 str = item_chars->str;
3905 g_string_free(item_chars, FALSE);
3909 /* return the number of spaces at a line's start */
3910 static int compose_left_offset_length(GtkTextBuffer *buffer,
3911 const GtkTextIter *start)
3913 GtkTextIter iter = *start;
3916 if (gtk_text_iter_ends_line(&iter))
3920 wc = gtk_text_iter_get_char(&iter);
3921 if (!g_unichar_isspace(wc))
3924 gtk_text_iter_forward_char(&iter);
3925 if (gtk_text_iter_ends_line(&iter))
3929 gtk_text_iter_forward_char(&iter);
3930 if (gtk_text_iter_ends_line(&iter))
3935 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3936 const GtkTextIter *start,
3937 GtkTextIter *break_pos,
3941 GtkTextIter iter = *start, line_end = *start;
3942 PangoLogAttr *attrs;
3949 gboolean can_break = FALSE;
3950 gboolean do_break = FALSE;
3951 gboolean was_white = FALSE;
3952 gboolean prev_dont_break = FALSE;
3954 gtk_text_iter_forward_to_line_end(&line_end);
3955 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3956 len = g_utf8_strlen(str, -1);
3960 g_warning("compose_get_line_break_pos: len = 0!\n");
3964 /* g_print("breaking line: %d: %s (len = %d)\n",
3965 gtk_text_iter_get_line(&iter), str, len); */
3967 attrs = g_new(PangoLogAttr, len + 1);
3969 pango_default_break(str, -1, NULL, attrs, len + 1);
3973 /* skip quote and leading spaces */
3974 for (i = 0; *p != '\0' && i < len; i++) {
3977 wc = g_utf8_get_char(p);
3978 if (i >= quote_len && !g_unichar_isspace(wc))
3980 if (g_unichar_iswide(wc))
3982 else if (*p == '\t')
3986 p = g_utf8_next_char(p);
3989 for (; *p != '\0' && i < len; i++) {
3990 PangoLogAttr *attr = attrs + i;
3994 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3997 was_white = attr->is_white;
3999 /* don't wrap URI */
4000 if ((uri_len = get_uri_len(p)) > 0) {
4002 if (pos > 0 && col > max_col) {
4012 wc = g_utf8_get_char(p);
4013 if (g_unichar_iswide(wc)) {
4015 if (prev_dont_break && can_break && attr->is_line_break)
4017 } else if (*p == '\t')
4021 if (pos > 0 && col > max_col) {
4026 if (*p == '-' || *p == '/')
4027 prev_dont_break = TRUE;
4029 prev_dont_break = FALSE;
4031 p = g_utf8_next_char(p);
4035 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4040 *break_pos = *start;
4041 gtk_text_iter_set_line_offset(break_pos, pos);
4046 static gboolean compose_join_next_line(Compose *compose,
4047 GtkTextBuffer *buffer,
4049 const gchar *quote_str)
4051 GtkTextIter iter_ = *iter, cur, prev, next, end;
4052 PangoLogAttr attrs[3];
4054 gchar *next_quote_str;
4057 gboolean keep_cursor = FALSE;
4059 if (!gtk_text_iter_forward_line(&iter_) ||
4060 gtk_text_iter_ends_line(&iter_)) {
4063 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4065 if ((quote_str || next_quote_str) &&
4066 strcmp2(quote_str, next_quote_str) != 0) {
4067 g_free(next_quote_str);
4070 g_free(next_quote_str);
4073 if (quote_len > 0) {
4074 gtk_text_iter_forward_chars(&end, quote_len);
4075 if (gtk_text_iter_ends_line(&end)) {
4080 /* don't join itemized lines */
4081 if (compose_itemized_length(buffer, &end) > 0) {
4085 /* don't join signature separator */
4086 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4089 /* delete quote str */
4091 gtk_text_buffer_delete(buffer, &iter_, &end);
4093 /* don't join line breaks put by the user */
4095 gtk_text_iter_backward_char(&cur);
4096 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4097 gtk_text_iter_forward_char(&cur);
4101 gtk_text_iter_forward_char(&cur);
4102 /* delete linebreak and extra spaces */
4103 while (gtk_text_iter_backward_char(&cur)) {
4104 wc1 = gtk_text_iter_get_char(&cur);
4105 if (!g_unichar_isspace(wc1))
4110 while (!gtk_text_iter_ends_line(&cur)) {
4111 wc1 = gtk_text_iter_get_char(&cur);
4112 if (!g_unichar_isspace(wc1))
4114 gtk_text_iter_forward_char(&cur);
4117 if (!gtk_text_iter_equal(&prev, &next)) {
4120 mark = gtk_text_buffer_get_insert(buffer);
4121 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4122 if (gtk_text_iter_equal(&prev, &cur))
4124 gtk_text_buffer_delete(buffer, &prev, &next);
4128 /* insert space if required */
4129 gtk_text_iter_backward_char(&prev);
4130 wc1 = gtk_text_iter_get_char(&prev);
4131 wc2 = gtk_text_iter_get_char(&next);
4132 gtk_text_iter_forward_char(&next);
4133 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4134 pango_default_break(str, -1, NULL, attrs, 3);
4135 if (!attrs[1].is_line_break ||
4136 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4137 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4139 gtk_text_iter_backward_char(&iter_);
4140 gtk_text_buffer_place_cursor(buffer, &iter_);
4149 #define ADD_TXT_POS(bp_, ep_, pti_) \
4150 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4151 last = last->next; \
4152 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4153 last->next = NULL; \
4155 g_warning("alloc error scanning URIs\n"); \
4158 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4160 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4161 GtkTextBuffer *buffer;
4162 GtkTextIter iter, break_pos, end_of_line;
4163 gchar *quote_str = NULL;
4165 gboolean wrap_quote = prefs_common.linewrap_quote;
4166 gboolean prev_autowrap = compose->autowrap;
4167 gint startq_offset = -1, noq_offset = -1;
4168 gint uri_start = -1, uri_stop = -1;
4169 gint nouri_start = -1, nouri_stop = -1;
4170 gint num_blocks = 0;
4171 gint quotelevel = -1;
4172 gboolean modified = force;
4173 gboolean removed = FALSE;
4174 gboolean modified_before_remove = FALSE;
4176 gboolean start = TRUE;
4177 gint itemized_len = 0, rem_item_len = 0;
4178 gchar *itemized_chars = NULL;
4179 gboolean item_continuation = FALSE;
4184 if (compose->draft_timeout_tag == -2) {
4188 compose->autowrap = FALSE;
4190 buffer = gtk_text_view_get_buffer(text);
4191 undo_wrapping(compose->undostruct, TRUE);
4196 mark = gtk_text_buffer_get_insert(buffer);
4197 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4201 if (compose->draft_timeout_tag == -2) {
4202 if (gtk_text_iter_ends_line(&iter)) {
4203 while (gtk_text_iter_ends_line(&iter) &&
4204 gtk_text_iter_forward_line(&iter))
4207 while (gtk_text_iter_backward_line(&iter)) {
4208 if (gtk_text_iter_ends_line(&iter)) {
4209 gtk_text_iter_forward_line(&iter);
4215 /* move to line start */
4216 gtk_text_iter_set_line_offset(&iter, 0);
4219 itemized_len = compose_itemized_length(buffer, &iter);
4221 if (!itemized_len) {
4222 itemized_len = compose_left_offset_length(buffer, &iter);
4223 item_continuation = TRUE;
4227 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4229 /* go until paragraph end (empty line) */
4230 while (start || !gtk_text_iter_ends_line(&iter)) {
4231 gchar *scanpos = NULL;
4232 /* parse table - in order of priority */
4234 const gchar *needle; /* token */
4236 /* token search function */
4237 gchar *(*search) (const gchar *haystack,
4238 const gchar *needle);
4239 /* part parsing function */
4240 gboolean (*parse) (const gchar *start,
4241 const gchar *scanpos,
4245 /* part to URI function */
4246 gchar *(*build_uri) (const gchar *bp,
4250 static struct table parser[] = {
4251 {"http://", strcasestr, get_uri_part, make_uri_string},
4252 {"https://", strcasestr, get_uri_part, make_uri_string},
4253 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4254 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4255 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4256 {"www.", strcasestr, get_uri_part, make_http_string},
4257 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4258 {"@", strcasestr, get_email_part, make_email_string}
4260 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4261 gint last_index = PARSE_ELEMS;
4263 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4267 if (!prev_autowrap && num_blocks == 0) {
4269 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4270 G_CALLBACK(text_inserted),
4273 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4276 uri_start = uri_stop = -1;
4278 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4281 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4282 if (startq_offset == -1)
4283 startq_offset = gtk_text_iter_get_offset(&iter);
4284 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4285 if (quotelevel > 2) {
4286 /* recycle colors */
4287 if (prefs_common.recycle_quote_colors)
4296 if (startq_offset == -1)
4297 noq_offset = gtk_text_iter_get_offset(&iter);
4301 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4304 if (gtk_text_iter_ends_line(&iter)) {
4306 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4307 prefs_common.linewrap_len,
4309 GtkTextIter prev, next, cur;
4310 if (prev_autowrap != FALSE || force) {
4311 compose->automatic_break = TRUE;
4313 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4314 compose->automatic_break = FALSE;
4315 if (itemized_len && compose->autoindent) {
4316 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4317 if (!item_continuation)
4318 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4320 } else if (quote_str && wrap_quote) {
4321 compose->automatic_break = TRUE;
4323 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4324 compose->automatic_break = FALSE;
4325 if (itemized_len && compose->autoindent) {
4326 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4327 if (!item_continuation)
4328 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4332 /* remove trailing spaces */
4334 rem_item_len = itemized_len;
4335 while (compose->autoindent && rem_item_len-- > 0)
4336 gtk_text_iter_backward_char(&cur);
4337 gtk_text_iter_backward_char(&cur);
4340 while (!gtk_text_iter_starts_line(&cur)) {
4343 gtk_text_iter_backward_char(&cur);
4344 wc = gtk_text_iter_get_char(&cur);
4345 if (!g_unichar_isspace(wc))
4349 if (!gtk_text_iter_equal(&prev, &next)) {
4350 gtk_text_buffer_delete(buffer, &prev, &next);
4352 gtk_text_iter_forward_char(&break_pos);
4356 gtk_text_buffer_insert(buffer, &break_pos,
4360 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4362 /* move iter to current line start */
4363 gtk_text_iter_set_line_offset(&iter, 0);
4370 /* move iter to next line start */
4376 if (!prev_autowrap && num_blocks > 0) {
4378 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4379 G_CALLBACK(text_inserted),
4383 while (!gtk_text_iter_ends_line(&end_of_line)) {
4384 gtk_text_iter_forward_char(&end_of_line);
4386 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4388 nouri_start = gtk_text_iter_get_offset(&iter);
4389 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4391 walk_pos = gtk_text_iter_get_offset(&iter);
4392 /* FIXME: this looks phony. scanning for anything in the parse table */
4393 for (n = 0; n < PARSE_ELEMS; n++) {
4396 tmp = parser[n].search(walk, parser[n].needle);
4398 if (scanpos == NULL || tmp < scanpos) {
4407 /* check if URI can be parsed */
4408 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4409 (const gchar **)&ep, FALSE)
4410 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4414 strlen(parser[last_index].needle);
4417 uri_start = walk_pos + (bp - o_walk);
4418 uri_stop = walk_pos + (ep - o_walk);
4422 gtk_text_iter_forward_line(&iter);
4425 if (startq_offset != -1) {
4426 GtkTextIter startquote, endquote;
4427 gtk_text_buffer_get_iter_at_offset(
4428 buffer, &startquote, startq_offset);
4431 switch (quotelevel) {
4433 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4434 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4435 gtk_text_buffer_apply_tag_by_name(
4436 buffer, "quote0", &startquote, &endquote);
4437 gtk_text_buffer_remove_tag_by_name(
4438 buffer, "quote1", &startquote, &endquote);
4439 gtk_text_buffer_remove_tag_by_name(
4440 buffer, "quote2", &startquote, &endquote);
4445 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4446 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4447 gtk_text_buffer_apply_tag_by_name(
4448 buffer, "quote1", &startquote, &endquote);
4449 gtk_text_buffer_remove_tag_by_name(
4450 buffer, "quote0", &startquote, &endquote);
4451 gtk_text_buffer_remove_tag_by_name(
4452 buffer, "quote2", &startquote, &endquote);
4457 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4458 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4459 gtk_text_buffer_apply_tag_by_name(
4460 buffer, "quote2", &startquote, &endquote);
4461 gtk_text_buffer_remove_tag_by_name(
4462 buffer, "quote0", &startquote, &endquote);
4463 gtk_text_buffer_remove_tag_by_name(
4464 buffer, "quote1", &startquote, &endquote);
4470 } else if (noq_offset != -1) {
4471 GtkTextIter startnoquote, endnoquote;
4472 gtk_text_buffer_get_iter_at_offset(
4473 buffer, &startnoquote, noq_offset);
4476 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4477 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4478 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4479 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4480 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4481 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4482 gtk_text_buffer_remove_tag_by_name(
4483 buffer, "quote0", &startnoquote, &endnoquote);
4484 gtk_text_buffer_remove_tag_by_name(
4485 buffer, "quote1", &startnoquote, &endnoquote);
4486 gtk_text_buffer_remove_tag_by_name(
4487 buffer, "quote2", &startnoquote, &endnoquote);
4493 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4494 GtkTextIter nouri_start_iter, nouri_end_iter;
4495 gtk_text_buffer_get_iter_at_offset(
4496 buffer, &nouri_start_iter, nouri_start);
4497 gtk_text_buffer_get_iter_at_offset(
4498 buffer, &nouri_end_iter, nouri_stop);
4499 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4500 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4501 gtk_text_buffer_remove_tag_by_name(
4502 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4503 modified_before_remove = modified;
4508 if (uri_start >= 0 && uri_stop > 0) {
4509 GtkTextIter uri_start_iter, uri_end_iter, back;
4510 gtk_text_buffer_get_iter_at_offset(
4511 buffer, &uri_start_iter, uri_start);
4512 gtk_text_buffer_get_iter_at_offset(
4513 buffer, &uri_end_iter, uri_stop);
4514 back = uri_end_iter;
4515 gtk_text_iter_backward_char(&back);
4516 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4517 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4518 gtk_text_buffer_apply_tag_by_name(
4519 buffer, "link", &uri_start_iter, &uri_end_iter);
4521 if (removed && !modified_before_remove) {
4527 // debug_print("not modified, out after %d lines\n", lines);
4531 // debug_print("modified, out after %d lines\n", lines);
4533 g_free(itemized_chars);
4536 undo_wrapping(compose->undostruct, FALSE);
4537 compose->autowrap = prev_autowrap;
4542 void compose_action_cb(void *data)
4544 Compose *compose = (Compose *)data;
4545 compose_wrap_all(compose);
4548 static void compose_wrap_all(Compose *compose)
4550 compose_wrap_all_full(compose, FALSE);
4553 static void compose_wrap_all_full(Compose *compose, gboolean force)
4555 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4556 GtkTextBuffer *buffer;
4558 gboolean modified = TRUE;
4560 buffer = gtk_text_view_get_buffer(text);
4562 gtk_text_buffer_get_start_iter(buffer, &iter);
4563 while (!gtk_text_iter_is_end(&iter) && modified)
4564 modified = compose_beautify_paragraph(compose, &iter, force);
4568 static void compose_set_title(Compose *compose)
4574 edited = compose->modified ? _(" [Edited]") : "";
4576 subject = gtk_editable_get_chars(
4577 GTK_EDITABLE(compose->subject_entry), 0, -1);
4579 #ifndef GENERIC_UMPC
4580 if (subject && strlen(subject))
4581 str = g_strdup_printf(_("%s - Compose message%s"),
4584 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4586 str = g_strdup(_("Compose message"));
4589 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4595 * compose_current_mail_account:
4597 * Find a current mail account (the currently selected account, or the
4598 * default account, if a news account is currently selected). If a
4599 * mail account cannot be found, display an error message.
4601 * Return value: Mail account, or NULL if not found.
4603 static PrefsAccount *
4604 compose_current_mail_account(void)
4608 if (cur_account && cur_account->protocol != A_NNTP)
4611 ac = account_get_default();
4612 if (!ac || ac->protocol == A_NNTP) {
4613 alertpanel_error(_("Account for sending mail is not specified.\n"
4614 "Please select a mail account before sending."));
4621 #define QUOTE_IF_REQUIRED(out, str) \
4623 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4627 len = strlen(str) + 3; \
4628 if ((__tmp = alloca(len)) == NULL) { \
4629 g_warning("can't allocate memory\n"); \
4630 g_string_free(header, TRUE); \
4633 g_snprintf(__tmp, len, "\"%s\"", str); \
4638 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4639 g_warning("can't allocate memory\n"); \
4640 g_string_free(header, TRUE); \
4643 strcpy(__tmp, str); \
4649 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4651 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4655 len = strlen(str) + 3; \
4656 if ((__tmp = alloca(len)) == NULL) { \
4657 g_warning("can't allocate memory\n"); \
4660 g_snprintf(__tmp, len, "\"%s\"", str); \
4665 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4666 g_warning("can't allocate memory\n"); \
4669 strcpy(__tmp, str); \
4675 static void compose_select_account(Compose *compose, PrefsAccount *account,
4678 gchar *from = NULL, *header;
4679 ComposeHeaderEntry *header_entry;
4681 cm_return_if_fail(account != NULL);
4683 compose->account = account;
4684 if (account->name && *account->name) {
4686 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4687 from = g_strdup_printf("%s <%s>",
4688 buf, account->address);
4689 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4691 from = g_strdup_printf("<%s>",
4693 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4698 compose_set_title(compose);
4700 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4701 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4703 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4704 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4705 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4707 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4709 activate_privacy_system(compose, account, FALSE);
4711 if (!init && compose->mode != COMPOSE_REDIRECT) {
4712 undo_block(compose->undostruct);
4713 compose_insert_sig(compose, TRUE);
4714 undo_unblock(compose->undostruct);
4717 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4718 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(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_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8710 g_free(mime_type_list->data);
8712 g_list_free(strlist);
8713 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8714 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8716 label = gtk_label_new(_("Encoding"));
8717 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8719 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8721 hbox = gtk_hbox_new(FALSE, 0);
8722 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8723 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8725 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8726 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8728 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8729 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8730 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8731 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8732 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8734 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8736 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8737 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8739 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8740 &ok_btn, GTK_STOCK_OK,
8742 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8743 gtk_widget_grab_default(ok_btn);
8745 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8746 G_CALLBACK(attach_property_ok),
8748 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8749 G_CALLBACK(attach_property_cancel),
8752 gtk_widget_show_all(vbox);
8754 attach_prop.window = window;
8755 attach_prop.mimetype_entry = mimetype_entry;
8756 attach_prop.encoding_optmenu = optmenu;
8757 attach_prop.path_entry = path_entry;
8758 attach_prop.filename_entry = filename_entry;
8759 attach_prop.ok_btn = ok_btn;
8760 attach_prop.cancel_btn = cancel_btn;
8763 #undef SET_LABEL_AND_ENTRY
8765 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8771 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8777 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8778 gboolean *cancelled)
8786 static gboolean attach_property_key_pressed(GtkWidget *widget,
8788 gboolean *cancelled)
8790 if (event && event->keyval == GDK_Escape) {
8794 if (event && event->keyval == GDK_Return) {
8802 static void compose_exec_ext_editor(Compose *compose)
8809 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8810 G_DIR_SEPARATOR, compose);
8812 if (pipe(pipe_fds) < 0) {
8818 if ((pid = fork()) < 0) {
8825 /* close the write side of the pipe */
8828 compose->exteditor_file = g_strdup(tmp);
8829 compose->exteditor_pid = pid;
8831 compose_set_ext_editor_sensitive(compose, FALSE);
8834 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8836 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8838 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8842 } else { /* process-monitoring process */
8848 /* close the read side of the pipe */
8851 if (compose_write_body_to_file(compose, tmp) < 0) {
8852 fd_write_all(pipe_fds[1], "2\n", 2);
8856 pid_ed = compose_exec_ext_editor_real(tmp);
8858 fd_write_all(pipe_fds[1], "1\n", 2);
8862 /* wait until editor is terminated */
8863 waitpid(pid_ed, NULL, 0);
8865 fd_write_all(pipe_fds[1], "0\n", 2);
8872 #endif /* G_OS_UNIX */
8876 static gint compose_exec_ext_editor_real(const gchar *file)
8883 cm_return_val_if_fail(file != NULL, -1);
8885 if ((pid = fork()) < 0) {
8890 if (pid != 0) return pid;
8892 /* grandchild process */
8894 if (setpgid(0, getppid()))
8897 if (prefs_common_get_ext_editor_cmd() &&
8898 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8899 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8900 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8902 if (prefs_common_get_ext_editor_cmd())
8903 g_warning("External editor command-line is invalid: '%s'\n",
8904 prefs_common_get_ext_editor_cmd());
8905 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8908 cmdline = strsplit_with_quote(buf, " ", 1024);
8909 execvp(cmdline[0], cmdline);
8912 g_strfreev(cmdline);
8917 static gboolean compose_ext_editor_kill(Compose *compose)
8919 pid_t pgid = compose->exteditor_pid * -1;
8922 ret = kill(pgid, 0);
8924 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8928 msg = g_strdup_printf
8929 (_("The external editor is still working.\n"
8930 "Force terminating the process?\n"
8931 "process group id: %d"), -pgid);
8932 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8933 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8937 if (val == G_ALERTALTERNATE) {
8938 g_source_remove(compose->exteditor_tag);
8939 g_io_channel_shutdown(compose->exteditor_ch,
8941 g_io_channel_unref(compose->exteditor_ch);
8943 if (kill(pgid, SIGTERM) < 0) perror("kill");
8944 waitpid(compose->exteditor_pid, NULL, 0);
8946 g_warning("Terminated process group id: %d", -pgid);
8947 g_warning("Temporary file: %s",
8948 compose->exteditor_file);
8950 compose_set_ext_editor_sensitive(compose, TRUE);
8952 g_free(compose->exteditor_file);
8953 compose->exteditor_file = NULL;
8954 compose->exteditor_pid = -1;
8955 compose->exteditor_ch = NULL;
8956 compose->exteditor_tag = -1;
8964 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8968 Compose *compose = (Compose *)data;
8971 debug_print("Compose: input from monitoring process\n");
8973 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8975 g_io_channel_shutdown(source, FALSE, NULL);
8976 g_io_channel_unref(source);
8978 waitpid(compose->exteditor_pid, NULL, 0);
8980 if (buf[0] == '0') { /* success */
8981 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8982 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8984 gtk_text_buffer_set_text(buffer, "", -1);
8985 compose_insert_file(compose, compose->exteditor_file);
8986 compose_changed_cb(NULL, compose);
8987 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8989 if (claws_unlink(compose->exteditor_file) < 0)
8990 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8991 } else if (buf[0] == '1') { /* failed */
8992 g_warning("Couldn't exec external editor\n");
8993 if (claws_unlink(compose->exteditor_file) < 0)
8994 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8995 } else if (buf[0] == '2') {
8996 g_warning("Couldn't write to file\n");
8997 } else if (buf[0] == '3') {
8998 g_warning("Pipe read failed\n");
9001 compose_set_ext_editor_sensitive(compose, TRUE);
9003 g_free(compose->exteditor_file);
9004 compose->exteditor_file = NULL;
9005 compose->exteditor_pid = -1;
9006 compose->exteditor_ch = NULL;
9007 compose->exteditor_tag = -1;
9012 static void compose_set_ext_editor_sensitive(Compose *compose,
9015 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9016 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9017 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9018 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9019 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9020 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9021 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9023 gtk_widget_set_sensitive(compose->text, sensitive);
9024 if (compose->toolbar->send_btn)
9025 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9026 if (compose->toolbar->sendl_btn)
9027 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9028 if (compose->toolbar->draft_btn)
9029 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9030 if (compose->toolbar->insert_btn)
9031 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9032 if (compose->toolbar->sig_btn)
9033 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9034 if (compose->toolbar->exteditor_btn)
9035 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9036 if (compose->toolbar->linewrap_current_btn)
9037 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9038 if (compose->toolbar->linewrap_all_btn)
9039 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9041 #endif /* G_OS_UNIX */
9044 * compose_undo_state_changed:
9046 * Change the sensivity of the menuentries undo and redo
9048 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9049 gint redo_state, gpointer data)
9051 Compose *compose = (Compose *)data;
9053 switch (undo_state) {
9054 case UNDO_STATE_TRUE:
9055 if (!undostruct->undo_state) {
9056 undostruct->undo_state = TRUE;
9057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9060 case UNDO_STATE_FALSE:
9061 if (undostruct->undo_state) {
9062 undostruct->undo_state = FALSE;
9063 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9066 case UNDO_STATE_UNCHANGED:
9068 case UNDO_STATE_REFRESH:
9069 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9072 g_warning("Undo state not recognized");
9076 switch (redo_state) {
9077 case UNDO_STATE_TRUE:
9078 if (!undostruct->redo_state) {
9079 undostruct->redo_state = TRUE;
9080 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9083 case UNDO_STATE_FALSE:
9084 if (undostruct->redo_state) {
9085 undostruct->redo_state = FALSE;
9086 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9089 case UNDO_STATE_UNCHANGED:
9091 case UNDO_STATE_REFRESH:
9092 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9095 g_warning("Redo state not recognized");
9100 /* callback functions */
9102 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9103 * includes "non-client" (windows-izm) in calculation, so this calculation
9104 * may not be accurate.
9106 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9107 GtkAllocation *allocation,
9108 GtkSHRuler *shruler)
9110 if (prefs_common.show_ruler) {
9111 gint char_width = 0, char_height = 0;
9112 gint line_width_in_chars;
9114 gtkut_get_font_size(GTK_WIDGET(widget),
9115 &char_width, &char_height);
9116 line_width_in_chars =
9117 (allocation->width - allocation->x) / char_width;
9119 /* got the maximum */
9120 gtk_ruler_set_range(GTK_RULER(shruler),
9121 0.0, line_width_in_chars, 0,
9122 /*line_width_in_chars*/ char_width);
9131 ComposePrefType type;
9132 gboolean entry_marked;
9135 static void account_activated(GtkComboBox *optmenu, gpointer data)
9137 Compose *compose = (Compose *)data;
9140 gchar *folderidentifier;
9141 gint account_id = 0;
9144 GSList *list, *saved_list = NULL;
9145 HeaderEntryState *state;
9146 GtkRcStyle *style = NULL;
9147 static GdkColor yellow;
9148 static gboolean color_set = FALSE;
9150 /* Get ID of active account in the combo box */
9151 menu = gtk_combo_box_get_model(optmenu);
9152 gtk_combo_box_get_active_iter(optmenu, &iter);
9153 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9155 ac = account_find_from_id(account_id);
9156 cm_return_if_fail(ac != NULL);
9158 if (ac != compose->account) {
9159 compose_select_account(compose, ac, FALSE);
9161 for (list = compose->header_list; list; list = list->next) {
9162 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9164 if (hentry->type == PREF_ACCOUNT || !list->next) {
9165 compose_destroy_headerentry(compose, hentry);
9169 state = g_malloc0(sizeof(HeaderEntryState));
9170 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9171 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9172 state->entry = gtk_editable_get_chars(
9173 GTK_EDITABLE(hentry->entry), 0, -1);
9174 state->type = hentry->type;
9177 gdk_color_parse("#f5f6be", &yellow);
9178 color_set = gdk_colormap_alloc_color(
9179 gdk_colormap_get_system(),
9180 &yellow, FALSE, TRUE);
9183 style = gtk_widget_get_modifier_style(hentry->entry);
9184 state->entry_marked = gdk_color_equal(&yellow,
9185 &style->base[GTK_STATE_NORMAL]);
9187 saved_list = g_slist_append(saved_list, state);
9188 compose_destroy_headerentry(compose, hentry);
9191 compose->header_last = NULL;
9192 g_slist_free(compose->header_list);
9193 compose->header_list = NULL;
9194 compose->header_nextrow = 1;
9195 compose_create_header_entry(compose);
9197 if (ac->set_autocc && ac->auto_cc)
9198 compose_entry_append(compose, ac->auto_cc,
9199 COMPOSE_CC, PREF_ACCOUNT);
9201 if (ac->set_autobcc && ac->auto_bcc)
9202 compose_entry_append(compose, ac->auto_bcc,
9203 COMPOSE_BCC, PREF_ACCOUNT);
9205 if (ac->set_autoreplyto && ac->auto_replyto)
9206 compose_entry_append(compose, ac->auto_replyto,
9207 COMPOSE_REPLYTO, PREF_ACCOUNT);
9209 for (list = saved_list; list; list = list->next) {
9210 state = (HeaderEntryState *) list->data;
9212 compose_add_header_entry(compose, state->header,
9213 state->entry, state->type);
9214 if (state->entry_marked)
9215 compose_entry_mark_default_to(compose, state->entry);
9217 g_free(state->header);
9218 g_free(state->entry);
9221 g_slist_free(saved_list);
9223 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9224 (ac->protocol == A_NNTP) ?
9225 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9228 /* Set message save folder */
9229 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9230 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9232 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9233 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9235 compose_set_save_to(compose, NULL);
9236 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9237 folderidentifier = folder_item_get_identifier(account_get_special_folder
9238 (compose->account, F_OUTBOX));
9239 compose_set_save_to(compose, folderidentifier);
9240 g_free(folderidentifier);
9244 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9245 GtkTreeViewColumn *column, Compose *compose)
9247 compose_attach_property(NULL, compose);
9250 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9253 Compose *compose = (Compose *)data;
9254 GtkTreeSelection *attach_selection;
9255 gint attach_nr_selected;
9257 if (!event) return FALSE;
9259 if (event->button == 3) {
9260 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9261 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9263 if (attach_nr_selected > 0)
9265 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9266 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9268 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9269 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9272 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9273 NULL, NULL, event->button, event->time);
9280 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9283 Compose *compose = (Compose *)data;
9285 if (!event) return FALSE;
9287 switch (event->keyval) {
9289 compose_attach_remove_selected(NULL, compose);
9295 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9297 toolbar_comp_set_sensitive(compose, allow);
9298 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9299 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9301 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9303 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9304 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9305 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9307 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9311 static void compose_send_cb(GtkAction *action, gpointer data)
9313 Compose *compose = (Compose *)data;
9315 if (prefs_common.work_offline &&
9316 !inc_offline_should_override(TRUE,
9317 _("Claws Mail needs network access in order "
9318 "to send this email.")))
9321 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9322 g_source_remove(compose->draft_timeout_tag);
9323 compose->draft_timeout_tag = -1;
9326 compose_send(compose);
9329 static void compose_send_later_cb(GtkAction *action, gpointer data)
9331 Compose *compose = (Compose *)data;
9335 compose_allow_user_actions(compose, FALSE);
9336 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9337 compose_allow_user_actions(compose, TRUE);
9341 compose_close(compose);
9342 } else if (val == -1) {
9343 alertpanel_error(_("Could not queue message."));
9344 } else if (val == -2) {
9345 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9346 } else if (val == -3) {
9347 if (privacy_peek_error())
9348 alertpanel_error(_("Could not queue message for sending:\n\n"
9349 "Signature failed: %s"), privacy_get_error());
9350 } else if (val == -4) {
9351 alertpanel_error(_("Could not queue message for sending:\n\n"
9352 "Charset conversion failed."));
9353 } else if (val == -5) {
9354 alertpanel_error(_("Could not queue message for sending:\n\n"
9355 "Couldn't get recipient encryption key."));
9356 } else if (val == -6) {
9359 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9362 #define DRAFTED_AT_EXIT "drafted_at_exit"
9363 static void compose_register_draft(MsgInfo *info)
9365 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9366 DRAFTED_AT_EXIT, NULL);
9367 FILE *fp = g_fopen(filepath, "ab");
9370 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9378 gboolean compose_draft (gpointer data, guint action)
9380 Compose *compose = (Compose *)data;
9384 MsgFlags flag = {0, 0};
9385 static gboolean lock = FALSE;
9386 MsgInfo *newmsginfo;
9388 gboolean target_locked = FALSE;
9389 gboolean err = FALSE;
9391 if (lock) return FALSE;
9393 if (compose->sending)
9396 draft = account_get_special_folder(compose->account, F_DRAFT);
9397 cm_return_val_if_fail(draft != NULL, FALSE);
9399 if (!g_mutex_trylock(compose->mutex)) {
9400 /* we don't want to lock the mutex once it's available,
9401 * because as the only other part of compose.c locking
9402 * it is compose_close - which means once unlocked,
9403 * the compose struct will be freed */
9404 debug_print("couldn't lock mutex, probably sending\n");
9410 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9411 G_DIR_SEPARATOR, compose);
9412 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9413 FILE_OP_ERROR(tmp, "fopen");
9417 /* chmod for security */
9418 if (change_file_mode_rw(fp, tmp) < 0) {
9419 FILE_OP_ERROR(tmp, "chmod");
9420 g_warning("can't change file mode\n");
9423 /* Save draft infos */
9424 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9425 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9427 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9428 gchar *savefolderid;
9430 savefolderid = compose_get_save_to(compose);
9431 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9432 g_free(savefolderid);
9434 if (compose->return_receipt) {
9435 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9437 if (compose->privacy_system) {
9438 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9439 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9440 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9443 /* Message-ID of message replying to */
9444 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9447 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9448 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9451 /* Message-ID of message forwarding to */
9452 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9455 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9456 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9460 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9461 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9463 /* end of headers */
9464 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9471 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9475 if (fclose(fp) == EOF) {
9479 if (compose->targetinfo) {
9480 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9481 flag.perm_flags = target_locked?MSG_LOCKED:0;
9483 flag.tmp_flags = MSG_DRAFT;
9485 folder_item_scan(draft);
9486 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9487 MsgInfo *tmpinfo = NULL;
9488 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9489 if (compose->msgid) {
9490 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9493 msgnum = tmpinfo->msgnum;
9494 procmsg_msginfo_free(tmpinfo);
9495 debug_print("got draft msgnum %d from scanning\n", msgnum);
9497 debug_print("didn't get draft msgnum after scanning\n");
9500 debug_print("got draft msgnum %d from adding\n", msgnum);
9506 if (action != COMPOSE_AUTO_SAVE) {
9507 if (action != COMPOSE_DRAFT_FOR_EXIT)
9508 alertpanel_error(_("Could not save draft."));
9511 gtkut_window_popup(compose->window);
9512 val = alertpanel_full(_("Could not save draft"),
9513 _("Could not save draft.\n"
9514 "Do you want to cancel exit or discard this email?"),
9515 _("_Cancel exit"), _("_Discard email"), NULL,
9516 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9517 if (val == G_ALERTALTERNATE) {
9519 g_mutex_unlock(compose->mutex); /* must be done before closing */
9520 compose_close(compose);
9524 g_mutex_unlock(compose->mutex); /* must be done before closing */
9533 if (compose->mode == COMPOSE_REEDIT) {
9534 compose_remove_reedit_target(compose, TRUE);
9537 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9540 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9542 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9544 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9545 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9546 procmsg_msginfo_set_flags(newmsginfo, 0,
9547 MSG_HAS_ATTACHMENT);
9549 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9550 compose_register_draft(newmsginfo);
9552 procmsg_msginfo_free(newmsginfo);
9555 folder_item_scan(draft);
9557 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9559 g_mutex_unlock(compose->mutex); /* must be done before closing */
9560 compose_close(compose);
9566 path = folder_item_fetch_msg(draft, msgnum);
9568 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9571 if (g_stat(path, &s) < 0) {
9572 FILE_OP_ERROR(path, "stat");
9578 procmsg_msginfo_free(compose->targetinfo);
9579 compose->targetinfo = procmsg_msginfo_new();
9580 compose->targetinfo->msgnum = msgnum;
9581 compose->targetinfo->size = (goffset)s.st_size;
9582 compose->targetinfo->mtime = s.st_mtime;
9583 compose->targetinfo->folder = draft;
9585 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9586 compose->mode = COMPOSE_REEDIT;
9588 if (action == COMPOSE_AUTO_SAVE) {
9589 compose->autosaved_draft = compose->targetinfo;
9591 compose->modified = FALSE;
9592 compose_set_title(compose);
9596 g_mutex_unlock(compose->mutex);
9600 void compose_clear_exit_drafts(void)
9602 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9603 DRAFTED_AT_EXIT, NULL);
9604 if (is_file_exist(filepath))
9605 claws_unlink(filepath);
9610 void compose_reopen_exit_drafts(void)
9612 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9613 DRAFTED_AT_EXIT, NULL);
9614 FILE *fp = g_fopen(filepath, "rb");
9618 while (fgets(buf, sizeof(buf), fp)) {
9619 gchar **parts = g_strsplit(buf, "\t", 2);
9620 const gchar *folder = parts[0];
9621 int msgnum = parts[1] ? atoi(parts[1]):-1;
9623 if (folder && *folder && msgnum > -1) {
9624 FolderItem *item = folder_find_item_from_identifier(folder);
9625 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9627 compose_reedit(info, FALSE);
9634 compose_clear_exit_drafts();
9637 static void compose_save_cb(GtkAction *action, gpointer data)
9639 Compose *compose = (Compose *)data;
9640 compose_draft(compose, COMPOSE_KEEP_EDITING);
9641 compose->rmode = COMPOSE_REEDIT;
9644 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9646 if (compose && file_list) {
9649 for ( tmp = file_list; tmp; tmp = tmp->next) {
9650 gchar *file = (gchar *) tmp->data;
9651 gchar *utf8_filename = conv_filename_to_utf8(file);
9652 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9653 compose_changed_cb(NULL, compose);
9658 g_free(utf8_filename);
9663 static void compose_attach_cb(GtkAction *action, gpointer data)
9665 Compose *compose = (Compose *)data;
9668 if (compose->redirect_filename != NULL)
9671 file_list = filesel_select_multiple_files_open(_("Select file"));
9674 compose_attach_from_list(compose, file_list, TRUE);
9675 g_list_free(file_list);
9679 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9681 Compose *compose = (Compose *)data;
9683 gint files_inserted = 0;
9685 file_list = filesel_select_multiple_files_open(_("Select file"));
9690 for ( tmp = file_list; tmp; tmp = tmp->next) {
9691 gchar *file = (gchar *) tmp->data;
9692 gchar *filedup = g_strdup(file);
9693 gchar *shortfile = g_path_get_basename(filedup);
9694 ComposeInsertResult res;
9695 /* insert the file if the file is short or if the user confirmed that
9696 he/she wants to insert the large file */
9697 res = compose_insert_file(compose, file);
9698 if (res == COMPOSE_INSERT_READ_ERROR) {
9699 alertpanel_error(_("File '%s' could not be read."), shortfile);
9700 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9701 alertpanel_error(_("File '%s' contained invalid characters\n"
9702 "for the current encoding, insertion may be incorrect."),
9704 } else if (res == COMPOSE_INSERT_SUCCESS)
9711 g_list_free(file_list);
9715 if (files_inserted > 0 && compose->gtkaspell &&
9716 compose->gtkaspell->check_while_typing)
9717 gtkaspell_highlight_all(compose->gtkaspell);
9721 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9723 Compose *compose = (Compose *)data;
9725 compose_insert_sig(compose, FALSE);
9728 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9732 Compose *compose = (Compose *)data;
9734 gtkut_widget_get_uposition(widget, &x, &y);
9735 if (!compose->batch) {
9736 prefs_common.compose_x = x;
9737 prefs_common.compose_y = y;
9739 if (compose->sending || compose->updating)
9741 compose_close_cb(NULL, compose);
9745 void compose_close_toolbar(Compose *compose)
9747 compose_close_cb(NULL, compose);
9750 static void compose_close_cb(GtkAction *action, gpointer data)
9752 Compose *compose = (Compose *)data;
9756 if (compose->exteditor_tag != -1) {
9757 if (!compose_ext_editor_kill(compose))
9762 if (compose->modified) {
9763 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9764 if (!g_mutex_trylock(compose->mutex)) {
9765 /* we don't want to lock the mutex once it's available,
9766 * because as the only other part of compose.c locking
9767 * it is compose_close - which means once unlocked,
9768 * the compose struct will be freed */
9769 debug_print("couldn't lock mutex, probably sending\n");
9773 val = alertpanel(_("Discard message"),
9774 _("This message has been modified. Discard it?"),
9775 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9777 val = alertpanel(_("Save changes"),
9778 _("This message has been modified. Save the latest changes?"),
9779 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9781 g_mutex_unlock(compose->mutex);
9783 case G_ALERTDEFAULT:
9784 if (prefs_common.autosave && !reedit)
9785 compose_remove_draft(compose);
9787 case G_ALERTALTERNATE:
9788 compose_draft(data, COMPOSE_QUIT_EDITING);
9795 compose_close(compose);
9798 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9800 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9801 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9802 Compose *compose = (Compose *) data;
9805 compose->out_encoding = (CharSet)value;
9808 static void compose_address_cb(GtkAction *action, gpointer data)
9810 Compose *compose = (Compose *)data;
9812 addressbook_open(compose);
9815 static void about_show_cb(GtkAction *action, gpointer data)
9820 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9822 Compose *compose = (Compose *)data;
9827 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9828 cm_return_if_fail(tmpl != NULL);
9830 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9832 val = alertpanel(_("Apply template"), msg,
9833 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9836 if (val == G_ALERTDEFAULT)
9837 compose_template_apply(compose, tmpl, TRUE);
9838 else if (val == G_ALERTALTERNATE)
9839 compose_template_apply(compose, tmpl, FALSE);
9842 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9844 Compose *compose = (Compose *)data;
9846 compose_exec_ext_editor(compose);
9849 static void compose_undo_cb(GtkAction *action, gpointer data)
9851 Compose *compose = (Compose *)data;
9852 gboolean prev_autowrap = compose->autowrap;
9854 compose->autowrap = FALSE;
9855 undo_undo(compose->undostruct);
9856 compose->autowrap = prev_autowrap;
9859 static void compose_redo_cb(GtkAction *action, gpointer data)
9861 Compose *compose = (Compose *)data;
9862 gboolean prev_autowrap = compose->autowrap;
9864 compose->autowrap = FALSE;
9865 undo_redo(compose->undostruct);
9866 compose->autowrap = prev_autowrap;
9869 static void entry_cut_clipboard(GtkWidget *entry)
9871 if (GTK_IS_EDITABLE(entry))
9872 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9873 else if (GTK_IS_TEXT_VIEW(entry))
9874 gtk_text_buffer_cut_clipboard(
9875 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9876 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9880 static void entry_copy_clipboard(GtkWidget *entry)
9882 if (GTK_IS_EDITABLE(entry))
9883 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9884 else if (GTK_IS_TEXT_VIEW(entry))
9885 gtk_text_buffer_copy_clipboard(
9886 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9887 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9890 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9891 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9893 if (GTK_IS_TEXT_VIEW(entry)) {
9894 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9895 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9896 GtkTextIter start_iter, end_iter;
9898 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9900 if (contents == NULL)
9903 /* we shouldn't delete the selection when middle-click-pasting, or we
9904 * can't mid-click-paste our own selection */
9905 if (clip != GDK_SELECTION_PRIMARY) {
9906 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9907 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9910 if (insert_place == NULL) {
9911 /* if insert_place isn't specified, insert at the cursor.
9912 * used for Ctrl-V pasting */
9913 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9914 start = gtk_text_iter_get_offset(&start_iter);
9915 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9917 /* if insert_place is specified, paste here.
9918 * used for mid-click-pasting */
9919 start = gtk_text_iter_get_offset(insert_place);
9920 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9921 if (prefs_common.primary_paste_unselects)
9922 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9926 /* paste unwrapped: mark the paste so it's not wrapped later */
9927 end = start + strlen(contents);
9928 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9929 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9930 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9931 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9932 /* rewrap paragraph now (after a mid-click-paste) */
9933 mark_start = gtk_text_buffer_get_insert(buffer);
9934 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9935 gtk_text_iter_backward_char(&start_iter);
9936 compose_beautify_paragraph(compose, &start_iter, TRUE);
9938 } else if (GTK_IS_EDITABLE(entry))
9939 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9941 compose->modified = TRUE;
9944 static void entry_allsel(GtkWidget *entry)
9946 if (GTK_IS_EDITABLE(entry))
9947 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9948 else if (GTK_IS_TEXT_VIEW(entry)) {
9949 GtkTextIter startiter, enditer;
9950 GtkTextBuffer *textbuf;
9952 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9953 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9954 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9956 gtk_text_buffer_move_mark_by_name(textbuf,
9957 "selection_bound", &startiter);
9958 gtk_text_buffer_move_mark_by_name(textbuf,
9959 "insert", &enditer);
9963 static void compose_cut_cb(GtkAction *action, gpointer data)
9965 Compose *compose = (Compose *)data;
9966 if (compose->focused_editable
9967 #ifndef GENERIC_UMPC
9968 && gtkut_widget_has_focus(compose->focused_editable)
9971 entry_cut_clipboard(compose->focused_editable);
9974 static void compose_copy_cb(GtkAction *action, gpointer data)
9976 Compose *compose = (Compose *)data;
9977 if (compose->focused_editable
9978 #ifndef GENERIC_UMPC
9979 && gtkut_widget_has_focus(compose->focused_editable)
9982 entry_copy_clipboard(compose->focused_editable);
9985 static void compose_paste_cb(GtkAction *action, gpointer data)
9987 Compose *compose = (Compose *)data;
9989 GtkTextBuffer *buffer;
9991 if (compose->focused_editable &&
9992 gtkut_widget_has_focus(compose->focused_editable))
9993 entry_paste_clipboard(compose, compose->focused_editable,
9994 prefs_common.linewrap_pastes,
9995 GDK_SELECTION_CLIPBOARD, NULL);
9999 if (gtkut_widget_has_focus(compose->text) &&
10000 compose->gtkaspell &&
10001 compose->gtkaspell->check_while_typing)
10002 gtkaspell_highlight_all(compose->gtkaspell);
10006 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10008 Compose *compose = (Compose *)data;
10009 gint wrap_quote = prefs_common.linewrap_quote;
10010 if (compose->focused_editable
10011 #ifndef GENERIC_UMPC
10012 && gtkut_widget_has_focus(compose->focused_editable)
10015 /* let text_insert() (called directly or at a later time
10016 * after the gtk_editable_paste_clipboard) know that
10017 * text is to be inserted as a quotation. implemented
10018 * by using a simple refcount... */
10019 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10020 G_OBJECT(compose->focused_editable),
10021 "paste_as_quotation"));
10022 g_object_set_data(G_OBJECT(compose->focused_editable),
10023 "paste_as_quotation",
10024 GINT_TO_POINTER(paste_as_quotation + 1));
10025 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10026 entry_paste_clipboard(compose, compose->focused_editable,
10027 prefs_common.linewrap_pastes,
10028 GDK_SELECTION_CLIPBOARD, NULL);
10029 prefs_common.linewrap_quote = wrap_quote;
10033 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10035 Compose *compose = (Compose *)data;
10036 gint prev_autowrap;
10037 GtkTextBuffer *buffer;
10039 if (compose->focused_editable
10040 #ifndef GENERIC_UMPC
10041 && gtkut_widget_has_focus(compose->focused_editable)
10044 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10045 GDK_SELECTION_CLIPBOARD, NULL);
10049 if (gtkut_widget_has_focus(compose->text) &&
10050 compose->gtkaspell &&
10051 compose->gtkaspell->check_while_typing)
10052 gtkaspell_highlight_all(compose->gtkaspell);
10056 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10058 Compose *compose = (Compose *)data;
10059 gint prev_autowrap;
10060 GtkTextBuffer *buffer;
10062 if (compose->focused_editable
10063 #ifndef GENERIC_UMPC
10064 && gtkut_widget_has_focus(compose->focused_editable)
10067 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10068 GDK_SELECTION_CLIPBOARD, NULL);
10072 if (gtkut_widget_has_focus(compose->text) &&
10073 compose->gtkaspell &&
10074 compose->gtkaspell->check_while_typing)
10075 gtkaspell_highlight_all(compose->gtkaspell);
10079 static void compose_allsel_cb(GtkAction *action, gpointer data)
10081 Compose *compose = (Compose *)data;
10082 if (compose->focused_editable
10083 #ifndef GENERIC_UMPC
10084 && gtkut_widget_has_focus(compose->focused_editable)
10087 entry_allsel(compose->focused_editable);
10090 static void textview_move_beginning_of_line (GtkTextView *text)
10092 GtkTextBuffer *buffer;
10096 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10098 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10099 mark = gtk_text_buffer_get_insert(buffer);
10100 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10101 gtk_text_iter_set_line_offset(&ins, 0);
10102 gtk_text_buffer_place_cursor(buffer, &ins);
10105 static void textview_move_forward_character (GtkTextView *text)
10107 GtkTextBuffer *buffer;
10111 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10113 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10114 mark = gtk_text_buffer_get_insert(buffer);
10115 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10116 if (gtk_text_iter_forward_cursor_position(&ins))
10117 gtk_text_buffer_place_cursor(buffer, &ins);
10120 static void textview_move_backward_character (GtkTextView *text)
10122 GtkTextBuffer *buffer;
10126 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10128 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10129 mark = gtk_text_buffer_get_insert(buffer);
10130 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10131 if (gtk_text_iter_backward_cursor_position(&ins))
10132 gtk_text_buffer_place_cursor(buffer, &ins);
10135 static void textview_move_forward_word (GtkTextView *text)
10137 GtkTextBuffer *buffer;
10142 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10144 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10145 mark = gtk_text_buffer_get_insert(buffer);
10146 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10147 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10148 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10149 gtk_text_iter_backward_word_start(&ins);
10150 gtk_text_buffer_place_cursor(buffer, &ins);
10154 static void textview_move_backward_word (GtkTextView *text)
10156 GtkTextBuffer *buffer;
10161 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10163 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10164 mark = gtk_text_buffer_get_insert(buffer);
10165 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10166 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10167 if (gtk_text_iter_backward_word_starts(&ins, 1))
10168 gtk_text_buffer_place_cursor(buffer, &ins);
10171 static void textview_move_end_of_line (GtkTextView *text)
10173 GtkTextBuffer *buffer;
10177 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10179 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10180 mark = gtk_text_buffer_get_insert(buffer);
10181 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10182 if (gtk_text_iter_forward_to_line_end(&ins))
10183 gtk_text_buffer_place_cursor(buffer, &ins);
10186 static void textview_move_next_line (GtkTextView *text)
10188 GtkTextBuffer *buffer;
10193 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10195 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10196 mark = gtk_text_buffer_get_insert(buffer);
10197 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10198 offset = gtk_text_iter_get_line_offset(&ins);
10199 if (gtk_text_iter_forward_line(&ins)) {
10200 gtk_text_iter_set_line_offset(&ins, offset);
10201 gtk_text_buffer_place_cursor(buffer, &ins);
10205 static void textview_move_previous_line (GtkTextView *text)
10207 GtkTextBuffer *buffer;
10212 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10214 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10215 mark = gtk_text_buffer_get_insert(buffer);
10216 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10217 offset = gtk_text_iter_get_line_offset(&ins);
10218 if (gtk_text_iter_backward_line(&ins)) {
10219 gtk_text_iter_set_line_offset(&ins, offset);
10220 gtk_text_buffer_place_cursor(buffer, &ins);
10224 static void textview_delete_forward_character (GtkTextView *text)
10226 GtkTextBuffer *buffer;
10228 GtkTextIter ins, end_iter;
10230 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10232 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10233 mark = gtk_text_buffer_get_insert(buffer);
10234 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10236 if (gtk_text_iter_forward_char(&end_iter)) {
10237 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10241 static void textview_delete_backward_character (GtkTextView *text)
10243 GtkTextBuffer *buffer;
10245 GtkTextIter ins, end_iter;
10247 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10249 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10250 mark = gtk_text_buffer_get_insert(buffer);
10251 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10253 if (gtk_text_iter_backward_char(&end_iter)) {
10254 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10258 static void textview_delete_forward_word (GtkTextView *text)
10260 GtkTextBuffer *buffer;
10262 GtkTextIter ins, end_iter;
10264 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10266 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10267 mark = gtk_text_buffer_get_insert(buffer);
10268 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10270 if (gtk_text_iter_forward_word_end(&end_iter)) {
10271 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10275 static void textview_delete_backward_word (GtkTextView *text)
10277 GtkTextBuffer *buffer;
10279 GtkTextIter ins, end_iter;
10281 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10283 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10284 mark = gtk_text_buffer_get_insert(buffer);
10285 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10287 if (gtk_text_iter_backward_word_start(&end_iter)) {
10288 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10292 static void textview_delete_line (GtkTextView *text)
10294 GtkTextBuffer *buffer;
10296 GtkTextIter ins, start_iter, end_iter;
10298 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10300 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10301 mark = gtk_text_buffer_get_insert(buffer);
10302 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10305 gtk_text_iter_set_line_offset(&start_iter, 0);
10308 if (gtk_text_iter_ends_line(&end_iter)){
10309 if (!gtk_text_iter_forward_char(&end_iter))
10310 gtk_text_iter_backward_char(&start_iter);
10313 gtk_text_iter_forward_to_line_end(&end_iter);
10314 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10317 static void textview_delete_to_line_end (GtkTextView *text)
10319 GtkTextBuffer *buffer;
10321 GtkTextIter ins, end_iter;
10323 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10325 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10326 mark = gtk_text_buffer_get_insert(buffer);
10327 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10329 if (gtk_text_iter_ends_line(&end_iter))
10330 gtk_text_iter_forward_char(&end_iter);
10332 gtk_text_iter_forward_to_line_end(&end_iter);
10333 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10336 #define DO_ACTION(name, act) { \
10337 if(!strcmp(name, a_name)) { \
10341 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10343 const gchar *a_name = gtk_action_get_name(action);
10344 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10345 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10346 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10347 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10348 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10349 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10350 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10351 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10352 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10353 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10354 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10355 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10356 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10357 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10361 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10363 Compose *compose = (Compose *)data;
10364 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10365 ComposeCallAdvancedAction action = -1;
10367 action = compose_call_advanced_action_from_path(gaction);
10370 void (*do_action) (GtkTextView *text);
10371 } action_table[] = {
10372 {textview_move_beginning_of_line},
10373 {textview_move_forward_character},
10374 {textview_move_backward_character},
10375 {textview_move_forward_word},
10376 {textview_move_backward_word},
10377 {textview_move_end_of_line},
10378 {textview_move_next_line},
10379 {textview_move_previous_line},
10380 {textview_delete_forward_character},
10381 {textview_delete_backward_character},
10382 {textview_delete_forward_word},
10383 {textview_delete_backward_word},
10384 {textview_delete_line},
10385 {textview_delete_to_line_end}
10388 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10390 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10391 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10392 if (action_table[action].do_action)
10393 action_table[action].do_action(text);
10395 g_warning("Not implemented yet.");
10399 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10403 if (GTK_IS_EDITABLE(widget)) {
10404 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10405 gtk_editable_set_position(GTK_EDITABLE(widget),
10408 if (widget->parent && widget->parent->parent
10409 && widget->parent->parent->parent) {
10410 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10411 gint y = widget->allocation.y;
10412 gint height = widget->allocation.height;
10413 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10414 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10416 if (y < (int)shown->value) {
10417 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10419 if (y + height > (int)shown->value + (int)shown->page_size) {
10420 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10421 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10422 y + height - (int)shown->page_size - 1);
10424 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10425 (int)shown->upper - (int)shown->page_size - 1);
10432 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10433 compose->focused_editable = widget;
10435 #ifdef GENERIC_UMPC
10436 if (GTK_IS_TEXT_VIEW(widget)
10437 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10438 g_object_ref(compose->notebook);
10439 g_object_ref(compose->edit_vbox);
10440 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10441 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10442 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10443 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10444 g_object_unref(compose->notebook);
10445 g_object_unref(compose->edit_vbox);
10446 g_signal_handlers_block_by_func(G_OBJECT(widget),
10447 G_CALLBACK(compose_grab_focus_cb),
10449 gtk_widget_grab_focus(widget);
10450 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10451 G_CALLBACK(compose_grab_focus_cb),
10453 } else if (!GTK_IS_TEXT_VIEW(widget)
10454 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10455 g_object_ref(compose->notebook);
10456 g_object_ref(compose->edit_vbox);
10457 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10458 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10459 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10460 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10461 g_object_unref(compose->notebook);
10462 g_object_unref(compose->edit_vbox);
10463 g_signal_handlers_block_by_func(G_OBJECT(widget),
10464 G_CALLBACK(compose_grab_focus_cb),
10466 gtk_widget_grab_focus(widget);
10467 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10468 G_CALLBACK(compose_grab_focus_cb),
10474 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10476 compose->modified = TRUE;
10477 // compose_beautify_paragraph(compose, NULL, TRUE);
10478 #ifndef GENERIC_UMPC
10479 compose_set_title(compose);
10483 static void compose_wrap_cb(GtkAction *action, gpointer data)
10485 Compose *compose = (Compose *)data;
10486 compose_beautify_paragraph(compose, NULL, TRUE);
10489 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10491 Compose *compose = (Compose *)data;
10492 compose_wrap_all_full(compose, TRUE);
10495 static void compose_find_cb(GtkAction *action, gpointer data)
10497 Compose *compose = (Compose *)data;
10499 message_search_compose(compose);
10502 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10505 Compose *compose = (Compose *)data;
10506 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10507 if (compose->autowrap)
10508 compose_wrap_all_full(compose, TRUE);
10509 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10512 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10515 Compose *compose = (Compose *)data;
10516 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10519 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10521 Compose *compose = (Compose *)data;
10523 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10526 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10528 Compose *compose = (Compose *)data;
10530 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10533 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10535 g_free(compose->privacy_system);
10537 compose->privacy_system = g_strdup(account->default_privacy_system);
10538 compose_update_privacy_system_menu_item(compose, warn);
10541 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10543 Compose *compose = (Compose *)data;
10545 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10546 gtk_widget_show(compose->ruler_hbox);
10547 prefs_common.show_ruler = TRUE;
10549 gtk_widget_hide(compose->ruler_hbox);
10550 gtk_widget_queue_resize(compose->edit_vbox);
10551 prefs_common.show_ruler = FALSE;
10555 static void compose_attach_drag_received_cb (GtkWidget *widget,
10556 GdkDragContext *context,
10559 GtkSelectionData *data,
10562 gpointer user_data)
10564 Compose *compose = (Compose *)user_data;
10567 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10569 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10571 ) && gtk_drag_get_source_widget(context) !=
10572 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10573 list = uri_list_extract_filenames((const gchar *)data->data);
10574 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10575 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10576 compose_attach_append
10577 (compose, (const gchar *)tmp->data,
10578 utf8_filename, NULL, NULL);
10579 g_free(utf8_filename);
10581 if (list) compose_changed_cb(NULL, compose);
10582 list_free_strings(list);
10584 } else if (gtk_drag_get_source_widget(context)
10585 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10586 /* comes from our summaryview */
10587 SummaryView * summaryview = NULL;
10588 GSList * list = NULL, *cur = NULL;
10590 if (mainwindow_get_mainwindow())
10591 summaryview = mainwindow_get_mainwindow()->summaryview;
10594 list = summary_get_selected_msg_list(summaryview);
10596 for (cur = list; cur; cur = cur->next) {
10597 MsgInfo *msginfo = (MsgInfo *)cur->data;
10598 gchar *file = NULL;
10600 file = procmsg_get_message_file_full(msginfo,
10603 compose_attach_append(compose, (const gchar *)file,
10604 (const gchar *)file, "message/rfc822", NULL);
10608 g_slist_free(list);
10612 static gboolean compose_drag_drop(GtkWidget *widget,
10613 GdkDragContext *drag_context,
10615 guint time, gpointer user_data)
10617 /* not handling this signal makes compose_insert_drag_received_cb
10622 static void compose_insert_drag_received_cb (GtkWidget *widget,
10623 GdkDragContext *drag_context,
10626 GtkSelectionData *data,
10629 gpointer user_data)
10631 Compose *compose = (Compose *)user_data;
10634 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10637 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10639 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10641 AlertValue val = G_ALERTDEFAULT;
10643 list = uri_list_extract_filenames((const gchar *)data->data);
10644 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10645 /* Assume a list of no files, and data has ://, is a remote link */
10646 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10647 gchar *tmpfile = get_tmp_file();
10648 str_write_to_file(tmpdata, tmpfile);
10650 compose_insert_file(compose, tmpfile);
10651 claws_unlink(tmpfile);
10653 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10654 compose_beautify_paragraph(compose, NULL, TRUE);
10657 switch (prefs_common.compose_dnd_mode) {
10658 case COMPOSE_DND_ASK:
10659 val = alertpanel_full(_("Insert or attach?"),
10660 _("Do you want to insert the contents of the file(s) "
10661 "into the message body, or attach it to the email?"),
10662 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10663 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10665 case COMPOSE_DND_INSERT:
10666 val = G_ALERTALTERNATE;
10668 case COMPOSE_DND_ATTACH:
10669 val = G_ALERTOTHER;
10672 /* unexpected case */
10673 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10676 if (val & G_ALERTDISABLE) {
10677 val &= ~G_ALERTDISABLE;
10678 /* remember what action to perform by default, only if we don't click Cancel */
10679 if (val == G_ALERTALTERNATE)
10680 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10681 else if (val == G_ALERTOTHER)
10682 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10685 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10686 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10687 list_free_strings(list);
10690 } else if (val == G_ALERTOTHER) {
10691 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10692 list_free_strings(list);
10697 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10698 compose_insert_file(compose, (const gchar *)tmp->data);
10700 list_free_strings(list);
10702 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10707 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10710 static void compose_header_drag_received_cb (GtkWidget *widget,
10711 GdkDragContext *drag_context,
10714 GtkSelectionData *data,
10717 gpointer user_data)
10719 GtkEditable *entry = (GtkEditable *)user_data;
10720 gchar *email = (gchar *)data->data;
10722 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10725 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10726 gchar *decoded=g_new(gchar, strlen(email));
10729 email += strlen("mailto:");
10730 decode_uri(decoded, email); /* will fit */
10731 gtk_editable_delete_text(entry, 0, -1);
10732 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10733 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10737 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10740 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10742 Compose *compose = (Compose *)data;
10744 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10745 compose->return_receipt = TRUE;
10747 compose->return_receipt = FALSE;
10750 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10752 Compose *compose = (Compose *)data;
10754 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10755 compose->remove_references = TRUE;
10757 compose->remove_references = FALSE;
10760 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10761 ComposeHeaderEntry *headerentry)
10763 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10767 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10768 GdkEventKey *event,
10769 ComposeHeaderEntry *headerentry)
10771 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10772 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10773 !(event->state & GDK_MODIFIER_MASK) &&
10774 (event->keyval == GDK_BackSpace) &&
10775 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10776 gtk_container_remove
10777 (GTK_CONTAINER(headerentry->compose->header_table),
10778 headerentry->combo);
10779 gtk_container_remove
10780 (GTK_CONTAINER(headerentry->compose->header_table),
10781 headerentry->entry);
10782 headerentry->compose->header_list =
10783 g_slist_remove(headerentry->compose->header_list,
10785 g_free(headerentry);
10786 } else if (event->keyval == GDK_Tab) {
10787 if (headerentry->compose->header_last == headerentry) {
10788 /* Override default next focus, and give it to subject_entry
10789 * instead of notebook tabs
10791 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10792 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10799 static gboolean scroll_postpone(gpointer data)
10801 Compose *compose = (Compose *)data;
10803 cm_return_val_if_fail(!compose->batch, FALSE);
10805 GTK_EVENTS_FLUSH();
10806 compose_show_first_last_header(compose, FALSE);
10810 static void compose_headerentry_changed_cb(GtkWidget *entry,
10811 ComposeHeaderEntry *headerentry)
10813 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10814 compose_create_header_entry(headerentry->compose);
10815 g_signal_handlers_disconnect_matched
10816 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10817 0, 0, NULL, NULL, headerentry);
10819 if (!headerentry->compose->batch)
10820 g_timeout_add(0, scroll_postpone, headerentry->compose);
10824 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10826 GtkAdjustment *vadj;
10828 cm_return_if_fail(compose);
10829 cm_return_if_fail(!compose->batch);
10830 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10831 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10832 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10833 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10834 gtk_adjustment_changed(vadj);
10837 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10838 const gchar *text, gint len, Compose *compose)
10840 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10841 (G_OBJECT(compose->text), "paste_as_quotation"));
10844 cm_return_if_fail(text != NULL);
10846 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10847 G_CALLBACK(text_inserted),
10849 if (paste_as_quotation) {
10851 const gchar *qmark;
10853 GtkTextIter start_iter;
10856 len = strlen(text);
10858 new_text = g_strndup(text, len);
10860 qmark = compose_quote_char_from_context(compose);
10862 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10863 gtk_text_buffer_place_cursor(buffer, iter);
10865 pos = gtk_text_iter_get_offset(iter);
10867 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10868 _("Quote format error at line %d."));
10869 quote_fmt_reset_vartable();
10871 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10872 GINT_TO_POINTER(paste_as_quotation - 1));
10874 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10875 gtk_text_buffer_place_cursor(buffer, iter);
10876 gtk_text_buffer_delete_mark(buffer, mark);
10878 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10879 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10880 compose_beautify_paragraph(compose, &start_iter, FALSE);
10881 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10882 gtk_text_buffer_delete_mark(buffer, mark);
10884 if (strcmp(text, "\n") || compose->automatic_break
10885 || gtk_text_iter_starts_line(iter)) {
10886 GtkTextIter before_ins;
10887 gtk_text_buffer_insert(buffer, iter, text, len);
10888 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10889 before_ins = *iter;
10890 gtk_text_iter_backward_chars(&before_ins, len);
10891 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10894 /* check if the preceding is just whitespace or quote */
10895 GtkTextIter start_line;
10896 gchar *tmp = NULL, *quote = NULL;
10897 gint quote_len = 0, is_normal = 0;
10898 start_line = *iter;
10899 gtk_text_iter_set_line_offset(&start_line, 0);
10900 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10903 if (*tmp == '\0') {
10906 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10914 gtk_text_buffer_insert(buffer, iter, text, len);
10916 gtk_text_buffer_insert_with_tags_by_name(buffer,
10917 iter, text, len, "no_join", NULL);
10922 if (!paste_as_quotation) {
10923 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10924 compose_beautify_paragraph(compose, iter, FALSE);
10925 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10926 gtk_text_buffer_delete_mark(buffer, mark);
10929 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10930 G_CALLBACK(text_inserted),
10932 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10934 if (prefs_common.autosave &&
10935 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10936 compose->draft_timeout_tag != -2 /* disabled while loading */)
10937 compose->draft_timeout_tag = g_timeout_add
10938 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10940 static gint compose_defer_auto_save_draft(Compose *compose)
10942 compose->draft_timeout_tag = -1;
10943 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10948 static void compose_check_all(GtkAction *action, gpointer data)
10950 Compose *compose = (Compose *)data;
10951 if (!compose->gtkaspell)
10954 if (gtkut_widget_has_focus(compose->subject_entry))
10955 claws_spell_entry_check_all(
10956 CLAWS_SPELL_ENTRY(compose->subject_entry));
10958 gtkaspell_check_all(compose->gtkaspell);
10961 static void compose_highlight_all(GtkAction *action, gpointer data)
10963 Compose *compose = (Compose *)data;
10964 if (compose->gtkaspell) {
10965 claws_spell_entry_recheck_all(
10966 CLAWS_SPELL_ENTRY(compose->subject_entry));
10967 gtkaspell_highlight_all(compose->gtkaspell);
10971 static void compose_check_backwards(GtkAction *action, gpointer data)
10973 Compose *compose = (Compose *)data;
10974 if (!compose->gtkaspell) {
10975 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10979 if (gtkut_widget_has_focus(compose->subject_entry))
10980 claws_spell_entry_check_backwards(
10981 CLAWS_SPELL_ENTRY(compose->subject_entry));
10983 gtkaspell_check_backwards(compose->gtkaspell);
10986 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10988 Compose *compose = (Compose *)data;
10989 if (!compose->gtkaspell) {
10990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10994 if (gtkut_widget_has_focus(compose->subject_entry))
10995 claws_spell_entry_check_forwards_go(
10996 CLAWS_SPELL_ENTRY(compose->subject_entry));
10998 gtkaspell_check_forwards_go(compose->gtkaspell);
11003 *\brief Guess originating forward account from MsgInfo and several
11004 * "common preference" settings. Return NULL if no guess.
11006 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11008 PrefsAccount *account = NULL;
11010 cm_return_val_if_fail(msginfo, NULL);
11011 cm_return_val_if_fail(msginfo->folder, NULL);
11012 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11014 if (msginfo->folder->prefs->enable_default_account)
11015 account = account_find_from_id(msginfo->folder->prefs->default_account);
11018 account = msginfo->folder->folder->account;
11020 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11022 Xstrdup_a(to, msginfo->to, return NULL);
11023 extract_address(to);
11024 account = account_find_from_address(to, FALSE);
11027 if (!account && prefs_common.forward_account_autosel) {
11028 gchar cc[BUFFSIZE];
11029 if (!procheader_get_header_from_msginfo
11030 (msginfo, cc,sizeof cc , "Cc:")) {
11031 gchar *buf = cc + strlen("Cc:");
11032 extract_address(buf);
11033 account = account_find_from_address(buf, FALSE);
11037 if (!account && prefs_common.forward_account_autosel) {
11038 gchar deliveredto[BUFFSIZE];
11039 if (!procheader_get_header_from_msginfo
11040 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11041 gchar *buf = deliveredto + strlen("Delivered-To:");
11042 extract_address(buf);
11043 account = account_find_from_address(buf, FALSE);
11050 gboolean compose_close(Compose *compose)
11054 if (!g_mutex_trylock(compose->mutex)) {
11055 /* we have to wait for the (possibly deferred by auto-save)
11056 * drafting to be done, before destroying the compose under
11058 debug_print("waiting for drafting to finish...\n");
11059 compose_allow_user_actions(compose, FALSE);
11060 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11063 cm_return_val_if_fail(compose, FALSE);
11064 gtkut_widget_get_uposition(compose->window, &x, &y);
11065 if (!compose->batch) {
11066 prefs_common.compose_x = x;
11067 prefs_common.compose_y = y;
11069 g_mutex_unlock(compose->mutex);
11070 compose_destroy(compose);
11075 * Add entry field for each address in list.
11076 * \param compose E-Mail composition object.
11077 * \param listAddress List of (formatted) E-Mail addresses.
11079 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11082 node = listAddress;
11084 addr = ( gchar * ) node->data;
11085 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11086 node = g_list_next( node );
11090 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11091 guint action, gboolean opening_multiple)
11093 gchar *body = NULL;
11094 GSList *new_msglist = NULL;
11095 MsgInfo *tmp_msginfo = NULL;
11096 gboolean originally_enc = FALSE;
11097 gboolean originally_sig = FALSE;
11098 Compose *compose = NULL;
11099 gchar *s_system = NULL;
11101 cm_return_if_fail(msgview != NULL);
11103 cm_return_if_fail(msginfo_list != NULL);
11105 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11106 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11107 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11109 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11110 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11111 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11112 orig_msginfo, mimeinfo);
11113 if (tmp_msginfo != NULL) {
11114 new_msglist = g_slist_append(NULL, tmp_msginfo);
11116 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11117 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11118 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11120 tmp_msginfo->folder = orig_msginfo->folder;
11121 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11122 if (orig_msginfo->tags) {
11123 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11124 tmp_msginfo->folder->tags_dirty = TRUE;
11130 if (!opening_multiple)
11131 body = messageview_get_selection(msgview);
11134 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11135 procmsg_msginfo_free(tmp_msginfo);
11136 g_slist_free(new_msglist);
11138 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11140 if (compose && originally_enc) {
11141 compose_force_encryption(compose, compose->account, FALSE, s_system);
11144 if (compose && originally_sig && compose->account->default_sign_reply) {
11145 compose_force_signing(compose, compose->account, s_system);
11149 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11152 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11155 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11156 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11157 GSList *cur = msginfo_list;
11158 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11159 "messages. Opening the windows "
11160 "could take some time. Do you "
11161 "want to continue?"),
11162 g_slist_length(msginfo_list));
11163 if (g_slist_length(msginfo_list) > 9
11164 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11165 != G_ALERTALTERNATE) {
11170 /* We'll open multiple compose windows */
11171 /* let the WM place the next windows */
11172 compose_force_window_origin = FALSE;
11173 for (; cur; cur = cur->next) {
11175 tmplist.data = cur->data;
11176 tmplist.next = NULL;
11177 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11179 compose_force_window_origin = TRUE;
11181 /* forwarding multiple mails as attachments is done via a
11182 * single compose window */
11183 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11187 void compose_check_for_email_account(Compose *compose)
11189 PrefsAccount *ac = NULL, *curr = NULL;
11195 if (compose->account && compose->account->protocol == A_NNTP) {
11196 ac = account_get_cur_account();
11197 if (ac->protocol == A_NNTP) {
11198 list = account_get_list();
11200 for( ; list != NULL ; list = g_list_next(list)) {
11201 curr = (PrefsAccount *) list->data;
11202 if (curr->protocol != A_NNTP) {
11208 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11213 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11214 const gchar *address)
11216 GSList *msginfo_list = NULL;
11217 gchar *body = messageview_get_selection(msgview);
11220 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11222 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11223 compose_check_for_email_account(compose);
11224 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11225 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11226 compose_reply_set_subject(compose, msginfo);
11229 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11232 void compose_set_position(Compose *compose, gint pos)
11234 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11236 gtkut_text_view_set_position(text, pos);
11239 gboolean compose_search_string(Compose *compose,
11240 const gchar *str, gboolean case_sens)
11242 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11244 return gtkut_text_view_search_string(text, str, case_sens);
11247 gboolean compose_search_string_backward(Compose *compose,
11248 const gchar *str, gboolean case_sens)
11250 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11252 return gtkut_text_view_search_string_backward(text, str, case_sens);
11255 /* allocate a msginfo structure and populate its data from a compose data structure */
11256 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11258 MsgInfo *newmsginfo;
11260 gchar buf[BUFFSIZE];
11262 cm_return_val_if_fail( compose != NULL, NULL );
11264 newmsginfo = procmsg_msginfo_new();
11267 get_rfc822_date(buf, sizeof(buf));
11268 newmsginfo->date = g_strdup(buf);
11271 if (compose->from_name) {
11272 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11273 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11277 if (compose->subject_entry)
11278 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11280 /* to, cc, reply-to, newsgroups */
11281 for (list = compose->header_list; list; list = list->next) {
11282 gchar *header = gtk_editable_get_chars(
11284 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11285 gchar *entry = gtk_editable_get_chars(
11286 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11288 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11289 if ( newmsginfo->to == NULL ) {
11290 newmsginfo->to = g_strdup(entry);
11291 } else if (entry && *entry) {
11292 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11293 g_free(newmsginfo->to);
11294 newmsginfo->to = tmp;
11297 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11298 if ( newmsginfo->cc == NULL ) {
11299 newmsginfo->cc = g_strdup(entry);
11300 } else if (entry && *entry) {
11301 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11302 g_free(newmsginfo->cc);
11303 newmsginfo->cc = tmp;
11306 if ( strcasecmp(header,
11307 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11308 if ( newmsginfo->newsgroups == NULL ) {
11309 newmsginfo->newsgroups = g_strdup(entry);
11310 } else if (entry && *entry) {
11311 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11312 g_free(newmsginfo->newsgroups);
11313 newmsginfo->newsgroups = tmp;
11321 /* other data is unset */
11327 /* update compose's dictionaries from folder dict settings */
11328 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11329 FolderItem *folder_item)
11331 cm_return_if_fail(compose != NULL);
11333 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11334 FolderItemPrefs *prefs = folder_item->prefs;
11336 if (prefs->enable_default_dictionary)
11337 gtkaspell_change_dict(compose->gtkaspell,
11338 prefs->default_dictionary, FALSE);
11339 if (folder_item->prefs->enable_default_alt_dictionary)
11340 gtkaspell_change_alt_dict(compose->gtkaspell,
11341 prefs->default_alt_dictionary);
11342 if (prefs->enable_default_dictionary
11343 || prefs->enable_default_alt_dictionary)
11344 compose_spell_menu_changed(compose);