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"
86 #include "gtkshruler.h"
88 #include "alertpanel.h"
89 #include "manage_window.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
116 #define N_ATTACH_COLS (N_COL_COLUMNS)
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
134 } ComposeCallAdvancedAction;
138 PRIORITY_HIGHEST = 1,
147 COMPOSE_INSERT_SUCCESS,
148 COMPOSE_INSERT_READ_ERROR,
149 COMPOSE_INSERT_INVALID_CHARACTER,
150 COMPOSE_INSERT_NO_FILE
151 } ComposeInsertResult;
155 COMPOSE_WRITE_FOR_SEND,
156 COMPOSE_WRITE_FOR_STORE
161 COMPOSE_QUOTE_FORCED,
168 SUBJECT_FIELD_PRESENT,
173 #define B64_LINE_SIZE 57
174 #define B64_BUFFSIZE 77
176 #define MAX_REFERENCES_LEN 999
178 static GList *compose_list = NULL;
180 static Compose *compose_generic_new (PrefsAccount *account,
183 GPtrArray *attach_files,
184 GList *listAddress );
186 static Compose *compose_create (PrefsAccount *account,
191 static void compose_entry_mark_default_to (Compose *compose,
192 const gchar *address);
193 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
194 ComposeQuoteMode quote_mode,
198 static Compose *compose_forward_multiple (PrefsAccount *account,
199 GSList *msginfo_list);
200 static Compose *compose_reply (MsgInfo *msginfo,
201 ComposeQuoteMode quote_mode,
206 static Compose *compose_reply_mode (ComposeMode mode,
207 GSList *msginfo_list,
209 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
210 static void compose_update_privacy_systems_menu(Compose *compose);
212 static GtkWidget *compose_account_option_menu_create
214 static void compose_set_out_encoding (Compose *compose);
215 static void compose_set_template_menu (Compose *compose);
216 static void compose_destroy (Compose *compose);
218 static MailField compose_entries_set (Compose *compose,
220 ComposeEntryType to_type);
221 static gint compose_parse_header (Compose *compose,
223 static gchar *compose_parse_references (const gchar *ref,
226 static gchar *compose_quote_fmt (Compose *compose,
232 gboolean need_unescape,
233 const gchar *err_msg);
235 static void compose_reply_set_entry (Compose *compose,
241 followup_and_reply_to);
242 static void compose_reedit_set_entry (Compose *compose,
245 static void compose_insert_sig (Compose *compose,
247 static ComposeInsertResult compose_insert_file (Compose *compose,
250 static gboolean compose_attach_append (Compose *compose,
253 const gchar *content_type,
254 const gchar *charset);
255 static void compose_attach_parts (Compose *compose,
258 static gboolean compose_beautify_paragraph (Compose *compose,
259 GtkTextIter *par_iter,
261 static void compose_wrap_all (Compose *compose);
262 static void compose_wrap_all_full (Compose *compose,
265 static void compose_set_title (Compose *compose);
266 static void compose_select_account (Compose *compose,
267 PrefsAccount *account,
270 static PrefsAccount *compose_current_mail_account(void);
271 /* static gint compose_send (Compose *compose); */
272 static gboolean compose_check_for_valid_recipient
274 static gboolean compose_check_entries (Compose *compose,
275 gboolean check_everything);
276 static gint compose_write_to_file (Compose *compose,
279 gboolean attach_parts);
280 static gint compose_write_body_to_file (Compose *compose,
282 static gint compose_remove_reedit_target (Compose *compose,
284 static void compose_remove_draft (Compose *compose);
285 static gint compose_queue_sub (Compose *compose,
289 gboolean check_subject,
290 gboolean remove_reedit_target);
291 static int compose_add_attachments (Compose *compose,
293 static gchar *compose_get_header (Compose *compose);
295 static void compose_convert_header (Compose *compose,
300 gboolean addr_field);
302 static void compose_attach_info_free (AttachInfo *ainfo);
303 static void compose_attach_remove_selected (GtkAction *action,
306 static void compose_template_apply (Compose *compose,
309 static void compose_attach_property (GtkAction *action,
311 static void compose_attach_property_create (gboolean *cancelled);
312 static void attach_property_ok (GtkWidget *widget,
313 gboolean *cancelled);
314 static void attach_property_cancel (GtkWidget *widget,
315 gboolean *cancelled);
316 static gint attach_property_delete_event (GtkWidget *widget,
318 gboolean *cancelled);
319 static gboolean attach_property_key_pressed (GtkWidget *widget,
321 gboolean *cancelled);
323 static void compose_exec_ext_editor (Compose *compose);
325 static gint compose_exec_ext_editor_real (const gchar *file);
326 static gboolean compose_ext_editor_kill (Compose *compose);
327 static gboolean compose_input_cb (GIOChannel *source,
328 GIOCondition condition,
330 static void compose_set_ext_editor_sensitive (Compose *compose,
332 #endif /* G_OS_UNIX */
334 static void compose_undo_state_changed (UndoMain *undostruct,
339 static void compose_create_header_entry (Compose *compose);
340 static void compose_add_header_entry (Compose *compose, const gchar *header,
341 gchar *text, ComposePrefType pref_type);
342 static void compose_remove_header_entries(Compose *compose);
344 static void compose_update_priority_menu_item(Compose * compose);
346 static void compose_spell_menu_changed (void *data);
347 static void compose_dict_changed (void *data);
349 static void compose_add_field_list ( Compose *compose,
350 GList *listAddress );
352 /* callback functions */
354 static gboolean compose_edit_size_alloc (GtkEditable *widget,
355 GtkAllocation *allocation,
356 GtkSHRuler *shruler);
357 static void account_activated (GtkComboBox *optmenu,
359 static void attach_selected (GtkTreeView *tree_view,
360 GtkTreePath *tree_path,
361 GtkTreeViewColumn *column,
363 static gboolean attach_button_pressed (GtkWidget *widget,
364 GdkEventButton *event,
366 static gboolean attach_key_pressed (GtkWidget *widget,
369 static void compose_send_cb (GtkAction *action, gpointer data);
370 static void compose_send_later_cb (GtkAction *action, gpointer data);
372 static void compose_save_cb (GtkAction *action,
375 static void compose_attach_cb (GtkAction *action,
377 static void compose_insert_file_cb (GtkAction *action,
379 static void compose_insert_sig_cb (GtkAction *action,
382 static void compose_close_cb (GtkAction *action,
385 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
387 static void compose_address_cb (GtkAction *action,
389 static void about_show_cb (GtkAction *action,
391 static void compose_template_activate_cb(GtkWidget *widget,
394 static void compose_ext_editor_cb (GtkAction *action,
397 static gint compose_delete_cb (GtkWidget *widget,
401 static void compose_undo_cb (GtkAction *action,
403 static void compose_redo_cb (GtkAction *action,
405 static void compose_cut_cb (GtkAction *action,
407 static void compose_copy_cb (GtkAction *action,
409 static void compose_paste_cb (GtkAction *action,
411 static void compose_paste_as_quote_cb (GtkAction *action,
413 static void compose_paste_no_wrap_cb (GtkAction *action,
415 static void compose_paste_wrap_cb (GtkAction *action,
417 static void compose_allsel_cb (GtkAction *action,
420 static void compose_advanced_action_cb (GtkAction *action,
423 static void compose_grab_focus_cb (GtkWidget *widget,
426 static void compose_changed_cb (GtkTextBuffer *textbuf,
429 static void compose_wrap_cb (GtkAction *action,
431 static void compose_wrap_all_cb (GtkAction *action,
433 static void compose_find_cb (GtkAction *action,
435 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
437 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
440 static void compose_toggle_ruler_cb (GtkToggleAction *action,
442 static void compose_toggle_sign_cb (GtkToggleAction *action,
444 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
446 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
447 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
448 static void activate_privacy_system (Compose *compose,
449 PrefsAccount *account,
451 static void compose_use_signing(Compose *compose, gboolean use_signing);
452 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
453 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
455 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
457 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
458 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
459 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
461 static void compose_attach_drag_received_cb (GtkWidget *widget,
462 GdkDragContext *drag_context,
465 GtkSelectionData *data,
469 static void compose_insert_drag_received_cb (GtkWidget *widget,
470 GdkDragContext *drag_context,
473 GtkSelectionData *data,
477 static void compose_header_drag_received_cb (GtkWidget *widget,
478 GdkDragContext *drag_context,
481 GtkSelectionData *data,
486 static gboolean compose_drag_drop (GtkWidget *widget,
487 GdkDragContext *drag_context,
489 guint time, gpointer user_data);
491 static void text_inserted (GtkTextBuffer *buffer,
496 static Compose *compose_generic_reply(MsgInfo *msginfo,
497 ComposeQuoteMode quote_mode,
501 gboolean followup_and_reply_to,
504 static void compose_headerentry_changed_cb (GtkWidget *entry,
505 ComposeHeaderEntry *headerentry);
506 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
508 ComposeHeaderEntry *headerentry);
509 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
510 ComposeHeaderEntry *headerentry);
512 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
514 static void compose_allow_user_actions (Compose *compose, gboolean allow);
516 static void compose_nothing_cb (GtkAction *action, gpointer data)
522 static void compose_check_all (GtkAction *action, gpointer data);
523 static void compose_highlight_all (GtkAction *action, gpointer data);
524 static void compose_check_backwards (GtkAction *action, gpointer data);
525 static void compose_check_forwards_go (GtkAction *action, gpointer data);
528 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
530 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
533 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
534 FolderItem *folder_item);
536 static void compose_attach_update_label(Compose *compose);
537 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
538 gboolean respect_default_to);
540 static GtkActionEntry compose_popup_entries[] =
542 {"Compose", NULL, "Compose" },
543 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
544 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
545 {"Compose/---", NULL, "---", NULL, NULL, NULL },
546 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
549 static GtkActionEntry compose_entries[] =
551 {"Menu", NULL, "Menu" },
553 {"Message", NULL, N_("_Message") },
554 {"Edit", NULL, N_("_Edit") },
556 {"Spelling", NULL, N_("_Spelling") },
558 {"Options", NULL, N_("_Options") },
559 {"Tools", NULL, N_("_Tools") },
560 {"Help", NULL, N_("_Help") },
562 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
563 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
564 {"Message/---", NULL, "---" },
566 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
567 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
568 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
569 /* {"Message/---", NULL, "---" }, */
570 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
571 /* {"Message/---", NULL, "---" }, */
572 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
575 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
576 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
577 {"Edit/---", NULL, "---" },
579 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
580 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
581 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
583 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
584 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
585 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
586 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
588 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
590 {"Edit/Advanced", NULL, N_("A_dvanced") },
591 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
592 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
593 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
594 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
595 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
596 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
597 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
598 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
599 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
600 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
601 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
602 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
603 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
604 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
606 /* {"Edit/---", NULL, "---" }, */
607 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
609 /* {"Edit/---", NULL, "---" }, */
610 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
611 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
612 /* {"Edit/---", NULL, "---" }, */
613 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
616 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
617 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
618 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
619 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
621 {"Spelling/---", NULL, "---" },
622 {"Spelling/Options", NULL, N_("_Options") },
627 {"Options/ReplyMode", NULL, N_("Reply _mode") },
628 {"Options/---", NULL, "---" },
629 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
630 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
632 /* {"Options/---", NULL, "---" }, */
634 {"Options/Priority", NULL, N_("_Priority") },
636 {"Options/Encoding", NULL, N_("Character _encoding") },
637 {"Options/Encoding/---", NULL, "---" },
638 #define ENC_ACTION(cs_char,c_char,string) \
639 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
641 {"Options/Encoding/Western", NULL, N_("Western European") },
642 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
643 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
644 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
645 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
646 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
647 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
648 {"Options/Encoding/Korean", NULL, N_("Korean") },
649 {"Options/Encoding/Thai", NULL, N_("Thai") },
652 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
654 {"Tools/Template", NULL, N_("_Template") },
655 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 {"Tools/Actions", NULL, N_("Actio_ns") },
657 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
660 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
663 static GtkToggleActionEntry compose_toggle_entries[] =
665 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
666 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
667 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
668 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
669 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
670 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
671 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
674 static GtkRadioActionEntry compose_radio_rm_entries[] =
676 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
677 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
679 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
682 static GtkRadioActionEntry compose_radio_prio_entries[] =
684 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
688 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
691 static GtkRadioActionEntry compose_radio_enc_entries[] =
693 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
727 static GtkTargetEntry compose_mime_types[] =
729 {"text/uri-list", 0, 0},
730 {"UTF8_STRING", 0, 0},
734 static gboolean compose_put_existing_to_front(MsgInfo *info)
736 GList *compose_list = compose_get_compose_list();
740 for (elem = compose_list; elem != NULL && elem->data != NULL;
742 Compose *c = (Compose*)elem->data;
744 if (!c->targetinfo || !c->targetinfo->msgid ||
748 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
749 gtkut_window_popup(c->window);
757 static GdkColor quote_color1 =
758 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
759 static GdkColor quote_color2 =
760 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor quote_color3 =
762 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
764 static GdkColor quote_bgcolor1 =
765 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
766 static GdkColor quote_bgcolor2 =
767 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
768 static GdkColor quote_bgcolor3 =
769 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
771 static GdkColor signature_color = {
778 static GdkColor uri_color = {
785 static void compose_create_tags(GtkTextView *text, Compose *compose)
787 GtkTextBuffer *buffer;
788 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
789 #if !GTK_CHECK_VERSION(2, 24, 0)
796 buffer = gtk_text_view_get_buffer(text);
798 if (prefs_common.enable_color) {
799 /* grab the quote colors, converting from an int to a GdkColor */
800 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
804 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
806 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
808 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
810 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
812 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
814 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
817 signature_color = quote_color1 = quote_color2 = quote_color3 =
818 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
821 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
822 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
823 "foreground-gdk", "e_color1,
824 "paragraph-background-gdk", "e_bgcolor1,
826 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
827 "foreground-gdk", "e_color2,
828 "paragraph-background-gdk", "e_bgcolor2,
830 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
831 "foreground-gdk", "e_color3,
832 "paragraph-background-gdk", "e_bgcolor3,
835 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
836 "foreground-gdk", "e_color1,
838 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
839 "foreground-gdk", "e_color2,
841 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
842 "foreground-gdk", "e_color3,
846 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
847 "foreground-gdk", &signature_color,
850 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
851 "foreground-gdk", &uri_color,
853 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
854 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
856 color[0] = quote_color1;
857 color[1] = quote_color2;
858 color[2] = quote_color3;
859 color[3] = quote_bgcolor1;
860 color[4] = quote_bgcolor2;
861 color[5] = quote_bgcolor3;
862 color[6] = signature_color;
863 color[7] = uri_color;
864 #if !GTK_CHECK_VERSION(2, 24, 0)
865 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
866 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
868 for (i = 0; i < 8; i++) {
869 if (success[i] == FALSE) {
872 g_warning("Compose: color allocation failed.\n");
873 style = gtk_widget_get_style(GTK_WIDGET(text));
874 quote_color1 = quote_color2 = quote_color3 =
875 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
876 signature_color = uri_color = black;
882 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
883 GPtrArray *attach_files)
885 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
888 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
890 return compose_generic_new(account, mailto, item, NULL, NULL);
893 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
895 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
898 #define SCROLL_TO_CURSOR(compose) { \
899 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
900 gtk_text_view_get_buffer( \
901 GTK_TEXT_VIEW(compose->text))); \
902 gtk_text_view_scroll_mark_onscreen( \
903 GTK_TEXT_VIEW(compose->text), \
907 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
910 if (folderidentifier) {
911 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
912 prefs_common.compose_save_to_history = add_history(
913 prefs_common.compose_save_to_history, folderidentifier);
914 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
915 prefs_common.compose_save_to_history);
918 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
919 if (folderidentifier)
920 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
922 gtk_entry_set_text(GTK_ENTRY(entry), "");
925 static gchar *compose_get_save_to(Compose *compose)
928 gchar *result = NULL;
929 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
930 result = gtk_editable_get_chars(entry, 0, -1);
933 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
934 prefs_common.compose_save_to_history = add_history(
935 prefs_common.compose_save_to_history, result);
936 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
937 prefs_common.compose_save_to_history);
942 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
943 GPtrArray *attach_files, GList *listAddress )
946 GtkTextView *textview;
947 GtkTextBuffer *textbuf;
949 const gchar *subject_format = NULL;
950 const gchar *body_format = NULL;
951 gchar *mailto_from = NULL;
952 PrefsAccount *mailto_account = NULL;
953 MsgInfo* dummyinfo = NULL;
954 gint cursor_pos = -1;
955 MailField mfield = NO_FIELD_PRESENT;
959 /* check if mailto defines a from */
960 if (mailto && *mailto != '\0') {
961 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
962 /* mailto defines a from, check if we can get account prefs from it,
963 if not, the account prefs will be guessed using other ways, but we'll keep
966 mailto_account = account_find_from_address(mailto_from, TRUE);
968 account = mailto_account;
971 /* if no account prefs set from mailto, set if from folder prefs (if any) */
972 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
973 account = account_find_from_id(item->prefs->default_account);
975 /* if no account prefs set, fallback to the current one */
976 if (!account) account = cur_account;
977 cm_return_val_if_fail(account != NULL, NULL);
979 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
981 /* override from name if mailto asked for it */
983 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
986 /* override from name according to folder properties */
987 if (item && item->prefs &&
988 item->prefs->compose_with_format &&
989 item->prefs->compose_override_from_format &&
990 *item->prefs->compose_override_from_format != '\0') {
995 dummyinfo = compose_msginfo_new_from_compose(compose);
997 /* decode \-escape sequences in the internal representation of the quote format */
998 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
999 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1002 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1003 compose->gtkaspell);
1005 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1007 quote_fmt_scan_string(tmp);
1010 buf = quote_fmt_get_buffer();
1012 alertpanel_error(_("New message From format error."));
1014 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1015 quote_fmt_reset_vartable();
1020 compose->replyinfo = NULL;
1021 compose->fwdinfo = NULL;
1023 textview = GTK_TEXT_VIEW(compose->text);
1024 textbuf = gtk_text_view_get_buffer(textview);
1025 compose_create_tags(textview, compose);
1027 undo_block(compose->undostruct);
1029 compose_set_dictionaries_from_folder_prefs(compose, item);
1032 if (account->auto_sig)
1033 compose_insert_sig(compose, FALSE);
1034 gtk_text_buffer_get_start_iter(textbuf, &iter);
1035 gtk_text_buffer_place_cursor(textbuf, &iter);
1037 if (account->protocol != A_NNTP) {
1038 if (mailto && *mailto != '\0') {
1039 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1042 compose_set_folder_prefs(compose, item, TRUE);
1044 if (item && item->ret_rcpt) {
1045 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1048 if (mailto && *mailto != '\0') {
1049 if (!strchr(mailto, '@'))
1050 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1052 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1053 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1054 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1055 mfield = TO_FIELD_PRESENT;
1058 * CLAWS: just don't allow return receipt request, even if the user
1059 * may want to send an email. simple but foolproof.
1061 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1063 compose_add_field_list( compose, listAddress );
1065 if (item && item->prefs && item->prefs->compose_with_format) {
1066 subject_format = item->prefs->compose_subject_format;
1067 body_format = item->prefs->compose_body_format;
1068 } else if (account->compose_with_format) {
1069 subject_format = account->compose_subject_format;
1070 body_format = account->compose_body_format;
1071 } else if (prefs_common.compose_with_format) {
1072 subject_format = prefs_common.compose_subject_format;
1073 body_format = prefs_common.compose_body_format;
1076 if (subject_format || body_format) {
1079 && *subject_format != '\0' )
1081 gchar *subject = NULL;
1086 dummyinfo = compose_msginfo_new_from_compose(compose);
1088 /* decode \-escape sequences in the internal representation of the quote format */
1089 tmp = g_malloc(strlen(subject_format)+1);
1090 pref_get_unescaped_pref(tmp, subject_format);
1092 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1094 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1095 compose->gtkaspell);
1097 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1099 quote_fmt_scan_string(tmp);
1102 buf = quote_fmt_get_buffer();
1104 alertpanel_error(_("New message subject format error."));
1106 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1107 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1108 quote_fmt_reset_vartable();
1112 mfield = SUBJECT_FIELD_PRESENT;
1116 && *body_format != '\0' )
1119 GtkTextBuffer *buffer;
1120 GtkTextIter start, end;
1124 dummyinfo = compose_msginfo_new_from_compose(compose);
1126 text = GTK_TEXT_VIEW(compose->text);
1127 buffer = gtk_text_view_get_buffer(text);
1128 gtk_text_buffer_get_start_iter(buffer, &start);
1129 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1130 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1132 compose_quote_fmt(compose, dummyinfo,
1134 NULL, tmp, FALSE, TRUE,
1135 _("The body of the \"New message\" template has an error at line %d."));
1136 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1137 quote_fmt_reset_vartable();
1141 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1142 gtkaspell_highlight_all(compose->gtkaspell);
1144 mfield = BODY_FIELD_PRESENT;
1148 procmsg_msginfo_free( dummyinfo );
1154 for (i = 0; i < attach_files->len; i++) {
1155 file = g_ptr_array_index(attach_files, i);
1156 compose_attach_append(compose, file, file, NULL, NULL);
1160 compose_show_first_last_header(compose, TRUE);
1162 /* Set save folder */
1163 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1164 gchar *folderidentifier;
1166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1167 folderidentifier = folder_item_get_identifier(item);
1168 compose_set_save_to(compose, folderidentifier);
1169 g_free(folderidentifier);
1172 /* Place cursor according to provided input (mfield) */
1174 case NO_FIELD_PRESENT:
1175 if (compose->header_last)
1176 gtk_widget_grab_focus(compose->header_last->entry);
1178 case TO_FIELD_PRESENT:
1179 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1181 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1184 gtk_widget_grab_focus(compose->subject_entry);
1186 case SUBJECT_FIELD_PRESENT:
1187 textview = GTK_TEXT_VIEW(compose->text);
1190 textbuf = gtk_text_view_get_buffer(textview);
1193 mark = gtk_text_buffer_get_insert(textbuf);
1194 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1195 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1197 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1198 * only defers where it comes to the variable body
1199 * is not null. If no body is present compose->text
1200 * will be null in which case you cannot place the
1201 * cursor inside the component so. An empty component
1202 * is therefore created before placing the cursor
1204 case BODY_FIELD_PRESENT:
1205 cursor_pos = quote_fmt_get_cursor_pos();
1206 if (cursor_pos == -1)
1207 gtk_widget_grab_focus(compose->header_last->entry);
1209 gtk_widget_grab_focus(compose->text);
1213 undo_unblock(compose->undostruct);
1215 if (prefs_common.auto_exteditor)
1216 compose_exec_ext_editor(compose);
1218 compose->draft_timeout_tag = -1;
1219 SCROLL_TO_CURSOR(compose);
1221 compose->modified = FALSE;
1222 compose_set_title(compose);
1224 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1229 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1230 gboolean override_pref, const gchar *system)
1232 const gchar *privacy = NULL;
1234 cm_return_if_fail(compose != NULL);
1235 cm_return_if_fail(account != NULL);
1237 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1242 else if (account->default_privacy_system
1243 && strlen(account->default_privacy_system)) {
1244 privacy = account->default_privacy_system;
1246 GSList *privacy_avail = privacy_get_system_ids();
1247 if (privacy_avail && g_slist_length(privacy_avail)) {
1248 privacy = (gchar *)(privacy_avail->data);
1251 if (privacy != NULL) {
1253 g_free(compose->privacy_system);
1254 compose->privacy_system = NULL;
1256 if (compose->privacy_system == NULL)
1257 compose->privacy_system = g_strdup(privacy);
1258 else if (*(compose->privacy_system) == '\0') {
1259 g_free(compose->privacy_system);
1260 compose->privacy_system = g_strdup(privacy);
1262 compose_update_privacy_system_menu_item(compose, FALSE);
1263 compose_use_encryption(compose, TRUE);
1267 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1269 const gchar *privacy = NULL;
1273 else if (account->default_privacy_system
1274 && strlen(account->default_privacy_system)) {
1275 privacy = account->default_privacy_system;
1277 GSList *privacy_avail = privacy_get_system_ids();
1278 if (privacy_avail && g_slist_length(privacy_avail)) {
1279 privacy = (gchar *)(privacy_avail->data);
1283 if (privacy != NULL) {
1285 g_free(compose->privacy_system);
1286 compose->privacy_system = NULL;
1288 if (compose->privacy_system == NULL)
1289 compose->privacy_system = g_strdup(privacy);
1290 compose_update_privacy_system_menu_item(compose, FALSE);
1291 compose_use_signing(compose, TRUE);
1295 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1299 Compose *compose = NULL;
1301 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1303 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1304 cm_return_val_if_fail(msginfo != NULL, NULL);
1306 list_len = g_slist_length(msginfo_list);
1310 case COMPOSE_REPLY_TO_ADDRESS:
1311 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1312 FALSE, prefs_common.default_reply_list, FALSE, body);
1314 case COMPOSE_REPLY_WITH_QUOTE:
1315 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1316 FALSE, prefs_common.default_reply_list, FALSE, body);
1318 case COMPOSE_REPLY_WITHOUT_QUOTE:
1319 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1320 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1322 case COMPOSE_REPLY_TO_SENDER:
1323 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1324 FALSE, FALSE, TRUE, body);
1326 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1327 compose = compose_followup_and_reply_to(msginfo,
1328 COMPOSE_QUOTE_CHECK,
1329 FALSE, FALSE, body);
1331 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1332 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1333 FALSE, FALSE, TRUE, body);
1335 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1336 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1337 FALSE, FALSE, TRUE, NULL);
1339 case COMPOSE_REPLY_TO_ALL:
1340 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1341 TRUE, FALSE, FALSE, body);
1343 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1344 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1345 TRUE, FALSE, FALSE, body);
1347 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1348 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1349 TRUE, FALSE, FALSE, NULL);
1351 case COMPOSE_REPLY_TO_LIST:
1352 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1353 FALSE, TRUE, FALSE, body);
1355 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1356 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1357 FALSE, TRUE, FALSE, body);
1359 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1360 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1361 FALSE, TRUE, FALSE, NULL);
1363 case COMPOSE_FORWARD:
1364 if (prefs_common.forward_as_attachment) {
1365 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1368 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1372 case COMPOSE_FORWARD_INLINE:
1373 /* check if we reply to more than one Message */
1374 if (list_len == 1) {
1375 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1378 /* more messages FALL THROUGH */
1379 case COMPOSE_FORWARD_AS_ATTACH:
1380 compose = compose_forward_multiple(NULL, msginfo_list);
1382 case COMPOSE_REDIRECT:
1383 compose = compose_redirect(NULL, msginfo, FALSE);
1386 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1389 if (compose == NULL) {
1390 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1394 compose->rmode = mode;
1395 switch (compose->rmode) {
1397 case COMPOSE_REPLY_WITH_QUOTE:
1398 case COMPOSE_REPLY_WITHOUT_QUOTE:
1399 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1400 debug_print("reply mode Normal\n");
1401 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1402 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1404 case COMPOSE_REPLY_TO_SENDER:
1405 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1406 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1407 debug_print("reply mode Sender\n");
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1410 case COMPOSE_REPLY_TO_ALL:
1411 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1412 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1413 debug_print("reply mode All\n");
1414 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1416 case COMPOSE_REPLY_TO_LIST:
1417 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1418 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1419 debug_print("reply mode List\n");
1420 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1422 case COMPOSE_REPLY_TO_ADDRESS:
1423 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1431 static Compose *compose_reply(MsgInfo *msginfo,
1432 ComposeQuoteMode quote_mode,
1438 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1439 to_sender, FALSE, body);
1442 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1443 ComposeQuoteMode quote_mode,
1448 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1449 to_sender, TRUE, body);
1452 static void compose_extract_original_charset(Compose *compose)
1454 MsgInfo *info = NULL;
1455 if (compose->replyinfo) {
1456 info = compose->replyinfo;
1457 } else if (compose->fwdinfo) {
1458 info = compose->fwdinfo;
1459 } else if (compose->targetinfo) {
1460 info = compose->targetinfo;
1463 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1464 MimeInfo *partinfo = mimeinfo;
1465 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1466 partinfo = procmime_mimeinfo_next(partinfo);
1468 compose->orig_charset =
1469 g_strdup(procmime_mimeinfo_get_parameter(
1470 partinfo, "charset"));
1472 procmime_mimeinfo_free_all(mimeinfo);
1476 #define SIGNAL_BLOCK(buffer) { \
1477 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1478 G_CALLBACK(compose_changed_cb), \
1480 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1481 G_CALLBACK(text_inserted), \
1485 #define SIGNAL_UNBLOCK(buffer) { \
1486 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1487 G_CALLBACK(compose_changed_cb), \
1489 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1490 G_CALLBACK(text_inserted), \
1494 static Compose *compose_generic_reply(MsgInfo *msginfo,
1495 ComposeQuoteMode quote_mode,
1496 gboolean to_all, gboolean to_ml,
1498 gboolean followup_and_reply_to,
1502 PrefsAccount *account = NULL;
1503 GtkTextView *textview;
1504 GtkTextBuffer *textbuf;
1505 gboolean quote = FALSE;
1506 const gchar *qmark = NULL;
1507 const gchar *body_fmt = NULL;
1508 gchar *s_system = NULL;
1510 cm_return_val_if_fail(msginfo != NULL, NULL);
1511 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1513 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1515 cm_return_val_if_fail(account != NULL, NULL);
1517 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1519 compose->updating = TRUE;
1521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1522 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1524 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1525 if (!compose->replyinfo)
1526 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1528 compose_extract_original_charset(compose);
1530 if (msginfo->folder && msginfo->folder->ret_rcpt)
1531 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1533 /* Set save folder */
1534 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1535 gchar *folderidentifier;
1537 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1538 folderidentifier = folder_item_get_identifier(msginfo->folder);
1539 compose_set_save_to(compose, folderidentifier);
1540 g_free(folderidentifier);
1543 if (compose_parse_header(compose, msginfo) < 0) {
1544 compose->updating = FALSE;
1545 compose_destroy(compose);
1549 /* override from name according to folder properties */
1550 if (msginfo->folder && msginfo->folder->prefs &&
1551 msginfo->folder->prefs->reply_with_format &&
1552 msginfo->folder->prefs->reply_override_from_format &&
1553 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1558 /* decode \-escape sequences in the internal representation of the quote format */
1559 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1560 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1563 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1564 compose->gtkaspell);
1566 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1568 quote_fmt_scan_string(tmp);
1571 buf = quote_fmt_get_buffer();
1573 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1575 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1576 quote_fmt_reset_vartable();
1581 textview = (GTK_TEXT_VIEW(compose->text));
1582 textbuf = gtk_text_view_get_buffer(textview);
1583 compose_create_tags(textview, compose);
1585 undo_block(compose->undostruct);
1587 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1588 gtkaspell_block_check(compose->gtkaspell);
1591 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1592 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1593 /* use the reply format of folder (if enabled), or the account's one
1594 (if enabled) or fallback to the global reply format, which is always
1595 enabled (even if empty), and use the relevant quotemark */
1597 if (msginfo->folder && msginfo->folder->prefs &&
1598 msginfo->folder->prefs->reply_with_format) {
1599 qmark = msginfo->folder->prefs->reply_quotemark;
1600 body_fmt = msginfo->folder->prefs->reply_body_format;
1602 } else if (account->reply_with_format) {
1603 qmark = account->reply_quotemark;
1604 body_fmt = account->reply_body_format;
1607 qmark = prefs_common.quotemark;
1608 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1609 body_fmt = gettext(prefs_common.quotefmt);
1616 /* empty quotemark is not allowed */
1617 if (qmark == NULL || *qmark == '\0')
1619 compose_quote_fmt(compose, compose->replyinfo,
1620 body_fmt, qmark, body, FALSE, TRUE,
1621 _("The body of the \"Reply\" template has an error at line %d."));
1622 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1623 quote_fmt_reset_vartable();
1626 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1627 compose_force_encryption(compose, account, FALSE, s_system);
1630 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1631 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1632 compose_force_signing(compose, account, s_system);
1636 SIGNAL_BLOCK(textbuf);
1638 if (account->auto_sig)
1639 compose_insert_sig(compose, FALSE);
1641 compose_wrap_all(compose);
1644 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1645 gtkaspell_highlight_all(compose->gtkaspell);
1646 gtkaspell_unblock_check(compose->gtkaspell);
1648 SIGNAL_UNBLOCK(textbuf);
1650 gtk_widget_grab_focus(compose->text);
1652 undo_unblock(compose->undostruct);
1654 if (prefs_common.auto_exteditor)
1655 compose_exec_ext_editor(compose);
1657 compose->modified = FALSE;
1658 compose_set_title(compose);
1660 compose->updating = FALSE;
1661 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1662 SCROLL_TO_CURSOR(compose);
1664 if (compose->deferred_destroy) {
1665 compose_destroy(compose);
1673 #define INSERT_FW_HEADER(var, hdr) \
1674 if (msginfo->var && *msginfo->var) { \
1675 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1676 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1677 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1680 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1681 gboolean as_attach, const gchar *body,
1682 gboolean no_extedit,
1686 GtkTextView *textview;
1687 GtkTextBuffer *textbuf;
1688 gint cursor_pos = -1;
1691 cm_return_val_if_fail(msginfo != NULL, NULL);
1692 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1695 !(account = compose_guess_forward_account_from_msginfo
1697 account = cur_account;
1699 if (!prefs_common.forward_as_attachment)
1700 mode = COMPOSE_FORWARD_INLINE;
1702 mode = COMPOSE_FORWARD;
1703 compose = compose_create(account, msginfo->folder, mode, batch);
1705 compose->updating = TRUE;
1706 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1707 if (!compose->fwdinfo)
1708 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1710 compose_extract_original_charset(compose);
1712 if (msginfo->subject && *msginfo->subject) {
1713 gchar *buf, *buf2, *p;
1715 buf = p = g_strdup(msginfo->subject);
1716 p += subject_get_prefix_length(p);
1717 memmove(buf, p, strlen(p) + 1);
1719 buf2 = g_strdup_printf("Fw: %s", buf);
1720 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1726 /* override from name according to folder properties */
1727 if (msginfo->folder && msginfo->folder->prefs &&
1728 msginfo->folder->prefs->forward_with_format &&
1729 msginfo->folder->prefs->forward_override_from_format &&
1730 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1734 MsgInfo *full_msginfo = NULL;
1737 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1739 full_msginfo = procmsg_msginfo_copy(msginfo);
1741 /* decode \-escape sequences in the internal representation of the quote format */
1742 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1743 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1746 gtkaspell_block_check(compose->gtkaspell);
1747 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1748 compose->gtkaspell);
1750 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1752 quote_fmt_scan_string(tmp);
1755 buf = quote_fmt_get_buffer();
1757 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1759 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1760 quote_fmt_reset_vartable();
1763 procmsg_msginfo_free(full_msginfo);
1766 textview = GTK_TEXT_VIEW(compose->text);
1767 textbuf = gtk_text_view_get_buffer(textview);
1768 compose_create_tags(textview, compose);
1770 undo_block(compose->undostruct);
1774 msgfile = procmsg_get_message_file(msginfo);
1775 if (!is_file_exist(msgfile))
1776 g_warning("%s: file not exist\n", msgfile);
1778 compose_attach_append(compose, msgfile, msgfile,
1779 "message/rfc822", NULL);
1783 const gchar *qmark = NULL;
1784 const gchar *body_fmt = NULL;
1785 MsgInfo *full_msginfo;
1787 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1788 body_fmt = gettext(prefs_common.fw_quotefmt);
1792 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1794 full_msginfo = procmsg_msginfo_copy(msginfo);
1796 /* use the forward format of folder (if enabled), or the account's one
1797 (if enabled) or fallback to the global forward format, which is always
1798 enabled (even if empty), and use the relevant quotemark */
1799 if (msginfo->folder && msginfo->folder->prefs &&
1800 msginfo->folder->prefs->forward_with_format) {
1801 qmark = msginfo->folder->prefs->forward_quotemark;
1802 body_fmt = msginfo->folder->prefs->forward_body_format;
1804 } else if (account->forward_with_format) {
1805 qmark = account->forward_quotemark;
1806 body_fmt = account->forward_body_format;
1809 qmark = prefs_common.fw_quotemark;
1810 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1811 body_fmt = gettext(prefs_common.fw_quotefmt);
1816 /* empty quotemark is not allowed */
1817 if (qmark == NULL || *qmark == '\0')
1820 compose_quote_fmt(compose, full_msginfo,
1821 body_fmt, qmark, body, FALSE, TRUE,
1822 _("The body of the \"Forward\" template has an error at line %d."));
1823 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1824 quote_fmt_reset_vartable();
1825 compose_attach_parts(compose, msginfo);
1827 procmsg_msginfo_free(full_msginfo);
1830 SIGNAL_BLOCK(textbuf);
1832 if (account->auto_sig)
1833 compose_insert_sig(compose, FALSE);
1835 compose_wrap_all(compose);
1838 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1839 gtkaspell_highlight_all(compose->gtkaspell);
1840 gtkaspell_unblock_check(compose->gtkaspell);
1842 SIGNAL_UNBLOCK(textbuf);
1844 cursor_pos = quote_fmt_get_cursor_pos();
1845 if (cursor_pos == -1)
1846 gtk_widget_grab_focus(compose->header_last->entry);
1848 gtk_widget_grab_focus(compose->text);
1850 if (!no_extedit && prefs_common.auto_exteditor)
1851 compose_exec_ext_editor(compose);
1854 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1855 gchar *folderidentifier;
1857 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1858 folderidentifier = folder_item_get_identifier(msginfo->folder);
1859 compose_set_save_to(compose, folderidentifier);
1860 g_free(folderidentifier);
1863 undo_unblock(compose->undostruct);
1865 compose->modified = FALSE;
1866 compose_set_title(compose);
1868 compose->updating = FALSE;
1869 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1870 SCROLL_TO_CURSOR(compose);
1872 if (compose->deferred_destroy) {
1873 compose_destroy(compose);
1877 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1882 #undef INSERT_FW_HEADER
1884 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1887 GtkTextView *textview;
1888 GtkTextBuffer *textbuf;
1892 gboolean single_mail = TRUE;
1894 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1896 if (g_slist_length(msginfo_list) > 1)
1897 single_mail = FALSE;
1899 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1900 if (((MsgInfo *)msginfo->data)->folder == NULL)
1903 /* guess account from first selected message */
1905 !(account = compose_guess_forward_account_from_msginfo
1906 (msginfo_list->data)))
1907 account = cur_account;
1909 cm_return_val_if_fail(account != NULL, NULL);
1911 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1912 if (msginfo->data) {
1913 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1914 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1918 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1919 g_warning("no msginfo_list");
1923 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1925 compose->updating = TRUE;
1927 /* override from name according to folder properties */
1928 if (msginfo_list->data) {
1929 MsgInfo *msginfo = msginfo_list->data;
1931 if (msginfo->folder && msginfo->folder->prefs &&
1932 msginfo->folder->prefs->forward_with_format &&
1933 msginfo->folder->prefs->forward_override_from_format &&
1934 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1939 /* decode \-escape sequences in the internal representation of the quote format */
1940 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1941 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1944 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1945 compose->gtkaspell);
1947 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1949 quote_fmt_scan_string(tmp);
1952 buf = quote_fmt_get_buffer();
1954 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1956 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1957 quote_fmt_reset_vartable();
1963 textview = GTK_TEXT_VIEW(compose->text);
1964 textbuf = gtk_text_view_get_buffer(textview);
1965 compose_create_tags(textview, compose);
1967 undo_block(compose->undostruct);
1968 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1969 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1971 if (!is_file_exist(msgfile))
1972 g_warning("%s: file not exist\n", msgfile);
1974 compose_attach_append(compose, msgfile, msgfile,
1975 "message/rfc822", NULL);
1980 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1981 if (info->subject && *info->subject) {
1982 gchar *buf, *buf2, *p;
1984 buf = p = g_strdup(info->subject);
1985 p += subject_get_prefix_length(p);
1986 memmove(buf, p, strlen(p) + 1);
1988 buf2 = g_strdup_printf("Fw: %s", buf);
1989 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1995 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1996 _("Fw: multiple emails"));
1999 SIGNAL_BLOCK(textbuf);
2001 if (account->auto_sig)
2002 compose_insert_sig(compose, FALSE);
2004 compose_wrap_all(compose);
2006 SIGNAL_UNBLOCK(textbuf);
2008 gtk_text_buffer_get_start_iter(textbuf, &iter);
2009 gtk_text_buffer_place_cursor(textbuf, &iter);
2011 gtk_widget_grab_focus(compose->header_last->entry);
2012 undo_unblock(compose->undostruct);
2013 compose->modified = FALSE;
2014 compose_set_title(compose);
2016 compose->updating = FALSE;
2017 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2018 SCROLL_TO_CURSOR(compose);
2020 if (compose->deferred_destroy) {
2021 compose_destroy(compose);
2025 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2030 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2032 GtkTextIter start = *iter;
2033 GtkTextIter end_iter;
2034 int start_pos = gtk_text_iter_get_offset(&start);
2036 if (!compose->account->sig_sep)
2039 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2040 start_pos+strlen(compose->account->sig_sep));
2042 /* check sig separator */
2043 str = gtk_text_iter_get_text(&start, &end_iter);
2044 if (!strcmp(str, compose->account->sig_sep)) {
2046 /* check end of line (\n) */
2047 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2048 start_pos+strlen(compose->account->sig_sep));
2049 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2050 start_pos+strlen(compose->account->sig_sep)+1);
2051 tmp = gtk_text_iter_get_text(&start, &end_iter);
2052 if (!strcmp(tmp,"\n")) {
2064 static void compose_colorize_signature(Compose *compose)
2066 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2068 GtkTextIter end_iter;
2069 gtk_text_buffer_get_start_iter(buffer, &iter);
2070 while (gtk_text_iter_forward_line(&iter))
2071 if (compose_is_sig_separator(compose, buffer, &iter)) {
2072 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2073 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2077 #define BLOCK_WRAP() { \
2078 prev_autowrap = compose->autowrap; \
2079 buffer = gtk_text_view_get_buffer( \
2080 GTK_TEXT_VIEW(compose->text)); \
2081 compose->autowrap = FALSE; \
2083 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2084 G_CALLBACK(compose_changed_cb), \
2086 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2087 G_CALLBACK(text_inserted), \
2090 #define UNBLOCK_WRAP() { \
2091 compose->autowrap = prev_autowrap; \
2092 if (compose->autowrap) { \
2093 gint old = compose->draft_timeout_tag; \
2094 compose->draft_timeout_tag = -2; \
2095 compose_wrap_all(compose); \
2096 compose->draft_timeout_tag = old; \
2099 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2100 G_CALLBACK(compose_changed_cb), \
2102 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2103 G_CALLBACK(text_inserted), \
2107 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2109 Compose *compose = NULL;
2110 PrefsAccount *account = NULL;
2111 GtkTextView *textview;
2112 GtkTextBuffer *textbuf;
2116 gchar buf[BUFFSIZE];
2117 gboolean use_signing = FALSE;
2118 gboolean use_encryption = FALSE;
2119 gchar *privacy_system = NULL;
2120 int priority = PRIORITY_NORMAL;
2121 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2122 gboolean autowrap = prefs_common.autowrap;
2123 gboolean autoindent = prefs_common.auto_indent;
2125 cm_return_val_if_fail(msginfo != NULL, NULL);
2126 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2128 if (compose_put_existing_to_front(msginfo)) {
2132 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2133 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2134 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2135 gchar queueheader_buf[BUFFSIZE];
2138 /* Select Account from queue headers */
2139 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2140 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2141 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2142 account = account_find_from_id(id);
2144 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2145 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2146 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2147 account = account_find_from_id(id);
2149 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2150 sizeof(queueheader_buf), "NAID:")) {
2151 id = atoi(&queueheader_buf[strlen("NAID:")]);
2152 account = account_find_from_id(id);
2154 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2155 sizeof(queueheader_buf), "MAID:")) {
2156 id = atoi(&queueheader_buf[strlen("MAID:")]);
2157 account = account_find_from_id(id);
2159 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2160 sizeof(queueheader_buf), "S:")) {
2161 account = account_find_from_address(queueheader_buf, FALSE);
2163 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2164 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2165 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2166 use_signing = param;
2169 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2170 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2171 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2172 use_signing = param;
2175 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2177 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2178 use_encryption = param;
2180 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2181 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2182 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2183 use_encryption = param;
2185 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2186 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2187 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2190 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2191 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2192 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2195 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2196 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2197 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2201 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Priority: ")) {
2205 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "RMID:")) {
2210 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2211 if (tokens[0] && tokens[1] && tokens[2]) {
2212 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2213 if (orig_item != NULL) {
2214 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2219 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2220 sizeof(queueheader_buf), "FMID:")) {
2221 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2222 if (tokens[0] && tokens[1] && tokens[2]) {
2223 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2224 if (orig_item != NULL) {
2225 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2231 account = msginfo->folder->folder->account;
2234 if (!account && prefs_common.reedit_account_autosel) {
2235 gchar from[BUFFSIZE];
2236 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2237 extract_address(from);
2238 account = account_find_from_address(from, FALSE);
2242 account = cur_account;
2244 cm_return_val_if_fail(account != NULL, NULL);
2246 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2248 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2249 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2250 compose->autowrap = autowrap;
2251 compose->replyinfo = replyinfo;
2252 compose->fwdinfo = fwdinfo;
2254 compose->updating = TRUE;
2255 compose->priority = priority;
2257 if (privacy_system != NULL) {
2258 compose->privacy_system = privacy_system;
2259 compose_use_signing(compose, use_signing);
2260 compose_use_encryption(compose, use_encryption);
2261 compose_update_privacy_system_menu_item(compose, FALSE);
2263 activate_privacy_system(compose, account, FALSE);
2266 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2268 compose_extract_original_charset(compose);
2270 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2271 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2272 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2273 gchar queueheader_buf[BUFFSIZE];
2275 /* Set message save folder */
2276 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2277 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2278 compose_set_save_to(compose, &queueheader_buf[4]);
2280 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2281 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2283 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2288 if (compose_parse_header(compose, msginfo) < 0) {
2289 compose->updating = FALSE;
2290 compose_destroy(compose);
2293 compose_reedit_set_entry(compose, msginfo);
2295 textview = GTK_TEXT_VIEW(compose->text);
2296 textbuf = gtk_text_view_get_buffer(textview);
2297 compose_create_tags(textview, compose);
2299 mark = gtk_text_buffer_get_insert(textbuf);
2300 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2302 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2303 G_CALLBACK(compose_changed_cb),
2306 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2307 fp = procmime_get_first_encrypted_text_content(msginfo);
2309 compose_force_encryption(compose, account, TRUE, NULL);
2312 fp = procmime_get_first_text_content(msginfo);
2315 g_warning("Can't get text part\n");
2319 gboolean prev_autowrap = compose->autowrap;
2320 GtkTextBuffer *buffer = textbuf;
2322 while (fgets(buf, sizeof(buf), fp) != NULL) {
2324 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2330 compose_attach_parts(compose, msginfo);
2332 compose_colorize_signature(compose);
2334 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2335 G_CALLBACK(compose_changed_cb),
2338 gtk_widget_grab_focus(compose->text);
2340 if (prefs_common.auto_exteditor) {
2341 compose_exec_ext_editor(compose);
2343 compose->modified = FALSE;
2344 compose_set_title(compose);
2346 compose->updating = FALSE;
2347 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2348 SCROLL_TO_CURSOR(compose);
2350 if (compose->deferred_destroy) {
2351 compose_destroy(compose);
2355 compose->sig_str = account_get_signature_str(compose->account);
2357 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2362 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2369 cm_return_val_if_fail(msginfo != NULL, NULL);
2372 account = account_get_reply_account(msginfo,
2373 prefs_common.reply_account_autosel);
2374 cm_return_val_if_fail(account != NULL, NULL);
2376 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2378 compose->updating = TRUE;
2380 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2381 compose->replyinfo = NULL;
2382 compose->fwdinfo = NULL;
2384 compose_show_first_last_header(compose, TRUE);
2386 gtk_widget_grab_focus(compose->header_last->entry);
2388 filename = procmsg_get_message_file(msginfo);
2390 if (filename == NULL) {
2391 compose->updating = FALSE;
2392 compose_destroy(compose);
2397 compose->redirect_filename = filename;
2399 /* Set save folder */
2400 item = msginfo->folder;
2401 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2402 gchar *folderidentifier;
2404 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2405 folderidentifier = folder_item_get_identifier(item);
2406 compose_set_save_to(compose, folderidentifier);
2407 g_free(folderidentifier);
2410 compose_attach_parts(compose, msginfo);
2412 if (msginfo->subject)
2413 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2415 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2417 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2418 _("The body of the \"Redirect\" template has an error at line %d."));
2419 quote_fmt_reset_vartable();
2420 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2422 compose_colorize_signature(compose);
2425 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2426 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2427 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2429 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2430 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2431 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2432 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2433 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2435 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2436 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2438 if (compose->toolbar->draft_btn)
2439 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2440 if (compose->toolbar->insert_btn)
2441 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2442 if (compose->toolbar->attach_btn)
2443 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2444 if (compose->toolbar->sig_btn)
2445 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2446 if (compose->toolbar->exteditor_btn)
2447 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2448 if (compose->toolbar->linewrap_current_btn)
2449 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2450 if (compose->toolbar->linewrap_all_btn)
2451 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2453 compose->modified = FALSE;
2454 compose_set_title(compose);
2455 compose->updating = FALSE;
2456 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2457 SCROLL_TO_CURSOR(compose);
2459 if (compose->deferred_destroy) {
2460 compose_destroy(compose);
2464 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2469 GList *compose_get_compose_list(void)
2471 return compose_list;
2474 void compose_entry_append(Compose *compose, const gchar *address,
2475 ComposeEntryType type, ComposePrefType pref_type)
2477 const gchar *header;
2479 gboolean in_quote = FALSE;
2480 if (!address || *address == '\0') return;
2487 header = N_("Bcc:");
2489 case COMPOSE_REPLYTO:
2490 header = N_("Reply-To:");
2492 case COMPOSE_NEWSGROUPS:
2493 header = N_("Newsgroups:");
2495 case COMPOSE_FOLLOWUPTO:
2496 header = N_( "Followup-To:");
2498 case COMPOSE_INREPLYTO:
2499 header = N_( "In-Reply-To:");
2506 header = prefs_common_translated_header_name(header);
2508 cur = begin = (gchar *)address;
2510 /* we separate the line by commas, but not if we're inside a quoted
2512 while (*cur != '\0') {
2514 in_quote = !in_quote;
2515 if (*cur == ',' && !in_quote) {
2516 gchar *tmp = g_strdup(begin);
2518 tmp[cur-begin]='\0';
2521 while (*tmp == ' ' || *tmp == '\t')
2523 compose_add_header_entry(compose, header, tmp, pref_type);
2530 gchar *tmp = g_strdup(begin);
2532 tmp[cur-begin]='\0';
2535 while (*tmp == ' ' || *tmp == '\t')
2537 compose_add_header_entry(compose, header, tmp, pref_type);
2542 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2544 #if !GTK_CHECK_VERSION(3, 0, 0)
2545 static GdkColor yellow;
2546 static GdkColor black;
2547 static gboolean yellow_initialised = FALSE;
2549 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2550 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2555 #if !GTK_CHECK_VERSION(3, 0, 0)
2556 if (!yellow_initialised) {
2557 gdk_color_parse("#f5f6be", &yellow);
2558 gdk_color_parse("#000000", &black);
2559 yellow_initialised = gdk_colormap_alloc_color(
2560 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2561 yellow_initialised &= gdk_colormap_alloc_color(
2562 gdk_colormap_get_system(), &black, FALSE, TRUE);
2566 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2567 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2568 if (gtk_entry_get_text(entry) &&
2569 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2570 #if !GTK_CHECK_VERSION(3, 0, 0)
2571 if (yellow_initialised) {
2573 gtk_widget_modify_base(
2574 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2575 GTK_STATE_NORMAL, &yellow);
2576 gtk_widget_modify_text(
2577 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2578 GTK_STATE_NORMAL, &black);
2579 #if !GTK_CHECK_VERSION(3, 0, 0)
2586 void compose_toolbar_cb(gint action, gpointer data)
2588 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2589 Compose *compose = (Compose*)toolbar_item->parent;
2591 cm_return_if_fail(compose != NULL);
2595 compose_send_cb(NULL, compose);
2598 compose_send_later_cb(NULL, compose);
2601 compose_draft(compose, COMPOSE_QUIT_EDITING);
2604 compose_insert_file_cb(NULL, compose);
2607 compose_attach_cb(NULL, compose);
2610 compose_insert_sig(compose, FALSE);
2613 compose_ext_editor_cb(NULL, compose);
2615 case A_LINEWRAP_CURRENT:
2616 compose_beautify_paragraph(compose, NULL, TRUE);
2618 case A_LINEWRAP_ALL:
2619 compose_wrap_all_full(compose, TRUE);
2622 compose_address_cb(NULL, compose);
2625 case A_CHECK_SPELLING:
2626 compose_check_all(NULL, compose);
2634 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2639 gchar *subject = NULL;
2643 gchar **attach = NULL;
2644 gchar *inreplyto = NULL;
2645 MailField mfield = NO_FIELD_PRESENT;
2647 /* get mailto parts but skip from */
2648 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2651 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2652 mfield = TO_FIELD_PRESENT;
2655 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2657 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2659 if (!g_utf8_validate (subject, -1, NULL)) {
2660 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2661 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2664 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2666 mfield = SUBJECT_FIELD_PRESENT;
2669 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2670 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2673 gboolean prev_autowrap = compose->autowrap;
2675 compose->autowrap = FALSE;
2677 mark = gtk_text_buffer_get_insert(buffer);
2678 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2680 if (!g_utf8_validate (body, -1, NULL)) {
2681 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2682 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2685 gtk_text_buffer_insert(buffer, &iter, body, -1);
2687 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2689 compose->autowrap = prev_autowrap;
2690 if (compose->autowrap)
2691 compose_wrap_all(compose);
2692 mfield = BODY_FIELD_PRESENT;
2696 gint i = 0, att = 0;
2697 gchar *warn_files = NULL;
2698 while (attach[i] != NULL) {
2699 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2700 if (utf8_filename) {
2701 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2702 gchar *tmp = g_strdup_printf("%s%s\n",
2703 warn_files?warn_files:"",
2709 g_free(utf8_filename);
2711 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2716 alertpanel_notice(ngettext(
2717 "The following file has been attached: \n%s",
2718 "The following files have been attached: \n%s", att), warn_files);
2723 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2736 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2738 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2739 {"Cc:", NULL, TRUE},
2740 {"References:", NULL, FALSE},
2741 {"Bcc:", NULL, TRUE},
2742 {"Newsgroups:", NULL, TRUE},
2743 {"Followup-To:", NULL, TRUE},
2744 {"List-Post:", NULL, FALSE},
2745 {"X-Priority:", NULL, FALSE},
2746 {NULL, NULL, FALSE}};
2762 cm_return_val_if_fail(msginfo != NULL, -1);
2764 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2765 procheader_get_header_fields(fp, hentry);
2768 if (hentry[H_REPLY_TO].body != NULL) {
2769 if (hentry[H_REPLY_TO].body[0] != '\0') {
2771 conv_unmime_header(hentry[H_REPLY_TO].body,
2774 g_free(hentry[H_REPLY_TO].body);
2775 hentry[H_REPLY_TO].body = NULL;
2777 if (hentry[H_CC].body != NULL) {
2778 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2779 g_free(hentry[H_CC].body);
2780 hentry[H_CC].body = NULL;
2782 if (hentry[H_REFERENCES].body != NULL) {
2783 if (compose->mode == COMPOSE_REEDIT)
2784 compose->references = hentry[H_REFERENCES].body;
2786 compose->references = compose_parse_references
2787 (hentry[H_REFERENCES].body, msginfo->msgid);
2788 g_free(hentry[H_REFERENCES].body);
2790 hentry[H_REFERENCES].body = NULL;
2792 if (hentry[H_BCC].body != NULL) {
2793 if (compose->mode == COMPOSE_REEDIT)
2795 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2796 g_free(hentry[H_BCC].body);
2797 hentry[H_BCC].body = NULL;
2799 if (hentry[H_NEWSGROUPS].body != NULL) {
2800 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2801 hentry[H_NEWSGROUPS].body = NULL;
2803 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2804 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2805 compose->followup_to =
2806 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2809 g_free(hentry[H_FOLLOWUP_TO].body);
2810 hentry[H_FOLLOWUP_TO].body = NULL;
2812 if (hentry[H_LIST_POST].body != NULL) {
2813 gchar *to = NULL, *start = NULL;
2815 extract_address(hentry[H_LIST_POST].body);
2816 if (hentry[H_LIST_POST].body[0] != '\0') {
2817 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2819 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2820 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2823 g_free(compose->ml_post);
2824 compose->ml_post = to;
2827 g_free(hentry[H_LIST_POST].body);
2828 hentry[H_LIST_POST].body = NULL;
2831 /* CLAWS - X-Priority */
2832 if (compose->mode == COMPOSE_REEDIT)
2833 if (hentry[H_X_PRIORITY].body != NULL) {
2836 priority = atoi(hentry[H_X_PRIORITY].body);
2837 g_free(hentry[H_X_PRIORITY].body);
2839 hentry[H_X_PRIORITY].body = NULL;
2841 if (priority < PRIORITY_HIGHEST ||
2842 priority > PRIORITY_LOWEST)
2843 priority = PRIORITY_NORMAL;
2845 compose->priority = priority;
2848 if (compose->mode == COMPOSE_REEDIT) {
2849 if (msginfo->inreplyto && *msginfo->inreplyto)
2850 compose->inreplyto = g_strdup(msginfo->inreplyto);
2854 if (msginfo->msgid && *msginfo->msgid)
2855 compose->inreplyto = g_strdup(msginfo->msgid);
2857 if (!compose->references) {
2858 if (msginfo->msgid && *msginfo->msgid) {
2859 if (msginfo->inreplyto && *msginfo->inreplyto)
2860 compose->references =
2861 g_strdup_printf("<%s>\n\t<%s>",
2865 compose->references =
2866 g_strconcat("<", msginfo->msgid, ">",
2868 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2869 compose->references =
2870 g_strconcat("<", msginfo->inreplyto, ">",
2878 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2880 GSList *ref_id_list, *cur;
2884 ref_id_list = references_list_append(NULL, ref);
2885 if (!ref_id_list) return NULL;
2886 if (msgid && *msgid)
2887 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2892 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2893 /* "<" + Message-ID + ">" + CR+LF+TAB */
2894 len += strlen((gchar *)cur->data) + 5;
2896 if (len > MAX_REFERENCES_LEN) {
2897 /* remove second message-ID */
2898 if (ref_id_list && ref_id_list->next &&
2899 ref_id_list->next->next) {
2900 g_free(ref_id_list->next->data);
2901 ref_id_list = g_slist_remove
2902 (ref_id_list, ref_id_list->next->data);
2904 slist_free_strings(ref_id_list);
2905 g_slist_free(ref_id_list);
2912 new_ref = g_string_new("");
2913 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2914 if (new_ref->len > 0)
2915 g_string_append(new_ref, "\n\t");
2916 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2919 slist_free_strings(ref_id_list);
2920 g_slist_free(ref_id_list);
2922 new_ref_str = new_ref->str;
2923 g_string_free(new_ref, FALSE);
2928 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2929 const gchar *fmt, const gchar *qmark,
2930 const gchar *body, gboolean rewrap,
2931 gboolean need_unescape,
2932 const gchar *err_msg)
2934 MsgInfo* dummyinfo = NULL;
2935 gchar *quote_str = NULL;
2937 gboolean prev_autowrap;
2938 const gchar *trimmed_body = body;
2939 gint cursor_pos = -1;
2940 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2941 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2946 SIGNAL_BLOCK(buffer);
2949 dummyinfo = compose_msginfo_new_from_compose(compose);
2950 msginfo = dummyinfo;
2953 if (qmark != NULL) {
2955 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2956 compose->gtkaspell);
2958 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2960 quote_fmt_scan_string(qmark);
2963 buf = quote_fmt_get_buffer();
2965 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2967 Xstrdup_a(quote_str, buf, goto error)
2970 if (fmt && *fmt != '\0') {
2973 while (*trimmed_body == '\n')
2977 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2978 compose->gtkaspell);
2980 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2982 if (need_unescape) {
2985 /* decode \-escape sequences in the internal representation of the quote format */
2986 tmp = g_malloc(strlen(fmt)+1);
2987 pref_get_unescaped_pref(tmp, fmt);
2988 quote_fmt_scan_string(tmp);
2992 quote_fmt_scan_string(fmt);
2996 buf = quote_fmt_get_buffer();
2998 gint line = quote_fmt_get_line();
2999 alertpanel_error(err_msg, line);
3005 prev_autowrap = compose->autowrap;
3006 compose->autowrap = FALSE;
3008 mark = gtk_text_buffer_get_insert(buffer);
3009 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3010 if (g_utf8_validate(buf, -1, NULL)) {
3011 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3013 gchar *tmpout = NULL;
3014 tmpout = conv_codeset_strdup
3015 (buf, conv_get_locale_charset_str_no_utf8(),
3017 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3019 tmpout = g_malloc(strlen(buf)*2+1);
3020 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3022 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3026 cursor_pos = quote_fmt_get_cursor_pos();
3027 if (cursor_pos == -1)
3028 cursor_pos = gtk_text_iter_get_offset(&iter);
3029 compose->set_cursor_pos = cursor_pos;
3031 gtk_text_buffer_get_start_iter(buffer, &iter);
3032 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3033 gtk_text_buffer_place_cursor(buffer, &iter);
3035 compose->autowrap = prev_autowrap;
3036 if (compose->autowrap && rewrap)
3037 compose_wrap_all(compose);
3044 SIGNAL_UNBLOCK(buffer);
3046 procmsg_msginfo_free( dummyinfo );
3051 /* if ml_post is of type addr@host and from is of type
3052 * addr-anything@host, return TRUE
3054 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3056 gchar *left_ml = NULL;
3057 gchar *right_ml = NULL;
3058 gchar *left_from = NULL;
3059 gchar *right_from = NULL;
3060 gboolean result = FALSE;
3062 if (!ml_post || !from)
3065 left_ml = g_strdup(ml_post);
3066 if (strstr(left_ml, "@")) {
3067 right_ml = strstr(left_ml, "@")+1;
3068 *(strstr(left_ml, "@")) = '\0';
3071 left_from = g_strdup(from);
3072 if (strstr(left_from, "@")) {
3073 right_from = strstr(left_from, "@")+1;
3074 *(strstr(left_from, "@")) = '\0';
3077 if (left_ml && left_from && right_ml && right_from
3078 && !strncmp(left_from, left_ml, strlen(left_ml))
3079 && !strcmp(right_from, right_ml)) {
3088 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3089 gboolean respect_default_to)
3093 if (!folder || !folder->prefs)
3096 if (respect_default_to && folder->prefs->enable_default_to) {
3097 compose_entry_append(compose, folder->prefs->default_to,
3098 COMPOSE_TO, PREF_FOLDER);
3099 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3101 if (folder->prefs->enable_default_cc)
3102 compose_entry_append(compose, folder->prefs->default_cc,
3103 COMPOSE_CC, PREF_FOLDER);
3104 if (folder->prefs->enable_default_bcc)
3105 compose_entry_append(compose, folder->prefs->default_bcc,
3106 COMPOSE_BCC, PREF_FOLDER);
3107 if (folder->prefs->enable_default_replyto)
3108 compose_entry_append(compose, folder->prefs->default_replyto,
3109 COMPOSE_REPLYTO, PREF_FOLDER);
3112 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3117 if (!compose || !msginfo)
3120 if (msginfo->subject && *msginfo->subject) {
3121 buf = p = g_strdup(msginfo->subject);
3122 p += subject_get_prefix_length(p);
3123 memmove(buf, p, strlen(p) + 1);
3125 buf2 = g_strdup_printf("Re: %s", buf);
3126 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3131 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3134 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3135 gboolean to_all, gboolean to_ml,
3137 gboolean followup_and_reply_to)
3139 GSList *cc_list = NULL;
3142 gchar *replyto = NULL;
3143 gchar *ac_email = NULL;
3145 gboolean reply_to_ml = FALSE;
3146 gboolean default_reply_to = FALSE;
3148 cm_return_if_fail(compose->account != NULL);
3149 cm_return_if_fail(msginfo != NULL);
3151 reply_to_ml = to_ml && compose->ml_post;
3153 default_reply_to = msginfo->folder &&
3154 msginfo->folder->prefs->enable_default_reply_to;
3156 if (compose->account->protocol != A_NNTP) {
3157 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3159 if (reply_to_ml && !default_reply_to) {
3161 gboolean is_subscr = is_subscription(compose->ml_post,
3164 /* normal answer to ml post with a reply-to */
3165 compose_entry_append(compose,
3167 COMPOSE_TO, PREF_ML);
3168 if (compose->replyto)
3169 compose_entry_append(compose,
3171 COMPOSE_CC, PREF_ML);
3173 /* answer to subscription confirmation */
3174 if (compose->replyto)
3175 compose_entry_append(compose,
3177 COMPOSE_TO, PREF_ML);
3178 else if (msginfo->from)
3179 compose_entry_append(compose,
3181 COMPOSE_TO, PREF_ML);
3184 else if (!(to_all || to_sender) && default_reply_to) {
3185 compose_entry_append(compose,
3186 msginfo->folder->prefs->default_reply_to,
3187 COMPOSE_TO, PREF_FOLDER);
3188 compose_entry_mark_default_to(compose,
3189 msginfo->folder->prefs->default_reply_to);
3194 Xstrdup_a(tmp1, msginfo->from, return);
3195 extract_address(tmp1);
3196 if (to_all || to_sender ||
3197 !account_find_from_address(tmp1, FALSE))
3198 compose_entry_append(compose,
3199 (compose->replyto && !to_sender)
3200 ? compose->replyto :
3201 msginfo->from ? msginfo->from : "",
3202 COMPOSE_TO, PREF_NONE);
3203 else if (!to_all && !to_sender) {
3204 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3205 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3206 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3207 if (compose->replyto) {
3208 compose_entry_append(compose,
3210 COMPOSE_TO, PREF_NONE);
3212 compose_entry_append(compose,
3213 msginfo->from ? msginfo->from : "",
3214 COMPOSE_TO, PREF_NONE);
3217 /* replying to own mail, use original recp */
3218 compose_entry_append(compose,
3219 msginfo->to ? msginfo->to : "",
3220 COMPOSE_TO, PREF_NONE);
3221 compose_entry_append(compose,
3222 msginfo->cc ? msginfo->cc : "",
3223 COMPOSE_CC, PREF_NONE);
3228 if (to_sender || (compose->followup_to &&
3229 !strncmp(compose->followup_to, "poster", 6)))
3230 compose_entry_append
3232 (compose->replyto ? compose->replyto :
3233 msginfo->from ? msginfo->from : ""),
3234 COMPOSE_TO, PREF_NONE);
3236 else if (followup_and_reply_to || to_all) {
3237 compose_entry_append
3239 (compose->replyto ? compose->replyto :
3240 msginfo->from ? msginfo->from : ""),
3241 COMPOSE_TO, PREF_NONE);
3243 compose_entry_append
3245 compose->followup_to ? compose->followup_to :
3246 compose->newsgroups ? compose->newsgroups : "",
3247 COMPOSE_NEWSGROUPS, PREF_NONE);
3250 compose_entry_append
3252 compose->followup_to ? compose->followup_to :
3253 compose->newsgroups ? compose->newsgroups : "",
3254 COMPOSE_NEWSGROUPS, PREF_NONE);
3256 compose_reply_set_subject(compose, msginfo);
3258 if (to_ml && compose->ml_post) return;
3259 if (!to_all || compose->account->protocol == A_NNTP) return;
3261 if (compose->replyto) {
3262 Xstrdup_a(replyto, compose->replyto, return);
3263 extract_address(replyto);
3265 if (msginfo->from) {
3266 Xstrdup_a(from, msginfo->from, return);
3267 extract_address(from);
3270 if (replyto && from)
3271 cc_list = address_list_append_with_comments(cc_list, from);
3272 if (to_all && msginfo->folder &&
3273 msginfo->folder->prefs->enable_default_reply_to)
3274 cc_list = address_list_append_with_comments(cc_list,
3275 msginfo->folder->prefs->default_reply_to);
3276 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3277 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3279 ac_email = g_utf8_strdown(compose->account->address, -1);
3282 for (cur = cc_list; cur != NULL; cur = cur->next) {
3283 gchar *addr = g_utf8_strdown(cur->data, -1);
3284 extract_address(addr);
3286 if (strcmp(ac_email, addr))
3287 compose_entry_append(compose, (gchar *)cur->data,
3288 COMPOSE_CC, PREF_NONE);
3290 debug_print("Cc address same as compose account's, ignoring\n");
3295 slist_free_strings(cc_list);
3296 g_slist_free(cc_list);
3302 #define SET_ENTRY(entry, str) \
3305 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3308 #define SET_ADDRESS(type, str) \
3311 compose_entry_append(compose, str, type, PREF_NONE); \
3314 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3316 cm_return_if_fail(msginfo != NULL);
3318 SET_ENTRY(subject_entry, msginfo->subject);
3319 SET_ENTRY(from_name, msginfo->from);
3320 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3321 SET_ADDRESS(COMPOSE_CC, compose->cc);
3322 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3323 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3324 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3325 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3327 compose_update_priority_menu_item(compose);
3328 compose_update_privacy_system_menu_item(compose, FALSE);
3329 compose_show_first_last_header(compose, TRUE);
3335 static void compose_insert_sig(Compose *compose, gboolean replace)
3337 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3338 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3340 GtkTextIter iter, iter_end;
3341 gint cur_pos, ins_pos;
3342 gboolean prev_autowrap;
3343 gboolean found = FALSE;
3344 gboolean exists = FALSE;
3346 cm_return_if_fail(compose->account != NULL);
3350 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3351 G_CALLBACK(compose_changed_cb),
3354 mark = gtk_text_buffer_get_insert(buffer);
3355 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3356 cur_pos = gtk_text_iter_get_offset (&iter);
3359 gtk_text_buffer_get_end_iter(buffer, &iter);
3361 exists = (compose->sig_str != NULL);
3364 GtkTextIter first_iter, start_iter, end_iter;
3366 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3368 if (!exists || compose->sig_str[0] == '\0')
3371 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3372 compose->signature_tag);
3375 /* include previous \n\n */
3376 gtk_text_iter_backward_chars(&first_iter, 1);
3377 start_iter = first_iter;
3378 end_iter = first_iter;
3380 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3381 compose->signature_tag);
3382 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3383 compose->signature_tag);
3385 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3391 g_free(compose->sig_str);
3392 compose->sig_str = account_get_signature_str(compose->account);
3394 cur_pos = gtk_text_iter_get_offset(&iter);
3396 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3397 g_free(compose->sig_str);
3398 compose->sig_str = NULL;
3400 if (compose->sig_inserted == FALSE)
3401 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3402 compose->sig_inserted = TRUE;
3404 cur_pos = gtk_text_iter_get_offset(&iter);
3405 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3407 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3408 gtk_text_iter_forward_chars(&iter, 1);
3409 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3410 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3412 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3413 cur_pos = gtk_text_buffer_get_char_count (buffer);
3416 /* put the cursor where it should be
3417 * either where the quote_fmt says, either where it was */
3418 if (compose->set_cursor_pos < 0)
3419 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3421 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3422 compose->set_cursor_pos);
3424 compose->set_cursor_pos = -1;
3425 gtk_text_buffer_place_cursor(buffer, &iter);
3426 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3427 G_CALLBACK(compose_changed_cb),
3433 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3436 GtkTextBuffer *buffer;
3439 const gchar *cur_encoding;
3440 gchar buf[BUFFSIZE];
3443 gboolean prev_autowrap;
3444 gboolean badtxt = FALSE;
3445 struct stat file_stat;
3448 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3450 /* get the size of the file we are about to insert */
3451 ret = g_stat(file, &file_stat);
3453 gchar *shortfile = g_path_get_basename(file);
3454 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3456 return COMPOSE_INSERT_NO_FILE;
3457 } else if (prefs_common.warn_large_insert == TRUE) {
3459 /* ask user for confirmation if the file is large */
3460 if (prefs_common.warn_large_insert_size < 0 ||
3461 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3465 msg = g_strdup_printf(_("You are about to insert a file of %s "
3466 "in the message body. Are you sure you want to do that?"),
3467 to_human_readable(file_stat.st_size));
3468 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3469 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3472 /* do we ask for confirmation next time? */
3473 if (aval & G_ALERTDISABLE) {
3474 /* no confirmation next time, disable feature in preferences */
3475 aval &= ~G_ALERTDISABLE;
3476 prefs_common.warn_large_insert = FALSE;
3479 /* abort file insertion if user canceled action */
3480 if (aval != G_ALERTALTERNATE) {
3481 return COMPOSE_INSERT_NO_FILE;
3487 if ((fp = g_fopen(file, "rb")) == NULL) {
3488 FILE_OP_ERROR(file, "fopen");
3489 return COMPOSE_INSERT_READ_ERROR;
3492 prev_autowrap = compose->autowrap;
3493 compose->autowrap = FALSE;
3495 text = GTK_TEXT_VIEW(compose->text);
3496 buffer = gtk_text_view_get_buffer(text);
3497 mark = gtk_text_buffer_get_insert(buffer);
3498 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3500 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3501 G_CALLBACK(text_inserted),
3504 cur_encoding = conv_get_locale_charset_str_no_utf8();
3506 while (fgets(buf, sizeof(buf), fp) != NULL) {
3509 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3510 str = g_strdup(buf);
3512 str = conv_codeset_strdup
3513 (buf, cur_encoding, CS_INTERNAL);
3516 /* strip <CR> if DOS/Windows file,
3517 replace <CR> with <LF> if Macintosh file. */
3520 if (len > 0 && str[len - 1] != '\n') {
3522 if (str[len] == '\r') str[len] = '\n';
3525 gtk_text_buffer_insert(buffer, &iter, str, -1);
3529 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3530 G_CALLBACK(text_inserted),
3532 compose->autowrap = prev_autowrap;
3533 if (compose->autowrap)
3534 compose_wrap_all(compose);
3539 return COMPOSE_INSERT_INVALID_CHARACTER;
3541 return COMPOSE_INSERT_SUCCESS;
3544 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3545 const gchar *filename,
3546 const gchar *content_type,
3547 const gchar *charset)
3555 GtkListStore *store;
3557 gboolean has_binary = FALSE;
3559 if (!is_file_exist(file)) {
3560 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3561 gboolean result = FALSE;
3562 if (file_from_uri && is_file_exist(file_from_uri)) {
3563 result = compose_attach_append(
3564 compose, file_from_uri,
3565 filename, content_type,
3568 g_free(file_from_uri);
3571 alertpanel_error("File %s doesn't exist\n", filename);
3574 if ((size = get_file_size(file)) < 0) {
3575 alertpanel_error("Can't get file size of %s\n", filename);
3579 alertpanel_error(_("File %s is empty."), filename);
3582 if ((fp = g_fopen(file, "rb")) == NULL) {
3583 alertpanel_error(_("Can't read %s."), filename);
3588 ainfo = g_new0(AttachInfo, 1);
3589 auto_ainfo = g_auto_pointer_new_with_free
3590 (ainfo, (GFreeFunc) compose_attach_info_free);
3591 ainfo->file = g_strdup(file);
3594 ainfo->content_type = g_strdup(content_type);
3595 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3597 MsgFlags flags = {0, 0};
3599 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3600 ainfo->encoding = ENC_7BIT;
3602 ainfo->encoding = ENC_8BIT;
3604 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3605 if (msginfo && msginfo->subject)
3606 name = g_strdup(msginfo->subject);
3608 name = g_path_get_basename(filename ? filename : file);
3610 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3612 procmsg_msginfo_free(msginfo);
3614 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3615 ainfo->charset = g_strdup(charset);
3616 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3618 ainfo->encoding = ENC_BASE64;
3620 name = g_path_get_basename(filename ? filename : file);
3621 ainfo->name = g_strdup(name);
3625 ainfo->content_type = procmime_get_mime_type(file);
3626 if (!ainfo->content_type) {
3627 ainfo->content_type =
3628 g_strdup("application/octet-stream");
3629 ainfo->encoding = ENC_BASE64;
3630 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3632 procmime_get_encoding_for_text_file(file, &has_binary);
3634 ainfo->encoding = ENC_BASE64;
3635 name = g_path_get_basename(filename ? filename : file);
3636 ainfo->name = g_strdup(name);
3640 if (ainfo->name != NULL
3641 && !strcmp(ainfo->name, ".")) {
3642 g_free(ainfo->name);
3646 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3647 g_free(ainfo->content_type);
3648 ainfo->content_type = g_strdup("application/octet-stream");
3649 g_free(ainfo->charset);
3650 ainfo->charset = NULL;
3653 ainfo->size = (goffset)size;
3654 size_text = to_human_readable((goffset)size);
3656 store = GTK_LIST_STORE(gtk_tree_view_get_model
3657 (GTK_TREE_VIEW(compose->attach_clist)));
3659 gtk_list_store_append(store, &iter);
3660 gtk_list_store_set(store, &iter,
3661 COL_MIMETYPE, ainfo->content_type,
3662 COL_SIZE, size_text,
3663 COL_NAME, ainfo->name,
3664 COL_CHARSET, ainfo->charset,
3666 COL_AUTODATA, auto_ainfo,
3669 g_auto_pointer_free(auto_ainfo);
3670 compose_attach_update_label(compose);
3674 static void compose_use_signing(Compose *compose, gboolean use_signing)
3676 compose->use_signing = use_signing;
3677 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3680 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3682 compose->use_encryption = use_encryption;
3683 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3686 #define NEXT_PART_NOT_CHILD(info) \
3688 node = info->node; \
3689 while (node->children) \
3690 node = g_node_last_child(node); \
3691 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3694 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3698 MimeInfo *firsttext = NULL;
3699 MimeInfo *encrypted = NULL;
3702 const gchar *partname = NULL;
3704 mimeinfo = procmime_scan_message(msginfo);
3705 if (!mimeinfo) return;
3707 if (mimeinfo->node->children == NULL) {
3708 procmime_mimeinfo_free_all(mimeinfo);
3712 /* find first content part */
3713 child = (MimeInfo *) mimeinfo->node->children->data;
3714 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3715 child = (MimeInfo *)child->node->children->data;
3718 if (child->type == MIMETYPE_TEXT) {
3720 debug_print("First text part found\n");
3721 } else if (compose->mode == COMPOSE_REEDIT &&
3722 child->type == MIMETYPE_APPLICATION &&
3723 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3724 encrypted = (MimeInfo *)child->node->parent->data;
3727 child = (MimeInfo *) mimeinfo->node->children->data;
3728 while (child != NULL) {
3731 if (child == encrypted) {
3732 /* skip this part of tree */
3733 NEXT_PART_NOT_CHILD(child);
3737 if (child->type == MIMETYPE_MULTIPART) {
3738 /* get the actual content */
3739 child = procmime_mimeinfo_next(child);
3743 if (child == firsttext) {
3744 child = procmime_mimeinfo_next(child);
3748 outfile = procmime_get_tmp_file_name(child);
3749 if ((err = procmime_get_part(outfile, child)) < 0)
3750 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3752 gchar *content_type;
3754 content_type = procmime_get_content_type_str(child->type, child->subtype);
3756 /* if we meet a pgp signature, we don't attach it, but
3757 * we force signing. */
3758 if ((strcmp(content_type, "application/pgp-signature") &&
3759 strcmp(content_type, "application/pkcs7-signature") &&
3760 strcmp(content_type, "application/x-pkcs7-signature"))
3761 || compose->mode == COMPOSE_REDIRECT) {
3762 partname = procmime_mimeinfo_get_parameter(child, "filename");
3763 if (partname == NULL)
3764 partname = procmime_mimeinfo_get_parameter(child, "name");
3765 if (partname == NULL)
3767 compose_attach_append(compose, outfile,
3768 partname, content_type,
3769 procmime_mimeinfo_get_parameter(child, "charset"));
3771 compose_force_signing(compose, compose->account, NULL);
3773 g_free(content_type);
3776 NEXT_PART_NOT_CHILD(child);
3778 procmime_mimeinfo_free_all(mimeinfo);
3781 #undef NEXT_PART_NOT_CHILD
3786 WAIT_FOR_INDENT_CHAR,
3787 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3790 /* return indent length, we allow:
3791 indent characters followed by indent characters or spaces/tabs,
3792 alphabets and numbers immediately followed by indent characters,
3793 and the repeating sequences of the above
3794 If quote ends with multiple spaces, only the first one is included. */
3795 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3796 const GtkTextIter *start, gint *len)
3798 GtkTextIter iter = *start;
3802 IndentState state = WAIT_FOR_INDENT_CHAR;
3805 gint alnum_count = 0;
3806 gint space_count = 0;
3809 if (prefs_common.quote_chars == NULL) {
3813 while (!gtk_text_iter_ends_line(&iter)) {
3814 wc = gtk_text_iter_get_char(&iter);
3815 if (g_unichar_iswide(wc))
3817 clen = g_unichar_to_utf8(wc, ch);
3821 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3822 is_space = g_unichar_isspace(wc);
3824 if (state == WAIT_FOR_INDENT_CHAR) {
3825 if (!is_indent && !g_unichar_isalnum(wc))
3828 quote_len += alnum_count + space_count + 1;
3829 alnum_count = space_count = 0;
3830 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3833 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3834 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3838 else if (is_indent) {
3839 quote_len += alnum_count + space_count + 1;
3840 alnum_count = space_count = 0;
3843 state = WAIT_FOR_INDENT_CHAR;
3847 gtk_text_iter_forward_char(&iter);
3850 if (quote_len > 0 && space_count > 0)
3856 if (quote_len > 0) {
3858 gtk_text_iter_forward_chars(&iter, quote_len);
3859 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3865 /* return >0 if the line is itemized */
3866 static int compose_itemized_length(GtkTextBuffer *buffer,
3867 const GtkTextIter *start)
3869 GtkTextIter iter = *start;
3874 if (gtk_text_iter_ends_line(&iter))
3879 wc = gtk_text_iter_get_char(&iter);
3880 if (!g_unichar_isspace(wc))
3882 gtk_text_iter_forward_char(&iter);
3883 if (gtk_text_iter_ends_line(&iter))
3887 clen = g_unichar_to_utf8(wc, ch);
3891 if (!strchr("*-+", ch[0]))
3894 gtk_text_iter_forward_char(&iter);
3895 if (gtk_text_iter_ends_line(&iter))
3897 wc = gtk_text_iter_get_char(&iter);
3898 if (g_unichar_isspace(wc)) {
3904 /* return the string at the start of the itemization */
3905 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3906 const GtkTextIter *start)
3908 GtkTextIter iter = *start;
3911 GString *item_chars = g_string_new("");
3914 if (gtk_text_iter_ends_line(&iter))
3919 wc = gtk_text_iter_get_char(&iter);
3920 if (!g_unichar_isspace(wc))
3922 gtk_text_iter_forward_char(&iter);
3923 if (gtk_text_iter_ends_line(&iter))
3925 g_string_append_unichar(item_chars, wc);
3928 str = item_chars->str;
3929 g_string_free(item_chars, FALSE);
3933 /* return the number of spaces at a line's start */
3934 static int compose_left_offset_length(GtkTextBuffer *buffer,
3935 const GtkTextIter *start)
3937 GtkTextIter iter = *start;
3940 if (gtk_text_iter_ends_line(&iter))
3944 wc = gtk_text_iter_get_char(&iter);
3945 if (!g_unichar_isspace(wc))
3948 gtk_text_iter_forward_char(&iter);
3949 if (gtk_text_iter_ends_line(&iter))
3953 gtk_text_iter_forward_char(&iter);
3954 if (gtk_text_iter_ends_line(&iter))
3959 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3960 const GtkTextIter *start,
3961 GtkTextIter *break_pos,
3965 GtkTextIter iter = *start, line_end = *start;
3966 PangoLogAttr *attrs;
3973 gboolean can_break = FALSE;
3974 gboolean do_break = FALSE;
3975 gboolean was_white = FALSE;
3976 gboolean prev_dont_break = FALSE;
3978 gtk_text_iter_forward_to_line_end(&line_end);
3979 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3980 len = g_utf8_strlen(str, -1);
3984 g_warning("compose_get_line_break_pos: len = 0!\n");
3988 /* g_print("breaking line: %d: %s (len = %d)\n",
3989 gtk_text_iter_get_line(&iter), str, len); */
3991 attrs = g_new(PangoLogAttr, len + 1);
3993 pango_default_break(str, -1, NULL, attrs, len + 1);
3997 /* skip quote and leading spaces */
3998 for (i = 0; *p != '\0' && i < len; i++) {
4001 wc = g_utf8_get_char(p);
4002 if (i >= quote_len && !g_unichar_isspace(wc))
4004 if (g_unichar_iswide(wc))
4006 else if (*p == '\t')
4010 p = g_utf8_next_char(p);
4013 for (; *p != '\0' && i < len; i++) {
4014 PangoLogAttr *attr = attrs + i;
4018 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4021 was_white = attr->is_white;
4023 /* don't wrap URI */
4024 if ((uri_len = get_uri_len(p)) > 0) {
4026 if (pos > 0 && col > max_col) {
4036 wc = g_utf8_get_char(p);
4037 if (g_unichar_iswide(wc)) {
4039 if (prev_dont_break && can_break && attr->is_line_break)
4041 } else if (*p == '\t')
4045 if (pos > 0 && col > max_col) {
4050 if (*p == '-' || *p == '/')
4051 prev_dont_break = TRUE;
4053 prev_dont_break = FALSE;
4055 p = g_utf8_next_char(p);
4059 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4064 *break_pos = *start;
4065 gtk_text_iter_set_line_offset(break_pos, pos);
4070 static gboolean compose_join_next_line(Compose *compose,
4071 GtkTextBuffer *buffer,
4073 const gchar *quote_str)
4075 GtkTextIter iter_ = *iter, cur, prev, next, end;
4076 PangoLogAttr attrs[3];
4078 gchar *next_quote_str;
4081 gboolean keep_cursor = FALSE;
4083 if (!gtk_text_iter_forward_line(&iter_) ||
4084 gtk_text_iter_ends_line(&iter_)) {
4087 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4089 if ((quote_str || next_quote_str) &&
4090 strcmp2(quote_str, next_quote_str) != 0) {
4091 g_free(next_quote_str);
4094 g_free(next_quote_str);
4097 if (quote_len > 0) {
4098 gtk_text_iter_forward_chars(&end, quote_len);
4099 if (gtk_text_iter_ends_line(&end)) {
4104 /* don't join itemized lines */
4105 if (compose_itemized_length(buffer, &end) > 0) {
4109 /* don't join signature separator */
4110 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4113 /* delete quote str */
4115 gtk_text_buffer_delete(buffer, &iter_, &end);
4117 /* don't join line breaks put by the user */
4119 gtk_text_iter_backward_char(&cur);
4120 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4121 gtk_text_iter_forward_char(&cur);
4125 gtk_text_iter_forward_char(&cur);
4126 /* delete linebreak and extra spaces */
4127 while (gtk_text_iter_backward_char(&cur)) {
4128 wc1 = gtk_text_iter_get_char(&cur);
4129 if (!g_unichar_isspace(wc1))
4134 while (!gtk_text_iter_ends_line(&cur)) {
4135 wc1 = gtk_text_iter_get_char(&cur);
4136 if (!g_unichar_isspace(wc1))
4138 gtk_text_iter_forward_char(&cur);
4141 if (!gtk_text_iter_equal(&prev, &next)) {
4144 mark = gtk_text_buffer_get_insert(buffer);
4145 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4146 if (gtk_text_iter_equal(&prev, &cur))
4148 gtk_text_buffer_delete(buffer, &prev, &next);
4152 /* insert space if required */
4153 gtk_text_iter_backward_char(&prev);
4154 wc1 = gtk_text_iter_get_char(&prev);
4155 wc2 = gtk_text_iter_get_char(&next);
4156 gtk_text_iter_forward_char(&next);
4157 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4158 pango_default_break(str, -1, NULL, attrs, 3);
4159 if (!attrs[1].is_line_break ||
4160 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4161 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4163 gtk_text_iter_backward_char(&iter_);
4164 gtk_text_buffer_place_cursor(buffer, &iter_);
4173 #define ADD_TXT_POS(bp_, ep_, pti_) \
4174 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4175 last = last->next; \
4176 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4177 last->next = NULL; \
4179 g_warning("alloc error scanning URIs\n"); \
4182 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4184 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4185 GtkTextBuffer *buffer;
4186 GtkTextIter iter, break_pos, end_of_line;
4187 gchar *quote_str = NULL;
4189 gboolean wrap_quote = prefs_common.linewrap_quote;
4190 gboolean prev_autowrap = compose->autowrap;
4191 gint startq_offset = -1, noq_offset = -1;
4192 gint uri_start = -1, uri_stop = -1;
4193 gint nouri_start = -1, nouri_stop = -1;
4194 gint num_blocks = 0;
4195 gint quotelevel = -1;
4196 gboolean modified = force;
4197 gboolean removed = FALSE;
4198 gboolean modified_before_remove = FALSE;
4200 gboolean start = TRUE;
4201 gint itemized_len = 0, rem_item_len = 0;
4202 gchar *itemized_chars = NULL;
4203 gboolean item_continuation = FALSE;
4208 if (compose->draft_timeout_tag == -2) {
4212 compose->autowrap = FALSE;
4214 buffer = gtk_text_view_get_buffer(text);
4215 undo_wrapping(compose->undostruct, TRUE);
4220 mark = gtk_text_buffer_get_insert(buffer);
4221 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4225 if (compose->draft_timeout_tag == -2) {
4226 if (gtk_text_iter_ends_line(&iter)) {
4227 while (gtk_text_iter_ends_line(&iter) &&
4228 gtk_text_iter_forward_line(&iter))
4231 while (gtk_text_iter_backward_line(&iter)) {
4232 if (gtk_text_iter_ends_line(&iter)) {
4233 gtk_text_iter_forward_line(&iter);
4239 /* move to line start */
4240 gtk_text_iter_set_line_offset(&iter, 0);
4243 itemized_len = compose_itemized_length(buffer, &iter);
4245 if (!itemized_len) {
4246 itemized_len = compose_left_offset_length(buffer, &iter);
4247 item_continuation = TRUE;
4251 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4253 /* go until paragraph end (empty line) */
4254 while (start || !gtk_text_iter_ends_line(&iter)) {
4255 gchar *scanpos = NULL;
4256 /* parse table - in order of priority */
4258 const gchar *needle; /* token */
4260 /* token search function */
4261 gchar *(*search) (const gchar *haystack,
4262 const gchar *needle);
4263 /* part parsing function */
4264 gboolean (*parse) (const gchar *start,
4265 const gchar *scanpos,
4269 /* part to URI function */
4270 gchar *(*build_uri) (const gchar *bp,
4274 static struct table parser[] = {
4275 {"http://", strcasestr, get_uri_part, make_uri_string},
4276 {"https://", strcasestr, get_uri_part, make_uri_string},
4277 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4278 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4279 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4280 {"www.", strcasestr, get_uri_part, make_http_string},
4281 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4282 {"@", strcasestr, get_email_part, make_email_string}
4284 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4285 gint last_index = PARSE_ELEMS;
4287 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4291 if (!prev_autowrap && num_blocks == 0) {
4293 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4294 G_CALLBACK(text_inserted),
4297 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4300 uri_start = uri_stop = -1;
4302 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4305 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4306 if (startq_offset == -1)
4307 startq_offset = gtk_text_iter_get_offset(&iter);
4308 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4309 if (quotelevel > 2) {
4310 /* recycle colors */
4311 if (prefs_common.recycle_quote_colors)
4320 if (startq_offset == -1)
4321 noq_offset = gtk_text_iter_get_offset(&iter);
4325 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4328 if (gtk_text_iter_ends_line(&iter)) {
4330 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4331 prefs_common.linewrap_len,
4333 GtkTextIter prev, next, cur;
4334 if (prev_autowrap != FALSE || force) {
4335 compose->automatic_break = TRUE;
4337 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4338 compose->automatic_break = FALSE;
4339 if (itemized_len && compose->autoindent) {
4340 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4341 if (!item_continuation)
4342 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4344 } else if (quote_str && wrap_quote) {
4345 compose->automatic_break = TRUE;
4347 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4348 compose->automatic_break = FALSE;
4349 if (itemized_len && compose->autoindent) {
4350 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4351 if (!item_continuation)
4352 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4356 /* remove trailing spaces */
4358 rem_item_len = itemized_len;
4359 while (compose->autoindent && rem_item_len-- > 0)
4360 gtk_text_iter_backward_char(&cur);
4361 gtk_text_iter_backward_char(&cur);
4364 while (!gtk_text_iter_starts_line(&cur)) {
4367 gtk_text_iter_backward_char(&cur);
4368 wc = gtk_text_iter_get_char(&cur);
4369 if (!g_unichar_isspace(wc))
4373 if (!gtk_text_iter_equal(&prev, &next)) {
4374 gtk_text_buffer_delete(buffer, &prev, &next);
4376 gtk_text_iter_forward_char(&break_pos);
4380 gtk_text_buffer_insert(buffer, &break_pos,
4384 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4386 /* move iter to current line start */
4387 gtk_text_iter_set_line_offset(&iter, 0);
4394 /* move iter to next line start */
4400 if (!prev_autowrap && num_blocks > 0) {
4402 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4403 G_CALLBACK(text_inserted),
4407 while (!gtk_text_iter_ends_line(&end_of_line)) {
4408 gtk_text_iter_forward_char(&end_of_line);
4410 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4412 nouri_start = gtk_text_iter_get_offset(&iter);
4413 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4415 walk_pos = gtk_text_iter_get_offset(&iter);
4416 /* FIXME: this looks phony. scanning for anything in the parse table */
4417 for (n = 0; n < PARSE_ELEMS; n++) {
4420 tmp = parser[n].search(walk, parser[n].needle);
4422 if (scanpos == NULL || tmp < scanpos) {
4431 /* check if URI can be parsed */
4432 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4433 (const gchar **)&ep, FALSE)
4434 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4438 strlen(parser[last_index].needle);
4441 uri_start = walk_pos + (bp - o_walk);
4442 uri_stop = walk_pos + (ep - o_walk);
4446 gtk_text_iter_forward_line(&iter);
4449 if (startq_offset != -1) {
4450 GtkTextIter startquote, endquote;
4451 gtk_text_buffer_get_iter_at_offset(
4452 buffer, &startquote, startq_offset);
4455 switch (quotelevel) {
4457 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4458 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4459 gtk_text_buffer_apply_tag_by_name(
4460 buffer, "quote0", &startquote, &endquote);
4461 gtk_text_buffer_remove_tag_by_name(
4462 buffer, "quote1", &startquote, &endquote);
4463 gtk_text_buffer_remove_tag_by_name(
4464 buffer, "quote2", &startquote, &endquote);
4469 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4470 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4471 gtk_text_buffer_apply_tag_by_name(
4472 buffer, "quote1", &startquote, &endquote);
4473 gtk_text_buffer_remove_tag_by_name(
4474 buffer, "quote0", &startquote, &endquote);
4475 gtk_text_buffer_remove_tag_by_name(
4476 buffer, "quote2", &startquote, &endquote);
4481 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4482 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4483 gtk_text_buffer_apply_tag_by_name(
4484 buffer, "quote2", &startquote, &endquote);
4485 gtk_text_buffer_remove_tag_by_name(
4486 buffer, "quote0", &startquote, &endquote);
4487 gtk_text_buffer_remove_tag_by_name(
4488 buffer, "quote1", &startquote, &endquote);
4494 } else if (noq_offset != -1) {
4495 GtkTextIter startnoquote, endnoquote;
4496 gtk_text_buffer_get_iter_at_offset(
4497 buffer, &startnoquote, noq_offset);
4500 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4501 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4502 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4503 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4504 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4505 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4506 gtk_text_buffer_remove_tag_by_name(
4507 buffer, "quote0", &startnoquote, &endnoquote);
4508 gtk_text_buffer_remove_tag_by_name(
4509 buffer, "quote1", &startnoquote, &endnoquote);
4510 gtk_text_buffer_remove_tag_by_name(
4511 buffer, "quote2", &startnoquote, &endnoquote);
4517 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4518 GtkTextIter nouri_start_iter, nouri_end_iter;
4519 gtk_text_buffer_get_iter_at_offset(
4520 buffer, &nouri_start_iter, nouri_start);
4521 gtk_text_buffer_get_iter_at_offset(
4522 buffer, &nouri_end_iter, nouri_stop);
4523 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4524 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4525 gtk_text_buffer_remove_tag_by_name(
4526 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4527 modified_before_remove = modified;
4532 if (uri_start >= 0 && uri_stop > 0) {
4533 GtkTextIter uri_start_iter, uri_end_iter, back;
4534 gtk_text_buffer_get_iter_at_offset(
4535 buffer, &uri_start_iter, uri_start);
4536 gtk_text_buffer_get_iter_at_offset(
4537 buffer, &uri_end_iter, uri_stop);
4538 back = uri_end_iter;
4539 gtk_text_iter_backward_char(&back);
4540 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4541 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4542 gtk_text_buffer_apply_tag_by_name(
4543 buffer, "link", &uri_start_iter, &uri_end_iter);
4545 if (removed && !modified_before_remove) {
4551 // debug_print("not modified, out after %d lines\n", lines);
4555 // debug_print("modified, out after %d lines\n", lines);
4557 g_free(itemized_chars);
4560 undo_wrapping(compose->undostruct, FALSE);
4561 compose->autowrap = prev_autowrap;
4566 void compose_action_cb(void *data)
4568 Compose *compose = (Compose *)data;
4569 compose_wrap_all(compose);
4572 static void compose_wrap_all(Compose *compose)
4574 compose_wrap_all_full(compose, FALSE);
4577 static void compose_wrap_all_full(Compose *compose, gboolean force)
4579 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4580 GtkTextBuffer *buffer;
4582 gboolean modified = TRUE;
4584 buffer = gtk_text_view_get_buffer(text);
4586 gtk_text_buffer_get_start_iter(buffer, &iter);
4587 while (!gtk_text_iter_is_end(&iter) && modified)
4588 modified = compose_beautify_paragraph(compose, &iter, force);
4592 static void compose_set_title(Compose *compose)
4598 edited = compose->modified ? _(" [Edited]") : "";
4600 subject = gtk_editable_get_chars(
4601 GTK_EDITABLE(compose->subject_entry), 0, -1);
4603 #ifndef GENERIC_UMPC
4604 if (subject && strlen(subject))
4605 str = g_strdup_printf(_("%s - Compose message%s"),
4608 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4610 str = g_strdup(_("Compose message"));
4613 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4619 * compose_current_mail_account:
4621 * Find a current mail account (the currently selected account, or the
4622 * default account, if a news account is currently selected). If a
4623 * mail account cannot be found, display an error message.
4625 * Return value: Mail account, or NULL if not found.
4627 static PrefsAccount *
4628 compose_current_mail_account(void)
4632 if (cur_account && cur_account->protocol != A_NNTP)
4635 ac = account_get_default();
4636 if (!ac || ac->protocol == A_NNTP) {
4637 alertpanel_error(_("Account for sending mail is not specified.\n"
4638 "Please select a mail account before sending."));
4645 #define QUOTE_IF_REQUIRED(out, str) \
4647 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4651 len = strlen(str) + 3; \
4652 if ((__tmp = alloca(len)) == NULL) { \
4653 g_warning("can't allocate memory\n"); \
4654 g_string_free(header, TRUE); \
4657 g_snprintf(__tmp, len, "\"%s\"", str); \
4662 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4663 g_warning("can't allocate memory\n"); \
4664 g_string_free(header, TRUE); \
4667 strcpy(__tmp, str); \
4673 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4675 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4679 len = strlen(str) + 3; \
4680 if ((__tmp = alloca(len)) == NULL) { \
4681 g_warning("can't allocate memory\n"); \
4684 g_snprintf(__tmp, len, "\"%s\"", str); \
4689 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4690 g_warning("can't allocate memory\n"); \
4693 strcpy(__tmp, str); \
4699 static void compose_select_account(Compose *compose, PrefsAccount *account,
4702 gchar *from = NULL, *header;
4703 ComposeHeaderEntry *header_entry;
4705 cm_return_if_fail(account != NULL);
4707 compose->account = account;
4708 if (account->name && *account->name) {
4710 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4711 from = g_strdup_printf("%s <%s>",
4712 buf, account->address);
4713 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4715 from = g_strdup_printf("<%s>",
4717 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4722 compose_set_title(compose);
4724 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4725 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4727 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4728 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4729 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4731 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4733 activate_privacy_system(compose, account, FALSE);
4735 if (!init && compose->mode != COMPOSE_REDIRECT) {
4736 undo_block(compose->undostruct);
4737 compose_insert_sig(compose, TRUE);
4738 undo_unblock(compose->undostruct);
4741 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4742 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4744 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4745 if (account->protocol == A_NNTP) {
4746 if (!strcmp(header, _("To:")))
4747 combobox_select_by_text(
4748 GTK_COMBO_BOX(header_entry->combo),
4751 if (!strcmp(header, _("Newsgroups:")))
4752 combobox_select_by_text(
4753 GTK_COMBO_BOX(header_entry->combo),
4761 /* use account's dict info if set */
4762 if (compose->gtkaspell) {
4763 if (account->enable_default_dictionary)
4764 gtkaspell_change_dict(compose->gtkaspell,
4765 account->default_dictionary, FALSE);
4766 if (account->enable_default_alt_dictionary)
4767 gtkaspell_change_alt_dict(compose->gtkaspell,
4768 account->default_alt_dictionary);
4769 if (account->enable_default_dictionary
4770 || account->enable_default_alt_dictionary)
4771 compose_spell_menu_changed(compose);
4776 gboolean compose_check_for_valid_recipient(Compose *compose) {
4777 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4778 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4779 gboolean recipient_found = FALSE;
4783 /* free to and newsgroup list */
4784 slist_free_strings(compose->to_list);
4785 g_slist_free(compose->to_list);
4786 compose->to_list = NULL;
4788 slist_free_strings(compose->newsgroup_list);
4789 g_slist_free(compose->newsgroup_list);
4790 compose->newsgroup_list = NULL;
4792 /* search header entries for to and newsgroup entries */
4793 for (list = compose->header_list; list; list = list->next) {
4796 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4797 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4800 if (entry[0] != '\0') {
4801 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4802 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4803 compose->to_list = address_list_append(compose->to_list, entry);
4804 recipient_found = TRUE;
4807 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4808 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4809 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4810 recipient_found = TRUE;
4817 return recipient_found;
4820 static gboolean compose_check_for_set_recipients(Compose *compose)
4822 if (compose->account->set_autocc && compose->account->auto_cc) {
4823 gboolean found_other = FALSE;
4825 /* search header entries for to and newsgroup entries */
4826 for (list = compose->header_list; list; list = list->next) {
4829 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4830 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4833 if (strcmp(entry, compose->account->auto_cc)
4834 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4844 if (compose->batch) {
4845 gtk_widget_show_all(compose->window);
4847 aval = alertpanel(_("Send"),
4848 _("The only recipient is the default CC address. Send anyway?"),
4849 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4850 if (aval != G_ALERTALTERNATE)
4854 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4855 gboolean found_other = FALSE;
4857 /* search header entries for to and newsgroup entries */
4858 for (list = compose->header_list; list; list = list->next) {
4861 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4862 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4865 if (strcmp(entry, compose->account->auto_bcc)
4866 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4876 if (compose->batch) {
4877 gtk_widget_show_all(compose->window);
4879 aval = alertpanel(_("Send"),
4880 _("The only recipient is the default BCC address. Send anyway?"),
4881 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4882 if (aval != G_ALERTALTERNATE)
4889 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4893 if (compose_check_for_valid_recipient(compose) == FALSE) {
4894 if (compose->batch) {
4895 gtk_widget_show_all(compose->window);
4897 alertpanel_error(_("Recipient is not specified."));
4901 if (compose_check_for_set_recipients(compose) == FALSE) {
4905 if (!compose->batch) {
4906 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4907 if (*str == '\0' && check_everything == TRUE &&
4908 compose->mode != COMPOSE_REDIRECT) {
4910 gchar *button_label;
4913 if (compose->sending)
4914 button_label = _("+_Send");
4916 button_label = _("+_Queue");
4917 message = g_strdup_printf(_("Subject is empty. %s"),
4918 compose->sending?_("Send it anyway?"):
4919 _("Queue it anyway?"));
4921 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4922 GTK_STOCK_CANCEL, button_label, NULL);
4924 if (aval != G_ALERTALTERNATE)
4929 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4935 gint compose_send(Compose *compose)
4938 FolderItem *folder = NULL;
4940 gchar *msgpath = NULL;
4941 gboolean discard_window = FALSE;
4942 gchar *errstr = NULL;
4943 gchar *tmsgid = NULL;
4944 MainWindow *mainwin = mainwindow_get_mainwindow();
4945 gboolean queued_removed = FALSE;
4947 if (prefs_common.send_dialog_invisible
4948 || compose->batch == TRUE)
4949 discard_window = TRUE;
4951 compose_allow_user_actions (compose, FALSE);
4952 compose->sending = TRUE;
4954 if (compose_check_entries(compose, TRUE) == FALSE) {
4955 if (compose->batch) {
4956 gtk_widget_show_all(compose->window);
4962 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4965 if (compose->batch) {
4966 gtk_widget_show_all(compose->window);
4969 alertpanel_error(_("Could not queue message for sending:\n\n"
4970 "Charset conversion failed."));
4971 } else if (val == -5) {
4972 alertpanel_error(_("Could not queue message for sending:\n\n"
4973 "Couldn't get recipient encryption key."));
4974 } else if (val == -6) {
4976 } else if (val == -3) {
4977 if (privacy_peek_error())
4978 alertpanel_error(_("Could not queue message for sending:\n\n"
4979 "Signature failed: %s"), privacy_get_error());
4980 } else if (val == -2 && errno != 0) {
4981 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4983 alertpanel_error(_("Could not queue message for sending."));
4988 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4989 if (discard_window) {
4990 compose->sending = FALSE;
4991 compose_close(compose);
4992 /* No more compose access in the normal codepath
4993 * after this point! */
4998 alertpanel_error(_("The message was queued but could not be "
4999 "sent.\nUse \"Send queued messages\" from "
5000 "the main window to retry."));
5001 if (!discard_window) {
5008 if (msgpath == NULL) {
5009 msgpath = folder_item_fetch_msg(folder, msgnum);
5010 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5013 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5014 claws_unlink(msgpath);
5017 if (!discard_window) {
5019 if (!queued_removed)
5020 folder_item_remove_msg(folder, msgnum);
5021 folder_item_scan(folder);
5023 /* make sure we delete that */
5024 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5026 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5027 folder_item_remove_msg(folder, tmp->msgnum);
5028 procmsg_msginfo_free(tmp);
5035 if (!queued_removed)
5036 folder_item_remove_msg(folder, msgnum);
5037 folder_item_scan(folder);
5039 /* make sure we delete that */
5040 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5042 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5043 folder_item_remove_msg(folder, tmp->msgnum);
5044 procmsg_msginfo_free(tmp);
5047 if (!discard_window) {
5048 compose->sending = FALSE;
5049 compose_allow_user_actions (compose, TRUE);
5050 compose_close(compose);
5054 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5055 "the main window to retry."), errstr);
5058 alertpanel_error_log(_("The message was queued but could not be "
5059 "sent.\nUse \"Send queued messages\" from "
5060 "the main window to retry."));
5062 if (!discard_window) {
5071 toolbar_main_set_sensitive(mainwin);
5072 main_window_set_menu_sensitive(mainwin);
5078 compose_allow_user_actions (compose, TRUE);
5079 compose->sending = FALSE;
5080 compose->modified = TRUE;
5081 toolbar_main_set_sensitive(mainwin);
5082 main_window_set_menu_sensitive(mainwin);
5087 static gboolean compose_use_attach(Compose *compose)
5089 GtkTreeModel *model = gtk_tree_view_get_model
5090 (GTK_TREE_VIEW(compose->attach_clist));
5091 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5094 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5097 gchar buf[BUFFSIZE];
5099 gboolean first_to_address;
5100 gboolean first_cc_address;
5102 ComposeHeaderEntry *headerentry;
5103 const gchar *headerentryname;
5104 const gchar *cc_hdr;
5105 const gchar *to_hdr;
5106 gboolean err = FALSE;
5108 debug_print("Writing redirect header\n");
5110 cc_hdr = prefs_common_translated_header_name("Cc:");
5111 to_hdr = prefs_common_translated_header_name("To:");
5113 first_to_address = TRUE;
5114 for (list = compose->header_list; list; list = list->next) {
5115 headerentry = ((ComposeHeaderEntry *)list->data);
5116 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5118 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5119 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5120 Xstrdup_a(str, entstr, return -1);
5122 if (str[0] != '\0') {
5123 compose_convert_header
5124 (compose, buf, sizeof(buf), str,
5125 strlen("Resent-To") + 2, TRUE);
5127 if (first_to_address) {
5128 err |= (fprintf(fp, "Resent-To: ") < 0);
5129 first_to_address = FALSE;
5131 err |= (fprintf(fp, ",") < 0);
5133 err |= (fprintf(fp, "%s", buf) < 0);
5137 if (!first_to_address) {
5138 err |= (fprintf(fp, "\n") < 0);
5141 first_cc_address = TRUE;
5142 for (list = compose->header_list; list; list = list->next) {
5143 headerentry = ((ComposeHeaderEntry *)list->data);
5144 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5146 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5147 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5148 Xstrdup_a(str, strg, return -1);
5150 if (str[0] != '\0') {
5151 compose_convert_header
5152 (compose, buf, sizeof(buf), str,
5153 strlen("Resent-Cc") + 2, TRUE);
5155 if (first_cc_address) {
5156 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5157 first_cc_address = FALSE;
5159 err |= (fprintf(fp, ",") < 0);
5161 err |= (fprintf(fp, "%s", buf) < 0);
5165 if (!first_cc_address) {
5166 err |= (fprintf(fp, "\n") < 0);
5169 return (err ? -1:0);
5172 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5174 gchar buf[BUFFSIZE];
5176 const gchar *entstr;
5177 /* struct utsname utsbuf; */
5178 gboolean err = FALSE;
5180 cm_return_val_if_fail(fp != NULL, -1);
5181 cm_return_val_if_fail(compose->account != NULL, -1);
5182 cm_return_val_if_fail(compose->account->address != NULL, -1);
5185 get_rfc822_date(buf, sizeof(buf));
5186 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5189 if (compose->account->name && *compose->account->name) {
5190 compose_convert_header
5191 (compose, buf, sizeof(buf), compose->account->name,
5192 strlen("From: "), TRUE);
5193 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5194 buf, compose->account->address) < 0);
5196 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5199 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5200 if (*entstr != '\0') {
5201 Xstrdup_a(str, entstr, return -1);
5204 compose_convert_header(compose, buf, sizeof(buf), str,
5205 strlen("Subject: "), FALSE);
5206 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5210 /* Resent-Message-ID */
5211 if (compose->account->set_domain && compose->account->domain) {
5212 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5213 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5214 g_snprintf(buf, sizeof(buf), "%s",
5215 strchr(compose->account->address, '@') ?
5216 strchr(compose->account->address, '@')+1 :
5217 compose->account->address);
5219 g_snprintf(buf, sizeof(buf), "%s", "");
5222 if (compose->account->gen_msgid) {
5224 if (compose->account->msgid_with_addr) {
5225 addr = compose->account->address;
5227 generate_msgid(buf, sizeof(buf), addr);
5228 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5229 compose->msgid = g_strdup(buf);
5231 compose->msgid = NULL;
5234 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5237 /* separator between header and body */
5238 err |= (fputs("\n", fp) == EOF);
5240 return (err ? -1:0);
5243 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5247 gchar buf[BUFFSIZE];
5249 gboolean skip = FALSE;
5250 gboolean err = FALSE;
5251 gchar *not_included[]={
5252 "Return-Path:", "Delivered-To:", "Received:",
5253 "Subject:", "X-UIDL:", "AF:",
5254 "NF:", "PS:", "SRH:",
5255 "SFN:", "DSR:", "MID:",
5256 "CFG:", "PT:", "S:",
5257 "RQ:", "SSV:", "NSV:",
5258 "SSH:", "R:", "MAID:",
5259 "NAID:", "RMID:", "FMID:",
5260 "SCF:", "RRCPT:", "NG:",
5261 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5262 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5263 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5264 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5265 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5268 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5269 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5273 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5275 for (i = 0; not_included[i] != NULL; i++) {
5276 if (g_ascii_strncasecmp(buf, not_included[i],
5277 strlen(not_included[i])) == 0) {
5284 if (fputs(buf, fdest) == -1)
5287 if (!prefs_common.redirect_keep_from) {
5288 if (g_ascii_strncasecmp(buf, "From:",
5289 strlen("From:")) == 0) {
5290 err |= (fputs(" (by way of ", fdest) == EOF);
5291 if (compose->account->name
5292 && *compose->account->name) {
5293 compose_convert_header
5294 (compose, buf, sizeof(buf),
5295 compose->account->name,
5298 err |= (fprintf(fdest, "%s <%s>",
5300 compose->account->address) < 0);
5302 err |= (fprintf(fdest, "%s",
5303 compose->account->address) < 0);
5304 err |= (fputs(")", fdest) == EOF);
5308 if (fputs("\n", fdest) == -1)
5315 if (compose_redirect_write_headers(compose, fdest))
5318 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5319 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5332 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5334 GtkTextBuffer *buffer;
5335 GtkTextIter start, end;
5338 const gchar *out_codeset;
5339 EncodingType encoding = ENC_UNKNOWN;
5340 MimeInfo *mimemsg, *mimetext;
5342 const gchar *src_codeset = CS_INTERNAL;
5343 gchar *from_addr = NULL;
5344 gchar *from_name = NULL;
5346 if (action == COMPOSE_WRITE_FOR_SEND)
5347 attach_parts = TRUE;
5349 /* create message MimeInfo */
5350 mimemsg = procmime_mimeinfo_new();
5351 mimemsg->type = MIMETYPE_MESSAGE;
5352 mimemsg->subtype = g_strdup("rfc822");
5353 mimemsg->content = MIMECONTENT_MEM;
5354 mimemsg->tmp = TRUE; /* must free content later */
5355 mimemsg->data.mem = compose_get_header(compose);
5357 /* Create text part MimeInfo */
5358 /* get all composed text */
5359 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5360 gtk_text_buffer_get_start_iter(buffer, &start);
5361 gtk_text_buffer_get_end_iter(buffer, &end);
5362 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5364 out_codeset = conv_get_charset_str(compose->out_encoding);
5366 if (!out_codeset && is_ascii_str(chars)) {
5367 out_codeset = CS_US_ASCII;
5368 } else if (prefs_common.outgoing_fallback_to_ascii &&
5369 is_ascii_str(chars)) {
5370 out_codeset = CS_US_ASCII;
5371 encoding = ENC_7BIT;
5375 gchar *test_conv_global_out = NULL;
5376 gchar *test_conv_reply = NULL;
5378 /* automatic mode. be automatic. */
5379 codeconv_set_strict(TRUE);
5381 out_codeset = conv_get_outgoing_charset_str();
5383 debug_print("trying to convert to %s\n", out_codeset);
5384 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5387 if (!test_conv_global_out && compose->orig_charset
5388 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5389 out_codeset = compose->orig_charset;
5390 debug_print("failure; trying to convert to %s\n", out_codeset);
5391 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5394 if (!test_conv_global_out && !test_conv_reply) {
5396 out_codeset = CS_INTERNAL;
5397 debug_print("failure; finally using %s\n", out_codeset);
5399 g_free(test_conv_global_out);
5400 g_free(test_conv_reply);
5401 codeconv_set_strict(FALSE);
5404 if (encoding == ENC_UNKNOWN) {
5405 if (prefs_common.encoding_method == CTE_BASE64)
5406 encoding = ENC_BASE64;
5407 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5408 encoding = ENC_QUOTED_PRINTABLE;
5409 else if (prefs_common.encoding_method == CTE_8BIT)
5410 encoding = ENC_8BIT;
5412 encoding = procmime_get_encoding_for_charset(out_codeset);
5415 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5416 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5418 if (action == COMPOSE_WRITE_FOR_SEND) {
5419 codeconv_set_strict(TRUE);
5420 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5421 codeconv_set_strict(FALSE);
5427 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5428 "to the specified %s charset.\n"
5429 "Send it as %s?"), out_codeset, src_codeset);
5430 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5431 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5434 if (aval != G_ALERTALTERNATE) {
5439 out_codeset = src_codeset;
5445 out_codeset = src_codeset;
5450 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5451 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5452 strstr(buf, "\nFrom ") != NULL) {
5453 encoding = ENC_QUOTED_PRINTABLE;
5457 mimetext = procmime_mimeinfo_new();
5458 mimetext->content = MIMECONTENT_MEM;
5459 mimetext->tmp = TRUE; /* must free content later */
5460 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5461 * and free the data, which we need later. */
5462 mimetext->data.mem = g_strdup(buf);
5463 mimetext->type = MIMETYPE_TEXT;
5464 mimetext->subtype = g_strdup("plain");
5465 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5466 g_strdup(out_codeset));
5468 /* protect trailing spaces when signing message */
5469 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5470 privacy_system_can_sign(compose->privacy_system)) {
5471 encoding = ENC_QUOTED_PRINTABLE;
5474 debug_print("main text: %zd bytes encoded as %s in %d\n",
5475 strlen(buf), out_codeset, encoding);
5477 /* check for line length limit */
5478 if (action == COMPOSE_WRITE_FOR_SEND &&
5479 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5480 check_line_length(buf, 1000, &line) < 0) {
5484 msg = g_strdup_printf
5485 (_("Line %d exceeds the line length limit (998 bytes).\n"
5486 "The contents of the message might be broken on the way to the delivery.\n"
5488 "Send it anyway?"), line + 1);
5489 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5491 if (aval != G_ALERTALTERNATE) {
5497 if (encoding != ENC_UNKNOWN)
5498 procmime_encode_content(mimetext, encoding);
5500 /* append attachment parts */
5501 if (compose_use_attach(compose) && attach_parts) {
5502 MimeInfo *mimempart;
5503 gchar *boundary = NULL;
5504 mimempart = procmime_mimeinfo_new();
5505 mimempart->content = MIMECONTENT_EMPTY;
5506 mimempart->type = MIMETYPE_MULTIPART;
5507 mimempart->subtype = g_strdup("mixed");
5511 boundary = generate_mime_boundary(NULL);
5512 } while (strstr(buf, boundary) != NULL);
5514 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5517 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5519 g_node_append(mimempart->node, mimetext->node);
5520 g_node_append(mimemsg->node, mimempart->node);
5522 if (compose_add_attachments(compose, mimempart) < 0)
5525 g_node_append(mimemsg->node, mimetext->node);
5529 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5530 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5531 /* extract name and address */
5532 if (strstr(spec, " <") && strstr(spec, ">")) {
5533 from_addr = g_strdup(strrchr(spec, '<')+1);
5534 *(strrchr(from_addr, '>')) = '\0';
5535 from_name = g_strdup(spec);
5536 *(strrchr(from_name, '<')) = '\0';
5543 /* sign message if sending */
5544 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5545 privacy_system_can_sign(compose->privacy_system))
5546 if (!privacy_sign(compose->privacy_system, mimemsg,
5547 compose->account, from_addr)) {
5554 procmime_write_mimeinfo(mimemsg, fp);
5556 procmime_mimeinfo_free_all(mimemsg);
5561 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5563 GtkTextBuffer *buffer;
5564 GtkTextIter start, end;
5569 if ((fp = g_fopen(file, "wb")) == NULL) {
5570 FILE_OP_ERROR(file, "fopen");
5574 /* chmod for security */
5575 if (change_file_mode_rw(fp, file) < 0) {
5576 FILE_OP_ERROR(file, "chmod");
5577 g_warning("can't change file mode\n");
5580 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5581 gtk_text_buffer_get_start_iter(buffer, &start);
5582 gtk_text_buffer_get_end_iter(buffer, &end);
5583 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5585 chars = conv_codeset_strdup
5586 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5589 if (!chars) return -1;
5592 len = strlen(chars);
5593 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5594 FILE_OP_ERROR(file, "fwrite");
5603 if (fclose(fp) == EOF) {
5604 FILE_OP_ERROR(file, "fclose");
5611 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5614 MsgInfo *msginfo = compose->targetinfo;
5616 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5617 if (!msginfo) return -1;
5619 if (!force && MSG_IS_LOCKED(msginfo->flags))
5622 item = msginfo->folder;
5623 cm_return_val_if_fail(item != NULL, -1);
5625 if (procmsg_msg_exist(msginfo) &&
5626 (folder_has_parent_of_type(item, F_QUEUE) ||
5627 folder_has_parent_of_type(item, F_DRAFT)
5628 || msginfo == compose->autosaved_draft)) {
5629 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5630 g_warning("can't remove the old message\n");
5633 debug_print("removed reedit target %d\n", msginfo->msgnum);
5640 static void compose_remove_draft(Compose *compose)
5643 MsgInfo *msginfo = compose->targetinfo;
5644 drafts = account_get_special_folder(compose->account, F_DRAFT);
5646 if (procmsg_msg_exist(msginfo)) {
5647 folder_item_remove_msg(drafts, msginfo->msgnum);
5652 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5653 gboolean remove_reedit_target)
5655 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5658 static gboolean compose_warn_encryption(Compose *compose)
5660 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5661 AlertValue val = G_ALERTALTERNATE;
5663 if (warning == NULL)
5666 val = alertpanel_full(_("Encryption warning"), warning,
5667 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5668 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5669 if (val & G_ALERTDISABLE) {
5670 val &= ~G_ALERTDISABLE;
5671 if (val == G_ALERTALTERNATE)
5672 privacy_inhibit_encrypt_warning(compose->privacy_system,
5676 if (val == G_ALERTALTERNATE) {
5683 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5684 gchar **msgpath, gboolean check_subject,
5685 gboolean remove_reedit_target)
5692 static gboolean lock = FALSE;
5693 PrefsAccount *mailac = NULL, *newsac = NULL;
5694 gboolean err = FALSE;
5696 debug_print("queueing message...\n");
5697 cm_return_val_if_fail(compose->account != NULL, -1);
5701 if (compose_check_entries(compose, check_subject) == FALSE) {
5703 if (compose->batch) {
5704 gtk_widget_show_all(compose->window);
5709 if (!compose->to_list && !compose->newsgroup_list) {
5710 g_warning("can't get recipient list.");
5715 if (compose->to_list) {
5716 if (compose->account->protocol != A_NNTP)
5717 mailac = compose->account;
5718 else if (cur_account && cur_account->protocol != A_NNTP)
5719 mailac = cur_account;
5720 else if (!(mailac = compose_current_mail_account())) {
5722 alertpanel_error(_("No account for sending mails available!"));
5727 if (compose->newsgroup_list) {
5728 if (compose->account->protocol == A_NNTP)
5729 newsac = compose->account;
5732 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5737 /* write queue header */
5738 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5739 G_DIR_SEPARATOR, compose, (guint) rand());
5740 debug_print("queuing to %s\n", tmp);
5741 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5742 FILE_OP_ERROR(tmp, "fopen");
5748 if (change_file_mode_rw(fp, tmp) < 0) {
5749 FILE_OP_ERROR(tmp, "chmod");
5750 g_warning("can't change file mode\n");
5753 /* queueing variables */
5754 err |= (fprintf(fp, "AF:\n") < 0);
5755 err |= (fprintf(fp, "NF:0\n") < 0);
5756 err |= (fprintf(fp, "PS:10\n") < 0);
5757 err |= (fprintf(fp, "SRH:1\n") < 0);
5758 err |= (fprintf(fp, "SFN:\n") < 0);
5759 err |= (fprintf(fp, "DSR:\n") < 0);
5761 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5763 err |= (fprintf(fp, "MID:\n") < 0);
5764 err |= (fprintf(fp, "CFG:\n") < 0);
5765 err |= (fprintf(fp, "PT:0\n") < 0);
5766 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5767 err |= (fprintf(fp, "RQ:\n") < 0);
5769 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5771 err |= (fprintf(fp, "SSV:\n") < 0);
5773 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5775 err |= (fprintf(fp, "NSV:\n") < 0);
5776 err |= (fprintf(fp, "SSH:\n") < 0);
5777 /* write recepient list */
5778 if (compose->to_list) {
5779 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5780 for (cur = compose->to_list->next; cur != NULL;
5782 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5783 err |= (fprintf(fp, "\n") < 0);
5785 /* write newsgroup list */
5786 if (compose->newsgroup_list) {
5787 err |= (fprintf(fp, "NG:") < 0);
5788 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5789 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5790 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5791 err |= (fprintf(fp, "\n") < 0);
5793 /* Sylpheed account IDs */
5795 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5797 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5800 if (compose->privacy_system != NULL) {
5801 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5802 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5803 if (compose->use_encryption) {
5805 if (!compose_warn_encryption(compose)) {
5812 if (mailac && mailac->encrypt_to_self) {
5813 GSList *tmp_list = g_slist_copy(compose->to_list);
5814 tmp_list = g_slist_append(tmp_list, compose->account->address);
5815 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5816 g_slist_free(tmp_list);
5818 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5820 if (encdata != NULL) {
5821 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5822 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5823 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5825 } /* else we finally dont want to encrypt */
5827 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5828 /* and if encdata was null, it means there's been a problem in
5840 /* Save copy folder */
5841 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5842 gchar *savefolderid;
5844 savefolderid = compose_get_save_to(compose);
5845 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5846 g_free(savefolderid);
5848 /* Save copy folder */
5849 if (compose->return_receipt) {
5850 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5852 /* Message-ID of message replying to */
5853 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5856 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5857 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5860 /* Message-ID of message forwarding to */
5861 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5864 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5865 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5869 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5870 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5872 /* end of headers */
5873 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5875 if (compose->redirect_filename != NULL) {
5876 if (compose_redirect_write_to_file(compose, fp) < 0) {
5885 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5890 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5894 g_warning("failed to write queue message\n");
5901 if (fclose(fp) == EOF) {
5902 FILE_OP_ERROR(tmp, "fclose");
5909 if (item && *item) {
5912 queue = account_get_special_folder(compose->account, F_QUEUE);
5915 g_warning("can't find queue folder\n");
5921 folder_item_scan(queue);
5922 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5923 g_warning("can't queue the message\n");
5930 if (msgpath == NULL) {
5936 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5937 compose_remove_reedit_target(compose, FALSE);
5940 if ((msgnum != NULL) && (item != NULL)) {
5948 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5951 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5953 struct stat statbuf;
5954 gchar *type, *subtype;
5955 GtkTreeModel *model;
5958 model = gtk_tree_view_get_model(tree_view);
5960 if (!gtk_tree_model_get_iter_first(model, &iter))
5963 gtk_tree_model_get(model, &iter,
5967 if (!is_file_exist(ainfo->file)) {
5968 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5969 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5970 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5972 if (val == G_ALERTDEFAULT) {
5977 mimepart = procmime_mimeinfo_new();
5978 mimepart->content = MIMECONTENT_FILE;
5979 mimepart->data.filename = g_strdup(ainfo->file);
5980 mimepart->tmp = FALSE; /* or we destroy our attachment */
5981 mimepart->offset = 0;
5983 g_stat(ainfo->file, &statbuf);
5984 mimepart->length = statbuf.st_size;
5986 type = g_strdup(ainfo->content_type);
5988 if (!strchr(type, '/')) {
5990 type = g_strdup("application/octet-stream");
5993 subtype = strchr(type, '/') + 1;
5994 *(subtype - 1) = '\0';
5995 mimepart->type = procmime_get_media_type(type);
5996 mimepart->subtype = g_strdup(subtype);
5999 if (mimepart->type == MIMETYPE_MESSAGE &&
6000 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6001 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6002 } else if (mimepart->type == MIMETYPE_TEXT) {
6003 if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) {
6004 /* Text parts with no name come from multipart/alternative
6005 * forwards. Make sure the recipient won't look at the
6006 * original HTML part by mistake. */
6007 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6008 ainfo->name = g_strdup_printf(_("Original %s part"),
6012 g_hash_table_insert(mimepart->typeparameters,
6013 g_strdup("charset"), g_strdup(ainfo->charset));
6015 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6016 if (mimepart->type == MIMETYPE_APPLICATION &&
6017 !strcmp2(mimepart->subtype, "octet-stream"))
6018 g_hash_table_insert(mimepart->typeparameters,
6019 g_strdup("name"), g_strdup(ainfo->name));
6020 g_hash_table_insert(mimepart->dispositionparameters,
6021 g_strdup("filename"), g_strdup(ainfo->name));
6022 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6025 if (mimepart->type == MIMETYPE_MESSAGE
6026 || mimepart->type == MIMETYPE_MULTIPART)
6027 ainfo->encoding = ENC_BINARY;
6028 else if (compose->use_signing) {
6029 if (ainfo->encoding == ENC_7BIT)
6030 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6031 else if (ainfo->encoding == ENC_8BIT)
6032 ainfo->encoding = ENC_BASE64;
6037 procmime_encode_content(mimepart, ainfo->encoding);
6039 g_node_append(parent->node, mimepart->node);
6040 } while (gtk_tree_model_iter_next(model, &iter));
6045 #define IS_IN_CUSTOM_HEADER(header) \
6046 (compose->account->add_customhdr && \
6047 custom_header_find(compose->account->customhdr_list, header) != NULL)
6049 static void compose_add_headerfield_from_headerlist(Compose *compose,
6051 const gchar *fieldname,
6052 const gchar *seperator)
6054 gchar *str, *fieldname_w_colon;
6055 gboolean add_field = FALSE;
6057 ComposeHeaderEntry *headerentry;
6058 const gchar *headerentryname;
6059 const gchar *trans_fieldname;
6062 if (IS_IN_CUSTOM_HEADER(fieldname))
6065 debug_print("Adding %s-fields\n", fieldname);
6067 fieldstr = g_string_sized_new(64);
6069 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6070 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6072 for (list = compose->header_list; list; list = list->next) {
6073 headerentry = ((ComposeHeaderEntry *)list->data);
6074 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6076 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6077 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6079 if (str[0] != '\0') {
6081 g_string_append(fieldstr, seperator);
6082 g_string_append(fieldstr, str);
6091 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6092 compose_convert_header
6093 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6094 strlen(fieldname) + 2, TRUE);
6095 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6099 g_free(fieldname_w_colon);
6100 g_string_free(fieldstr, TRUE);
6105 static gchar *compose_get_header(Compose *compose)
6107 gchar buf[BUFFSIZE];
6108 const gchar *entry_str;
6112 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6114 gchar *from_name = NULL, *from_address = NULL;
6117 cm_return_val_if_fail(compose->account != NULL, NULL);
6118 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6120 header = g_string_sized_new(64);
6123 get_rfc822_date(buf, sizeof(buf));
6124 g_string_append_printf(header, "Date: %s\n", buf);
6128 if (compose->account->name && *compose->account->name) {
6130 QUOTE_IF_REQUIRED(buf, compose->account->name);
6131 tmp = g_strdup_printf("%s <%s>",
6132 buf, compose->account->address);
6134 tmp = g_strdup_printf("%s",
6135 compose->account->address);
6137 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6138 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6140 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6141 from_address = g_strdup(compose->account->address);
6143 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6144 /* extract name and address */
6145 if (strstr(spec, " <") && strstr(spec, ">")) {
6146 from_address = g_strdup(strrchr(spec, '<')+1);
6147 *(strrchr(from_address, '>')) = '\0';
6148 from_name = g_strdup(spec);
6149 *(strrchr(from_name, '<')) = '\0';
6152 from_address = g_strdup(spec);
6159 if (from_name && *from_name) {
6160 compose_convert_header
6161 (compose, buf, sizeof(buf), from_name,
6162 strlen("From: "), TRUE);
6163 QUOTE_IF_REQUIRED(name, buf);
6165 g_string_append_printf(header, "From: %s <%s>\n",
6166 name, from_address);
6168 g_string_append_printf(header, "From: %s\n", from_address);
6171 g_free(from_address);
6174 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6177 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6180 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6184 * If this account is a NNTP account remove Bcc header from
6185 * message body since it otherwise will be publicly shown
6187 if (compose->account->protocol != A_NNTP)
6188 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6191 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6193 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6196 compose_convert_header(compose, buf, sizeof(buf), str,
6197 strlen("Subject: "), FALSE);
6198 g_string_append_printf(header, "Subject: %s\n", buf);
6204 if (compose->account->set_domain && compose->account->domain) {
6205 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6206 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6207 g_snprintf(buf, sizeof(buf), "%s",
6208 strchr(compose->account->address, '@') ?
6209 strchr(compose->account->address, '@')+1 :
6210 compose->account->address);
6212 g_snprintf(buf, sizeof(buf), "%s", "");
6215 if (compose->account->gen_msgid) {
6217 if (compose->account->msgid_with_addr) {
6218 addr = compose->account->address;
6220 generate_msgid(buf, sizeof(buf), addr);
6221 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6222 compose->msgid = g_strdup(buf);
6224 compose->msgid = NULL;
6227 if (compose->remove_references == FALSE) {
6229 if (compose->inreplyto && compose->to_list)
6230 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6233 if (compose->references)
6234 g_string_append_printf(header, "References: %s\n", compose->references);
6238 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6241 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6244 if (compose->account->organization &&
6245 strlen(compose->account->organization) &&
6246 !IS_IN_CUSTOM_HEADER("Organization")) {
6247 compose_convert_header(compose, buf, sizeof(buf),
6248 compose->account->organization,
6249 strlen("Organization: "), FALSE);
6250 g_string_append_printf(header, "Organization: %s\n", buf);
6253 /* Program version and system info */
6254 if (compose->account->gen_xmailer &&
6255 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6256 !compose->newsgroup_list) {
6257 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6259 gtk_major_version, gtk_minor_version, gtk_micro_version,
6262 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6263 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6265 gtk_major_version, gtk_minor_version, gtk_micro_version,
6269 /* custom headers */
6270 if (compose->account->add_customhdr) {
6273 for (cur = compose->account->customhdr_list; cur != NULL;
6275 CustomHeader *chdr = (CustomHeader *)cur->data;
6277 if (custom_header_is_allowed(chdr->name)
6278 && chdr->value != NULL
6279 && *(chdr->value) != '\0') {
6280 compose_convert_header
6281 (compose, buf, sizeof(buf),
6283 strlen(chdr->name) + 2, FALSE);
6284 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6289 /* Automatic Faces and X-Faces */
6290 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6291 g_string_append_printf(header, "X-Face: %s\n", buf);
6293 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6294 g_string_append_printf(header, "X-Face: %s\n", buf);
6296 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6297 g_string_append_printf(header, "Face: %s\n", buf);
6299 else if (get_default_face (buf, sizeof(buf)) == 0) {
6300 g_string_append_printf(header, "Face: %s\n", buf);
6304 switch (compose->priority) {
6305 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6306 "X-Priority: 1 (Highest)\n");
6308 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6309 "X-Priority: 2 (High)\n");
6311 case PRIORITY_NORMAL: break;
6312 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6313 "X-Priority: 4 (Low)\n");
6315 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6316 "X-Priority: 5 (Lowest)\n");
6318 default: debug_print("compose: priority unknown : %d\n",
6322 /* Request Return Receipt */
6323 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6324 if (compose->return_receipt) {
6325 if (compose->account->name
6326 && *compose->account->name) {
6327 compose_convert_header(compose, buf, sizeof(buf),
6328 compose->account->name,
6329 strlen("Disposition-Notification-To: "),
6331 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6333 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6337 /* get special headers */
6338 for (list = compose->header_list; list; list = list->next) {
6339 ComposeHeaderEntry *headerentry;
6342 gchar *headername_wcolon;
6343 const gchar *headername_trans;
6346 gboolean standard_header = FALSE;
6348 headerentry = ((ComposeHeaderEntry *)list->data);
6350 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6352 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6357 if (!strstr(tmp, ":")) {
6358 headername_wcolon = g_strconcat(tmp, ":", NULL);
6359 headername = g_strdup(tmp);
6361 headername_wcolon = g_strdup(tmp);
6362 headername = g_strdup(strtok(tmp, ":"));
6366 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6367 Xstrdup_a(headervalue, entry_str, return NULL);
6368 subst_char(headervalue, '\r', ' ');
6369 subst_char(headervalue, '\n', ' ');
6370 string = std_headers;
6371 while (*string != NULL) {
6372 headername_trans = prefs_common_translated_header_name(*string);
6373 if (!strcmp(headername_trans, headername_wcolon))
6374 standard_header = TRUE;
6377 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6378 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6381 g_free(headername_wcolon);
6385 g_string_free(header, FALSE);
6390 #undef IS_IN_CUSTOM_HEADER
6392 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6393 gint header_len, gboolean addr_field)
6395 gchar *tmpstr = NULL;
6396 const gchar *out_codeset = NULL;
6398 cm_return_if_fail(src != NULL);
6399 cm_return_if_fail(dest != NULL);
6401 if (len < 1) return;
6403 tmpstr = g_strdup(src);
6405 subst_char(tmpstr, '\n', ' ');
6406 subst_char(tmpstr, '\r', ' ');
6409 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6410 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6411 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6416 codeconv_set_strict(TRUE);
6417 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6418 conv_get_charset_str(compose->out_encoding));
6419 codeconv_set_strict(FALSE);
6421 if (!dest || *dest == '\0') {
6422 gchar *test_conv_global_out = NULL;
6423 gchar *test_conv_reply = NULL;
6425 /* automatic mode. be automatic. */
6426 codeconv_set_strict(TRUE);
6428 out_codeset = conv_get_outgoing_charset_str();
6430 debug_print("trying to convert to %s\n", out_codeset);
6431 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6434 if (!test_conv_global_out && compose->orig_charset
6435 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6436 out_codeset = compose->orig_charset;
6437 debug_print("failure; trying to convert to %s\n", out_codeset);
6438 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6441 if (!test_conv_global_out && !test_conv_reply) {
6443 out_codeset = CS_INTERNAL;
6444 debug_print("finally using %s\n", out_codeset);
6446 g_free(test_conv_global_out);
6447 g_free(test_conv_reply);
6448 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6450 codeconv_set_strict(FALSE);
6455 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6459 cm_return_if_fail(user_data != NULL);
6461 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6462 g_strstrip(address);
6463 if (*address != '\0') {
6464 gchar *name = procheader_get_fromname(address);
6465 extract_address(address);
6466 addressbook_add_contact(name, address, NULL, NULL);
6471 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6473 GtkWidget *menuitem;
6476 cm_return_if_fail(menu != NULL);
6477 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6479 menuitem = gtk_separator_menu_item_new();
6480 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6481 gtk_widget_show(menuitem);
6483 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6484 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6486 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6487 g_strstrip(address);
6488 if (*address == '\0') {
6489 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6492 g_signal_connect(G_OBJECT(menuitem), "activate",
6493 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6494 gtk_widget_show(menuitem);
6497 static void compose_create_header_entry(Compose *compose)
6499 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6506 const gchar *header = NULL;
6507 ComposeHeaderEntry *headerentry;
6508 gboolean standard_header = FALSE;
6509 GtkListStore *model;
6511 #if !(GTK_CHECK_VERSION(2,12,0))
6512 GtkTooltips *tips = compose->tooltips;
6515 headerentry = g_new0(ComposeHeaderEntry, 1);
6518 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6519 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6520 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6522 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6524 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6526 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6527 COMPOSE_NEWSGROUPS);
6528 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6530 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6531 COMPOSE_FOLLOWUPTO);
6533 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6534 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6535 G_CALLBACK(compose_grab_focus_cb), compose);
6536 gtk_widget_show(combo);
6537 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6538 compose->header_nextrow, compose->header_nextrow+1,
6539 GTK_SHRINK, GTK_FILL, 0, 0);
6540 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6541 const gchar *last_header_entry = gtk_entry_get_text(
6542 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6544 while (*string != NULL) {
6545 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6546 standard_header = TRUE;
6549 if (standard_header)
6550 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6552 if (!compose->header_last || !standard_header) {
6553 switch(compose->account->protocol) {
6555 header = prefs_common_translated_header_name("Newsgroups:");
6558 header = prefs_common_translated_header_name("To:");
6563 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6565 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6566 G_CALLBACK(compose_grab_focus_cb), compose);
6568 /* Entry field with cleanup button */
6569 button = gtk_button_new();
6570 gtk_button_set_image(GTK_BUTTON(button),
6571 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6572 gtk_widget_show(button);
6573 CLAWS_SET_TIP(button,
6574 _("Delete entry contents"));
6575 entry = gtk_entry_new();
6576 gtk_widget_show(entry);
6577 CLAWS_SET_TIP(entry,
6578 _("Use <tab> to autocomplete from addressbook"));
6579 hbox = gtk_hbox_new (FALSE, 0);
6580 gtk_widget_show(hbox);
6581 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6582 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6583 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6584 compose->header_nextrow, compose->header_nextrow+1,
6585 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6587 g_signal_connect(G_OBJECT(entry), "key-press-event",
6588 G_CALLBACK(compose_headerentry_key_press_event_cb),
6590 g_signal_connect(G_OBJECT(entry), "changed",
6591 G_CALLBACK(compose_headerentry_changed_cb),
6593 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6594 G_CALLBACK(compose_grab_focus_cb), compose);
6596 g_signal_connect(G_OBJECT(button), "clicked",
6597 G_CALLBACK(compose_headerentry_button_clicked_cb),
6601 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6602 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6603 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6604 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6605 G_CALLBACK(compose_header_drag_received_cb),
6607 g_signal_connect(G_OBJECT(entry), "drag-drop",
6608 G_CALLBACK(compose_drag_drop),
6610 g_signal_connect(G_OBJECT(entry), "populate-popup",
6611 G_CALLBACK(compose_entry_popup_extend),
6614 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6616 headerentry->compose = compose;
6617 headerentry->combo = combo;
6618 headerentry->entry = entry;
6619 headerentry->button = button;
6620 headerentry->hbox = hbox;
6621 headerentry->headernum = compose->header_nextrow;
6622 headerentry->type = PREF_NONE;
6624 compose->header_nextrow++;
6625 compose->header_last = headerentry;
6626 compose->header_list =
6627 g_slist_append(compose->header_list,
6631 static void compose_add_header_entry(Compose *compose, const gchar *header,
6632 gchar *text, ComposePrefType pref_type)
6634 ComposeHeaderEntry *last_header = compose->header_last;
6635 gchar *tmp = g_strdup(text), *email;
6636 gboolean replyto_hdr;
6638 replyto_hdr = (!strcasecmp(header,
6639 prefs_common_translated_header_name("Reply-To:")) ||
6641 prefs_common_translated_header_name("Followup-To:")) ||
6643 prefs_common_translated_header_name("In-Reply-To:")));
6645 extract_address(tmp);
6646 email = g_utf8_strdown(tmp, -1);
6648 if (replyto_hdr == FALSE &&
6649 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6651 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6652 header, text, (gint) pref_type);
6658 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6659 gtk_entry_set_text(GTK_ENTRY(
6660 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6662 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6663 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6664 last_header->type = pref_type;
6666 if (replyto_hdr == FALSE)
6667 g_hash_table_insert(compose->email_hashtable, email,
6668 GUINT_TO_POINTER(1));
6675 static void compose_destroy_headerentry(Compose *compose,
6676 ComposeHeaderEntry *headerentry)
6678 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6681 extract_address(text);
6682 email = g_utf8_strdown(text, -1);
6683 g_hash_table_remove(compose->email_hashtable, email);
6687 gtk_widget_destroy(headerentry->combo);
6688 gtk_widget_destroy(headerentry->entry);
6689 gtk_widget_destroy(headerentry->button);
6690 gtk_widget_destroy(headerentry->hbox);
6691 g_free(headerentry);
6694 static void compose_remove_header_entries(Compose *compose)
6697 for (list = compose->header_list; list; list = list->next)
6698 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6700 compose->header_last = NULL;
6701 g_slist_free(compose->header_list);
6702 compose->header_list = NULL;
6703 compose->header_nextrow = 1;
6704 compose_create_header_entry(compose);
6707 static GtkWidget *compose_create_header(Compose *compose)
6709 GtkWidget *from_optmenu_hbox;
6710 GtkWidget *header_scrolledwin;
6711 GtkWidget *header_table;
6715 /* header labels and entries */
6716 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6717 gtk_widget_show(header_scrolledwin);
6718 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6720 header_table = gtk_table_new(2, 2, FALSE);
6721 gtk_widget_show(header_table);
6722 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6723 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6724 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6727 /* option menu for selecting accounts */
6728 from_optmenu_hbox = compose_account_option_menu_create(compose);
6729 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6730 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6733 compose->header_table = header_table;
6734 compose->header_list = NULL;
6735 compose->header_nextrow = count;
6737 compose_create_header_entry(compose);
6739 compose->table = NULL;
6741 return header_scrolledwin ;
6744 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6746 Compose *compose = (Compose *)data;
6747 GdkEventButton event;
6750 event.time = gtk_get_current_event_time();
6752 return attach_button_pressed(compose->attach_clist, &event, compose);
6755 static GtkWidget *compose_create_attach(Compose *compose)
6757 GtkWidget *attach_scrwin;
6758 GtkWidget *attach_clist;
6760 GtkListStore *store;
6761 GtkCellRenderer *renderer;
6762 GtkTreeViewColumn *column;
6763 GtkTreeSelection *selection;
6765 /* attachment list */
6766 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6767 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6768 GTK_POLICY_AUTOMATIC,
6769 GTK_POLICY_AUTOMATIC);
6770 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6772 store = gtk_list_store_new(N_ATTACH_COLS,
6778 G_TYPE_AUTO_POINTER,
6780 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6781 (GTK_TREE_MODEL(store)));
6782 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6783 g_object_unref(store);
6785 renderer = gtk_cell_renderer_text_new();
6786 column = gtk_tree_view_column_new_with_attributes
6787 (_("Mime type"), renderer, "text",
6788 COL_MIMETYPE, NULL);
6789 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6791 renderer = gtk_cell_renderer_text_new();
6792 column = gtk_tree_view_column_new_with_attributes
6793 (_("Size"), renderer, "text",
6795 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6797 renderer = gtk_cell_renderer_text_new();
6798 column = gtk_tree_view_column_new_with_attributes
6799 (_("Name"), renderer, "text",
6801 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6803 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6804 prefs_common.use_stripes_everywhere);
6805 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6806 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6808 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6809 G_CALLBACK(attach_selected), compose);
6810 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6811 G_CALLBACK(attach_button_pressed), compose);
6813 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6814 G_CALLBACK(popup_attach_button_pressed), compose);
6816 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6817 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6818 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6819 G_CALLBACK(popup_attach_button_pressed), compose);
6821 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6822 G_CALLBACK(attach_key_pressed), compose);
6825 gtk_drag_dest_set(attach_clist,
6826 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6827 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6828 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6829 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6830 G_CALLBACK(compose_attach_drag_received_cb),
6832 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6833 G_CALLBACK(compose_drag_drop),
6836 compose->attach_scrwin = attach_scrwin;
6837 compose->attach_clist = attach_clist;
6839 return attach_scrwin;
6842 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6843 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6845 static GtkWidget *compose_create_others(Compose *compose)
6848 GtkWidget *savemsg_checkbtn;
6849 GtkWidget *savemsg_combo;
6850 GtkWidget *savemsg_select;
6853 gchar *folderidentifier;
6855 /* Table for settings */
6856 table = gtk_table_new(3, 1, FALSE);
6857 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6858 gtk_widget_show(table);
6859 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6862 /* Save Message to folder */
6863 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6864 gtk_widget_show(savemsg_checkbtn);
6865 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6866 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6867 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6869 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6870 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6872 savemsg_combo = gtk_combo_box_entry_new_text();
6873 compose->savemsg_checkbtn = savemsg_checkbtn;
6874 compose->savemsg_combo = savemsg_combo;
6875 gtk_widget_show(savemsg_combo);
6877 if (prefs_common.compose_save_to_history)
6878 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6879 prefs_common.compose_save_to_history);
6881 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6882 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6883 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6884 G_CALLBACK(compose_grab_focus_cb), compose);
6885 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6886 folderidentifier = folder_item_get_identifier(account_get_special_folder
6887 (compose->account, F_OUTBOX));
6888 compose_set_save_to(compose, folderidentifier);
6889 g_free(folderidentifier);
6892 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6893 gtk_widget_show(savemsg_select);
6894 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6895 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6896 G_CALLBACK(compose_savemsg_select_cb),
6904 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6906 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6907 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6910 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6915 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6918 path = folder_item_get_identifier(dest);
6920 compose_set_save_to(compose, path);
6924 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6925 GdkAtom clip, GtkTextIter *insert_place);
6928 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6932 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6934 if (event->button == 3) {
6936 GtkTextIter sel_start, sel_end;
6937 gboolean stuff_selected;
6939 /* move the cursor to allow GtkAspell to check the word
6940 * under the mouse */
6941 if (event->x && event->y) {
6942 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6943 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6945 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6948 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6949 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6952 stuff_selected = gtk_text_buffer_get_selection_bounds(
6954 &sel_start, &sel_end);
6956 gtk_text_buffer_place_cursor (buffer, &iter);
6957 /* reselect stuff */
6959 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6960 gtk_text_buffer_select_range(buffer,
6961 &sel_start, &sel_end);
6963 return FALSE; /* pass the event so that the right-click goes through */
6966 if (event->button == 2) {
6971 /* get the middle-click position to paste at the correct place */
6972 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6973 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6975 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6978 entry_paste_clipboard(compose, text,
6979 prefs_common.linewrap_pastes,
6980 GDK_SELECTION_PRIMARY, &iter);
6988 static void compose_spell_menu_changed(void *data)
6990 Compose *compose = (Compose *)data;
6992 GtkWidget *menuitem;
6993 GtkWidget *parent_item;
6994 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6997 if (compose->gtkaspell == NULL)
7000 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7001 "/Menu/Spelling/Options");
7003 /* setting the submenu removes /Spelling/Options from the factory
7004 * so we need to save it */
7006 if (parent_item == NULL) {
7007 parent_item = compose->aspell_options_menu;
7008 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7010 compose->aspell_options_menu = parent_item;
7012 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7014 spell_menu = g_slist_reverse(spell_menu);
7015 for (items = spell_menu;
7016 items; items = items->next) {
7017 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7018 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7019 gtk_widget_show(GTK_WIDGET(menuitem));
7021 g_slist_free(spell_menu);
7023 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7024 gtk_widget_show(parent_item);
7027 static void compose_dict_changed(void *data)
7029 Compose *compose = (Compose *) data;
7031 if(compose->gtkaspell &&
7032 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7035 gtkaspell_highlight_all(compose->gtkaspell);
7036 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7040 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7042 Compose *compose = (Compose *)data;
7043 GdkEventButton event;
7046 event.time = gtk_get_current_event_time();
7050 return text_clicked(compose->text, &event, compose);
7053 static gboolean compose_force_window_origin = TRUE;
7054 static Compose *compose_create(PrefsAccount *account,
7063 GtkWidget *handlebox;
7065 GtkWidget *notebook;
7067 GtkWidget *attach_hbox;
7068 GtkWidget *attach_lab1;
7069 GtkWidget *attach_lab2;
7074 GtkWidget *subject_hbox;
7075 GtkWidget *subject_frame;
7076 GtkWidget *subject_entry;
7080 GtkWidget *edit_vbox;
7081 GtkWidget *ruler_hbox;
7083 GtkWidget *scrolledwin;
7085 GtkTextBuffer *buffer;
7086 GtkClipboard *clipboard;
7088 UndoMain *undostruct;
7090 gchar *titles[N_ATTACH_COLS];
7091 GtkWidget *popupmenu;
7092 GtkWidget *tmpl_menu;
7093 GtkActionGroup *action_group = NULL;
7096 GtkAspell * gtkaspell = NULL;
7099 static GdkGeometry geometry;
7101 cm_return_val_if_fail(account != NULL, NULL);
7103 debug_print("Creating compose window...\n");
7104 compose = g_new0(Compose, 1);
7106 titles[COL_MIMETYPE] = _("MIME type");
7107 titles[COL_SIZE] = _("Size");
7108 titles[COL_NAME] = _("Name");
7109 titles[COL_CHARSET] = _("Charset");
7111 compose->batch = batch;
7112 compose->account = account;
7113 compose->folder = folder;
7115 compose->mutex = g_mutex_new();
7116 compose->set_cursor_pos = -1;
7118 #if !(GTK_CHECK_VERSION(2,12,0))
7119 compose->tooltips = tips;
7122 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7124 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7125 gtk_widget_set_size_request(window, prefs_common.compose_width,
7126 prefs_common.compose_height);
7128 if (!geometry.max_width) {
7129 geometry.max_width = gdk_screen_width();
7130 geometry.max_height = gdk_screen_height();
7133 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7134 &geometry, GDK_HINT_MAX_SIZE);
7135 if (!geometry.min_width) {
7136 geometry.min_width = 600;
7137 geometry.min_height = 440;
7139 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7140 &geometry, GDK_HINT_MIN_SIZE);
7142 #ifndef GENERIC_UMPC
7143 if (compose_force_window_origin)
7144 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7145 prefs_common.compose_y);
7147 g_signal_connect(G_OBJECT(window), "delete_event",
7148 G_CALLBACK(compose_delete_cb), compose);
7149 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7150 gtk_widget_realize(window);
7152 gtkut_widget_set_composer_icon(window);
7154 vbox = gtk_vbox_new(FALSE, 0);
7155 gtk_container_add(GTK_CONTAINER(window), vbox);
7157 compose->ui_manager = gtk_ui_manager_new();
7158 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7159 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7160 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7161 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7162 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7163 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7164 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7165 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7166 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7167 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7185 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7187 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7189 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7190 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7191 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7198 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7199 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7206 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7212 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7213 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7221 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7238 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7242 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7244 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7245 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7246 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7252 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7254 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7258 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7260 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7264 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7266 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7267 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7268 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7270 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7274 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7280 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7281 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7286 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7287 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)
7288 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)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7294 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)
7295 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)
7297 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7299 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7300 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)
7301 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7303 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7304 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)
7305 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7307 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7309 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7310 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)
7311 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7312 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7313 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7315 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7316 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)
7317 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)
7318 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7319 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7321 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7322 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7323 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7324 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7325 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7326 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7328 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7330 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)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7341 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7348 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7349 gtk_widget_show_all(menubar);
7351 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7353 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7355 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7358 if (prefs_common.toolbar_detachable) {
7359 handlebox = gtk_handle_box_new();
7361 handlebox = gtk_hbox_new(FALSE, 0);
7363 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7365 gtk_widget_realize(handlebox);
7367 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7370 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7374 vbox2 = gtk_vbox_new(FALSE, 2);
7375 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7376 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7379 notebook = gtk_notebook_new();
7380 gtk_widget_set_size_request(notebook, -1, 130);
7381 gtk_widget_show(notebook);
7383 /* header labels and entries */
7384 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7385 compose_create_header(compose),
7386 gtk_label_new_with_mnemonic(_("Hea_der")));
7387 /* attachment list */
7388 attach_hbox = gtk_hbox_new(FALSE, 0);
7389 gtk_widget_show(attach_hbox);
7391 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7392 gtk_widget_show(attach_lab1);
7393 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7395 attach_lab2 = gtk_label_new("");
7396 gtk_widget_show(attach_lab2);
7397 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7399 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7400 compose_create_attach(compose),
7403 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7404 compose_create_others(compose),
7405 gtk_label_new_with_mnemonic(_("Othe_rs")));
7408 subject_hbox = gtk_hbox_new(FALSE, 0);
7409 gtk_widget_show(subject_hbox);
7411 subject_frame = gtk_frame_new(NULL);
7412 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7413 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7414 gtk_widget_show(subject_frame);
7416 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7417 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7418 gtk_widget_show(subject);
7420 label = gtk_label_new(_("Subject:"));
7421 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7422 gtk_widget_show(label);
7425 subject_entry = claws_spell_entry_new();
7427 subject_entry = gtk_entry_new();
7429 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7430 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7431 G_CALLBACK(compose_grab_focus_cb), compose);
7432 gtk_widget_show(subject_entry);
7433 compose->subject_entry = subject_entry;
7434 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7436 edit_vbox = gtk_vbox_new(FALSE, 0);
7438 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7441 ruler_hbox = gtk_hbox_new(FALSE, 0);
7442 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7444 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7445 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7446 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7450 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7451 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7452 GTK_POLICY_AUTOMATIC,
7453 GTK_POLICY_AUTOMATIC);
7454 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7456 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7458 text = gtk_text_view_new();
7459 if (prefs_common.show_compose_margin) {
7460 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7461 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7463 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7464 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7465 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7466 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7467 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7469 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7470 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7471 G_CALLBACK(compose_edit_size_alloc),
7473 g_signal_connect(G_OBJECT(buffer), "changed",
7474 G_CALLBACK(compose_changed_cb), compose);
7475 g_signal_connect(G_OBJECT(text), "grab_focus",
7476 G_CALLBACK(compose_grab_focus_cb), compose);
7477 g_signal_connect(G_OBJECT(buffer), "insert_text",
7478 G_CALLBACK(text_inserted), compose);
7479 g_signal_connect(G_OBJECT(text), "button_press_event",
7480 G_CALLBACK(text_clicked), compose);
7482 g_signal_connect(G_OBJECT(text), "popup-menu",
7483 G_CALLBACK(compose_popup_menu), compose);
7485 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7486 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7487 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7488 G_CALLBACK(compose_popup_menu), compose);
7490 g_signal_connect(G_OBJECT(subject_entry), "changed",
7491 G_CALLBACK(compose_changed_cb), compose);
7494 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7495 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7496 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7497 g_signal_connect(G_OBJECT(text), "drag_data_received",
7498 G_CALLBACK(compose_insert_drag_received_cb),
7500 g_signal_connect(G_OBJECT(text), "drag-drop",
7501 G_CALLBACK(compose_drag_drop),
7503 gtk_widget_show_all(vbox);
7505 /* pane between attach clist and text */
7506 paned = gtk_vpaned_new();
7507 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7509 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7510 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7512 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7514 gtk_paned_add1(GTK_PANED(paned), notebook);
7515 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7516 gtk_widget_show_all(paned);
7519 if (prefs_common.textfont) {
7520 PangoFontDescription *font_desc;
7522 font_desc = pango_font_description_from_string
7523 (prefs_common.textfont);
7525 gtk_widget_modify_font(text, font_desc);
7526 pango_font_description_free(font_desc);
7530 gtk_action_group_add_actions(action_group, compose_popup_entries,
7531 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7532 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7533 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7534 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7535 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7536 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7537 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7539 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7541 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7542 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7543 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7545 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7547 undostruct = undo_init(text);
7548 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7551 address_completion_start(window);
7553 compose->window = window;
7554 compose->vbox = vbox;
7555 compose->menubar = menubar;
7556 compose->handlebox = handlebox;
7558 compose->vbox2 = vbox2;
7560 compose->paned = paned;
7562 compose->attach_label = attach_lab2;
7564 compose->notebook = notebook;
7565 compose->edit_vbox = edit_vbox;
7566 compose->ruler_hbox = ruler_hbox;
7567 compose->ruler = ruler;
7568 compose->scrolledwin = scrolledwin;
7569 compose->text = text;
7571 compose->focused_editable = NULL;
7573 compose->popupmenu = popupmenu;
7575 compose->tmpl_menu = tmpl_menu;
7577 compose->mode = mode;
7578 compose->rmode = mode;
7580 compose->targetinfo = NULL;
7581 compose->replyinfo = NULL;
7582 compose->fwdinfo = NULL;
7584 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7585 g_str_equal, (GDestroyNotify) g_free, NULL);
7587 compose->replyto = NULL;
7589 compose->bcc = NULL;
7590 compose->followup_to = NULL;
7592 compose->ml_post = NULL;
7594 compose->inreplyto = NULL;
7595 compose->references = NULL;
7596 compose->msgid = NULL;
7597 compose->boundary = NULL;
7599 compose->autowrap = prefs_common.autowrap;
7600 compose->autoindent = prefs_common.auto_indent;
7601 compose->use_signing = FALSE;
7602 compose->use_encryption = FALSE;
7603 compose->privacy_system = NULL;
7605 compose->modified = FALSE;
7607 compose->return_receipt = FALSE;
7609 compose->to_list = NULL;
7610 compose->newsgroup_list = NULL;
7612 compose->undostruct = undostruct;
7614 compose->sig_str = NULL;
7616 compose->exteditor_file = NULL;
7617 compose->exteditor_pid = -1;
7618 compose->exteditor_tag = -1;
7619 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7622 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7623 if (mode != COMPOSE_REDIRECT) {
7624 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7625 strcmp(prefs_common.dictionary, "")) {
7626 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7627 prefs_common.alt_dictionary,
7628 conv_get_locale_charset_str(),
7629 prefs_common.misspelled_col,
7630 prefs_common.check_while_typing,
7631 prefs_common.recheck_when_changing_dict,
7632 prefs_common.use_alternate,
7633 prefs_common.use_both_dicts,
7634 GTK_TEXT_VIEW(text),
7635 GTK_WINDOW(compose->window),
7636 compose_dict_changed,
7637 compose_spell_menu_changed,
7640 alertpanel_error(_("Spell checker could not "
7642 gtkaspell_checkers_strerror());
7643 gtkaspell_checkers_reset_error();
7645 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7649 compose->gtkaspell = gtkaspell;
7650 compose_spell_menu_changed(compose);
7651 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7654 compose_select_account(compose, account, TRUE);
7656 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7657 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7659 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7660 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7662 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7663 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7665 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7666 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7668 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7669 if (account->protocol != A_NNTP)
7670 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7671 prefs_common_translated_header_name("To:"));
7673 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7674 prefs_common_translated_header_name("Newsgroups:"));
7676 addressbook_set_target_compose(compose);
7678 if (mode != COMPOSE_REDIRECT)
7679 compose_set_template_menu(compose);
7681 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7684 compose_list = g_list_append(compose_list, compose);
7686 if (!prefs_common.show_ruler)
7687 gtk_widget_hide(ruler_hbox);
7689 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7692 compose->priority = PRIORITY_NORMAL;
7693 compose_update_priority_menu_item(compose);
7695 compose_set_out_encoding(compose);
7698 compose_update_actions_menu(compose);
7700 /* Privacy Systems menu */
7701 compose_update_privacy_systems_menu(compose);
7703 activate_privacy_system(compose, account, TRUE);
7704 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7706 gtk_widget_realize(window);
7708 gtk_widget_show(window);
7710 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7711 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7718 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7723 GtkWidget *optmenubox;
7726 GtkWidget *from_name = NULL;
7727 #if !(GTK_CHECK_VERSION(2,12,0))
7728 GtkTooltips *tips = compose->tooltips;
7731 gint num = 0, def_menu = 0;
7733 accounts = account_get_list();
7734 cm_return_val_if_fail(accounts != NULL, NULL);
7736 optmenubox = gtk_event_box_new();
7737 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7738 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7740 hbox = gtk_hbox_new(FALSE, 6);
7741 from_name = gtk_entry_new();
7743 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7744 G_CALLBACK(compose_grab_focus_cb), compose);
7746 for (; accounts != NULL; accounts = accounts->next, num++) {
7747 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7748 gchar *name, *from = NULL;
7750 if (ac == compose->account) def_menu = num;
7752 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7755 if (ac == compose->account) {
7756 if (ac->name && *ac->name) {
7758 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7759 from = g_strdup_printf("%s <%s>",
7761 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7763 from = g_strdup_printf("%s",
7765 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7768 COMBOBOX_ADD(menu, name, ac->account_id);
7773 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7775 g_signal_connect(G_OBJECT(optmenu), "changed",
7776 G_CALLBACK(account_activated),
7778 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7779 G_CALLBACK(compose_entry_popup_extend),
7782 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7783 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7785 CLAWS_SET_TIP(optmenubox,
7786 _("Account to use for this email"));
7787 CLAWS_SET_TIP(from_name,
7788 _("Sender address to be used"));
7790 compose->account_combo = optmenu;
7791 compose->from_name = from_name;
7796 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7798 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7799 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7800 Compose *compose = (Compose *) data;
7802 compose->priority = value;
7806 static void compose_reply_change_mode(Compose *compose,
7809 gboolean was_modified = compose->modified;
7811 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7813 cm_return_if_fail(compose->replyinfo != NULL);
7815 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7817 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7819 if (action == COMPOSE_REPLY_TO_ALL)
7821 if (action == COMPOSE_REPLY_TO_SENDER)
7823 if (action == COMPOSE_REPLY_TO_LIST)
7826 compose_remove_header_entries(compose);
7827 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7828 if (compose->account->set_autocc && compose->account->auto_cc)
7829 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7831 if (compose->account->set_autobcc && compose->account->auto_bcc)
7832 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7834 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7835 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7836 compose_show_first_last_header(compose, TRUE);
7837 compose->modified = was_modified;
7838 compose_set_title(compose);
7841 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7843 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7844 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7845 Compose *compose = (Compose *) data;
7848 compose_reply_change_mode(compose, value);
7851 static void compose_update_priority_menu_item(Compose * compose)
7853 GtkWidget *menuitem = NULL;
7854 switch (compose->priority) {
7855 case PRIORITY_HIGHEST:
7856 menuitem = gtk_ui_manager_get_widget
7857 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7860 menuitem = gtk_ui_manager_get_widget
7861 (compose->ui_manager, "/Menu/Options/Priority/High");
7863 case PRIORITY_NORMAL:
7864 menuitem = gtk_ui_manager_get_widget
7865 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7868 menuitem = gtk_ui_manager_get_widget
7869 (compose->ui_manager, "/Menu/Options/Priority/Low");
7871 case PRIORITY_LOWEST:
7872 menuitem = gtk_ui_manager_get_widget
7873 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7876 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7879 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7881 Compose *compose = (Compose *) data;
7883 gboolean can_sign = FALSE, can_encrypt = FALSE;
7885 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7887 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
7890 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7891 g_free(compose->privacy_system);
7892 compose->privacy_system = NULL;
7893 if (systemid != NULL) {
7894 compose->privacy_system = g_strdup(systemid);
7896 can_sign = privacy_system_can_sign(systemid);
7897 can_encrypt = privacy_system_can_encrypt(systemid);
7900 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7902 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7903 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7906 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7908 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7909 GtkWidget *menuitem = NULL;
7910 GList *children, *amenu;
7911 gboolean can_sign = FALSE, can_encrypt = FALSE;
7912 gboolean found = FALSE;
7914 if (compose->privacy_system != NULL) {
7916 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7917 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7918 cm_return_if_fail(menuitem != NULL);
7920 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
7923 while (amenu != NULL) {
7924 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7925 if (systemid != NULL) {
7926 if (strcmp(systemid, compose->privacy_system) == 0 &&
7927 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7928 menuitem = GTK_WIDGET(amenu->data);
7930 can_sign = privacy_system_can_sign(systemid);
7931 can_encrypt = privacy_system_can_encrypt(systemid);
7935 } else if (strlen(compose->privacy_system) == 0 &&
7936 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7937 menuitem = GTK_WIDGET(amenu->data);
7940 can_encrypt = FALSE;
7945 amenu = amenu->next;
7947 g_list_free(children);
7948 if (menuitem != NULL)
7949 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7951 if (warn && !found && strlen(compose->privacy_system)) {
7952 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7953 "will not be able to sign or encrypt this message."),
7954 compose->privacy_system);
7958 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7959 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7962 static void compose_set_out_encoding(Compose *compose)
7964 CharSet out_encoding;
7965 const gchar *branch = NULL;
7966 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7968 switch(out_encoding) {
7969 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7970 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7971 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7972 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7973 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7974 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7975 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7976 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7977 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7978 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7979 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7980 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7981 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7982 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7983 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7984 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7985 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7986 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7987 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7988 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7989 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7990 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7991 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7992 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7993 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7994 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7995 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7996 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7997 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7998 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7999 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8000 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8001 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8003 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8006 static void compose_set_template_menu(Compose *compose)
8008 GSList *tmpl_list, *cur;
8012 tmpl_list = template_get_config();
8014 menu = gtk_menu_new();
8016 gtk_menu_set_accel_group (GTK_MENU (menu),
8017 gtk_ui_manager_get_accel_group(compose->ui_manager));
8018 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8019 Template *tmpl = (Template *)cur->data;
8020 gchar *accel_path = NULL;
8021 item = gtk_menu_item_new_with_label(tmpl->name);
8022 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8023 g_signal_connect(G_OBJECT(item), "activate",
8024 G_CALLBACK(compose_template_activate_cb),
8026 g_object_set_data(G_OBJECT(item), "template", tmpl);
8027 gtk_widget_show(item);
8028 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8029 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8033 gtk_widget_show(menu);
8034 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8037 void compose_update_actions_menu(Compose *compose)
8039 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8042 static void compose_update_privacy_systems_menu(Compose *compose)
8044 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8045 GSList *systems, *cur;
8047 GtkWidget *system_none;
8049 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8050 GtkWidget *privacy_menu = gtk_menu_new();
8052 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8053 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8055 g_signal_connect(G_OBJECT(system_none), "activate",
8056 G_CALLBACK(compose_set_privacy_system_cb), compose);
8058 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8059 gtk_widget_show(system_none);
8061 systems = privacy_get_system_ids();
8062 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8063 gchar *systemid = cur->data;
8065 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8066 widget = gtk_radio_menu_item_new_with_label(group,
8067 privacy_system_get_name(systemid));
8068 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8069 g_strdup(systemid), g_free);
8070 g_signal_connect(G_OBJECT(widget), "activate",
8071 G_CALLBACK(compose_set_privacy_system_cb), compose);
8073 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8074 gtk_widget_show(widget);
8077 g_slist_free(systems);
8078 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8079 gtk_widget_show_all(privacy_menu);
8080 gtk_widget_show_all(privacy_menuitem);
8083 void compose_reflect_prefs_all(void)
8088 for (cur = compose_list; cur != NULL; cur = cur->next) {
8089 compose = (Compose *)cur->data;
8090 compose_set_template_menu(compose);
8094 void compose_reflect_prefs_pixmap_theme(void)
8099 for (cur = compose_list; cur != NULL; cur = cur->next) {
8100 compose = (Compose *)cur->data;
8101 toolbar_update(TOOLBAR_COMPOSE, compose);
8105 static const gchar *compose_quote_char_from_context(Compose *compose)
8107 const gchar *qmark = NULL;
8109 cm_return_val_if_fail(compose != NULL, NULL);
8111 switch (compose->mode) {
8112 /* use forward-specific quote char */
8113 case COMPOSE_FORWARD:
8114 case COMPOSE_FORWARD_AS_ATTACH:
8115 case COMPOSE_FORWARD_INLINE:
8116 if (compose->folder && compose->folder->prefs &&
8117 compose->folder->prefs->forward_with_format)
8118 qmark = compose->folder->prefs->forward_quotemark;
8119 else if (compose->account->forward_with_format)
8120 qmark = compose->account->forward_quotemark;
8122 qmark = prefs_common.fw_quotemark;
8125 /* use reply-specific quote char in all other modes */
8127 if (compose->folder && compose->folder->prefs &&
8128 compose->folder->prefs->reply_with_format)
8129 qmark = compose->folder->prefs->reply_quotemark;
8130 else if (compose->account->reply_with_format)
8131 qmark = compose->account->reply_quotemark;
8133 qmark = prefs_common.quotemark;
8137 if (qmark == NULL || *qmark == '\0')
8143 static void compose_template_apply(Compose *compose, Template *tmpl,
8147 GtkTextBuffer *buffer;
8151 gchar *parsed_str = NULL;
8152 gint cursor_pos = 0;
8153 const gchar *err_msg = _("The body of the template has an error at line %d.");
8156 /* process the body */
8158 text = GTK_TEXT_VIEW(compose->text);
8159 buffer = gtk_text_view_get_buffer(text);
8162 qmark = compose_quote_char_from_context(compose);
8164 if (compose->replyinfo != NULL) {
8167 gtk_text_buffer_set_text(buffer, "", -1);
8168 mark = gtk_text_buffer_get_insert(buffer);
8169 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8171 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8172 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8174 } else if (compose->fwdinfo != NULL) {
8177 gtk_text_buffer_set_text(buffer, "", -1);
8178 mark = gtk_text_buffer_get_insert(buffer);
8179 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8181 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8182 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8185 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8187 GtkTextIter start, end;
8190 gtk_text_buffer_get_start_iter(buffer, &start);
8191 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8192 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8194 /* clear the buffer now */
8196 gtk_text_buffer_set_text(buffer, "", -1);
8198 parsed_str = compose_quote_fmt(compose, dummyinfo,
8199 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8200 procmsg_msginfo_free( dummyinfo );
8206 gtk_text_buffer_set_text(buffer, "", -1);
8207 mark = gtk_text_buffer_get_insert(buffer);
8208 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8211 if (replace && parsed_str && compose->account->auto_sig)
8212 compose_insert_sig(compose, FALSE);
8214 if (replace && parsed_str) {
8215 gtk_text_buffer_get_start_iter(buffer, &iter);
8216 gtk_text_buffer_place_cursor(buffer, &iter);
8220 cursor_pos = quote_fmt_get_cursor_pos();
8221 compose->set_cursor_pos = cursor_pos;
8222 if (cursor_pos == -1)
8224 gtk_text_buffer_get_start_iter(buffer, &iter);
8225 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8226 gtk_text_buffer_place_cursor(buffer, &iter);
8229 /* process the other fields */
8231 compose_template_apply_fields(compose, tmpl);
8232 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8233 quote_fmt_reset_vartable();
8234 compose_changed_cb(NULL, compose);
8237 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8238 gtkaspell_highlight_all(compose->gtkaspell);
8242 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8244 MsgInfo* dummyinfo = NULL;
8245 MsgInfo *msginfo = NULL;
8248 if (compose->replyinfo != NULL)
8249 msginfo = compose->replyinfo;
8250 else if (compose->fwdinfo != NULL)
8251 msginfo = compose->fwdinfo;
8253 dummyinfo = compose_msginfo_new_from_compose(compose);
8254 msginfo = dummyinfo;
8257 if (tmpl->from && *tmpl->from != '\0') {
8259 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8260 compose->gtkaspell);
8262 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8264 quote_fmt_scan_string(tmpl->from);
8267 buf = quote_fmt_get_buffer();
8269 alertpanel_error(_("Template From format error."));
8271 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8275 if (tmpl->to && *tmpl->to != '\0') {
8277 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8278 compose->gtkaspell);
8280 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8282 quote_fmt_scan_string(tmpl->to);
8285 buf = quote_fmt_get_buffer();
8287 alertpanel_error(_("Template To format error."));
8289 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8293 if (tmpl->cc && *tmpl->cc != '\0') {
8295 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8296 compose->gtkaspell);
8298 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8300 quote_fmt_scan_string(tmpl->cc);
8303 buf = quote_fmt_get_buffer();
8305 alertpanel_error(_("Template Cc format error."));
8307 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8311 if (tmpl->bcc && *tmpl->bcc != '\0') {
8313 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8314 compose->gtkaspell);
8316 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8318 quote_fmt_scan_string(tmpl->bcc);
8321 buf = quote_fmt_get_buffer();
8323 alertpanel_error(_("Template Bcc format error."));
8325 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8329 /* process the subject */
8330 if (tmpl->subject && *tmpl->subject != '\0') {
8332 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8333 compose->gtkaspell);
8335 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8337 quote_fmt_scan_string(tmpl->subject);
8340 buf = quote_fmt_get_buffer();
8342 alertpanel_error(_("Template subject format error."));
8344 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8348 procmsg_msginfo_free( dummyinfo );
8351 static void compose_destroy(Compose *compose)
8353 GtkAllocation allocation;
8354 GtkTextBuffer *buffer;
8355 GtkClipboard *clipboard;
8357 compose_list = g_list_remove(compose_list, compose);
8359 if (compose->updating) {
8360 debug_print("danger, not destroying anything now\n");
8361 compose->deferred_destroy = TRUE;
8364 /* NOTE: address_completion_end() does nothing with the window
8365 * however this may change. */
8366 address_completion_end(compose->window);
8368 slist_free_strings(compose->to_list);
8369 g_slist_free(compose->to_list);
8370 slist_free_strings(compose->newsgroup_list);
8371 g_slist_free(compose->newsgroup_list);
8372 slist_free_strings(compose->header_list);
8373 g_slist_free(compose->header_list);
8375 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8377 g_hash_table_destroy(compose->email_hashtable);
8379 procmsg_msginfo_free(compose->targetinfo);
8380 procmsg_msginfo_free(compose->replyinfo);
8381 procmsg_msginfo_free(compose->fwdinfo);
8383 g_free(compose->replyto);
8384 g_free(compose->cc);
8385 g_free(compose->bcc);
8386 g_free(compose->newsgroups);
8387 g_free(compose->followup_to);
8389 g_free(compose->ml_post);
8391 g_free(compose->inreplyto);
8392 g_free(compose->references);
8393 g_free(compose->msgid);
8394 g_free(compose->boundary);
8396 g_free(compose->redirect_filename);
8397 if (compose->undostruct)
8398 undo_destroy(compose->undostruct);
8400 g_free(compose->sig_str);
8402 g_free(compose->exteditor_file);
8404 g_free(compose->orig_charset);
8406 g_free(compose->privacy_system);
8408 if (addressbook_get_target_compose() == compose)
8409 addressbook_set_target_compose(NULL);
8412 if (compose->gtkaspell) {
8413 gtkaspell_delete(compose->gtkaspell);
8414 compose->gtkaspell = NULL;
8418 if (!compose->batch) {
8419 gtk_widget_get_allocation(compose->window, &allocation);
8420 prefs_common.compose_width = allocation.width;
8421 prefs_common.compose_height = allocation.height;
8424 if (!gtk_widget_get_parent(compose->paned))
8425 gtk_widget_destroy(compose->paned);
8426 gtk_widget_destroy(compose->popupmenu);
8428 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8429 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8430 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8432 gtk_widget_destroy(compose->window);
8433 toolbar_destroy(compose->toolbar);
8434 g_free(compose->toolbar);
8435 g_mutex_free(compose->mutex);
8439 static void compose_attach_info_free(AttachInfo *ainfo)
8441 g_free(ainfo->file);
8442 g_free(ainfo->content_type);
8443 g_free(ainfo->name);
8444 g_free(ainfo->charset);
8448 static void compose_attach_update_label(Compose *compose)
8453 GtkTreeModel *model;
8458 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8459 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8460 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8464 while(gtk_tree_model_iter_next(model, &iter))
8467 text = g_strdup_printf("(%d)", i);
8468 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8472 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8474 Compose *compose = (Compose *)data;
8475 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8476 GtkTreeSelection *selection;
8478 GtkTreeModel *model;
8480 selection = gtk_tree_view_get_selection(tree_view);
8481 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8486 for (cur = sel; cur != NULL; cur = cur->next) {
8487 GtkTreePath *path = cur->data;
8488 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8491 gtk_tree_path_free(path);
8494 for (cur = sel; cur != NULL; cur = cur->next) {
8495 GtkTreeRowReference *ref = cur->data;
8496 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8499 if (gtk_tree_model_get_iter(model, &iter, path))
8500 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8502 gtk_tree_path_free(path);
8503 gtk_tree_row_reference_free(ref);
8507 compose_attach_update_label(compose);
8510 static struct _AttachProperty
8513 GtkWidget *mimetype_entry;
8514 GtkWidget *encoding_optmenu;
8515 GtkWidget *path_entry;
8516 GtkWidget *filename_entry;
8518 GtkWidget *cancel_btn;
8521 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8523 gtk_tree_path_free((GtkTreePath *)ptr);
8526 static void compose_attach_property(GtkAction *action, gpointer data)
8528 Compose *compose = (Compose *)data;
8529 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8531 GtkComboBox *optmenu;
8532 GtkTreeSelection *selection;
8534 GtkTreeModel *model;
8537 static gboolean cancelled;
8539 /* only if one selected */
8540 selection = gtk_tree_view_get_selection(tree_view);
8541 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8544 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8548 path = (GtkTreePath *) sel->data;
8549 gtk_tree_model_get_iter(model, &iter, path);
8550 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8553 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8559 if (!attach_prop.window)
8560 compose_attach_property_create(&cancelled);
8561 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8562 gtk_widget_grab_focus(attach_prop.ok_btn);
8563 gtk_widget_show(attach_prop.window);
8564 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8566 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8567 if (ainfo->encoding == ENC_UNKNOWN)
8568 combobox_select_by_data(optmenu, ENC_BASE64);
8570 combobox_select_by_data(optmenu, ainfo->encoding);
8572 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8573 ainfo->content_type ? ainfo->content_type : "");
8574 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8575 ainfo->file ? ainfo->file : "");
8576 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8577 ainfo->name ? ainfo->name : "");
8580 const gchar *entry_text;
8582 gchar *cnttype = NULL;
8589 gtk_widget_hide(attach_prop.window);
8590 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8595 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8596 if (*entry_text != '\0') {
8599 text = g_strstrip(g_strdup(entry_text));
8600 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8601 cnttype = g_strdup(text);
8604 alertpanel_error(_("Invalid MIME type."));
8610 ainfo->encoding = combobox_get_active_data(optmenu);
8612 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8613 if (*entry_text != '\0') {
8614 if (is_file_exist(entry_text) &&
8615 (size = get_file_size(entry_text)) > 0)
8616 file = g_strdup(entry_text);
8619 (_("File doesn't exist or is empty."));
8625 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8626 if (*entry_text != '\0') {
8627 g_free(ainfo->name);
8628 ainfo->name = g_strdup(entry_text);
8632 g_free(ainfo->content_type);
8633 ainfo->content_type = cnttype;
8636 g_free(ainfo->file);
8640 ainfo->size = (goffset)size;
8642 /* update tree store */
8643 text = to_human_readable(ainfo->size);
8644 gtk_tree_model_get_iter(model, &iter, path);
8645 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8646 COL_MIMETYPE, ainfo->content_type,
8648 COL_NAME, ainfo->name,
8649 COL_CHARSET, ainfo->charset,
8655 gtk_tree_path_free(path);
8658 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8660 label = gtk_label_new(str); \
8661 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8662 GTK_FILL, 0, 0, 0); \
8663 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8665 entry = gtk_entry_new(); \
8666 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8667 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8670 static void compose_attach_property_create(gboolean *cancelled)
8676 GtkWidget *mimetype_entry;
8679 GtkListStore *optmenu_menu;
8680 GtkWidget *path_entry;
8681 GtkWidget *filename_entry;
8684 GtkWidget *cancel_btn;
8685 GList *mime_type_list, *strlist;
8688 debug_print("Creating attach_property window...\n");
8690 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8691 gtk_widget_set_size_request(window, 480, -1);
8692 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8693 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8694 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8695 g_signal_connect(G_OBJECT(window), "delete_event",
8696 G_CALLBACK(attach_property_delete_event),
8698 g_signal_connect(G_OBJECT(window), "key_press_event",
8699 G_CALLBACK(attach_property_key_pressed),
8702 vbox = gtk_vbox_new(FALSE, 8);
8703 gtk_container_add(GTK_CONTAINER(window), vbox);
8705 table = gtk_table_new(4, 2, FALSE);
8706 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8707 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8708 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8710 label = gtk_label_new(_("MIME type"));
8711 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8713 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8714 mimetype_entry = gtk_combo_box_entry_new_text();
8715 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8716 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8718 /* stuff with list */
8719 mime_type_list = procmime_get_mime_type_list();
8721 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8722 MimeType *type = (MimeType *) mime_type_list->data;
8725 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8727 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8730 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8731 (GCompareFunc)strcmp2);
8734 for (mime_type_list = strlist; mime_type_list != NULL;
8735 mime_type_list = mime_type_list->next) {
8736 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8737 g_free(mime_type_list->data);
8739 g_list_free(strlist);
8740 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8741 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8743 label = gtk_label_new(_("Encoding"));
8744 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8746 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8748 hbox = gtk_hbox_new(FALSE, 0);
8749 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8750 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8752 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8753 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8755 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8756 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8757 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8758 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8759 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8761 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8763 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8764 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8766 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8767 &ok_btn, GTK_STOCK_OK,
8769 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8770 gtk_widget_grab_default(ok_btn);
8772 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8773 G_CALLBACK(attach_property_ok),
8775 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8776 G_CALLBACK(attach_property_cancel),
8779 gtk_widget_show_all(vbox);
8781 attach_prop.window = window;
8782 attach_prop.mimetype_entry = mimetype_entry;
8783 attach_prop.encoding_optmenu = optmenu;
8784 attach_prop.path_entry = path_entry;
8785 attach_prop.filename_entry = filename_entry;
8786 attach_prop.ok_btn = ok_btn;
8787 attach_prop.cancel_btn = cancel_btn;
8790 #undef SET_LABEL_AND_ENTRY
8792 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8798 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8804 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8805 gboolean *cancelled)
8813 static gboolean attach_property_key_pressed(GtkWidget *widget,
8815 gboolean *cancelled)
8817 if (event && event->keyval == GDK_KEY_Escape) {
8821 if (event && event->keyval == GDK_KEY_Return) {
8829 static void compose_exec_ext_editor(Compose *compose)
8836 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8837 G_DIR_SEPARATOR, compose);
8839 if (pipe(pipe_fds) < 0) {
8845 if ((pid = fork()) < 0) {
8852 /* close the write side of the pipe */
8855 compose->exteditor_file = g_strdup(tmp);
8856 compose->exteditor_pid = pid;
8858 compose_set_ext_editor_sensitive(compose, FALSE);
8861 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8863 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8865 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8869 } else { /* process-monitoring process */
8875 /* close the read side of the pipe */
8878 if (compose_write_body_to_file(compose, tmp) < 0) {
8879 fd_write_all(pipe_fds[1], "2\n", 2);
8883 pid_ed = compose_exec_ext_editor_real(tmp);
8885 fd_write_all(pipe_fds[1], "1\n", 2);
8889 /* wait until editor is terminated */
8890 waitpid(pid_ed, NULL, 0);
8892 fd_write_all(pipe_fds[1], "0\n", 2);
8899 #endif /* G_OS_UNIX */
8903 static gint compose_exec_ext_editor_real(const gchar *file)
8910 cm_return_val_if_fail(file != NULL, -1);
8912 if ((pid = fork()) < 0) {
8917 if (pid != 0) return pid;
8919 /* grandchild process */
8921 if (setpgid(0, getppid()))
8924 if (prefs_common_get_ext_editor_cmd() &&
8925 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8926 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8927 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8929 if (prefs_common_get_ext_editor_cmd())
8930 g_warning("External editor command-line is invalid: '%s'\n",
8931 prefs_common_get_ext_editor_cmd());
8932 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8935 cmdline = strsplit_with_quote(buf, " ", 1024);
8936 execvp(cmdline[0], cmdline);
8939 g_strfreev(cmdline);
8944 static gboolean compose_ext_editor_kill(Compose *compose)
8946 pid_t pgid = compose->exteditor_pid * -1;
8949 ret = kill(pgid, 0);
8951 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8955 msg = g_strdup_printf
8956 (_("The external editor is still working.\n"
8957 "Force terminating the process?\n"
8958 "process group id: %d"), -pgid);
8959 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8960 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8964 if (val == G_ALERTALTERNATE) {
8965 g_source_remove(compose->exteditor_tag);
8966 g_io_channel_shutdown(compose->exteditor_ch,
8968 g_io_channel_unref(compose->exteditor_ch);
8970 if (kill(pgid, SIGTERM) < 0) perror("kill");
8971 waitpid(compose->exteditor_pid, NULL, 0);
8973 g_warning("Terminated process group id: %d", -pgid);
8974 g_warning("Temporary file: %s",
8975 compose->exteditor_file);
8977 compose_set_ext_editor_sensitive(compose, TRUE);
8979 g_free(compose->exteditor_file);
8980 compose->exteditor_file = NULL;
8981 compose->exteditor_pid = -1;
8982 compose->exteditor_ch = NULL;
8983 compose->exteditor_tag = -1;
8991 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8995 Compose *compose = (Compose *)data;
8998 debug_print("Compose: input from monitoring process\n");
9000 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9002 g_io_channel_shutdown(source, FALSE, NULL);
9003 g_io_channel_unref(source);
9005 waitpid(compose->exteditor_pid, NULL, 0);
9007 if (buf[0] == '0') { /* success */
9008 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9009 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9011 gtk_text_buffer_set_text(buffer, "", -1);
9012 compose_insert_file(compose, compose->exteditor_file);
9013 compose_changed_cb(NULL, compose);
9014 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9016 if (claws_unlink(compose->exteditor_file) < 0)
9017 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9018 } else if (buf[0] == '1') { /* failed */
9019 g_warning("Couldn't exec external editor\n");
9020 if (claws_unlink(compose->exteditor_file) < 0)
9021 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9022 } else if (buf[0] == '2') {
9023 g_warning("Couldn't write to file\n");
9024 } else if (buf[0] == '3') {
9025 g_warning("Pipe read failed\n");
9028 compose_set_ext_editor_sensitive(compose, TRUE);
9030 g_free(compose->exteditor_file);
9031 compose->exteditor_file = NULL;
9032 compose->exteditor_pid = -1;
9033 compose->exteditor_ch = NULL;
9034 compose->exteditor_tag = -1;
9039 static void compose_set_ext_editor_sensitive(Compose *compose,
9042 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9043 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9044 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9045 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9046 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9047 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9048 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9050 gtk_widget_set_sensitive(compose->text, sensitive);
9051 if (compose->toolbar->send_btn)
9052 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9053 if (compose->toolbar->sendl_btn)
9054 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9055 if (compose->toolbar->draft_btn)
9056 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9057 if (compose->toolbar->insert_btn)
9058 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9059 if (compose->toolbar->sig_btn)
9060 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9061 if (compose->toolbar->exteditor_btn)
9062 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9063 if (compose->toolbar->linewrap_current_btn)
9064 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9065 if (compose->toolbar->linewrap_all_btn)
9066 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9068 #endif /* G_OS_UNIX */
9071 * compose_undo_state_changed:
9073 * Change the sensivity of the menuentries undo and redo
9075 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9076 gint redo_state, gpointer data)
9078 Compose *compose = (Compose *)data;
9080 switch (undo_state) {
9081 case UNDO_STATE_TRUE:
9082 if (!undostruct->undo_state) {
9083 undostruct->undo_state = TRUE;
9084 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9087 case UNDO_STATE_FALSE:
9088 if (undostruct->undo_state) {
9089 undostruct->undo_state = FALSE;
9090 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9093 case UNDO_STATE_UNCHANGED:
9095 case UNDO_STATE_REFRESH:
9096 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9099 g_warning("Undo state not recognized");
9103 switch (redo_state) {
9104 case UNDO_STATE_TRUE:
9105 if (!undostruct->redo_state) {
9106 undostruct->redo_state = TRUE;
9107 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9110 case UNDO_STATE_FALSE:
9111 if (undostruct->redo_state) {
9112 undostruct->redo_state = FALSE;
9113 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9116 case UNDO_STATE_UNCHANGED:
9118 case UNDO_STATE_REFRESH:
9119 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9122 g_warning("Redo state not recognized");
9127 /* callback functions */
9129 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9130 * includes "non-client" (windows-izm) in calculation, so this calculation
9131 * may not be accurate.
9133 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9134 GtkAllocation *allocation,
9135 GtkSHRuler *shruler)
9137 if (prefs_common.show_ruler) {
9138 gint char_width = 0, char_height = 0;
9139 gint line_width_in_chars;
9141 gtkut_get_font_size(GTK_WIDGET(widget),
9142 &char_width, &char_height);
9143 line_width_in_chars =
9144 (allocation->width - allocation->x) / char_width;
9146 /* got the maximum */
9147 gtk_shruler_set_range(GTK_SHRULER(shruler),
9148 0.0, line_width_in_chars, 0);
9157 ComposePrefType type;
9158 gboolean entry_marked;
9161 static void account_activated(GtkComboBox *optmenu, gpointer data)
9163 Compose *compose = (Compose *)data;
9166 gchar *folderidentifier;
9167 gint account_id = 0;
9170 GSList *list, *saved_list = NULL;
9171 HeaderEntryState *state;
9172 GtkRcStyle *style = NULL;
9173 static GdkColor yellow;
9174 static gboolean color_set = FALSE;
9176 /* Get ID of active account in the combo box */
9177 menu = gtk_combo_box_get_model(optmenu);
9178 gtk_combo_box_get_active_iter(optmenu, &iter);
9179 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9181 ac = account_find_from_id(account_id);
9182 cm_return_if_fail(ac != NULL);
9184 if (ac != compose->account) {
9185 compose_select_account(compose, ac, FALSE);
9187 for (list = compose->header_list; list; list = list->next) {
9188 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9190 if (hentry->type == PREF_ACCOUNT || !list->next) {
9191 compose_destroy_headerentry(compose, hentry);
9195 state = g_malloc0(sizeof(HeaderEntryState));
9196 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9197 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9198 state->entry = gtk_editable_get_chars(
9199 GTK_EDITABLE(hentry->entry), 0, -1);
9200 state->type = hentry->type;
9203 gdk_color_parse("#f5f6be", &yellow);
9204 color_set = gdk_colormap_alloc_color(
9205 gdk_colormap_get_system(),
9206 &yellow, FALSE, TRUE);
9209 style = gtk_widget_get_modifier_style(hentry->entry);
9210 state->entry_marked = gdk_color_equal(&yellow,
9211 &style->base[GTK_STATE_NORMAL]);
9213 saved_list = g_slist_append(saved_list, state);
9214 compose_destroy_headerentry(compose, hentry);
9217 compose->header_last = NULL;
9218 g_slist_free(compose->header_list);
9219 compose->header_list = NULL;
9220 compose->header_nextrow = 1;
9221 compose_create_header_entry(compose);
9223 if (ac->set_autocc && ac->auto_cc)
9224 compose_entry_append(compose, ac->auto_cc,
9225 COMPOSE_CC, PREF_ACCOUNT);
9227 if (ac->set_autobcc && ac->auto_bcc)
9228 compose_entry_append(compose, ac->auto_bcc,
9229 COMPOSE_BCC, PREF_ACCOUNT);
9231 if (ac->set_autoreplyto && ac->auto_replyto)
9232 compose_entry_append(compose, ac->auto_replyto,
9233 COMPOSE_REPLYTO, PREF_ACCOUNT);
9235 for (list = saved_list; list; list = list->next) {
9236 state = (HeaderEntryState *) list->data;
9238 compose_add_header_entry(compose, state->header,
9239 state->entry, state->type);
9240 if (state->entry_marked)
9241 compose_entry_mark_default_to(compose, state->entry);
9243 g_free(state->header);
9244 g_free(state->entry);
9247 g_slist_free(saved_list);
9249 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9250 (ac->protocol == A_NNTP) ?
9251 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9254 /* Set message save folder */
9255 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9256 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9258 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9259 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9261 compose_set_save_to(compose, NULL);
9262 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9263 folderidentifier = folder_item_get_identifier(account_get_special_folder
9264 (compose->account, F_OUTBOX));
9265 compose_set_save_to(compose, folderidentifier);
9266 g_free(folderidentifier);
9270 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9271 GtkTreeViewColumn *column, Compose *compose)
9273 compose_attach_property(NULL, compose);
9276 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9279 Compose *compose = (Compose *)data;
9280 GtkTreeSelection *attach_selection;
9281 gint attach_nr_selected;
9283 if (!event) return FALSE;
9285 if (event->button == 3) {
9286 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9287 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9289 if (attach_nr_selected > 0)
9291 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9292 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9294 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9295 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9298 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9299 NULL, NULL, event->button, event->time);
9306 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9309 Compose *compose = (Compose *)data;
9311 if (!event) return FALSE;
9313 switch (event->keyval) {
9314 case GDK_KEY_Delete:
9315 compose_attach_remove_selected(NULL, compose);
9321 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9323 toolbar_comp_set_sensitive(compose, allow);
9324 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9325 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9327 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9329 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9330 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9331 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9333 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9337 static void compose_send_cb(GtkAction *action, gpointer data)
9339 Compose *compose = (Compose *)data;
9341 if (prefs_common.work_offline &&
9342 !inc_offline_should_override(TRUE,
9343 _("Claws Mail needs network access in order "
9344 "to send this email.")))
9347 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9348 g_source_remove(compose->draft_timeout_tag);
9349 compose->draft_timeout_tag = -1;
9352 compose_send(compose);
9355 static void compose_send_later_cb(GtkAction *action, gpointer data)
9357 Compose *compose = (Compose *)data;
9361 compose_allow_user_actions(compose, FALSE);
9362 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9363 compose_allow_user_actions(compose, TRUE);
9367 compose_close(compose);
9368 } else if (val == -1) {
9369 alertpanel_error(_("Could not queue message."));
9370 } else if (val == -2) {
9371 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9372 } else if (val == -3) {
9373 if (privacy_peek_error())
9374 alertpanel_error(_("Could not queue message for sending:\n\n"
9375 "Signature failed: %s"), privacy_get_error());
9376 } else if (val == -4) {
9377 alertpanel_error(_("Could not queue message for sending:\n\n"
9378 "Charset conversion failed."));
9379 } else if (val == -5) {
9380 alertpanel_error(_("Could not queue message for sending:\n\n"
9381 "Couldn't get recipient encryption key."));
9382 } else if (val == -6) {
9385 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9388 #define DRAFTED_AT_EXIT "drafted_at_exit"
9389 static void compose_register_draft(MsgInfo *info)
9391 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9392 DRAFTED_AT_EXIT, NULL);
9393 FILE *fp = g_fopen(filepath, "ab");
9396 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9404 gboolean compose_draft (gpointer data, guint action)
9406 Compose *compose = (Compose *)data;
9410 MsgFlags flag = {0, 0};
9411 static gboolean lock = FALSE;
9412 MsgInfo *newmsginfo;
9414 gboolean target_locked = FALSE;
9415 gboolean err = FALSE;
9417 if (lock) return FALSE;
9419 if (compose->sending)
9422 draft = account_get_special_folder(compose->account, F_DRAFT);
9423 cm_return_val_if_fail(draft != NULL, FALSE);
9425 if (!g_mutex_trylock(compose->mutex)) {
9426 /* we don't want to lock the mutex once it's available,
9427 * because as the only other part of compose.c locking
9428 * it is compose_close - which means once unlocked,
9429 * the compose struct will be freed */
9430 debug_print("couldn't lock mutex, probably sending\n");
9436 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9437 G_DIR_SEPARATOR, compose);
9438 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9439 FILE_OP_ERROR(tmp, "fopen");
9443 /* chmod for security */
9444 if (change_file_mode_rw(fp, tmp) < 0) {
9445 FILE_OP_ERROR(tmp, "chmod");
9446 g_warning("can't change file mode\n");
9449 /* Save draft infos */
9450 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9451 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9453 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9454 gchar *savefolderid;
9456 savefolderid = compose_get_save_to(compose);
9457 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9458 g_free(savefolderid);
9460 if (compose->return_receipt) {
9461 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9463 if (compose->privacy_system) {
9464 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9465 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9466 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9469 /* Message-ID of message replying to */
9470 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9473 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9474 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9477 /* Message-ID of message forwarding to */
9478 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9481 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9482 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9486 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9487 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9489 /* end of headers */
9490 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9497 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9501 if (fclose(fp) == EOF) {
9505 if (compose->targetinfo) {
9506 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9507 flag.perm_flags = target_locked?MSG_LOCKED:0;
9509 flag.tmp_flags = MSG_DRAFT;
9511 folder_item_scan(draft);
9512 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9513 MsgInfo *tmpinfo = NULL;
9514 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9515 if (compose->msgid) {
9516 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9519 msgnum = tmpinfo->msgnum;
9520 procmsg_msginfo_free(tmpinfo);
9521 debug_print("got draft msgnum %d from scanning\n", msgnum);
9523 debug_print("didn't get draft msgnum after scanning\n");
9526 debug_print("got draft msgnum %d from adding\n", msgnum);
9532 if (action != COMPOSE_AUTO_SAVE) {
9533 if (action != COMPOSE_DRAFT_FOR_EXIT)
9534 alertpanel_error(_("Could not save draft."));
9537 gtkut_window_popup(compose->window);
9538 val = alertpanel_full(_("Could not save draft"),
9539 _("Could not save draft.\n"
9540 "Do you want to cancel exit or discard this email?"),
9541 _("_Cancel exit"), _("_Discard email"), NULL,
9542 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9543 if (val == G_ALERTALTERNATE) {
9545 g_mutex_unlock(compose->mutex); /* must be done before closing */
9546 compose_close(compose);
9550 g_mutex_unlock(compose->mutex); /* must be done before closing */
9559 if (compose->mode == COMPOSE_REEDIT) {
9560 compose_remove_reedit_target(compose, TRUE);
9563 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9566 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9568 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9570 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9571 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9572 procmsg_msginfo_set_flags(newmsginfo, 0,
9573 MSG_HAS_ATTACHMENT);
9575 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9576 compose_register_draft(newmsginfo);
9578 procmsg_msginfo_free(newmsginfo);
9581 folder_item_scan(draft);
9583 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9585 g_mutex_unlock(compose->mutex); /* must be done before closing */
9586 compose_close(compose);
9592 path = folder_item_fetch_msg(draft, msgnum);
9594 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9597 if (g_stat(path, &s) < 0) {
9598 FILE_OP_ERROR(path, "stat");
9604 procmsg_msginfo_free(compose->targetinfo);
9605 compose->targetinfo = procmsg_msginfo_new();
9606 compose->targetinfo->msgnum = msgnum;
9607 compose->targetinfo->size = (goffset)s.st_size;
9608 compose->targetinfo->mtime = s.st_mtime;
9609 compose->targetinfo->folder = draft;
9611 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9612 compose->mode = COMPOSE_REEDIT;
9614 if (action == COMPOSE_AUTO_SAVE) {
9615 compose->autosaved_draft = compose->targetinfo;
9617 compose->modified = FALSE;
9618 compose_set_title(compose);
9622 g_mutex_unlock(compose->mutex);
9626 void compose_clear_exit_drafts(void)
9628 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9629 DRAFTED_AT_EXIT, NULL);
9630 if (is_file_exist(filepath))
9631 claws_unlink(filepath);
9636 void compose_reopen_exit_drafts(void)
9638 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9639 DRAFTED_AT_EXIT, NULL);
9640 FILE *fp = g_fopen(filepath, "rb");
9644 while (fgets(buf, sizeof(buf), fp)) {
9645 gchar **parts = g_strsplit(buf, "\t", 2);
9646 const gchar *folder = parts[0];
9647 int msgnum = parts[1] ? atoi(parts[1]):-1;
9649 if (folder && *folder && msgnum > -1) {
9650 FolderItem *item = folder_find_item_from_identifier(folder);
9651 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9653 compose_reedit(info, FALSE);
9660 compose_clear_exit_drafts();
9663 static void compose_save_cb(GtkAction *action, gpointer data)
9665 Compose *compose = (Compose *)data;
9666 compose_draft(compose, COMPOSE_KEEP_EDITING);
9667 compose->rmode = COMPOSE_REEDIT;
9670 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9672 if (compose && file_list) {
9675 for ( tmp = file_list; tmp; tmp = tmp->next) {
9676 gchar *file = (gchar *) tmp->data;
9677 gchar *utf8_filename = conv_filename_to_utf8(file);
9678 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9679 compose_changed_cb(NULL, compose);
9684 g_free(utf8_filename);
9689 static void compose_attach_cb(GtkAction *action, gpointer data)
9691 Compose *compose = (Compose *)data;
9694 if (compose->redirect_filename != NULL)
9697 file_list = filesel_select_multiple_files_open(_("Select file"));
9700 compose_attach_from_list(compose, file_list, TRUE);
9701 g_list_free(file_list);
9705 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9707 Compose *compose = (Compose *)data;
9709 gint files_inserted = 0;
9711 file_list = filesel_select_multiple_files_open(_("Select file"));
9716 for ( tmp = file_list; tmp; tmp = tmp->next) {
9717 gchar *file = (gchar *) tmp->data;
9718 gchar *filedup = g_strdup(file);
9719 gchar *shortfile = g_path_get_basename(filedup);
9720 ComposeInsertResult res;
9721 /* insert the file if the file is short or if the user confirmed that
9722 he/she wants to insert the large file */
9723 res = compose_insert_file(compose, file);
9724 if (res == COMPOSE_INSERT_READ_ERROR) {
9725 alertpanel_error(_("File '%s' could not be read."), shortfile);
9726 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9727 alertpanel_error(_("File '%s' contained invalid characters\n"
9728 "for the current encoding, insertion may be incorrect."),
9730 } else if (res == COMPOSE_INSERT_SUCCESS)
9737 g_list_free(file_list);
9741 if (files_inserted > 0 && compose->gtkaspell &&
9742 compose->gtkaspell->check_while_typing)
9743 gtkaspell_highlight_all(compose->gtkaspell);
9747 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9749 Compose *compose = (Compose *)data;
9751 compose_insert_sig(compose, FALSE);
9754 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9758 Compose *compose = (Compose *)data;
9760 gtkut_widget_get_uposition(widget, &x, &y);
9761 if (!compose->batch) {
9762 prefs_common.compose_x = x;
9763 prefs_common.compose_y = y;
9765 if (compose->sending || compose->updating)
9767 compose_close_cb(NULL, compose);
9771 void compose_close_toolbar(Compose *compose)
9773 compose_close_cb(NULL, compose);
9776 static void compose_close_cb(GtkAction *action, gpointer data)
9778 Compose *compose = (Compose *)data;
9782 if (compose->exteditor_tag != -1) {
9783 if (!compose_ext_editor_kill(compose))
9788 if (compose->modified) {
9789 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9790 if (!g_mutex_trylock(compose->mutex)) {
9791 /* we don't want to lock the mutex once it's available,
9792 * because as the only other part of compose.c locking
9793 * it is compose_close - which means once unlocked,
9794 * the compose struct will be freed */
9795 debug_print("couldn't lock mutex, probably sending\n");
9799 val = alertpanel(_("Discard message"),
9800 _("This message has been modified. Discard it?"),
9801 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9803 val = alertpanel(_("Save changes"),
9804 _("This message has been modified. Save the latest changes?"),
9805 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9807 g_mutex_unlock(compose->mutex);
9809 case G_ALERTDEFAULT:
9810 if (prefs_common.autosave && !reedit)
9811 compose_remove_draft(compose);
9813 case G_ALERTALTERNATE:
9814 compose_draft(data, COMPOSE_QUIT_EDITING);
9821 compose_close(compose);
9824 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9826 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9827 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9828 Compose *compose = (Compose *) data;
9831 compose->out_encoding = (CharSet)value;
9834 static void compose_address_cb(GtkAction *action, gpointer data)
9836 Compose *compose = (Compose *)data;
9838 addressbook_open(compose);
9841 static void about_show_cb(GtkAction *action, gpointer data)
9846 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9848 Compose *compose = (Compose *)data;
9853 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9854 cm_return_if_fail(tmpl != NULL);
9856 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9858 val = alertpanel(_("Apply template"), msg,
9859 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9862 if (val == G_ALERTDEFAULT)
9863 compose_template_apply(compose, tmpl, TRUE);
9864 else if (val == G_ALERTALTERNATE)
9865 compose_template_apply(compose, tmpl, FALSE);
9868 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9870 Compose *compose = (Compose *)data;
9872 compose_exec_ext_editor(compose);
9875 static void compose_undo_cb(GtkAction *action, gpointer data)
9877 Compose *compose = (Compose *)data;
9878 gboolean prev_autowrap = compose->autowrap;
9880 compose->autowrap = FALSE;
9881 undo_undo(compose->undostruct);
9882 compose->autowrap = prev_autowrap;
9885 static void compose_redo_cb(GtkAction *action, gpointer data)
9887 Compose *compose = (Compose *)data;
9888 gboolean prev_autowrap = compose->autowrap;
9890 compose->autowrap = FALSE;
9891 undo_redo(compose->undostruct);
9892 compose->autowrap = prev_autowrap;
9895 static void entry_cut_clipboard(GtkWidget *entry)
9897 if (GTK_IS_EDITABLE(entry))
9898 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9899 else if (GTK_IS_TEXT_VIEW(entry))
9900 gtk_text_buffer_cut_clipboard(
9901 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9902 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9906 static void entry_copy_clipboard(GtkWidget *entry)
9908 if (GTK_IS_EDITABLE(entry))
9909 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9910 else if (GTK_IS_TEXT_VIEW(entry))
9911 gtk_text_buffer_copy_clipboard(
9912 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9913 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9916 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9917 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9919 if (GTK_IS_TEXT_VIEW(entry)) {
9920 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9921 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9922 GtkTextIter start_iter, end_iter;
9924 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9926 if (contents == NULL)
9929 /* we shouldn't delete the selection when middle-click-pasting, or we
9930 * can't mid-click-paste our own selection */
9931 if (clip != GDK_SELECTION_PRIMARY) {
9932 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9933 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9936 if (insert_place == NULL) {
9937 /* if insert_place isn't specified, insert at the cursor.
9938 * used for Ctrl-V pasting */
9939 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9940 start = gtk_text_iter_get_offset(&start_iter);
9941 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9943 /* if insert_place is specified, paste here.
9944 * used for mid-click-pasting */
9945 start = gtk_text_iter_get_offset(insert_place);
9946 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9947 if (prefs_common.primary_paste_unselects)
9948 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9952 /* paste unwrapped: mark the paste so it's not wrapped later */
9953 end = start + strlen(contents);
9954 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9955 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9956 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9957 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9958 /* rewrap paragraph now (after a mid-click-paste) */
9959 mark_start = gtk_text_buffer_get_insert(buffer);
9960 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9961 gtk_text_iter_backward_char(&start_iter);
9962 compose_beautify_paragraph(compose, &start_iter, TRUE);
9964 } else if (GTK_IS_EDITABLE(entry))
9965 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9967 compose->modified = TRUE;
9970 static void entry_allsel(GtkWidget *entry)
9972 if (GTK_IS_EDITABLE(entry))
9973 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9974 else if (GTK_IS_TEXT_VIEW(entry)) {
9975 GtkTextIter startiter, enditer;
9976 GtkTextBuffer *textbuf;
9978 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9979 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9980 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9982 gtk_text_buffer_move_mark_by_name(textbuf,
9983 "selection_bound", &startiter);
9984 gtk_text_buffer_move_mark_by_name(textbuf,
9985 "insert", &enditer);
9989 static void compose_cut_cb(GtkAction *action, gpointer data)
9991 Compose *compose = (Compose *)data;
9992 if (compose->focused_editable
9993 #ifndef GENERIC_UMPC
9994 && gtk_widget_has_focus(compose->focused_editable)
9997 entry_cut_clipboard(compose->focused_editable);
10000 static void compose_copy_cb(GtkAction *action, gpointer data)
10002 Compose *compose = (Compose *)data;
10003 if (compose->focused_editable
10004 #ifndef GENERIC_UMPC
10005 && gtk_widget_has_focus(compose->focused_editable)
10008 entry_copy_clipboard(compose->focused_editable);
10011 static void compose_paste_cb(GtkAction *action, gpointer data)
10013 Compose *compose = (Compose *)data;
10014 gint prev_autowrap;
10015 GtkTextBuffer *buffer;
10017 if (compose->focused_editable &&
10018 gtk_widget_has_focus(compose->focused_editable))
10019 entry_paste_clipboard(compose, compose->focused_editable,
10020 prefs_common.linewrap_pastes,
10021 GDK_SELECTION_CLIPBOARD, NULL);
10025 if (gtk_widget_has_focus(compose->text) &&
10026 compose->gtkaspell &&
10027 compose->gtkaspell->check_while_typing)
10028 gtkaspell_highlight_all(compose->gtkaspell);
10032 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10034 Compose *compose = (Compose *)data;
10035 gint wrap_quote = prefs_common.linewrap_quote;
10036 if (compose->focused_editable
10037 #ifndef GENERIC_UMPC
10038 && gtk_widget_has_focus(compose->focused_editable)
10041 /* let text_insert() (called directly or at a later time
10042 * after the gtk_editable_paste_clipboard) know that
10043 * text is to be inserted as a quotation. implemented
10044 * by using a simple refcount... */
10045 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10046 G_OBJECT(compose->focused_editable),
10047 "paste_as_quotation"));
10048 g_object_set_data(G_OBJECT(compose->focused_editable),
10049 "paste_as_quotation",
10050 GINT_TO_POINTER(paste_as_quotation + 1));
10051 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10052 entry_paste_clipboard(compose, compose->focused_editable,
10053 prefs_common.linewrap_pastes,
10054 GDK_SELECTION_CLIPBOARD, NULL);
10055 prefs_common.linewrap_quote = wrap_quote;
10059 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10061 Compose *compose = (Compose *)data;
10062 gint prev_autowrap;
10063 GtkTextBuffer *buffer;
10065 if (compose->focused_editable
10066 #ifndef GENERIC_UMPC
10067 && gtk_widget_has_focus(compose->focused_editable)
10070 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10071 GDK_SELECTION_CLIPBOARD, NULL);
10075 if (gtk_widget_has_focus(compose->text) &&
10076 compose->gtkaspell &&
10077 compose->gtkaspell->check_while_typing)
10078 gtkaspell_highlight_all(compose->gtkaspell);
10082 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10084 Compose *compose = (Compose *)data;
10085 gint prev_autowrap;
10086 GtkTextBuffer *buffer;
10088 if (compose->focused_editable
10089 #ifndef GENERIC_UMPC
10090 && gtk_widget_has_focus(compose->focused_editable)
10093 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10094 GDK_SELECTION_CLIPBOARD, NULL);
10098 if (gtk_widget_has_focus(compose->text) &&
10099 compose->gtkaspell &&
10100 compose->gtkaspell->check_while_typing)
10101 gtkaspell_highlight_all(compose->gtkaspell);
10105 static void compose_allsel_cb(GtkAction *action, gpointer data)
10107 Compose *compose = (Compose *)data;
10108 if (compose->focused_editable
10109 #ifndef GENERIC_UMPC
10110 && gtk_widget_has_focus(compose->focused_editable)
10113 entry_allsel(compose->focused_editable);
10116 static void textview_move_beginning_of_line (GtkTextView *text)
10118 GtkTextBuffer *buffer;
10122 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10124 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10125 mark = gtk_text_buffer_get_insert(buffer);
10126 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10127 gtk_text_iter_set_line_offset(&ins, 0);
10128 gtk_text_buffer_place_cursor(buffer, &ins);
10131 static void textview_move_forward_character (GtkTextView *text)
10133 GtkTextBuffer *buffer;
10137 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10139 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10140 mark = gtk_text_buffer_get_insert(buffer);
10141 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10142 if (gtk_text_iter_forward_cursor_position(&ins))
10143 gtk_text_buffer_place_cursor(buffer, &ins);
10146 static void textview_move_backward_character (GtkTextView *text)
10148 GtkTextBuffer *buffer;
10152 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10154 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10155 mark = gtk_text_buffer_get_insert(buffer);
10156 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10157 if (gtk_text_iter_backward_cursor_position(&ins))
10158 gtk_text_buffer_place_cursor(buffer, &ins);
10161 static void textview_move_forward_word (GtkTextView *text)
10163 GtkTextBuffer *buffer;
10168 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10170 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10171 mark = gtk_text_buffer_get_insert(buffer);
10172 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10173 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10174 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10175 gtk_text_iter_backward_word_start(&ins);
10176 gtk_text_buffer_place_cursor(buffer, &ins);
10180 static void textview_move_backward_word (GtkTextView *text)
10182 GtkTextBuffer *buffer;
10187 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10189 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10190 mark = gtk_text_buffer_get_insert(buffer);
10191 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10192 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10193 if (gtk_text_iter_backward_word_starts(&ins, 1))
10194 gtk_text_buffer_place_cursor(buffer, &ins);
10197 static void textview_move_end_of_line (GtkTextView *text)
10199 GtkTextBuffer *buffer;
10203 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10205 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10206 mark = gtk_text_buffer_get_insert(buffer);
10207 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10208 if (gtk_text_iter_forward_to_line_end(&ins))
10209 gtk_text_buffer_place_cursor(buffer, &ins);
10212 static void textview_move_next_line (GtkTextView *text)
10214 GtkTextBuffer *buffer;
10219 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10221 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10222 mark = gtk_text_buffer_get_insert(buffer);
10223 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10224 offset = gtk_text_iter_get_line_offset(&ins);
10225 if (gtk_text_iter_forward_line(&ins)) {
10226 gtk_text_iter_set_line_offset(&ins, offset);
10227 gtk_text_buffer_place_cursor(buffer, &ins);
10231 static void textview_move_previous_line (GtkTextView *text)
10233 GtkTextBuffer *buffer;
10238 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10240 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10241 mark = gtk_text_buffer_get_insert(buffer);
10242 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10243 offset = gtk_text_iter_get_line_offset(&ins);
10244 if (gtk_text_iter_backward_line(&ins)) {
10245 gtk_text_iter_set_line_offset(&ins, offset);
10246 gtk_text_buffer_place_cursor(buffer, &ins);
10250 static void textview_delete_forward_character (GtkTextView *text)
10252 GtkTextBuffer *buffer;
10254 GtkTextIter ins, end_iter;
10256 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10258 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10259 mark = gtk_text_buffer_get_insert(buffer);
10260 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10262 if (gtk_text_iter_forward_char(&end_iter)) {
10263 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10267 static void textview_delete_backward_character (GtkTextView *text)
10269 GtkTextBuffer *buffer;
10271 GtkTextIter ins, end_iter;
10273 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10275 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10276 mark = gtk_text_buffer_get_insert(buffer);
10277 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10279 if (gtk_text_iter_backward_char(&end_iter)) {
10280 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10284 static void textview_delete_forward_word (GtkTextView *text)
10286 GtkTextBuffer *buffer;
10288 GtkTextIter ins, end_iter;
10290 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10292 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10293 mark = gtk_text_buffer_get_insert(buffer);
10294 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10296 if (gtk_text_iter_forward_word_end(&end_iter)) {
10297 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10301 static void textview_delete_backward_word (GtkTextView *text)
10303 GtkTextBuffer *buffer;
10305 GtkTextIter ins, end_iter;
10307 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10309 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10310 mark = gtk_text_buffer_get_insert(buffer);
10311 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10313 if (gtk_text_iter_backward_word_start(&end_iter)) {
10314 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10318 static void textview_delete_line (GtkTextView *text)
10320 GtkTextBuffer *buffer;
10322 GtkTextIter ins, start_iter, end_iter;
10324 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10326 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10327 mark = gtk_text_buffer_get_insert(buffer);
10328 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10331 gtk_text_iter_set_line_offset(&start_iter, 0);
10334 if (gtk_text_iter_ends_line(&end_iter)){
10335 if (!gtk_text_iter_forward_char(&end_iter))
10336 gtk_text_iter_backward_char(&start_iter);
10339 gtk_text_iter_forward_to_line_end(&end_iter);
10340 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10343 static void textview_delete_to_line_end (GtkTextView *text)
10345 GtkTextBuffer *buffer;
10347 GtkTextIter ins, end_iter;
10349 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10351 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10352 mark = gtk_text_buffer_get_insert(buffer);
10353 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10355 if (gtk_text_iter_ends_line(&end_iter))
10356 gtk_text_iter_forward_char(&end_iter);
10358 gtk_text_iter_forward_to_line_end(&end_iter);
10359 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10362 #define DO_ACTION(name, act) { \
10363 if(!strcmp(name, a_name)) { \
10367 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10369 const gchar *a_name = gtk_action_get_name(action);
10370 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10371 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10372 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10373 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10374 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10375 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10376 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10377 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10378 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10379 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10380 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10381 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10382 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10383 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10387 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10389 Compose *compose = (Compose *)data;
10390 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10391 ComposeCallAdvancedAction action = -1;
10393 action = compose_call_advanced_action_from_path(gaction);
10396 void (*do_action) (GtkTextView *text);
10397 } action_table[] = {
10398 {textview_move_beginning_of_line},
10399 {textview_move_forward_character},
10400 {textview_move_backward_character},
10401 {textview_move_forward_word},
10402 {textview_move_backward_word},
10403 {textview_move_end_of_line},
10404 {textview_move_next_line},
10405 {textview_move_previous_line},
10406 {textview_delete_forward_character},
10407 {textview_delete_backward_character},
10408 {textview_delete_forward_word},
10409 {textview_delete_backward_word},
10410 {textview_delete_line},
10411 {textview_delete_to_line_end}
10414 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10416 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10417 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10418 if (action_table[action].do_action)
10419 action_table[action].do_action(text);
10421 g_warning("Not implemented yet.");
10425 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10427 GtkAllocation allocation;
10431 if (GTK_IS_EDITABLE(widget)) {
10432 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10433 gtk_editable_set_position(GTK_EDITABLE(widget),
10436 if ((parent = gtk_widget_get_parent(widget))
10437 && (parent = gtk_widget_get_parent(parent))
10438 && (parent = gtk_widget_get_parent(parent))) {
10439 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10440 gtk_widget_get_allocation(widget, &allocation);
10441 gint y = allocation.y;
10442 gint height = allocation.height;
10443 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10444 (GTK_SCROLLED_WINDOW(parent));
10446 gfloat value = gtk_adjustment_get_value(shown);
10447 gfloat upper = gtk_adjustment_get_upper(shown);
10448 gfloat page_size = gtk_adjustment_get_page_size(shown);
10449 if (y < (int)value) {
10450 gtk_adjustment_set_value(shown, y - 1);
10452 if ((y + height) > ((int)value + (int)page_size)) {
10453 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10454 gtk_adjustment_set_value(shown,
10455 y + height - (int)page_size - 1);
10457 gtk_adjustment_set_value(shown,
10458 (int)upper - (int)page_size - 1);
10465 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10466 compose->focused_editable = widget;
10468 #ifdef GENERIC_UMPC
10469 if (GTK_IS_TEXT_VIEW(widget)
10470 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10471 g_object_ref(compose->notebook);
10472 g_object_ref(compose->edit_vbox);
10473 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10474 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10475 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10476 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10477 g_object_unref(compose->notebook);
10478 g_object_unref(compose->edit_vbox);
10479 g_signal_handlers_block_by_func(G_OBJECT(widget),
10480 G_CALLBACK(compose_grab_focus_cb),
10482 gtk_widget_grab_focus(widget);
10483 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10484 G_CALLBACK(compose_grab_focus_cb),
10486 } else if (!GTK_IS_TEXT_VIEW(widget)
10487 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10488 g_object_ref(compose->notebook);
10489 g_object_ref(compose->edit_vbox);
10490 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10491 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10492 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10493 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10494 g_object_unref(compose->notebook);
10495 g_object_unref(compose->edit_vbox);
10496 g_signal_handlers_block_by_func(G_OBJECT(widget),
10497 G_CALLBACK(compose_grab_focus_cb),
10499 gtk_widget_grab_focus(widget);
10500 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10501 G_CALLBACK(compose_grab_focus_cb),
10507 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10509 compose->modified = TRUE;
10510 // compose_beautify_paragraph(compose, NULL, TRUE);
10511 #ifndef GENERIC_UMPC
10512 compose_set_title(compose);
10516 static void compose_wrap_cb(GtkAction *action, gpointer data)
10518 Compose *compose = (Compose *)data;
10519 compose_beautify_paragraph(compose, NULL, TRUE);
10522 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10524 Compose *compose = (Compose *)data;
10525 compose_wrap_all_full(compose, TRUE);
10528 static void compose_find_cb(GtkAction *action, gpointer data)
10530 Compose *compose = (Compose *)data;
10532 message_search_compose(compose);
10535 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10538 Compose *compose = (Compose *)data;
10539 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10540 if (compose->autowrap)
10541 compose_wrap_all_full(compose, TRUE);
10542 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10545 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10548 Compose *compose = (Compose *)data;
10549 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10552 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10554 Compose *compose = (Compose *)data;
10556 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10559 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10561 Compose *compose = (Compose *)data;
10563 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10566 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10568 g_free(compose->privacy_system);
10570 compose->privacy_system = g_strdup(account->default_privacy_system);
10571 compose_update_privacy_system_menu_item(compose, warn);
10574 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10576 Compose *compose = (Compose *)data;
10578 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10579 gtk_widget_show(compose->ruler_hbox);
10580 prefs_common.show_ruler = TRUE;
10582 gtk_widget_hide(compose->ruler_hbox);
10583 gtk_widget_queue_resize(compose->edit_vbox);
10584 prefs_common.show_ruler = FALSE;
10588 static void compose_attach_drag_received_cb (GtkWidget *widget,
10589 GdkDragContext *context,
10592 GtkSelectionData *data,
10595 gpointer user_data)
10597 Compose *compose = (Compose *)user_data;
10601 type = gtk_selection_data_get_data_type(data);
10602 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10604 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10606 ) && gtk_drag_get_source_widget(context) !=
10607 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10608 list = uri_list_extract_filenames(
10609 (const gchar *)gtk_selection_data_get_data(data));
10610 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10611 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10612 compose_attach_append
10613 (compose, (const gchar *)tmp->data,
10614 utf8_filename, NULL, NULL);
10615 g_free(utf8_filename);
10617 if (list) compose_changed_cb(NULL, compose);
10618 list_free_strings(list);
10620 } else if (gtk_drag_get_source_widget(context)
10621 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10622 /* comes from our summaryview */
10623 SummaryView * summaryview = NULL;
10624 GSList * list = NULL, *cur = NULL;
10626 if (mainwindow_get_mainwindow())
10627 summaryview = mainwindow_get_mainwindow()->summaryview;
10630 list = summary_get_selected_msg_list(summaryview);
10632 for (cur = list; cur; cur = cur->next) {
10633 MsgInfo *msginfo = (MsgInfo *)cur->data;
10634 gchar *file = NULL;
10636 file = procmsg_get_message_file_full(msginfo,
10639 compose_attach_append(compose, (const gchar *)file,
10640 (const gchar *)file, "message/rfc822", NULL);
10644 g_slist_free(list);
10648 static gboolean compose_drag_drop(GtkWidget *widget,
10649 GdkDragContext *drag_context,
10651 guint time, gpointer user_data)
10653 /* not handling this signal makes compose_insert_drag_received_cb
10658 static void compose_insert_drag_received_cb (GtkWidget *widget,
10659 GdkDragContext *drag_context,
10662 GtkSelectionData *data,
10665 gpointer user_data)
10667 Compose *compose = (Compose *)user_data;
10671 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10673 type = gtk_selection_data_get_data_type(data);
10675 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10677 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10679 AlertValue val = G_ALERTDEFAULT;
10680 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10682 list = uri_list_extract_filenames(ddata);
10683 if (list == NULL && strstr(ddata, "://")) {
10684 /* Assume a list of no files, and data has ://, is a remote link */
10685 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10686 gchar *tmpfile = get_tmp_file();
10687 str_write_to_file(tmpdata, tmpfile);
10689 compose_insert_file(compose, tmpfile);
10690 claws_unlink(tmpfile);
10692 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10693 compose_beautify_paragraph(compose, NULL, TRUE);
10696 switch (prefs_common.compose_dnd_mode) {
10697 case COMPOSE_DND_ASK:
10698 val = alertpanel_full(_("Insert or attach?"),
10699 _("Do you want to insert the contents of the file(s) "
10700 "into the message body, or attach it to the email?"),
10701 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10702 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10704 case COMPOSE_DND_INSERT:
10705 val = G_ALERTALTERNATE;
10707 case COMPOSE_DND_ATTACH:
10708 val = G_ALERTOTHER;
10711 /* unexpected case */
10712 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10715 if (val & G_ALERTDISABLE) {
10716 val &= ~G_ALERTDISABLE;
10717 /* remember what action to perform by default, only if we don't click Cancel */
10718 if (val == G_ALERTALTERNATE)
10719 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10720 else if (val == G_ALERTOTHER)
10721 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10724 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10725 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10726 list_free_strings(list);
10729 } else if (val == G_ALERTOTHER) {
10730 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10731 list_free_strings(list);
10736 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10737 compose_insert_file(compose, (const gchar *)tmp->data);
10739 list_free_strings(list);
10741 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10746 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10749 static void compose_header_drag_received_cb (GtkWidget *widget,
10750 GdkDragContext *drag_context,
10753 GtkSelectionData *data,
10756 gpointer user_data)
10758 GtkEditable *entry = (GtkEditable *)user_data;
10759 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10761 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10764 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10765 gchar *decoded=g_new(gchar, strlen(email));
10768 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10769 gtk_editable_delete_text(entry, 0, -1);
10770 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10771 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10775 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10778 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10780 Compose *compose = (Compose *)data;
10782 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10783 compose->return_receipt = TRUE;
10785 compose->return_receipt = FALSE;
10788 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10790 Compose *compose = (Compose *)data;
10792 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10793 compose->remove_references = TRUE;
10795 compose->remove_references = FALSE;
10798 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10799 ComposeHeaderEntry *headerentry)
10801 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10805 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10806 GdkEventKey *event,
10807 ComposeHeaderEntry *headerentry)
10809 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10810 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10811 !(event->state & GDK_MODIFIER_MASK) &&
10812 (event->keyval == GDK_KEY_BackSpace) &&
10813 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10814 gtk_container_remove
10815 (GTK_CONTAINER(headerentry->compose->header_table),
10816 headerentry->combo);
10817 gtk_container_remove
10818 (GTK_CONTAINER(headerentry->compose->header_table),
10819 headerentry->entry);
10820 headerentry->compose->header_list =
10821 g_slist_remove(headerentry->compose->header_list,
10823 g_free(headerentry);
10824 } else if (event->keyval == GDK_KEY_Tab) {
10825 if (headerentry->compose->header_last == headerentry) {
10826 /* Override default next focus, and give it to subject_entry
10827 * instead of notebook tabs
10829 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10830 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10837 static gboolean scroll_postpone(gpointer data)
10839 Compose *compose = (Compose *)data;
10841 cm_return_val_if_fail(!compose->batch, FALSE);
10843 GTK_EVENTS_FLUSH();
10844 compose_show_first_last_header(compose, FALSE);
10848 static void compose_headerentry_changed_cb(GtkWidget *entry,
10849 ComposeHeaderEntry *headerentry)
10851 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10852 compose_create_header_entry(headerentry->compose);
10853 g_signal_handlers_disconnect_matched
10854 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10855 0, 0, NULL, NULL, headerentry);
10857 if (!headerentry->compose->batch)
10858 g_timeout_add(0, scroll_postpone, headerentry->compose);
10862 static gboolean compose_defer_auto_save_draft(Compose *compose)
10864 compose->draft_timeout_tag = -1;
10865 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10869 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10871 GtkAdjustment *vadj;
10873 cm_return_if_fail(compose);
10874 cm_return_if_fail(!compose->batch);
10875 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10876 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
10877 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
10878 gtk_widget_get_parent(compose->header_table)));
10879 gtk_adjustment_set_value(vadj, (show_first ?
10880 gtk_adjustment_get_lower(vadj) :
10881 (gtk_adjustment_get_upper(vadj) -
10882 gtk_adjustment_get_page_size(vadj))));
10883 gtk_adjustment_changed(vadj);
10886 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10887 const gchar *text, gint len, Compose *compose)
10889 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10890 (G_OBJECT(compose->text), "paste_as_quotation"));
10893 cm_return_if_fail(text != NULL);
10895 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10896 G_CALLBACK(text_inserted),
10898 if (paste_as_quotation) {
10900 const gchar *qmark;
10902 GtkTextIter start_iter;
10905 len = strlen(text);
10907 new_text = g_strndup(text, len);
10909 qmark = compose_quote_char_from_context(compose);
10911 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10912 gtk_text_buffer_place_cursor(buffer, iter);
10914 pos = gtk_text_iter_get_offset(iter);
10916 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10917 _("Quote format error at line %d."));
10918 quote_fmt_reset_vartable();
10920 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10921 GINT_TO_POINTER(paste_as_quotation - 1));
10923 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10924 gtk_text_buffer_place_cursor(buffer, iter);
10925 gtk_text_buffer_delete_mark(buffer, mark);
10927 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10928 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10929 compose_beautify_paragraph(compose, &start_iter, FALSE);
10930 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10931 gtk_text_buffer_delete_mark(buffer, mark);
10933 if (strcmp(text, "\n") || compose->automatic_break
10934 || gtk_text_iter_starts_line(iter)) {
10935 GtkTextIter before_ins;
10936 gtk_text_buffer_insert(buffer, iter, text, len);
10937 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10938 before_ins = *iter;
10939 gtk_text_iter_backward_chars(&before_ins, len);
10940 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10943 /* check if the preceding is just whitespace or quote */
10944 GtkTextIter start_line;
10945 gchar *tmp = NULL, *quote = NULL;
10946 gint quote_len = 0, is_normal = 0;
10947 start_line = *iter;
10948 gtk_text_iter_set_line_offset(&start_line, 0);
10949 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10952 if (*tmp == '\0') {
10955 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10963 gtk_text_buffer_insert(buffer, iter, text, len);
10965 gtk_text_buffer_insert_with_tags_by_name(buffer,
10966 iter, text, len, "no_join", NULL);
10971 if (!paste_as_quotation) {
10972 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10973 compose_beautify_paragraph(compose, iter, FALSE);
10974 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10975 gtk_text_buffer_delete_mark(buffer, mark);
10978 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10979 G_CALLBACK(text_inserted),
10981 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10983 if (prefs_common.autosave &&
10984 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10985 compose->draft_timeout_tag != -2 /* disabled while loading */)
10986 compose->draft_timeout_tag = g_timeout_add
10987 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
10991 static void compose_check_all(GtkAction *action, gpointer data)
10993 Compose *compose = (Compose *)data;
10994 if (!compose->gtkaspell)
10997 if (gtk_widget_has_focus(compose->subject_entry))
10998 claws_spell_entry_check_all(
10999 CLAWS_SPELL_ENTRY(compose->subject_entry));
11001 gtkaspell_check_all(compose->gtkaspell);
11004 static void compose_highlight_all(GtkAction *action, gpointer data)
11006 Compose *compose = (Compose *)data;
11007 if (compose->gtkaspell) {
11008 claws_spell_entry_recheck_all(
11009 CLAWS_SPELL_ENTRY(compose->subject_entry));
11010 gtkaspell_highlight_all(compose->gtkaspell);
11014 static void compose_check_backwards(GtkAction *action, gpointer data)
11016 Compose *compose = (Compose *)data;
11017 if (!compose->gtkaspell) {
11018 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11022 if (gtk_widget_has_focus(compose->subject_entry))
11023 claws_spell_entry_check_backwards(
11024 CLAWS_SPELL_ENTRY(compose->subject_entry));
11026 gtkaspell_check_backwards(compose->gtkaspell);
11029 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11031 Compose *compose = (Compose *)data;
11032 if (!compose->gtkaspell) {
11033 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11037 if (gtk_widget_has_focus(compose->subject_entry))
11038 claws_spell_entry_check_forwards_go(
11039 CLAWS_SPELL_ENTRY(compose->subject_entry));
11041 gtkaspell_check_forwards_go(compose->gtkaspell);
11046 *\brief Guess originating forward account from MsgInfo and several
11047 * "common preference" settings. Return NULL if no guess.
11049 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11051 PrefsAccount *account = NULL;
11053 cm_return_val_if_fail(msginfo, NULL);
11054 cm_return_val_if_fail(msginfo->folder, NULL);
11055 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11057 if (msginfo->folder->prefs->enable_default_account)
11058 account = account_find_from_id(msginfo->folder->prefs->default_account);
11061 account = msginfo->folder->folder->account;
11063 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11065 Xstrdup_a(to, msginfo->to, return NULL);
11066 extract_address(to);
11067 account = account_find_from_address(to, FALSE);
11070 if (!account && prefs_common.forward_account_autosel) {
11071 gchar cc[BUFFSIZE];
11072 if (!procheader_get_header_from_msginfo
11073 (msginfo, cc,sizeof cc , "Cc:")) {
11074 gchar *buf = cc + strlen("Cc:");
11075 extract_address(buf);
11076 account = account_find_from_address(buf, FALSE);
11080 if (!account && prefs_common.forward_account_autosel) {
11081 gchar deliveredto[BUFFSIZE];
11082 if (!procheader_get_header_from_msginfo
11083 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11084 gchar *buf = deliveredto + strlen("Delivered-To:");
11085 extract_address(buf);
11086 account = account_find_from_address(buf, FALSE);
11093 gboolean compose_close(Compose *compose)
11097 if (!g_mutex_trylock(compose->mutex)) {
11098 /* we have to wait for the (possibly deferred by auto-save)
11099 * drafting to be done, before destroying the compose under
11101 debug_print("waiting for drafting to finish...\n");
11102 compose_allow_user_actions(compose, FALSE);
11103 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11106 cm_return_val_if_fail(compose, FALSE);
11107 gtkut_widget_get_uposition(compose->window, &x, &y);
11108 if (!compose->batch) {
11109 prefs_common.compose_x = x;
11110 prefs_common.compose_y = y;
11112 g_mutex_unlock(compose->mutex);
11113 compose_destroy(compose);
11118 * Add entry field for each address in list.
11119 * \param compose E-Mail composition object.
11120 * \param listAddress List of (formatted) E-Mail addresses.
11122 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11125 node = listAddress;
11127 addr = ( gchar * ) node->data;
11128 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11129 node = g_list_next( node );
11133 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11134 guint action, gboolean opening_multiple)
11136 gchar *body = NULL;
11137 GSList *new_msglist = NULL;
11138 MsgInfo *tmp_msginfo = NULL;
11139 gboolean originally_enc = FALSE;
11140 gboolean originally_sig = FALSE;
11141 Compose *compose = NULL;
11142 gchar *s_system = NULL;
11144 cm_return_if_fail(msgview != NULL);
11146 cm_return_if_fail(msginfo_list != NULL);
11148 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11149 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11150 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11152 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11153 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11154 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11155 orig_msginfo, mimeinfo);
11156 if (tmp_msginfo != NULL) {
11157 new_msglist = g_slist_append(NULL, tmp_msginfo);
11159 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11160 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11161 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11163 tmp_msginfo->folder = orig_msginfo->folder;
11164 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11165 if (orig_msginfo->tags) {
11166 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11167 tmp_msginfo->folder->tags_dirty = TRUE;
11173 if (!opening_multiple)
11174 body = messageview_get_selection(msgview);
11177 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11178 procmsg_msginfo_free(tmp_msginfo);
11179 g_slist_free(new_msglist);
11181 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11183 if (compose && originally_enc) {
11184 compose_force_encryption(compose, compose->account, FALSE, s_system);
11187 if (compose && originally_sig && compose->account->default_sign_reply) {
11188 compose_force_signing(compose, compose->account, s_system);
11192 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11195 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11198 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11199 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11200 GSList *cur = msginfo_list;
11201 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11202 "messages. Opening the windows "
11203 "could take some time. Do you "
11204 "want to continue?"),
11205 g_slist_length(msginfo_list));
11206 if (g_slist_length(msginfo_list) > 9
11207 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11208 != G_ALERTALTERNATE) {
11213 /* We'll open multiple compose windows */
11214 /* let the WM place the next windows */
11215 compose_force_window_origin = FALSE;
11216 for (; cur; cur = cur->next) {
11218 tmplist.data = cur->data;
11219 tmplist.next = NULL;
11220 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11222 compose_force_window_origin = TRUE;
11224 /* forwarding multiple mails as attachments is done via a
11225 * single compose window */
11226 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11230 void compose_check_for_email_account(Compose *compose)
11232 PrefsAccount *ac = NULL, *curr = NULL;
11238 if (compose->account && compose->account->protocol == A_NNTP) {
11239 ac = account_get_cur_account();
11240 if (ac->protocol == A_NNTP) {
11241 list = account_get_list();
11243 for( ; list != NULL ; list = g_list_next(list)) {
11244 curr = (PrefsAccount *) list->data;
11245 if (curr->protocol != A_NNTP) {
11251 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11256 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11257 const gchar *address)
11259 GSList *msginfo_list = NULL;
11260 gchar *body = messageview_get_selection(msgview);
11263 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11265 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11266 compose_check_for_email_account(compose);
11267 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11268 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11269 compose_reply_set_subject(compose, msginfo);
11272 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11275 void compose_set_position(Compose *compose, gint pos)
11277 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11279 gtkut_text_view_set_position(text, pos);
11282 gboolean compose_search_string(Compose *compose,
11283 const gchar *str, gboolean case_sens)
11285 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11287 return gtkut_text_view_search_string(text, str, case_sens);
11290 gboolean compose_search_string_backward(Compose *compose,
11291 const gchar *str, gboolean case_sens)
11293 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11295 return gtkut_text_view_search_string_backward(text, str, case_sens);
11298 /* allocate a msginfo structure and populate its data from a compose data structure */
11299 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11301 MsgInfo *newmsginfo;
11303 gchar buf[BUFFSIZE];
11305 cm_return_val_if_fail( compose != NULL, NULL );
11307 newmsginfo = procmsg_msginfo_new();
11310 get_rfc822_date(buf, sizeof(buf));
11311 newmsginfo->date = g_strdup(buf);
11314 if (compose->from_name) {
11315 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11316 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11320 if (compose->subject_entry)
11321 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11323 /* to, cc, reply-to, newsgroups */
11324 for (list = compose->header_list; list; list = list->next) {
11325 gchar *header = gtk_editable_get_chars(
11327 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11328 gchar *entry = gtk_editable_get_chars(
11329 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11331 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11332 if ( newmsginfo->to == NULL ) {
11333 newmsginfo->to = g_strdup(entry);
11334 } else if (entry && *entry) {
11335 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11336 g_free(newmsginfo->to);
11337 newmsginfo->to = tmp;
11340 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11341 if ( newmsginfo->cc == NULL ) {
11342 newmsginfo->cc = g_strdup(entry);
11343 } else if (entry && *entry) {
11344 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11345 g_free(newmsginfo->cc);
11346 newmsginfo->cc = tmp;
11349 if ( strcasecmp(header,
11350 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11351 if ( newmsginfo->newsgroups == NULL ) {
11352 newmsginfo->newsgroups = g_strdup(entry);
11353 } else if (entry && *entry) {
11354 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11355 g_free(newmsginfo->newsgroups);
11356 newmsginfo->newsgroups = tmp;
11364 /* other data is unset */
11370 /* update compose's dictionaries from folder dict settings */
11371 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11372 FolderItem *folder_item)
11374 cm_return_if_fail(compose != NULL);
11376 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11377 FolderItemPrefs *prefs = folder_item->prefs;
11379 if (prefs->enable_default_dictionary)
11380 gtkaspell_change_dict(compose->gtkaspell,
11381 prefs->default_dictionary, FALSE);
11382 if (folder_item->prefs->enable_default_alt_dictionary)
11383 gtkaspell_change_alt_dict(compose->gtkaspell,
11384 prefs->default_alt_dictionary);
11385 if (prefs->enable_default_dictionary
11386 || prefs->enable_default_alt_dictionary)
11387 compose_spell_menu_changed(compose);