2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2008 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static ComposeInsertResult compose_insert_file (Compose *compose,
244 static gboolean compose_attach_append (Compose *compose,
247 const gchar *content_type);
248 static void compose_attach_parts (Compose *compose,
251 static gboolean compose_beautify_paragraph (Compose *compose,
252 GtkTextIter *par_iter,
254 static void compose_wrap_all (Compose *compose);
255 static void compose_wrap_all_full (Compose *compose,
258 static void compose_set_title (Compose *compose);
259 static void compose_select_account (Compose *compose,
260 PrefsAccount *account,
263 static PrefsAccount *compose_current_mail_account(void);
264 /* static gint compose_send (Compose *compose); */
265 static gboolean compose_check_for_valid_recipient
267 static gboolean compose_check_entries (Compose *compose,
268 gboolean check_everything);
269 static gint compose_write_to_file (Compose *compose,
272 gboolean attach_parts);
273 static gint compose_write_body_to_file (Compose *compose,
275 static gint compose_remove_reedit_target (Compose *compose,
277 static void compose_remove_draft (Compose *compose);
278 static gint compose_queue_sub (Compose *compose,
282 gboolean check_subject,
283 gboolean remove_reedit_target);
284 static void compose_add_attachments (Compose *compose,
286 static gchar *compose_get_header (Compose *compose);
288 static void compose_convert_header (Compose *compose,
293 gboolean addr_field);
295 static void compose_attach_info_free (AttachInfo *ainfo);
296 static void compose_attach_remove_selected (GtkAction *action,
299 static void compose_attach_property (GtkAction *action,
301 static void compose_attach_property_create (gboolean *cancelled);
302 static void attach_property_ok (GtkWidget *widget,
303 gboolean *cancelled);
304 static void attach_property_cancel (GtkWidget *widget,
305 gboolean *cancelled);
306 static gint attach_property_delete_event (GtkWidget *widget,
308 gboolean *cancelled);
309 static gboolean attach_property_key_pressed (GtkWidget *widget,
311 gboolean *cancelled);
313 static void compose_exec_ext_editor (Compose *compose);
315 static gint compose_exec_ext_editor_real (const gchar *file);
316 static gboolean compose_ext_editor_kill (Compose *compose);
317 static gboolean compose_input_cb (GIOChannel *source,
318 GIOCondition condition,
320 static void compose_set_ext_editor_sensitive (Compose *compose,
322 #endif /* G_OS_UNIX */
324 static void compose_undo_state_changed (UndoMain *undostruct,
329 static void compose_create_header_entry (Compose *compose);
330 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
331 static void compose_remove_header_entries(Compose *compose);
333 static void compose_update_priority_menu_item(Compose * compose);
335 static void compose_spell_menu_changed (void *data);
337 static void compose_add_field_list ( Compose *compose,
338 GList *listAddress );
340 /* callback functions */
342 static gboolean compose_edit_size_alloc (GtkEditable *widget,
343 GtkAllocation *allocation,
344 GtkSHRuler *shruler);
345 static void account_activated (GtkComboBox *optmenu,
347 static void attach_selected (GtkTreeView *tree_view,
348 GtkTreePath *tree_path,
349 GtkTreeViewColumn *column,
351 static gboolean attach_button_pressed (GtkWidget *widget,
352 GdkEventButton *event,
354 static gboolean attach_key_pressed (GtkWidget *widget,
357 static void compose_send_cb (GtkAction *action, gpointer data);
358 static void compose_send_later_cb (GtkAction *action, gpointer data);
360 static void compose_save_cb (GtkAction *action,
363 static void compose_attach_cb (GtkAction *action,
365 static void compose_insert_file_cb (GtkAction *action,
367 static void compose_insert_sig_cb (GtkAction *action,
370 static void compose_close_cb (GtkAction *action,
373 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
375 static void compose_address_cb (GtkAction *action,
377 static void about_show_cb (GtkAction *action,
379 static void compose_template_activate_cb(GtkWidget *widget,
382 static void compose_ext_editor_cb (GtkAction *action,
385 static gint compose_delete_cb (GtkWidget *widget,
389 static void compose_undo_cb (GtkAction *action,
391 static void compose_redo_cb (GtkAction *action,
393 static void compose_cut_cb (GtkAction *action,
395 static void compose_copy_cb (GtkAction *action,
397 static void compose_paste_cb (GtkAction *action,
399 static void compose_paste_as_quote_cb (GtkAction *action,
401 static void compose_paste_no_wrap_cb (GtkAction *action,
403 static void compose_paste_wrap_cb (GtkAction *action,
405 static void compose_allsel_cb (GtkAction *action,
408 static void compose_advanced_action_cb (GtkAction *action,
411 static void compose_grab_focus_cb (GtkWidget *widget,
414 static void compose_changed_cb (GtkTextBuffer *textbuf,
417 static void compose_wrap_cb (GtkAction *action,
419 static void compose_wrap_all_cb (GtkAction *action,
421 static void compose_find_cb (GtkAction *action,
423 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
425 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
428 static void compose_toggle_ruler_cb (GtkToggleAction *action,
430 static void compose_toggle_sign_cb (GtkToggleAction *action,
432 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
434 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
435 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
436 static void activate_privacy_system (Compose *compose,
437 PrefsAccount *account,
439 static void compose_use_signing(Compose *compose, gboolean use_signing);
440 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
441 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
443 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
445 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
446 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
447 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
449 static void compose_attach_drag_received_cb (GtkWidget *widget,
450 GdkDragContext *drag_context,
453 GtkSelectionData *data,
457 static void compose_insert_drag_received_cb (GtkWidget *widget,
458 GdkDragContext *drag_context,
461 GtkSelectionData *data,
465 static void compose_header_drag_received_cb (GtkWidget *widget,
466 GdkDragContext *drag_context,
469 GtkSelectionData *data,
474 static gboolean compose_drag_drop (GtkWidget *widget,
475 GdkDragContext *drag_context,
477 guint time, gpointer user_data);
479 static void text_inserted (GtkTextBuffer *buffer,
484 static Compose *compose_generic_reply(MsgInfo *msginfo,
485 ComposeQuoteMode quote_mode,
489 gboolean followup_and_reply_to,
492 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
493 ComposeHeaderEntry *headerentry);
494 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
496 ComposeHeaderEntry *headerentry);
498 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
500 static void compose_allow_user_actions (Compose *compose, gboolean allow);
502 static void compose_nothing_cb (GtkAction *action, gpointer data)
508 static void compose_check_all (GtkAction *action, gpointer data);
509 static void compose_highlight_all (GtkAction *action, gpointer data);
510 static void compose_check_backwards (GtkAction *action, gpointer data);
511 static void compose_check_forwards_go (GtkAction *action, gpointer data);
514 static gint compose_defer_auto_save_draft (Compose *compose);
515 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
517 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
520 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
521 FolderItem *folder_item);
523 static void compose_attach_update_label(Compose *compose);
525 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
527 static GtkActionEntry compose_popup_entries[] =
529 {"Compose", NULL, "Compose" },
530 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
531 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
532 {"Compose/---", NULL, "---", NULL, NULL, NULL },
533 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
536 static GtkActionEntry compose_entries[] =
538 {"Menu", NULL, "Menu" },
540 {"Message", NULL, N_("_Message") },
541 {"Edit", NULL, N_("_Edit") },
543 {"Spelling", NULL, N_("_Spelling") },
545 {"Options", NULL, N_("_Options") },
546 {"Tools", NULL, N_("_Tools") },
547 {"Help", NULL, N_("_Help") },
549 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
550 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
551 {"Message/---", NULL, "---" },
553 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
554 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
555 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
556 /* {"Message/---", NULL, "---" }, */
557 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
562 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
563 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
564 {"Edit/---", NULL, "---" },
566 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
567 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
568 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
570 {"Edit/SpecialPaste", NULL, N_("Special paste") },
571 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
572 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
573 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
575 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
577 {"Edit/Advanced", NULL, N_("A_dvanced") },
578 {"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*/
579 {"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*/
580 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
581 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
582 {"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*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"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*/
588 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
589 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
590 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
591 {"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*/
593 /* {"Edit/---", NULL, "---" }, */
594 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
596 /* {"Edit/---", NULL, "---" }, */
597 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
598 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
599 /* {"Edit/---", NULL, "---" }, */
600 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
603 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
604 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
605 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
606 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
608 {"Spelling/---", NULL, "---" },
609 {"Spelling/Options", NULL, N_("_Options") },
614 {"Options/ReplyMode", NULL, N_("Reply _mode") },
615 {"Options/---", NULL, "---" },
616 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
617 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
619 /* {"Options/---", NULL, "---" }, */
621 {"Options/Priority", NULL, N_("_Priority") },
623 {"Options/Encoding", NULL, N_("Character _encoding") },
624 {"Options/Encoding/---", NULL, "---" },
625 #define ENC_ACTION(cs_char,c_char,string) \
626 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
628 {"Options/Encoding/Western", NULL, N_("Western European") },
629 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
630 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
631 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
632 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
633 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
634 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
635 {"Options/Encoding/Korean", NULL, N_("Korean") },
636 {"Options/Encoding/Thai", NULL, N_("Thai") },
639 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
641 {"Tools/Template", NULL, N_("_Template") },
642 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 {"Tools/Actions", NULL, N_("Actio_ns") },
644 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
650 static GtkToggleActionEntry compose_toggle_entries[] =
652 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
653 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
654 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
655 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
656 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
657 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
658 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
661 static GtkRadioActionEntry compose_radio_rm_entries[] =
663 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
669 static GtkRadioActionEntry compose_radio_prio_entries[] =
671 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
678 static GtkRadioActionEntry compose_radio_enc_entries[] =
680 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
713 static GtkTargetEntry compose_mime_types[] =
715 {"text/uri-list", 0, 0},
716 {"UTF8_STRING", 0, 0},
720 static gboolean compose_put_existing_to_front(MsgInfo *info)
722 GList *compose_list = compose_get_compose_list();
726 for (elem = compose_list; elem != NULL && elem->data != NULL;
728 Compose *c = (Compose*)elem->data;
730 if (!c->targetinfo || !c->targetinfo->msgid ||
734 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
735 gtkut_window_popup(c->window);
743 static GdkColor quote_color1 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color2 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color3 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor1 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor2 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor3 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
757 static GdkColor signature_color = {
764 static GdkColor uri_color = {
771 static void compose_create_tags(GtkTextView *text, Compose *compose)
773 GtkTextBuffer *buffer;
774 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 buffer = gtk_text_view_get_buffer(text);
782 if (prefs_common.enable_color) {
783 /* grab the quote colors, converting from an int to a GdkColor */
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
798 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
801 signature_color = quote_color1 = quote_color2 = quote_color3 =
802 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
805 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
806 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
807 "foreground-gdk", "e_color1,
808 "paragraph-background-gdk", "e_bgcolor1,
810 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
811 "foreground-gdk", "e_color2,
812 "paragraph-background-gdk", "e_bgcolor2,
814 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
815 "foreground-gdk", "e_color3,
816 "paragraph-background-gdk", "e_bgcolor3,
819 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820 "foreground-gdk", "e_color1,
822 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
823 "foreground-gdk", "e_color2,
825 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
826 "foreground-gdk", "e_color3,
830 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
831 "foreground-gdk", &signature_color,
834 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
835 "foreground-gdk", &uri_color,
837 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
838 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
840 color[0] = quote_color1;
841 color[1] = quote_color2;
842 color[2] = quote_color3;
843 color[3] = quote_bgcolor1;
844 color[4] = quote_bgcolor2;
845 color[5] = quote_bgcolor3;
846 color[6] = signature_color;
847 color[7] = uri_color;
848 cmap = gdk_drawable_get_colormap(compose->window->window);
849 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
851 for (i = 0; i < 8; i++) {
852 if (success[i] == FALSE) {
855 g_warning("Compose: color allocation failed.\n");
856 style = gtk_widget_get_style(GTK_WIDGET(text));
857 quote_color1 = quote_color2 = quote_color3 =
858 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
859 signature_color = uri_color = black;
864 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
865 GPtrArray *attach_files)
867 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
870 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
872 return compose_generic_new(account, mailto, item, NULL, NULL);
875 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
877 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
880 #define SCROLL_TO_CURSOR(compose) { \
881 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
882 gtk_text_view_get_buffer( \
883 GTK_TEXT_VIEW(compose->text))); \
884 gtk_text_view_scroll_mark_onscreen( \
885 GTK_TEXT_VIEW(compose->text), \
889 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
890 GPtrArray *attach_files, GList *listAddress )
893 GtkTextView *textview;
894 GtkTextBuffer *textbuf;
896 const gchar *subject_format = NULL;
897 const gchar *body_format = NULL;
898 gchar *mailto_from = NULL;
899 PrefsAccount *mailto_account = NULL;
900 MsgInfo* dummyinfo = NULL;
902 /* check if mailto defines a from */
903 if (mailto && *mailto != '\0') {
904 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
905 /* mailto defines a from, check if we can get account prefs from it,
906 if not, the account prefs will be guessed using other ways, but we'll keep
909 mailto_account = account_find_from_address(mailto_from, TRUE);
911 account = mailto_account;
914 /* if no account prefs set from mailto, set if from folder prefs (if any) */
915 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
916 account = account_find_from_id(item->prefs->default_account);
918 /* if no account prefs set, fallback to the current one */
919 if (!account) account = cur_account;
920 g_return_val_if_fail(account != NULL, NULL);
922 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
924 /* override from name if mailto asked for it */
926 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
929 /* override from name according to folder properties */
930 if (item && item->prefs &&
931 item->prefs->compose_with_format &&
932 item->prefs->compose_override_from_format &&
933 *item->prefs->compose_override_from_format != '\0') {
938 dummyinfo = compose_msginfo_new_from_compose(compose);
940 /* decode \-escape sequences in the internal representation of the quote format */
941 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
942 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
945 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
948 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
950 quote_fmt_scan_string(tmp);
953 buf = quote_fmt_get_buffer();
955 alertpanel_error(_("New message From format error."));
957 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
958 quote_fmt_reset_vartable();
963 compose->replyinfo = NULL;
964 compose->fwdinfo = NULL;
966 textview = GTK_TEXT_VIEW(compose->text);
967 textbuf = gtk_text_view_get_buffer(textview);
968 compose_create_tags(textview, compose);
970 undo_block(compose->undostruct);
972 compose_set_dictionaries_from_folder_prefs(compose, item);
975 if (account->auto_sig)
976 compose_insert_sig(compose, FALSE);
977 gtk_text_buffer_get_start_iter(textbuf, &iter);
978 gtk_text_buffer_place_cursor(textbuf, &iter);
980 if (account->protocol != A_NNTP) {
981 if (mailto && *mailto != '\0') {
982 compose_entries_set(compose, mailto, COMPOSE_TO);
984 } else if (item && item->prefs->enable_default_to) {
985 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
986 compose_entry_mark_default_to(compose, item->prefs->default_to);
988 if (item && item->ret_rcpt) {
989 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
992 if (mailto && *mailto != '\0') {
993 if (!strchr(mailto, '@'))
994 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
996 compose_entries_set(compose, mailto, COMPOSE_TO);
997 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
998 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1001 * CLAWS: just don't allow return receipt request, even if the user
1002 * may want to send an email. simple but foolproof.
1004 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1006 compose_add_field_list( compose, listAddress );
1008 if (item && item->prefs && item->prefs->compose_with_format) {
1009 subject_format = item->prefs->compose_subject_format;
1010 body_format = item->prefs->compose_body_format;
1011 } else if (account->compose_with_format) {
1012 subject_format = account->compose_subject_format;
1013 body_format = account->compose_body_format;
1014 } else if (prefs_common.compose_with_format) {
1015 subject_format = prefs_common.compose_subject_format;
1016 body_format = prefs_common.compose_body_format;
1019 if (subject_format || body_format) {
1022 && *subject_format != '\0' )
1024 gchar *subject = NULL;
1029 dummyinfo = compose_msginfo_new_from_compose(compose);
1031 /* decode \-escape sequences in the internal representation of the quote format */
1032 tmp = malloc(strlen(subject_format)+1);
1033 pref_get_unescaped_pref(tmp, subject_format);
1035 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1037 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1038 compose->gtkaspell);
1040 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1042 quote_fmt_scan_string(tmp);
1045 buf = quote_fmt_get_buffer();
1047 alertpanel_error(_("New message subject format error."));
1049 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1050 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1051 quote_fmt_reset_vartable();
1058 && *body_format != '\0' )
1061 GtkTextBuffer *buffer;
1062 GtkTextIter start, end;
1066 dummyinfo = compose_msginfo_new_from_compose(compose);
1068 text = GTK_TEXT_VIEW(compose->text);
1069 buffer = gtk_text_view_get_buffer(text);
1070 gtk_text_buffer_get_start_iter(buffer, &start);
1071 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1072 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1074 compose_quote_fmt(compose, dummyinfo,
1076 NULL, tmp, FALSE, TRUE,
1077 _("New message body format error at line %d."));
1078 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1079 quote_fmt_reset_vartable();
1085 procmsg_msginfo_free( dummyinfo );
1091 for (i = 0; i < attach_files->len; i++) {
1092 file = g_ptr_array_index(attach_files, i);
1093 compose_attach_append(compose, file, file, NULL);
1097 compose_show_first_last_header(compose, TRUE);
1099 /* Set save folder */
1100 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1101 gchar *folderidentifier;
1103 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1104 folderidentifier = folder_item_get_identifier(item);
1105 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1106 g_free(folderidentifier);
1109 gtk_widget_grab_focus(compose->header_last->entry);
1111 undo_unblock(compose->undostruct);
1113 if (prefs_common.auto_exteditor)
1114 compose_exec_ext_editor(compose);
1116 compose->draft_timeout_tag = -1;
1117 SCROLL_TO_CURSOR(compose);
1119 compose->modified = FALSE;
1120 compose_set_title(compose);
1124 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1125 gboolean override_pref)
1127 gchar *privacy = NULL;
1129 g_return_if_fail(compose != NULL);
1130 g_return_if_fail(account != NULL);
1132 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1135 if (account->default_privacy_system
1136 && strlen(account->default_privacy_system)) {
1137 privacy = account->default_privacy_system;
1139 GSList *privacy_avail = privacy_get_system_ids();
1140 if (privacy_avail && g_slist_length(privacy_avail)) {
1141 privacy = (gchar *)(privacy_avail->data);
1144 if (privacy != NULL) {
1145 if (compose->privacy_system == NULL)
1146 compose->privacy_system = g_strdup(privacy);
1147 else if (*(compose->privacy_system) == '\0') {
1148 g_free(compose->privacy_system);
1149 compose->privacy_system = g_strdup(privacy);
1151 compose_update_privacy_system_menu_item(compose, FALSE);
1152 compose_use_encryption(compose, TRUE);
1156 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1158 gchar *privacy = NULL;
1160 if (account->default_privacy_system
1161 && strlen(account->default_privacy_system)) {
1162 privacy = account->default_privacy_system;
1164 GSList *privacy_avail = privacy_get_system_ids();
1165 if (privacy_avail && g_slist_length(privacy_avail)) {
1166 privacy = (gchar *)(privacy_avail->data);
1169 if (privacy != NULL) {
1170 if (compose->privacy_system == NULL)
1171 compose->privacy_system = g_strdup(privacy);
1172 compose_update_privacy_system_menu_item(compose, FALSE);
1173 compose_use_signing(compose, TRUE);
1177 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1181 Compose *compose = NULL;
1183 g_return_val_if_fail(msginfo_list != NULL, NULL);
1185 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1186 g_return_val_if_fail(msginfo != NULL, NULL);
1188 list_len = g_slist_length(msginfo_list);
1192 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1193 FALSE, prefs_common.default_reply_list, FALSE, body);
1195 case COMPOSE_REPLY_WITH_QUOTE:
1196 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1197 FALSE, prefs_common.default_reply_list, FALSE, body);
1199 case COMPOSE_REPLY_WITHOUT_QUOTE:
1200 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1201 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1203 case COMPOSE_REPLY_TO_SENDER:
1204 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1205 FALSE, FALSE, TRUE, body);
1207 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1208 compose = compose_followup_and_reply_to(msginfo,
1209 COMPOSE_QUOTE_CHECK,
1210 FALSE, FALSE, body);
1212 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1213 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1214 FALSE, FALSE, TRUE, body);
1216 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1217 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1218 FALSE, FALSE, TRUE, NULL);
1220 case COMPOSE_REPLY_TO_ALL:
1221 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1222 TRUE, FALSE, FALSE, body);
1224 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1225 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1226 TRUE, FALSE, FALSE, body);
1228 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1229 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1230 TRUE, FALSE, FALSE, NULL);
1232 case COMPOSE_REPLY_TO_LIST:
1233 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1234 FALSE, TRUE, FALSE, body);
1236 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1237 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1238 FALSE, TRUE, FALSE, body);
1240 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1241 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1242 FALSE, TRUE, FALSE, NULL);
1244 case COMPOSE_FORWARD:
1245 if (prefs_common.forward_as_attachment) {
1246 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1249 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1253 case COMPOSE_FORWARD_INLINE:
1254 /* check if we reply to more than one Message */
1255 if (list_len == 1) {
1256 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1259 /* more messages FALL THROUGH */
1260 case COMPOSE_FORWARD_AS_ATTACH:
1261 compose = compose_forward_multiple(NULL, msginfo_list);
1263 case COMPOSE_REDIRECT:
1264 compose = compose_redirect(NULL, msginfo, FALSE);
1267 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1270 if (compose == NULL) {
1271 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1275 compose->rmode = mode;
1276 switch (compose->rmode) {
1278 case COMPOSE_REPLY_WITH_QUOTE:
1279 case COMPOSE_REPLY_WITHOUT_QUOTE:
1280 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1281 debug_print("reply mode Normal\n");
1282 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1283 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1285 case COMPOSE_REPLY_TO_SENDER:
1286 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1287 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1288 debug_print("reply mode Sender\n");
1289 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1291 case COMPOSE_REPLY_TO_ALL:
1292 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1293 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1294 debug_print("reply mode All\n");
1295 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1297 case COMPOSE_REPLY_TO_LIST:
1298 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1299 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1300 debug_print("reply mode List\n");
1301 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1309 static Compose *compose_reply(MsgInfo *msginfo,
1310 ComposeQuoteMode quote_mode,
1316 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1317 to_sender, FALSE, body);
1320 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1321 ComposeQuoteMode quote_mode,
1326 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1327 to_sender, TRUE, body);
1330 static void compose_extract_original_charset(Compose *compose)
1332 MsgInfo *info = NULL;
1333 if (compose->replyinfo) {
1334 info = compose->replyinfo;
1335 } else if (compose->fwdinfo) {
1336 info = compose->fwdinfo;
1337 } else if (compose->targetinfo) {
1338 info = compose->targetinfo;
1341 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1342 MimeInfo *partinfo = mimeinfo;
1343 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1344 partinfo = procmime_mimeinfo_next(partinfo);
1346 compose->orig_charset =
1347 g_strdup(procmime_mimeinfo_get_parameter(
1348 partinfo, "charset"));
1350 procmime_mimeinfo_free_all(mimeinfo);
1354 #define SIGNAL_BLOCK(buffer) { \
1355 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1356 G_CALLBACK(compose_changed_cb), \
1358 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1359 G_CALLBACK(text_inserted), \
1363 #define SIGNAL_UNBLOCK(buffer) { \
1364 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1365 G_CALLBACK(compose_changed_cb), \
1367 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1368 G_CALLBACK(text_inserted), \
1372 static Compose *compose_generic_reply(MsgInfo *msginfo,
1373 ComposeQuoteMode quote_mode,
1374 gboolean to_all, gboolean to_ml,
1376 gboolean followup_and_reply_to,
1380 PrefsAccount *account = NULL;
1381 GtkTextView *textview;
1382 GtkTextBuffer *textbuf;
1383 gboolean quote = FALSE;
1384 const gchar *qmark = NULL;
1385 const gchar *body_fmt = NULL;
1387 g_return_val_if_fail(msginfo != NULL, NULL);
1388 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1390 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1392 g_return_val_if_fail(account != NULL, NULL);
1394 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1396 compose->updating = TRUE;
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1399 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1401 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1402 if (!compose->replyinfo)
1403 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1405 compose_extract_original_charset(compose);
1407 if (msginfo->folder && msginfo->folder->ret_rcpt)
1408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1410 /* Set save folder */
1411 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1412 gchar *folderidentifier;
1414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1415 folderidentifier = folder_item_get_identifier(msginfo->folder);
1416 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1417 g_free(folderidentifier);
1420 if (compose_parse_header(compose, msginfo) < 0) {
1421 compose->updating = FALSE;
1422 compose_destroy(compose);
1426 /* override from name according to folder properties */
1427 if (msginfo->folder && msginfo->folder->prefs &&
1428 msginfo->folder->prefs->reply_with_format &&
1429 msginfo->folder->prefs->reply_override_from_format &&
1430 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1435 /* decode \-escape sequences in the internal representation of the quote format */
1436 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1437 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1440 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1441 compose->gtkaspell);
1443 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1445 quote_fmt_scan_string(tmp);
1448 buf = quote_fmt_get_buffer();
1450 alertpanel_error(_("Message reply From format error."));
1452 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1453 quote_fmt_reset_vartable();
1458 textview = (GTK_TEXT_VIEW(compose->text));
1459 textbuf = gtk_text_view_get_buffer(textview);
1460 compose_create_tags(textview, compose);
1462 undo_block(compose->undostruct);
1464 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1467 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1468 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1469 /* use the reply format of folder (if enabled), or the account's one
1470 (if enabled) or fallback to the global reply format, which is always
1471 enabled (even if empty), and use the relevant quotemark */
1473 if (msginfo->folder && msginfo->folder->prefs &&
1474 msginfo->folder->prefs->reply_with_format) {
1475 qmark = msginfo->folder->prefs->reply_quotemark;
1476 body_fmt = msginfo->folder->prefs->reply_body_format;
1478 } else if (account->reply_with_format) {
1479 qmark = account->reply_quotemark;
1480 body_fmt = account->reply_body_format;
1483 qmark = prefs_common.quotemark;
1484 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1485 body_fmt = gettext(prefs_common.quotefmt);
1492 /* empty quotemark is not allowed */
1493 if (qmark == NULL || *qmark == '\0')
1495 compose_quote_fmt(compose, compose->replyinfo,
1496 body_fmt, qmark, body, FALSE, TRUE,
1497 _("Message reply format error at line %d."));
1498 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1499 quote_fmt_reset_vartable();
1502 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1503 compose_force_encryption(compose, account, FALSE);
1506 privacy_msginfo_get_signed_state(compose->replyinfo);
1507 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1508 compose_force_signing(compose, account);
1511 SIGNAL_BLOCK(textbuf);
1513 if (account->auto_sig)
1514 compose_insert_sig(compose, FALSE);
1516 compose_wrap_all(compose);
1518 SIGNAL_UNBLOCK(textbuf);
1520 gtk_widget_grab_focus(compose->text);
1522 undo_unblock(compose->undostruct);
1524 if (prefs_common.auto_exteditor)
1525 compose_exec_ext_editor(compose);
1527 compose->modified = FALSE;
1528 compose_set_title(compose);
1530 compose->updating = FALSE;
1531 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1532 SCROLL_TO_CURSOR(compose);
1534 if (compose->deferred_destroy) {
1535 compose_destroy(compose);
1542 #define INSERT_FW_HEADER(var, hdr) \
1543 if (msginfo->var && *msginfo->var) { \
1544 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1545 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1546 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1549 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1550 gboolean as_attach, const gchar *body,
1551 gboolean no_extedit,
1555 GtkTextView *textview;
1556 GtkTextBuffer *textbuf;
1559 g_return_val_if_fail(msginfo != NULL, NULL);
1560 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1563 !(account = compose_guess_forward_account_from_msginfo
1565 account = cur_account;
1567 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1569 compose->updating = TRUE;
1570 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1571 if (!compose->fwdinfo)
1572 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1574 compose_extract_original_charset(compose);
1576 if (msginfo->subject && *msginfo->subject) {
1577 gchar *buf, *buf2, *p;
1579 buf = p = g_strdup(msginfo->subject);
1580 p += subject_get_prefix_length(p);
1581 memmove(buf, p, strlen(p) + 1);
1583 buf2 = g_strdup_printf("Fw: %s", buf);
1584 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1590 /* override from name according to folder properties */
1591 if (msginfo->folder && msginfo->folder->prefs &&
1592 msginfo->folder->prefs->forward_with_format &&
1593 msginfo->folder->prefs->forward_override_from_format &&
1594 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1598 MsgInfo *full_msginfo = NULL;
1601 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1603 full_msginfo = procmsg_msginfo_copy(msginfo);
1605 /* decode \-escape sequences in the internal representation of the quote format */
1606 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1607 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1610 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1611 compose->gtkaspell);
1613 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1615 quote_fmt_scan_string(tmp);
1618 buf = quote_fmt_get_buffer();
1620 alertpanel_error(_("Message forward From format error."));
1622 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1623 quote_fmt_reset_vartable();
1626 procmsg_msginfo_free(full_msginfo);
1629 textview = GTK_TEXT_VIEW(compose->text);
1630 textbuf = gtk_text_view_get_buffer(textview);
1631 compose_create_tags(textview, compose);
1633 undo_block(compose->undostruct);
1637 msgfile = procmsg_get_message_file(msginfo);
1638 if (!is_file_exist(msgfile))
1639 g_warning("%s: file not exist\n", msgfile);
1641 compose_attach_append(compose, msgfile, msgfile,
1646 const gchar *qmark = NULL;
1647 const gchar *body_fmt = NULL;
1648 MsgInfo *full_msginfo;
1650 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1651 body_fmt = gettext(prefs_common.fw_quotefmt);
1655 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1657 full_msginfo = procmsg_msginfo_copy(msginfo);
1659 /* use the forward format of folder (if enabled), or the account's one
1660 (if enabled) or fallback to the global forward format, which is always
1661 enabled (even if empty), and use the relevant quotemark */
1662 if (msginfo->folder && msginfo->folder->prefs &&
1663 msginfo->folder->prefs->forward_with_format) {
1664 qmark = msginfo->folder->prefs->forward_quotemark;
1665 body_fmt = msginfo->folder->prefs->forward_body_format;
1667 } else if (account->forward_with_format) {
1668 qmark = account->forward_quotemark;
1669 body_fmt = account->forward_body_format;
1672 qmark = prefs_common.fw_quotemark;
1673 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1674 body_fmt = gettext(prefs_common.fw_quotefmt);
1679 /* empty quotemark is not allowed */
1680 if (qmark == NULL || *qmark == '\0')
1683 compose_quote_fmt(compose, full_msginfo,
1684 body_fmt, qmark, body, FALSE, TRUE,
1685 _("Message forward format error at line %d."));
1686 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1687 quote_fmt_reset_vartable();
1688 compose_attach_parts(compose, msginfo);
1690 procmsg_msginfo_free(full_msginfo);
1693 SIGNAL_BLOCK(textbuf);
1695 if (account->auto_sig)
1696 compose_insert_sig(compose, FALSE);
1698 compose_wrap_all(compose);
1700 SIGNAL_UNBLOCK(textbuf);
1702 gtk_text_buffer_get_start_iter(textbuf, &iter);
1703 gtk_text_buffer_place_cursor(textbuf, &iter);
1705 gtk_widget_grab_focus(compose->header_last->entry);
1707 if (!no_extedit && prefs_common.auto_exteditor)
1708 compose_exec_ext_editor(compose);
1711 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1712 gchar *folderidentifier;
1714 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1715 folderidentifier = folder_item_get_identifier(msginfo->folder);
1716 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1717 g_free(folderidentifier);
1720 undo_unblock(compose->undostruct);
1722 compose->modified = FALSE;
1723 compose_set_title(compose);
1725 compose->updating = FALSE;
1726 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1727 SCROLL_TO_CURSOR(compose);
1729 if (compose->deferred_destroy) {
1730 compose_destroy(compose);
1737 #undef INSERT_FW_HEADER
1739 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1742 GtkTextView *textview;
1743 GtkTextBuffer *textbuf;
1747 gboolean single_mail = TRUE;
1749 g_return_val_if_fail(msginfo_list != NULL, NULL);
1751 if (g_slist_length(msginfo_list) > 1)
1752 single_mail = FALSE;
1754 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1755 if (((MsgInfo *)msginfo->data)->folder == NULL)
1758 /* guess account from first selected message */
1760 !(account = compose_guess_forward_account_from_msginfo
1761 (msginfo_list->data)))
1762 account = cur_account;
1764 g_return_val_if_fail(account != NULL, NULL);
1766 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1767 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1768 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1771 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1773 compose->updating = TRUE;
1775 /* override from name according to folder properties */
1776 if (msginfo_list->data) {
1777 MsgInfo *msginfo = msginfo_list->data;
1779 if (msginfo->folder && msginfo->folder->prefs &&
1780 msginfo->folder->prefs->forward_with_format &&
1781 msginfo->folder->prefs->forward_override_from_format &&
1782 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1787 /* decode \-escape sequences in the internal representation of the quote format */
1788 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1789 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1792 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1793 compose->gtkaspell);
1795 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1797 quote_fmt_scan_string(tmp);
1800 buf = quote_fmt_get_buffer();
1802 alertpanel_error(_("Message forward From format error."));
1804 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1805 quote_fmt_reset_vartable();
1811 textview = GTK_TEXT_VIEW(compose->text);
1812 textbuf = gtk_text_view_get_buffer(textview);
1813 compose_create_tags(textview, compose);
1815 undo_block(compose->undostruct);
1816 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1817 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1819 if (!is_file_exist(msgfile))
1820 g_warning("%s: file not exist\n", msgfile);
1822 compose_attach_append(compose, msgfile, msgfile,
1828 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1829 if (info->subject && *info->subject) {
1830 gchar *buf, *buf2, *p;
1832 buf = p = g_strdup(info->subject);
1833 p += subject_get_prefix_length(p);
1834 memmove(buf, p, strlen(p) + 1);
1836 buf2 = g_strdup_printf("Fw: %s", buf);
1837 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1843 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1844 _("Fw: multiple emails"));
1847 SIGNAL_BLOCK(textbuf);
1849 if (account->auto_sig)
1850 compose_insert_sig(compose, FALSE);
1852 compose_wrap_all(compose);
1854 SIGNAL_UNBLOCK(textbuf);
1856 gtk_text_buffer_get_start_iter(textbuf, &iter);
1857 gtk_text_buffer_place_cursor(textbuf, &iter);
1859 gtk_widget_grab_focus(compose->header_last->entry);
1860 undo_unblock(compose->undostruct);
1861 compose->modified = FALSE;
1862 compose_set_title(compose);
1864 compose->updating = FALSE;
1865 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1866 SCROLL_TO_CURSOR(compose);
1868 if (compose->deferred_destroy) {
1869 compose_destroy(compose);
1876 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1878 GtkTextIter start = *iter;
1879 GtkTextIter end_iter;
1880 int start_pos = gtk_text_iter_get_offset(&start);
1882 if (!compose->account->sig_sep)
1885 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1886 start_pos+strlen(compose->account->sig_sep));
1888 /* check sig separator */
1889 str = gtk_text_iter_get_text(&start, &end_iter);
1890 if (!strcmp(str, compose->account->sig_sep)) {
1892 /* check end of line (\n) */
1893 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1894 start_pos+strlen(compose->account->sig_sep));
1895 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1896 start_pos+strlen(compose->account->sig_sep)+1);
1897 tmp = gtk_text_iter_get_text(&start, &end_iter);
1898 if (!strcmp(tmp,"\n")) {
1910 static void compose_colorize_signature(Compose *compose)
1912 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1914 GtkTextIter end_iter;
1915 gtk_text_buffer_get_start_iter(buffer, &iter);
1916 while (gtk_text_iter_forward_line(&iter))
1917 if (compose_is_sig_separator(compose, buffer, &iter)) {
1918 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1919 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1923 #define BLOCK_WRAP() { \
1924 prev_autowrap = compose->autowrap; \
1925 buffer = gtk_text_view_get_buffer( \
1926 GTK_TEXT_VIEW(compose->text)); \
1927 compose->autowrap = FALSE; \
1929 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1930 G_CALLBACK(compose_changed_cb), \
1932 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1933 G_CALLBACK(text_inserted), \
1936 #define UNBLOCK_WRAP() { \
1937 compose->autowrap = prev_autowrap; \
1938 if (compose->autowrap) { \
1939 gint old = compose->draft_timeout_tag; \
1940 compose->draft_timeout_tag = -2; \
1941 compose_wrap_all(compose); \
1942 compose->draft_timeout_tag = old; \
1945 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1946 G_CALLBACK(compose_changed_cb), \
1948 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1949 G_CALLBACK(text_inserted), \
1953 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1955 Compose *compose = NULL;
1956 PrefsAccount *account = NULL;
1957 GtkTextView *textview;
1958 GtkTextBuffer *textbuf;
1962 gchar buf[BUFFSIZE];
1963 gboolean use_signing = FALSE;
1964 gboolean use_encryption = FALSE;
1965 gchar *privacy_system = NULL;
1966 int priority = PRIORITY_NORMAL;
1967 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1968 gboolean autowrap = prefs_common.autowrap;
1969 gboolean autoindent = prefs_common.auto_indent;
1971 g_return_val_if_fail(msginfo != NULL, NULL);
1972 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1974 if (compose_put_existing_to_front(msginfo)) {
1978 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1979 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1980 gchar queueheader_buf[BUFFSIZE];
1983 /* Select Account from queue headers */
1984 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1985 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1986 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1987 account = account_find_from_id(id);
1989 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1990 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1991 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1992 account = account_find_from_id(id);
1994 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1995 sizeof(queueheader_buf), "NAID:")) {
1996 id = atoi(&queueheader_buf[strlen("NAID:")]);
1997 account = account_find_from_id(id);
1999 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2000 sizeof(queueheader_buf), "MAID:")) {
2001 id = atoi(&queueheader_buf[strlen("MAID:")]);
2002 account = account_find_from_id(id);
2004 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2005 sizeof(queueheader_buf), "S:")) {
2006 account = account_find_from_address(queueheader_buf, FALSE);
2008 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2009 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2010 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2011 use_signing = param;
2014 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2015 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2016 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2017 use_signing = param;
2020 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2021 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2022 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2023 use_encryption = param;
2025 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2026 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2027 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2028 use_encryption = param;
2030 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2031 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2032 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2035 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2036 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2037 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2042 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2044 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2045 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2046 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2048 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2049 sizeof(queueheader_buf), "X-Priority: ")) {
2050 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2053 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2054 sizeof(queueheader_buf), "RMID:")) {
2055 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2056 if (tokens[0] && tokens[1] && tokens[2]) {
2057 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2058 if (orig_item != NULL) {
2059 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2064 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2065 sizeof(queueheader_buf), "FMID:")) {
2066 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2067 if (tokens[0] && tokens[1] && tokens[2]) {
2068 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2069 if (orig_item != NULL) {
2070 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2076 account = msginfo->folder->folder->account;
2079 if (!account && prefs_common.reedit_account_autosel) {
2080 gchar from[BUFFSIZE];
2081 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2082 extract_address(from);
2083 account = account_find_from_address(from, FALSE);
2087 account = cur_account;
2089 g_return_val_if_fail(account != NULL, NULL);
2091 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2093 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2094 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2095 compose->autowrap = autowrap;
2096 compose->replyinfo = replyinfo;
2097 compose->fwdinfo = fwdinfo;
2099 compose->updating = TRUE;
2100 compose->priority = priority;
2102 if (privacy_system != NULL) {
2103 compose->privacy_system = privacy_system;
2104 compose_use_signing(compose, use_signing);
2105 compose_use_encryption(compose, use_encryption);
2106 compose_update_privacy_system_menu_item(compose, FALSE);
2108 activate_privacy_system(compose, account, FALSE);
2111 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2113 compose_extract_original_charset(compose);
2115 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2116 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2117 gchar queueheader_buf[BUFFSIZE];
2119 /* Set message save folder */
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2123 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2124 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2125 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2127 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2128 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2130 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2135 if (compose_parse_header(compose, msginfo) < 0) {
2136 compose->updating = FALSE;
2137 compose_destroy(compose);
2140 compose_reedit_set_entry(compose, msginfo);
2142 textview = GTK_TEXT_VIEW(compose->text);
2143 textbuf = gtk_text_view_get_buffer(textview);
2144 compose_create_tags(textview, compose);
2146 mark = gtk_text_buffer_get_insert(textbuf);
2147 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2149 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2150 G_CALLBACK(compose_changed_cb),
2153 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2154 fp = procmime_get_first_encrypted_text_content(msginfo);
2156 compose_force_encryption(compose, account, TRUE);
2159 fp = procmime_get_first_text_content(msginfo);
2162 g_warning("Can't get text part\n");
2166 gboolean prev_autowrap = compose->autowrap;
2167 GtkTextBuffer *buffer = textbuf;
2169 while (fgets(buf, sizeof(buf), fp) != NULL) {
2171 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2177 compose_attach_parts(compose, msginfo);
2179 compose_colorize_signature(compose);
2181 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2182 G_CALLBACK(compose_changed_cb),
2185 gtk_widget_grab_focus(compose->text);
2187 if (prefs_common.auto_exteditor) {
2188 compose_exec_ext_editor(compose);
2190 compose->modified = FALSE;
2191 compose_set_title(compose);
2193 compose->updating = FALSE;
2194 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2195 SCROLL_TO_CURSOR(compose);
2197 if (compose->deferred_destroy) {
2198 compose_destroy(compose);
2202 compose->sig_str = account_get_signature_str(compose->account);
2207 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2214 g_return_val_if_fail(msginfo != NULL, NULL);
2217 account = account_get_reply_account(msginfo,
2218 prefs_common.reply_account_autosel);
2219 g_return_val_if_fail(account != NULL, NULL);
2221 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2223 compose->updating = TRUE;
2225 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2226 compose->replyinfo = NULL;
2227 compose->fwdinfo = NULL;
2229 compose_show_first_last_header(compose, TRUE);
2231 gtk_widget_grab_focus(compose->header_last->entry);
2233 filename = procmsg_get_message_file(msginfo);
2235 if (filename == NULL) {
2236 compose->updating = FALSE;
2237 compose_destroy(compose);
2242 compose->redirect_filename = filename;
2244 /* Set save folder */
2245 item = msginfo->folder;
2246 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2247 gchar *folderidentifier;
2249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2250 folderidentifier = folder_item_get_identifier(item);
2251 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2252 g_free(folderidentifier);
2255 compose_attach_parts(compose, msginfo);
2257 if (msginfo->subject)
2258 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2260 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2262 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2263 _("Message redirect format error at line %d."));
2264 quote_fmt_reset_vartable();
2265 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2267 compose_colorize_signature(compose);
2270 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2271 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2272 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2277 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2279 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2281 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2283 if (compose->toolbar->draft_btn)
2284 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2285 if (compose->toolbar->insert_btn)
2286 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2287 if (compose->toolbar->attach_btn)
2288 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2289 if (compose->toolbar->sig_btn)
2290 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2291 if (compose->toolbar->exteditor_btn)
2292 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2293 if (compose->toolbar->linewrap_current_btn)
2294 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2295 if (compose->toolbar->linewrap_all_btn)
2296 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2298 compose->modified = FALSE;
2299 compose_set_title(compose);
2300 compose->updating = FALSE;
2301 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2302 SCROLL_TO_CURSOR(compose);
2304 if (compose->deferred_destroy) {
2305 compose_destroy(compose);
2312 GList *compose_get_compose_list(void)
2314 return compose_list;
2317 void compose_entry_append(Compose *compose, const gchar *address,
2318 ComposeEntryType type)
2320 const gchar *header;
2322 gboolean in_quote = FALSE;
2323 if (!address || *address == '\0') return;
2330 header = N_("Bcc:");
2332 case COMPOSE_REPLYTO:
2333 header = N_("Reply-To:");
2335 case COMPOSE_NEWSGROUPS:
2336 header = N_("Newsgroups:");
2338 case COMPOSE_FOLLOWUPTO:
2339 header = N_( "Followup-To:");
2346 header = prefs_common_translated_header_name(header);
2348 cur = begin = (gchar *)address;
2350 /* we separate the line by commas, but not if we're inside a quoted
2352 while (*cur != '\0') {
2354 in_quote = !in_quote;
2355 if (*cur == ',' && !in_quote) {
2356 gchar *tmp = g_strdup(begin);
2358 tmp[cur-begin]='\0';
2361 while (*tmp == ' ' || *tmp == '\t')
2363 compose_add_header_entry(compose, header, tmp);
2370 gchar *tmp = g_strdup(begin);
2372 tmp[cur-begin]='\0';
2375 while (*tmp == ' ' || *tmp == '\t')
2377 compose_add_header_entry(compose, header, tmp);
2382 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2384 static GdkColor yellow;
2385 static GdkColor black;
2386 static gboolean yellow_initialised = FALSE;
2390 if (!yellow_initialised) {
2391 gdk_color_parse("#f5f6be", &yellow);
2392 gdk_color_parse("#000000", &black);
2393 yellow_initialised = gdk_colormap_alloc_color(
2394 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2395 yellow_initialised &= gdk_colormap_alloc_color(
2396 gdk_colormap_get_system(), &black, FALSE, TRUE);
2399 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2400 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2401 if (gtk_entry_get_text(entry) &&
2402 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2403 if (yellow_initialised) {
2404 gtk_widget_modify_base(
2405 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2406 GTK_STATE_NORMAL, &yellow);
2407 gtk_widget_modify_text(
2408 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2409 GTK_STATE_NORMAL, &black);
2415 void compose_toolbar_cb(gint action, gpointer data)
2417 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2418 Compose *compose = (Compose*)toolbar_item->parent;
2420 g_return_if_fail(compose != NULL);
2424 compose_send_cb(NULL, compose);
2427 compose_send_later_cb(NULL, compose);
2430 compose_draft(compose, COMPOSE_QUIT_EDITING);
2433 compose_insert_file_cb(NULL, compose);
2436 compose_attach_cb(NULL, compose);
2439 compose_insert_sig(compose, FALSE);
2442 compose_ext_editor_cb(NULL, compose);
2444 case A_LINEWRAP_CURRENT:
2445 compose_beautify_paragraph(compose, NULL, TRUE);
2447 case A_LINEWRAP_ALL:
2448 compose_wrap_all_full(compose, TRUE);
2451 compose_address_cb(NULL, compose);
2454 case A_CHECK_SPELLING:
2455 compose_check_all(NULL, compose);
2463 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2468 gchar *subject = NULL;
2472 gchar **attach = NULL;
2474 /* get mailto parts but skip from */
2475 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2478 compose_entry_append(compose, to, to_type);
2480 compose_entry_append(compose, cc, COMPOSE_CC);
2482 compose_entry_append(compose, bcc, COMPOSE_BCC);
2484 if (!g_utf8_validate (subject, -1, NULL)) {
2485 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2486 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2489 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2493 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2494 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2497 gboolean prev_autowrap = compose->autowrap;
2499 compose->autowrap = FALSE;
2501 mark = gtk_text_buffer_get_insert(buffer);
2502 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2504 if (!g_utf8_validate (body, -1, NULL)) {
2505 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2506 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2509 gtk_text_buffer_insert(buffer, &iter, body, -1);
2511 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2513 compose->autowrap = prev_autowrap;
2514 if (compose->autowrap)
2515 compose_wrap_all(compose);
2519 gint i = 0, att = 0;
2520 gchar *warn_files = NULL;
2521 while (attach[i] != NULL) {
2522 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2523 if (utf8_filename) {
2524 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2525 gchar *tmp = g_strdup_printf("%s%s\n",
2526 warn_files?warn_files:"",
2532 g_free(utf8_filename);
2534 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2539 alertpanel_notice(ngettext(
2540 "The following file has been attached: \n%s",
2541 "The following files have been attached: \n%s", att), warn_files);
2553 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2555 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2556 {"Cc:", NULL, TRUE},
2557 {"References:", NULL, FALSE},
2558 {"Bcc:", NULL, TRUE},
2559 {"Newsgroups:", NULL, TRUE},
2560 {"Followup-To:", NULL, TRUE},
2561 {"List-Post:", NULL, FALSE},
2562 {"X-Priority:", NULL, FALSE},
2563 {NULL, NULL, FALSE}};
2579 g_return_val_if_fail(msginfo != NULL, -1);
2581 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2582 procheader_get_header_fields(fp, hentry);
2585 if (hentry[H_REPLY_TO].body != NULL) {
2586 if (hentry[H_REPLY_TO].body[0] != '\0') {
2588 conv_unmime_header(hentry[H_REPLY_TO].body,
2591 g_free(hentry[H_REPLY_TO].body);
2592 hentry[H_REPLY_TO].body = NULL;
2594 if (hentry[H_CC].body != NULL) {
2595 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2596 g_free(hentry[H_CC].body);
2597 hentry[H_CC].body = NULL;
2599 if (hentry[H_REFERENCES].body != NULL) {
2600 if (compose->mode == COMPOSE_REEDIT)
2601 compose->references = hentry[H_REFERENCES].body;
2603 compose->references = compose_parse_references
2604 (hentry[H_REFERENCES].body, msginfo->msgid);
2605 g_free(hentry[H_REFERENCES].body);
2607 hentry[H_REFERENCES].body = NULL;
2609 if (hentry[H_BCC].body != NULL) {
2610 if (compose->mode == COMPOSE_REEDIT)
2612 conv_unmime_header(hentry[H_BCC].body, NULL);
2613 g_free(hentry[H_BCC].body);
2614 hentry[H_BCC].body = NULL;
2616 if (hentry[H_NEWSGROUPS].body != NULL) {
2617 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2618 hentry[H_NEWSGROUPS].body = NULL;
2620 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2621 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2622 compose->followup_to =
2623 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2626 g_free(hentry[H_FOLLOWUP_TO].body);
2627 hentry[H_FOLLOWUP_TO].body = NULL;
2629 if (hentry[H_LIST_POST].body != NULL) {
2632 extract_address(hentry[H_LIST_POST].body);
2633 if (hentry[H_LIST_POST].body[0] != '\0') {
2634 scan_mailto_url(hentry[H_LIST_POST].body,
2635 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2637 g_free(compose->ml_post);
2638 compose->ml_post = to;
2641 g_free(hentry[H_LIST_POST].body);
2642 hentry[H_LIST_POST].body = NULL;
2645 /* CLAWS - X-Priority */
2646 if (compose->mode == COMPOSE_REEDIT)
2647 if (hentry[H_X_PRIORITY].body != NULL) {
2650 priority = atoi(hentry[H_X_PRIORITY].body);
2651 g_free(hentry[H_X_PRIORITY].body);
2653 hentry[H_X_PRIORITY].body = NULL;
2655 if (priority < PRIORITY_HIGHEST ||
2656 priority > PRIORITY_LOWEST)
2657 priority = PRIORITY_NORMAL;
2659 compose->priority = priority;
2662 if (compose->mode == COMPOSE_REEDIT) {
2663 if (msginfo->inreplyto && *msginfo->inreplyto)
2664 compose->inreplyto = g_strdup(msginfo->inreplyto);
2668 if (msginfo->msgid && *msginfo->msgid)
2669 compose->inreplyto = g_strdup(msginfo->msgid);
2671 if (!compose->references) {
2672 if (msginfo->msgid && *msginfo->msgid) {
2673 if (msginfo->inreplyto && *msginfo->inreplyto)
2674 compose->references =
2675 g_strdup_printf("<%s>\n\t<%s>",
2679 compose->references =
2680 g_strconcat("<", msginfo->msgid, ">",
2682 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2683 compose->references =
2684 g_strconcat("<", msginfo->inreplyto, ">",
2692 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2694 GSList *ref_id_list, *cur;
2698 ref_id_list = references_list_append(NULL, ref);
2699 if (!ref_id_list) return NULL;
2700 if (msgid && *msgid)
2701 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2706 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2707 /* "<" + Message-ID + ">" + CR+LF+TAB */
2708 len += strlen((gchar *)cur->data) + 5;
2710 if (len > MAX_REFERENCES_LEN) {
2711 /* remove second message-ID */
2712 if (ref_id_list && ref_id_list->next &&
2713 ref_id_list->next->next) {
2714 g_free(ref_id_list->next->data);
2715 ref_id_list = g_slist_remove
2716 (ref_id_list, ref_id_list->next->data);
2718 slist_free_strings(ref_id_list);
2719 g_slist_free(ref_id_list);
2726 new_ref = g_string_new("");
2727 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2728 if (new_ref->len > 0)
2729 g_string_append(new_ref, "\n\t");
2730 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2733 slist_free_strings(ref_id_list);
2734 g_slist_free(ref_id_list);
2736 new_ref_str = new_ref->str;
2737 g_string_free(new_ref, FALSE);
2742 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2743 const gchar *fmt, const gchar *qmark,
2744 const gchar *body, gboolean rewrap,
2745 gboolean need_unescape,
2746 const gchar *err_msg)
2748 MsgInfo* dummyinfo = NULL;
2749 gchar *quote_str = NULL;
2751 gboolean prev_autowrap;
2752 const gchar *trimmed_body = body;
2753 gint cursor_pos = -1;
2754 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2755 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2760 SIGNAL_BLOCK(buffer);
2763 dummyinfo = compose_msginfo_new_from_compose(compose);
2764 msginfo = dummyinfo;
2767 if (qmark != NULL) {
2769 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2770 compose->gtkaspell);
2772 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2774 quote_fmt_scan_string(qmark);
2777 buf = quote_fmt_get_buffer();
2779 alertpanel_error(_("Quote mark format error."));
2781 Xstrdup_a(quote_str, buf, goto error)
2784 if (fmt && *fmt != '\0') {
2787 while (*trimmed_body == '\n')
2791 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2792 compose->gtkaspell);
2794 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2796 if (need_unescape) {
2799 /* decode \-escape sequences in the internal representation of the quote format */
2800 tmp = malloc(strlen(fmt)+1);
2801 pref_get_unescaped_pref(tmp, fmt);
2802 quote_fmt_scan_string(tmp);
2806 quote_fmt_scan_string(fmt);
2810 buf = quote_fmt_get_buffer();
2812 gint line = quote_fmt_get_line();
2813 alertpanel_error(err_msg, line);
2819 prev_autowrap = compose->autowrap;
2820 compose->autowrap = FALSE;
2822 mark = gtk_text_buffer_get_insert(buffer);
2823 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2824 if (g_utf8_validate(buf, -1, NULL)) {
2825 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2827 gchar *tmpout = NULL;
2828 tmpout = conv_codeset_strdup
2829 (buf, conv_get_locale_charset_str_no_utf8(),
2831 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2833 tmpout = g_malloc(strlen(buf)*2+1);
2834 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2836 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2840 cursor_pos = quote_fmt_get_cursor_pos();
2841 if (cursor_pos == -1)
2842 cursor_pos = gtk_text_iter_get_offset(&iter);
2843 compose->set_cursor_pos = cursor_pos;
2845 gtk_text_buffer_get_start_iter(buffer, &iter);
2846 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2847 gtk_text_buffer_place_cursor(buffer, &iter);
2849 compose->autowrap = prev_autowrap;
2850 if (compose->autowrap && rewrap)
2851 compose_wrap_all(compose);
2858 SIGNAL_UNBLOCK(buffer);
2860 procmsg_msginfo_free( dummyinfo );
2865 /* if ml_post is of type addr@host and from is of type
2866 * addr-anything@host, return TRUE
2868 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2870 gchar *left_ml = NULL;
2871 gchar *right_ml = NULL;
2872 gchar *left_from = NULL;
2873 gchar *right_from = NULL;
2874 gboolean result = FALSE;
2876 if (!ml_post || !from)
2879 left_ml = g_strdup(ml_post);
2880 if (strstr(left_ml, "@")) {
2881 right_ml = strstr(left_ml, "@")+1;
2882 *(strstr(left_ml, "@")) = '\0';
2885 left_from = g_strdup(from);
2886 if (strstr(left_from, "@")) {
2887 right_from = strstr(left_from, "@")+1;
2888 *(strstr(left_from, "@")) = '\0';
2891 if (left_ml && left_from && right_ml && right_from
2892 && !strncmp(left_from, left_ml, strlen(left_ml))
2893 && !strcmp(right_from, right_ml)) {
2902 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2904 gchar *my_addr1, *my_addr2;
2906 if (!addr1 || !addr2)
2909 Xstrdup_a(my_addr1, addr1, return FALSE);
2910 Xstrdup_a(my_addr2, addr2, return FALSE);
2912 extract_address(my_addr1);
2913 extract_address(my_addr2);
2915 return !strcasecmp(my_addr1, my_addr2);
2918 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2919 gboolean to_all, gboolean to_ml,
2921 gboolean followup_and_reply_to)
2923 GSList *cc_list = NULL;
2926 gchar *replyto = NULL;
2927 GHashTable *to_table;
2929 gboolean reply_to_ml = FALSE;
2930 gboolean default_reply_to = FALSE;
2932 g_return_if_fail(compose->account != NULL);
2933 g_return_if_fail(msginfo != NULL);
2935 reply_to_ml = to_ml && compose->ml_post;
2937 default_reply_to = msginfo->folder &&
2938 msginfo->folder->prefs->enable_default_reply_to;
2940 if (compose->account->protocol != A_NNTP) {
2941 if (reply_to_ml && !default_reply_to) {
2943 gboolean is_subscr = is_subscription(compose->ml_post,
2946 /* normal answer to ml post with a reply-to */
2947 compose_entry_append(compose,
2950 if (compose->replyto
2951 && !same_address(compose->ml_post, compose->replyto))
2952 compose_entry_append(compose,
2956 /* answer to subscription confirmation */
2957 if (compose->replyto)
2958 compose_entry_append(compose,
2961 else if (msginfo->from)
2962 compose_entry_append(compose,
2967 else if (!(to_all || to_sender) && default_reply_to) {
2968 compose_entry_append(compose,
2969 msginfo->folder->prefs->default_reply_to,
2971 compose_entry_mark_default_to(compose,
2972 msginfo->folder->prefs->default_reply_to);
2977 Xstrdup_a(tmp1, msginfo->from, return);
2978 extract_address(tmp1);
2979 if (to_all || to_sender ||
2980 !account_find_from_address(tmp1, FALSE))
2981 compose_entry_append(compose,
2982 (compose->replyto && !to_sender)
2983 ? compose->replyto :
2984 msginfo->from ? msginfo->from : "",
2986 else if (!to_all && !to_sender) {
2987 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2988 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2989 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2990 if (compose->replyto) {
2991 compose_entry_append(compose,
2995 compose_entry_append(compose,
2996 msginfo->from ? msginfo->from : "",
3000 /* replying to own mail, use original recp */
3001 compose_entry_append(compose,
3002 msginfo->to ? msginfo->to : "",
3004 compose_entry_append(compose,
3005 msginfo->cc ? msginfo->cc : "",
3011 if (to_sender || (compose->followup_to &&
3012 !strncmp(compose->followup_to, "poster", 6)))
3013 compose_entry_append
3015 (compose->replyto ? compose->replyto :
3016 msginfo->from ? msginfo->from : ""),
3019 else if (followup_and_reply_to || to_all) {
3020 compose_entry_append
3022 (compose->replyto ? compose->replyto :
3023 msginfo->from ? msginfo->from : ""),
3026 compose_entry_append
3028 compose->followup_to ? compose->followup_to :
3029 compose->newsgroups ? compose->newsgroups : "",
3030 COMPOSE_NEWSGROUPS);
3033 compose_entry_append
3035 compose->followup_to ? compose->followup_to :
3036 compose->newsgroups ? compose->newsgroups : "",
3037 COMPOSE_NEWSGROUPS);
3040 if (msginfo->subject && *msginfo->subject) {
3044 buf = p = g_strdup(msginfo->subject);
3045 p += subject_get_prefix_length(p);
3046 memmove(buf, p, strlen(p) + 1);
3048 buf2 = g_strdup_printf("Re: %s", buf);
3049 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3054 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3056 if (to_ml && compose->ml_post) return;
3057 if (!to_all || compose->account->protocol == A_NNTP) return;
3059 if (compose->replyto) {
3060 Xstrdup_a(replyto, compose->replyto, return);
3061 extract_address(replyto);
3063 if (msginfo->from) {
3064 Xstrdup_a(from, msginfo->from, return);
3065 extract_address(from);
3068 if (replyto && from)
3069 cc_list = address_list_append_with_comments(cc_list, from);
3070 if (to_all && msginfo->folder &&
3071 msginfo->folder->prefs->enable_default_reply_to)
3072 cc_list = address_list_append_with_comments(cc_list,
3073 msginfo->folder->prefs->default_reply_to);
3074 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3075 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3077 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3079 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3080 if (compose->account) {
3081 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3082 GINT_TO_POINTER(1));
3084 /* remove address on To: and that of current account */
3085 for (cur = cc_list; cur != NULL; ) {
3086 GSList *next = cur->next;
3089 addr = g_utf8_strdown(cur->data, -1);
3090 extract_address(addr);
3092 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3093 cc_list = g_slist_remove(cc_list, cur->data);
3095 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3099 hash_free_strings(to_table);
3100 g_hash_table_destroy(to_table);
3103 for (cur = cc_list; cur != NULL; cur = cur->next)
3104 compose_entry_append(compose, (gchar *)cur->data,
3106 slist_free_strings(cc_list);
3107 g_slist_free(cc_list);
3112 #define SET_ENTRY(entry, str) \
3115 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3118 #define SET_ADDRESS(type, str) \
3121 compose_entry_append(compose, str, type); \
3124 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3126 g_return_if_fail(msginfo != NULL);
3128 SET_ENTRY(subject_entry, msginfo->subject);
3129 SET_ENTRY(from_name, msginfo->from);
3130 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3131 SET_ADDRESS(COMPOSE_CC, compose->cc);
3132 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3133 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3134 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3135 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3137 compose_update_priority_menu_item(compose);
3138 compose_update_privacy_system_menu_item(compose, FALSE);
3139 compose_show_first_last_header(compose, TRUE);
3145 static void compose_insert_sig(Compose *compose, gboolean replace)
3147 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3148 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3150 GtkTextIter iter, iter_end;
3151 gint cur_pos, ins_pos;
3152 gboolean prev_autowrap;
3153 gboolean found = FALSE;
3154 gboolean exists = FALSE;
3156 g_return_if_fail(compose->account != NULL);
3160 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3161 G_CALLBACK(compose_changed_cb),
3164 mark = gtk_text_buffer_get_insert(buffer);
3165 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3166 cur_pos = gtk_text_iter_get_offset (&iter);
3169 gtk_text_buffer_get_end_iter(buffer, &iter);
3171 exists = (compose->sig_str != NULL);
3174 GtkTextIter first_iter, start_iter, end_iter;
3176 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3178 if (!exists || compose->sig_str[0] == '\0')
3181 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3182 compose->signature_tag);
3185 /* include previous \n\n */
3186 gtk_text_iter_backward_chars(&first_iter, 1);
3187 start_iter = first_iter;
3188 end_iter = first_iter;
3190 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3191 compose->signature_tag);
3192 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3193 compose->signature_tag);
3195 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3201 g_free(compose->sig_str);
3202 compose->sig_str = account_get_signature_str(compose->account);
3204 cur_pos = gtk_text_iter_get_offset(&iter);
3206 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3207 g_free(compose->sig_str);
3208 compose->sig_str = NULL;
3210 if (compose->sig_inserted == FALSE)
3211 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3212 compose->sig_inserted = TRUE;
3214 cur_pos = gtk_text_iter_get_offset(&iter);
3215 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3217 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3218 gtk_text_iter_forward_chars(&iter, 1);
3219 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3220 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3222 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3223 cur_pos = gtk_text_buffer_get_char_count (buffer);
3226 /* put the cursor where it should be
3227 * either where the quote_fmt says, either where it was */
3228 if (compose->set_cursor_pos < 0)
3229 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3231 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3232 compose->set_cursor_pos);
3234 compose->set_cursor_pos = -1;
3235 gtk_text_buffer_place_cursor(buffer, &iter);
3236 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3237 G_CALLBACK(compose_changed_cb),
3243 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3246 GtkTextBuffer *buffer;
3249 const gchar *cur_encoding;
3250 gchar buf[BUFFSIZE];
3253 gboolean prev_autowrap;
3254 gboolean badtxt = FALSE;
3256 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3258 if ((fp = g_fopen(file, "rb")) == NULL) {
3259 FILE_OP_ERROR(file, "fopen");
3260 return COMPOSE_INSERT_READ_ERROR;
3263 prev_autowrap = compose->autowrap;
3264 compose->autowrap = FALSE;
3266 text = GTK_TEXT_VIEW(compose->text);
3267 buffer = gtk_text_view_get_buffer(text);
3268 mark = gtk_text_buffer_get_insert(buffer);
3269 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3271 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3272 G_CALLBACK(text_inserted),
3275 cur_encoding = conv_get_locale_charset_str_no_utf8();
3277 while (fgets(buf, sizeof(buf), fp) != NULL) {
3280 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3281 str = g_strdup(buf);
3283 str = conv_codeset_strdup
3284 (buf, cur_encoding, CS_INTERNAL);
3287 /* strip <CR> if DOS/Windows file,
3288 replace <CR> with <LF> if Macintosh file. */
3291 if (len > 0 && str[len - 1] != '\n') {
3293 if (str[len] == '\r') str[len] = '\n';
3296 gtk_text_buffer_insert(buffer, &iter, str, -1);
3300 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3301 G_CALLBACK(text_inserted),
3303 compose->autowrap = prev_autowrap;
3304 if (compose->autowrap)
3305 compose_wrap_all(compose);
3310 return COMPOSE_INSERT_INVALID_CHARACTER;
3312 return COMPOSE_INSERT_SUCCESS;
3315 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3316 const gchar *filename,
3317 const gchar *content_type)
3325 GtkListStore *store;
3327 gboolean has_binary = FALSE;
3329 if (!is_file_exist(file)) {
3330 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3331 gboolean result = FALSE;
3332 if (file_from_uri && is_file_exist(file_from_uri)) {
3333 result = compose_attach_append(
3334 compose, file_from_uri,
3338 g_free(file_from_uri);
3341 alertpanel_error("File %s doesn't exist\n", filename);
3344 if ((size = get_file_size(file)) < 0) {
3345 alertpanel_error("Can't get file size of %s\n", filename);
3349 alertpanel_error(_("File %s is empty."), filename);
3352 if ((fp = g_fopen(file, "rb")) == NULL) {
3353 alertpanel_error(_("Can't read %s."), filename);
3358 ainfo = g_new0(AttachInfo, 1);
3359 auto_ainfo = g_auto_pointer_new_with_free
3360 (ainfo, (GFreeFunc) compose_attach_info_free);
3361 ainfo->file = g_strdup(file);
3364 ainfo->content_type = g_strdup(content_type);
3365 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3367 MsgFlags flags = {0, 0};
3369 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3370 ainfo->encoding = ENC_7BIT;
3372 ainfo->encoding = ENC_8BIT;
3374 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3375 if (msginfo && msginfo->subject)
3376 name = g_strdup(msginfo->subject);
3378 name = g_path_get_basename(filename ? filename : file);
3380 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3382 procmsg_msginfo_free(msginfo);
3384 if (!g_ascii_strncasecmp(content_type, "text", 4))
3385 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3387 ainfo->encoding = ENC_BASE64;
3388 name = g_path_get_basename(filename ? filename : file);
3389 ainfo->name = g_strdup(name);
3393 ainfo->content_type = procmime_get_mime_type(file);
3394 if (!ainfo->content_type) {
3395 ainfo->content_type =
3396 g_strdup("application/octet-stream");
3397 ainfo->encoding = ENC_BASE64;
3398 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3400 procmime_get_encoding_for_text_file(file, &has_binary);
3402 ainfo->encoding = ENC_BASE64;
3403 name = g_path_get_basename(filename ? filename : file);
3404 ainfo->name = g_strdup(name);
3408 if (ainfo->name != NULL
3409 && !strcmp(ainfo->name, ".")) {
3410 g_free(ainfo->name);
3414 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3415 g_free(ainfo->content_type);
3416 ainfo->content_type = g_strdup("application/octet-stream");
3419 ainfo->size = (goffset)size;
3420 size_text = to_human_readable((goffset)size);
3422 store = GTK_LIST_STORE(gtk_tree_view_get_model
3423 (GTK_TREE_VIEW(compose->attach_clist)));
3425 gtk_list_store_append(store, &iter);
3426 gtk_list_store_set(store, &iter,
3427 COL_MIMETYPE, ainfo->content_type,
3428 COL_SIZE, size_text,
3429 COL_NAME, ainfo->name,
3431 COL_AUTODATA, auto_ainfo,
3434 g_auto_pointer_free(auto_ainfo);
3435 compose_attach_update_label(compose);
3439 static void compose_use_signing(Compose *compose, gboolean use_signing)
3441 compose->use_signing = use_signing;
3442 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3445 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3447 compose->use_encryption = use_encryption;
3448 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3451 #define NEXT_PART_NOT_CHILD(info) \
3453 node = info->node; \
3454 while (node->children) \
3455 node = g_node_last_child(node); \
3456 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3459 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3463 MimeInfo *firsttext = NULL;
3464 MimeInfo *encrypted = NULL;
3467 const gchar *partname = NULL;
3469 mimeinfo = procmime_scan_message(msginfo);
3470 if (!mimeinfo) return;
3472 if (mimeinfo->node->children == NULL) {
3473 procmime_mimeinfo_free_all(mimeinfo);
3477 /* find first content part */
3478 child = (MimeInfo *) mimeinfo->node->children->data;
3479 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3480 child = (MimeInfo *)child->node->children->data;
3482 if (child->type == MIMETYPE_TEXT) {
3484 debug_print("First text part found\n");
3485 } else if (compose->mode == COMPOSE_REEDIT &&
3486 child->type == MIMETYPE_APPLICATION &&
3487 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3488 encrypted = (MimeInfo *)child->node->parent->data;
3491 child = (MimeInfo *) mimeinfo->node->children->data;
3492 while (child != NULL) {
3495 if (child == encrypted) {
3496 /* skip this part of tree */
3497 NEXT_PART_NOT_CHILD(child);
3501 if (child->type == MIMETYPE_MULTIPART) {
3502 /* get the actual content */
3503 child = procmime_mimeinfo_next(child);
3507 if (child == firsttext) {
3508 child = procmime_mimeinfo_next(child);
3512 outfile = procmime_get_tmp_file_name(child);
3513 if ((err = procmime_get_part(outfile, child)) < 0)
3514 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3516 gchar *content_type;
3518 content_type = procmime_get_content_type_str(child->type, child->subtype);
3520 /* if we meet a pgp signature, we don't attach it, but
3521 * we force signing. */
3522 if ((strcmp(content_type, "application/pgp-signature") &&
3523 strcmp(content_type, "application/pkcs7-signature") &&
3524 strcmp(content_type, "application/x-pkcs7-signature"))
3525 || compose->mode == COMPOSE_REDIRECT) {
3526 partname = procmime_mimeinfo_get_parameter(child, "filename");
3527 if (partname == NULL)
3528 partname = procmime_mimeinfo_get_parameter(child, "name");
3529 if (partname == NULL)
3531 compose_attach_append(compose, outfile,
3532 partname, content_type);
3534 compose_force_signing(compose, compose->account);
3536 g_free(content_type);
3539 NEXT_PART_NOT_CHILD(child);
3541 procmime_mimeinfo_free_all(mimeinfo);
3544 #undef NEXT_PART_NOT_CHILD
3549 WAIT_FOR_INDENT_CHAR,
3550 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3553 /* return indent length, we allow:
3554 indent characters followed by indent characters or spaces/tabs,
3555 alphabets and numbers immediately followed by indent characters,
3556 and the repeating sequences of the above
3557 If quote ends with multiple spaces, only the first one is included. */
3558 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3559 const GtkTextIter *start, gint *len)
3561 GtkTextIter iter = *start;
3565 IndentState state = WAIT_FOR_INDENT_CHAR;
3568 gint alnum_count = 0;
3569 gint space_count = 0;
3572 if (prefs_common.quote_chars == NULL) {
3576 while (!gtk_text_iter_ends_line(&iter)) {
3577 wc = gtk_text_iter_get_char(&iter);
3578 if (g_unichar_iswide(wc))
3580 clen = g_unichar_to_utf8(wc, ch);
3584 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3585 is_space = g_unichar_isspace(wc);
3587 if (state == WAIT_FOR_INDENT_CHAR) {
3588 if (!is_indent && !g_unichar_isalnum(wc))
3591 quote_len += alnum_count + space_count + 1;
3592 alnum_count = space_count = 0;
3593 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3596 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3597 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3601 else if (is_indent) {
3602 quote_len += alnum_count + space_count + 1;
3603 alnum_count = space_count = 0;
3606 state = WAIT_FOR_INDENT_CHAR;
3610 gtk_text_iter_forward_char(&iter);
3613 if (quote_len > 0 && space_count > 0)
3619 if (quote_len > 0) {
3621 gtk_text_iter_forward_chars(&iter, quote_len);
3622 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3628 /* return >0 if the line is itemized */
3629 static int compose_itemized_length(GtkTextBuffer *buffer,
3630 const GtkTextIter *start)
3632 GtkTextIter iter = *start;
3637 if (gtk_text_iter_ends_line(&iter))
3642 wc = gtk_text_iter_get_char(&iter);
3643 if (!g_unichar_isspace(wc))
3645 gtk_text_iter_forward_char(&iter);
3646 if (gtk_text_iter_ends_line(&iter))
3650 clen = g_unichar_to_utf8(wc, ch);
3654 if (!strchr("*-+", ch[0]))
3657 gtk_text_iter_forward_char(&iter);
3658 if (gtk_text_iter_ends_line(&iter))
3660 wc = gtk_text_iter_get_char(&iter);
3661 if (g_unichar_isspace(wc)) {
3667 /* return the string at the start of the itemization */
3668 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3669 const GtkTextIter *start)
3671 GtkTextIter iter = *start;
3674 GString *item_chars = g_string_new("");
3677 if (gtk_text_iter_ends_line(&iter))
3682 wc = gtk_text_iter_get_char(&iter);
3683 if (!g_unichar_isspace(wc))
3685 gtk_text_iter_forward_char(&iter);
3686 if (gtk_text_iter_ends_line(&iter))
3688 g_string_append_unichar(item_chars, wc);
3691 str = item_chars->str;
3692 g_string_free(item_chars, FALSE);
3696 /* return the number of spaces at a line's start */
3697 static int compose_left_offset_length(GtkTextBuffer *buffer,
3698 const GtkTextIter *start)
3700 GtkTextIter iter = *start;
3703 if (gtk_text_iter_ends_line(&iter))
3707 wc = gtk_text_iter_get_char(&iter);
3708 if (!g_unichar_isspace(wc))
3711 gtk_text_iter_forward_char(&iter);
3712 if (gtk_text_iter_ends_line(&iter))
3716 gtk_text_iter_forward_char(&iter);
3717 if (gtk_text_iter_ends_line(&iter))
3722 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3723 const GtkTextIter *start,
3724 GtkTextIter *break_pos,
3728 GtkTextIter iter = *start, line_end = *start;
3729 PangoLogAttr *attrs;
3736 gboolean can_break = FALSE;
3737 gboolean do_break = FALSE;
3738 gboolean was_white = FALSE;
3739 gboolean prev_dont_break = FALSE;
3741 gtk_text_iter_forward_to_line_end(&line_end);
3742 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3743 len = g_utf8_strlen(str, -1);
3747 g_warning("compose_get_line_break_pos: len = 0!\n");
3751 /* g_print("breaking line: %d: %s (len = %d)\n",
3752 gtk_text_iter_get_line(&iter), str, len); */
3754 attrs = g_new(PangoLogAttr, len + 1);
3756 pango_default_break(str, -1, NULL, attrs, len + 1);
3760 /* skip quote and leading spaces */
3761 for (i = 0; *p != '\0' && i < len; i++) {
3764 wc = g_utf8_get_char(p);
3765 if (i >= quote_len && !g_unichar_isspace(wc))
3767 if (g_unichar_iswide(wc))
3769 else if (*p == '\t')
3773 p = g_utf8_next_char(p);
3776 for (; *p != '\0' && i < len; i++) {
3777 PangoLogAttr *attr = attrs + i;
3781 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3784 was_white = attr->is_white;
3786 /* don't wrap URI */
3787 if ((uri_len = get_uri_len(p)) > 0) {
3789 if (pos > 0 && col > max_col) {
3799 wc = g_utf8_get_char(p);
3800 if (g_unichar_iswide(wc)) {
3802 if (prev_dont_break && can_break && attr->is_line_break)
3804 } else if (*p == '\t')
3808 if (pos > 0 && col > max_col) {
3813 if (*p == '-' || *p == '/')
3814 prev_dont_break = TRUE;
3816 prev_dont_break = FALSE;
3818 p = g_utf8_next_char(p);
3822 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3827 *break_pos = *start;
3828 gtk_text_iter_set_line_offset(break_pos, pos);
3833 static gboolean compose_join_next_line(Compose *compose,
3834 GtkTextBuffer *buffer,
3836 const gchar *quote_str)
3838 GtkTextIter iter_ = *iter, cur, prev, next, end;
3839 PangoLogAttr attrs[3];
3841 gchar *next_quote_str;
3844 gboolean keep_cursor = FALSE;
3846 if (!gtk_text_iter_forward_line(&iter_) ||
3847 gtk_text_iter_ends_line(&iter_)) {
3850 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3852 if ((quote_str || next_quote_str) &&
3853 strcmp2(quote_str, next_quote_str) != 0) {
3854 g_free(next_quote_str);
3857 g_free(next_quote_str);
3860 if (quote_len > 0) {
3861 gtk_text_iter_forward_chars(&end, quote_len);
3862 if (gtk_text_iter_ends_line(&end)) {
3867 /* don't join itemized lines */
3868 if (compose_itemized_length(buffer, &end) > 0) {
3872 /* don't join signature separator */
3873 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3876 /* delete quote str */
3878 gtk_text_buffer_delete(buffer, &iter_, &end);
3880 /* don't join line breaks put by the user */
3882 gtk_text_iter_backward_char(&cur);
3883 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3884 gtk_text_iter_forward_char(&cur);
3888 gtk_text_iter_forward_char(&cur);
3889 /* delete linebreak and extra spaces */
3890 while (gtk_text_iter_backward_char(&cur)) {
3891 wc1 = gtk_text_iter_get_char(&cur);
3892 if (!g_unichar_isspace(wc1))
3897 while (!gtk_text_iter_ends_line(&cur)) {
3898 wc1 = gtk_text_iter_get_char(&cur);
3899 if (!g_unichar_isspace(wc1))
3901 gtk_text_iter_forward_char(&cur);
3904 if (!gtk_text_iter_equal(&prev, &next)) {
3907 mark = gtk_text_buffer_get_insert(buffer);
3908 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3909 if (gtk_text_iter_equal(&prev, &cur))
3911 gtk_text_buffer_delete(buffer, &prev, &next);
3915 /* insert space if required */
3916 gtk_text_iter_backward_char(&prev);
3917 wc1 = gtk_text_iter_get_char(&prev);
3918 wc2 = gtk_text_iter_get_char(&next);
3919 gtk_text_iter_forward_char(&next);
3920 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3921 pango_default_break(str, -1, NULL, attrs, 3);
3922 if (!attrs[1].is_line_break ||
3923 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3924 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3926 gtk_text_iter_backward_char(&iter_);
3927 gtk_text_buffer_place_cursor(buffer, &iter_);
3936 #define ADD_TXT_POS(bp_, ep_, pti_) \
3937 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3938 last = last->next; \
3939 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3940 last->next = NULL; \
3942 g_warning("alloc error scanning URIs\n"); \
3945 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3947 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3948 GtkTextBuffer *buffer;
3949 GtkTextIter iter, break_pos, end_of_line;
3950 gchar *quote_str = NULL;
3952 gboolean wrap_quote = prefs_common.linewrap_quote;
3953 gboolean prev_autowrap = compose->autowrap;
3954 gint startq_offset = -1, noq_offset = -1;
3955 gint uri_start = -1, uri_stop = -1;
3956 gint nouri_start = -1, nouri_stop = -1;
3957 gint num_blocks = 0;
3958 gint quotelevel = -1;
3959 gboolean modified = force;
3960 gboolean removed = FALSE;
3961 gboolean modified_before_remove = FALSE;
3963 gboolean start = TRUE;
3964 gint itemized_len = 0, rem_item_len = 0;
3965 gchar *itemized_chars = NULL;
3966 gboolean item_continuation = FALSE;
3971 if (compose->draft_timeout_tag == -2) {
3975 compose->autowrap = FALSE;
3977 buffer = gtk_text_view_get_buffer(text);
3978 undo_wrapping(compose->undostruct, TRUE);
3983 mark = gtk_text_buffer_get_insert(buffer);
3984 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3988 if (compose->draft_timeout_tag == -2) {
3989 if (gtk_text_iter_ends_line(&iter)) {
3990 while (gtk_text_iter_ends_line(&iter) &&
3991 gtk_text_iter_forward_line(&iter))
3994 while (gtk_text_iter_backward_line(&iter)) {
3995 if (gtk_text_iter_ends_line(&iter)) {
3996 gtk_text_iter_forward_line(&iter);
4002 /* move to line start */
4003 gtk_text_iter_set_line_offset(&iter, 0);
4006 itemized_len = compose_itemized_length(buffer, &iter);
4008 if (!itemized_len) {
4009 itemized_len = compose_left_offset_length(buffer, &iter);
4010 item_continuation = TRUE;
4014 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4016 /* go until paragraph end (empty line) */
4017 while (start || !gtk_text_iter_ends_line(&iter)) {
4018 gchar *scanpos = NULL;
4019 /* parse table - in order of priority */
4021 const gchar *needle; /* token */
4023 /* token search function */
4024 gchar *(*search) (const gchar *haystack,
4025 const gchar *needle);
4026 /* part parsing function */
4027 gboolean (*parse) (const gchar *start,
4028 const gchar *scanpos,
4032 /* part to URI function */
4033 gchar *(*build_uri) (const gchar *bp,
4037 static struct table parser[] = {
4038 {"http://", strcasestr, get_uri_part, make_uri_string},
4039 {"https://", strcasestr, get_uri_part, make_uri_string},
4040 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4041 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4042 {"www.", strcasestr, get_uri_part, make_http_string},
4043 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4044 {"@", strcasestr, get_email_part, make_email_string}
4046 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4047 gint last_index = PARSE_ELEMS;
4049 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4053 if (!prev_autowrap && num_blocks == 0) {
4055 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4056 G_CALLBACK(text_inserted),
4059 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4062 uri_start = uri_stop = -1;
4064 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4067 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4068 if (startq_offset == -1)
4069 startq_offset = gtk_text_iter_get_offset(&iter);
4070 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4071 if (quotelevel > 2) {
4072 /* recycle colors */
4073 if (prefs_common.recycle_quote_colors)
4082 if (startq_offset == -1)
4083 noq_offset = gtk_text_iter_get_offset(&iter);
4087 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4090 if (gtk_text_iter_ends_line(&iter)) {
4092 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4093 prefs_common.linewrap_len,
4095 GtkTextIter prev, next, cur;
4096 if (prev_autowrap != FALSE || force) {
4097 compose->automatic_break = TRUE;
4099 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4100 compose->automatic_break = FALSE;
4101 if (itemized_len && compose->autoindent) {
4102 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4103 if (!item_continuation)
4104 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4106 } else if (quote_str && wrap_quote) {
4107 compose->automatic_break = TRUE;
4109 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4110 compose->automatic_break = FALSE;
4111 if (itemized_len && compose->autoindent) {
4112 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4113 if (!item_continuation)
4114 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4118 /* remove trailing spaces */
4120 rem_item_len = itemized_len;
4121 while (compose->autoindent && rem_item_len-- > 0)
4122 gtk_text_iter_backward_char(&cur);
4123 gtk_text_iter_backward_char(&cur);
4126 while (!gtk_text_iter_starts_line(&cur)) {
4129 gtk_text_iter_backward_char(&cur);
4130 wc = gtk_text_iter_get_char(&cur);
4131 if (!g_unichar_isspace(wc))
4135 if (!gtk_text_iter_equal(&prev, &next)) {
4136 gtk_text_buffer_delete(buffer, &prev, &next);
4138 gtk_text_iter_forward_char(&break_pos);
4142 gtk_text_buffer_insert(buffer, &break_pos,
4146 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4148 /* move iter to current line start */
4149 gtk_text_iter_set_line_offset(&iter, 0);
4156 /* move iter to next line start */
4162 if (!prev_autowrap && num_blocks > 0) {
4164 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4165 G_CALLBACK(text_inserted),
4169 while (!gtk_text_iter_ends_line(&end_of_line)) {
4170 gtk_text_iter_forward_char(&end_of_line);
4172 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4174 nouri_start = gtk_text_iter_get_offset(&iter);
4175 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4177 walk_pos = gtk_text_iter_get_offset(&iter);
4178 /* FIXME: this looks phony. scanning for anything in the parse table */
4179 for (n = 0; n < PARSE_ELEMS; n++) {
4182 tmp = parser[n].search(walk, parser[n].needle);
4184 if (scanpos == NULL || tmp < scanpos) {
4193 /* check if URI can be parsed */
4194 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4195 (const gchar **)&ep, FALSE)
4196 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4200 strlen(parser[last_index].needle);
4203 uri_start = walk_pos + (bp - o_walk);
4204 uri_stop = walk_pos + (ep - o_walk);
4208 gtk_text_iter_forward_line(&iter);
4211 if (startq_offset != -1) {
4212 GtkTextIter startquote, endquote;
4213 gtk_text_buffer_get_iter_at_offset(
4214 buffer, &startquote, startq_offset);
4217 switch (quotelevel) {
4219 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4220 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4221 gtk_text_buffer_apply_tag_by_name(
4222 buffer, "quote0", &startquote, &endquote);
4223 gtk_text_buffer_remove_tag_by_name(
4224 buffer, "quote1", &startquote, &endquote);
4225 gtk_text_buffer_remove_tag_by_name(
4226 buffer, "quote2", &startquote, &endquote);
4231 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4232 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4233 gtk_text_buffer_apply_tag_by_name(
4234 buffer, "quote1", &startquote, &endquote);
4235 gtk_text_buffer_remove_tag_by_name(
4236 buffer, "quote0", &startquote, &endquote);
4237 gtk_text_buffer_remove_tag_by_name(
4238 buffer, "quote2", &startquote, &endquote);
4243 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4244 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4245 gtk_text_buffer_apply_tag_by_name(
4246 buffer, "quote2", &startquote, &endquote);
4247 gtk_text_buffer_remove_tag_by_name(
4248 buffer, "quote0", &startquote, &endquote);
4249 gtk_text_buffer_remove_tag_by_name(
4250 buffer, "quote1", &startquote, &endquote);
4256 } else if (noq_offset != -1) {
4257 GtkTextIter startnoquote, endnoquote;
4258 gtk_text_buffer_get_iter_at_offset(
4259 buffer, &startnoquote, noq_offset);
4262 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4263 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4264 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4265 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4266 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4267 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4268 gtk_text_buffer_remove_tag_by_name(
4269 buffer, "quote0", &startnoquote, &endnoquote);
4270 gtk_text_buffer_remove_tag_by_name(
4271 buffer, "quote1", &startnoquote, &endnoquote);
4272 gtk_text_buffer_remove_tag_by_name(
4273 buffer, "quote2", &startnoquote, &endnoquote);
4279 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4280 GtkTextIter nouri_start_iter, nouri_end_iter;
4281 gtk_text_buffer_get_iter_at_offset(
4282 buffer, &nouri_start_iter, nouri_start);
4283 gtk_text_buffer_get_iter_at_offset(
4284 buffer, &nouri_end_iter, nouri_stop);
4285 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4286 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4287 gtk_text_buffer_remove_tag_by_name(
4288 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4289 modified_before_remove = modified;
4294 if (uri_start >= 0 && uri_stop > 0) {
4295 GtkTextIter uri_start_iter, uri_end_iter, back;
4296 gtk_text_buffer_get_iter_at_offset(
4297 buffer, &uri_start_iter, uri_start);
4298 gtk_text_buffer_get_iter_at_offset(
4299 buffer, &uri_end_iter, uri_stop);
4300 back = uri_end_iter;
4301 gtk_text_iter_backward_char(&back);
4302 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4303 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4304 gtk_text_buffer_apply_tag_by_name(
4305 buffer, "link", &uri_start_iter, &uri_end_iter);
4307 if (removed && !modified_before_remove) {
4313 debug_print("not modified, out after %d lines\n", lines);
4317 debug_print("modified, out after %d lines\n", lines);
4319 g_free(itemized_chars);
4322 undo_wrapping(compose->undostruct, FALSE);
4323 compose->autowrap = prev_autowrap;
4328 void compose_action_cb(void *data)
4330 Compose *compose = (Compose *)data;
4331 compose_wrap_all(compose);
4334 static void compose_wrap_all(Compose *compose)
4336 compose_wrap_all_full(compose, FALSE);
4339 static void compose_wrap_all_full(Compose *compose, gboolean force)
4341 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4342 GtkTextBuffer *buffer;
4344 gboolean modified = TRUE;
4346 buffer = gtk_text_view_get_buffer(text);
4348 gtk_text_buffer_get_start_iter(buffer, &iter);
4349 while (!gtk_text_iter_is_end(&iter) && modified)
4350 modified = compose_beautify_paragraph(compose, &iter, force);
4354 static void compose_set_title(Compose *compose)
4360 edited = compose->modified ? _(" [Edited]") : "";
4362 subject = gtk_editable_get_chars(
4363 GTK_EDITABLE(compose->subject_entry), 0, -1);
4365 #ifndef GENERIC_UMPC
4366 if (subject && strlen(subject))
4367 str = g_strdup_printf(_("%s - Compose message%s"),
4370 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4372 str = g_strdup(_("Compose message"));
4375 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4381 * compose_current_mail_account:
4383 * Find a current mail account (the currently selected account, or the
4384 * default account, if a news account is currently selected). If a
4385 * mail account cannot be found, display an error message.
4387 * Return value: Mail account, or NULL if not found.
4389 static PrefsAccount *
4390 compose_current_mail_account(void)
4394 if (cur_account && cur_account->protocol != A_NNTP)
4397 ac = account_get_default();
4398 if (!ac || ac->protocol == A_NNTP) {
4399 alertpanel_error(_("Account for sending mail is not specified.\n"
4400 "Please select a mail account before sending."));
4407 #define QUOTE_IF_REQUIRED(out, str) \
4409 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4413 len = strlen(str) + 3; \
4414 if ((__tmp = alloca(len)) == NULL) { \
4415 g_warning("can't allocate memory\n"); \
4416 g_string_free(header, TRUE); \
4419 g_snprintf(__tmp, len, "\"%s\"", str); \
4424 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4425 g_warning("can't allocate memory\n"); \
4426 g_string_free(header, TRUE); \
4429 strcpy(__tmp, str); \
4435 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4437 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4441 len = strlen(str) + 3; \
4442 if ((__tmp = alloca(len)) == NULL) { \
4443 g_warning("can't allocate memory\n"); \
4446 g_snprintf(__tmp, len, "\"%s\"", str); \
4451 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4452 g_warning("can't allocate memory\n"); \
4455 strcpy(__tmp, str); \
4461 static void compose_select_account(Compose *compose, PrefsAccount *account,
4466 g_return_if_fail(account != NULL);
4468 compose->account = account;
4470 if (account->name && *account->name) {
4472 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4473 from = g_strdup_printf("%s <%s>",
4474 buf, account->address);
4475 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4477 from = g_strdup_printf("<%s>",
4479 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4484 compose_set_title(compose);
4486 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4487 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4489 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4490 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4491 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4493 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4495 activate_privacy_system(compose, account, FALSE);
4497 if (!init && compose->mode != COMPOSE_REDIRECT) {
4498 undo_block(compose->undostruct);
4499 compose_insert_sig(compose, TRUE);
4500 undo_unblock(compose->undostruct);
4504 /* use account's dict info if set */
4505 if (compose->gtkaspell) {
4506 if (account->enable_default_dictionary)
4507 gtkaspell_change_dict(compose->gtkaspell,
4508 account->default_dictionary, FALSE);
4509 if (account->enable_default_alt_dictionary)
4510 gtkaspell_change_alt_dict(compose->gtkaspell,
4511 account->default_alt_dictionary);
4512 if (account->enable_default_dictionary
4513 || account->enable_default_alt_dictionary)
4514 compose_spell_menu_changed(compose);
4519 gboolean compose_check_for_valid_recipient(Compose *compose) {
4520 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4521 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4522 gboolean recipient_found = FALSE;
4526 /* free to and newsgroup list */
4527 slist_free_strings(compose->to_list);
4528 g_slist_free(compose->to_list);
4529 compose->to_list = NULL;
4531 slist_free_strings(compose->newsgroup_list);
4532 g_slist_free(compose->newsgroup_list);
4533 compose->newsgroup_list = NULL;
4535 /* search header entries for to and newsgroup entries */
4536 for (list = compose->header_list; list; list = list->next) {
4539 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4540 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4543 if (entry[0] != '\0') {
4544 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4545 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4546 compose->to_list = address_list_append(compose->to_list, entry);
4547 recipient_found = TRUE;
4550 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4551 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4552 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4553 recipient_found = TRUE;
4560 return recipient_found;
4563 static gboolean compose_check_for_set_recipients(Compose *compose)
4565 if (compose->account->set_autocc && compose->account->auto_cc) {
4566 gboolean found_other = FALSE;
4568 /* search header entries for to and newsgroup entries */
4569 for (list = compose->header_list; list; list = list->next) {
4572 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4573 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4576 if (strcmp(entry, compose->account->auto_cc)
4577 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4587 if (compose->batch) {
4588 gtk_widget_show_all(compose->window);
4590 aval = alertpanel(_("Send"),
4591 _("The only recipient is the default CC address. Send anyway?"),
4592 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4593 if (aval != G_ALERTALTERNATE)
4597 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4598 gboolean found_other = FALSE;
4600 /* search header entries for to and newsgroup entries */
4601 for (list = compose->header_list; list; list = list->next) {
4604 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4605 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4608 if (strcmp(entry, compose->account->auto_bcc)
4609 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4619 if (compose->batch) {
4620 gtk_widget_show_all(compose->window);
4622 aval = alertpanel(_("Send"),
4623 _("The only recipient is the default BCC address. Send anyway?"),
4624 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4625 if (aval != G_ALERTALTERNATE)
4632 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4636 if (compose_check_for_valid_recipient(compose) == FALSE) {
4637 if (compose->batch) {
4638 gtk_widget_show_all(compose->window);
4640 alertpanel_error(_("Recipient is not specified."));
4644 if (compose_check_for_set_recipients(compose) == FALSE) {
4648 if (!compose->batch) {
4649 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4650 if (*str == '\0' && check_everything == TRUE &&
4651 compose->mode != COMPOSE_REDIRECT) {
4653 gchar *button_label;
4656 if (compose->sending)
4657 button_label = _("+_Send");
4659 button_label = _("+_Queue");
4660 message = g_strdup_printf(_("Subject is empty. %s"),
4661 compose->sending?_("Send it anyway?"):
4662 _("Queue it anyway?"));
4664 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4665 GTK_STOCK_CANCEL, button_label, NULL);
4667 if (aval != G_ALERTALTERNATE)
4672 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4678 gint compose_send(Compose *compose)
4681 FolderItem *folder = NULL;
4683 gchar *msgpath = NULL;
4684 gboolean discard_window = FALSE;
4685 gchar *errstr = NULL;
4686 gchar *tmsgid = NULL;
4687 MainWindow *mainwin = mainwindow_get_mainwindow();
4688 gboolean queued_removed = FALSE;
4690 if (prefs_common.send_dialog_invisible
4691 || compose->batch == TRUE)
4692 discard_window = TRUE;
4694 compose_allow_user_actions (compose, FALSE);
4695 compose->sending = TRUE;
4697 if (compose_check_entries(compose, TRUE) == FALSE) {
4698 if (compose->batch) {
4699 gtk_widget_show_all(compose->window);
4705 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4708 if (compose->batch) {
4709 gtk_widget_show_all(compose->window);
4712 alertpanel_error(_("Could not queue message for sending:\n\n"
4713 "Charset conversion failed."));
4714 } else if (val == -5) {
4715 alertpanel_error(_("Could not queue message for sending:\n\n"
4716 "Couldn't get recipient encryption key."));
4717 } else if (val == -6) {
4719 } else if (val == -3) {
4720 if (privacy_peek_error())
4721 alertpanel_error(_("Could not queue message for sending:\n\n"
4722 "Signature failed: %s"), privacy_get_error());
4723 } else if (val == -2 && errno != 0) {
4724 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4726 alertpanel_error(_("Could not queue message for sending."));
4731 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4732 if (discard_window) {
4733 compose->sending = FALSE;
4734 compose_close(compose);
4735 /* No more compose access in the normal codepath
4736 * after this point! */
4741 alertpanel_error(_("The message was queued but could not be "
4742 "sent.\nUse \"Send queued messages\" from "
4743 "the main window to retry."));
4744 if (!discard_window) {
4751 if (msgpath == NULL) {
4752 msgpath = folder_item_fetch_msg(folder, msgnum);
4753 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4756 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4757 claws_unlink(msgpath);
4760 if (!discard_window) {
4762 if (!queued_removed)
4763 folder_item_remove_msg(folder, msgnum);
4764 folder_item_scan(folder);
4766 /* make sure we delete that */
4767 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4769 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4770 folder_item_remove_msg(folder, tmp->msgnum);
4771 procmsg_msginfo_free(tmp);
4778 if (!queued_removed)
4779 folder_item_remove_msg(folder, msgnum);
4780 folder_item_scan(folder);
4782 /* make sure we delete that */
4783 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4785 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4786 folder_item_remove_msg(folder, tmp->msgnum);
4787 procmsg_msginfo_free(tmp);
4790 if (!discard_window) {
4791 compose->sending = FALSE;
4792 compose_allow_user_actions (compose, TRUE);
4793 compose_close(compose);
4797 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4798 "the main window to retry."), errstr);
4801 alertpanel_error_log(_("The message was queued but could not be "
4802 "sent.\nUse \"Send queued messages\" from "
4803 "the main window to retry."));
4805 if (!discard_window) {
4814 toolbar_main_set_sensitive(mainwin);
4815 main_window_set_menu_sensitive(mainwin);
4821 compose_allow_user_actions (compose, TRUE);
4822 compose->sending = FALSE;
4823 compose->modified = TRUE;
4824 toolbar_main_set_sensitive(mainwin);
4825 main_window_set_menu_sensitive(mainwin);
4830 static gboolean compose_use_attach(Compose *compose)
4832 GtkTreeModel *model = gtk_tree_view_get_model
4833 (GTK_TREE_VIEW(compose->attach_clist));
4834 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4837 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4840 gchar buf[BUFFSIZE];
4842 gboolean first_to_address;
4843 gboolean first_cc_address;
4845 ComposeHeaderEntry *headerentry;
4846 const gchar *headerentryname;
4847 const gchar *cc_hdr;
4848 const gchar *to_hdr;
4849 gboolean err = FALSE;
4851 debug_print("Writing redirect header\n");
4853 cc_hdr = prefs_common_translated_header_name("Cc:");
4854 to_hdr = prefs_common_translated_header_name("To:");
4856 first_to_address = TRUE;
4857 for (list = compose->header_list; list; list = list->next) {
4858 headerentry = ((ComposeHeaderEntry *)list->data);
4859 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4861 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4862 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4863 Xstrdup_a(str, entstr, return -1);
4865 if (str[0] != '\0') {
4866 compose_convert_header
4867 (compose, buf, sizeof(buf), str,
4868 strlen("Resent-To") + 2, TRUE);
4870 if (first_to_address) {
4871 err |= (fprintf(fp, "Resent-To: ") < 0);
4872 first_to_address = FALSE;
4874 err |= (fprintf(fp, ",") < 0);
4876 err |= (fprintf(fp, "%s", buf) < 0);
4880 if (!first_to_address) {
4881 err |= (fprintf(fp, "\n") < 0);
4884 first_cc_address = TRUE;
4885 for (list = compose->header_list; list; list = list->next) {
4886 headerentry = ((ComposeHeaderEntry *)list->data);
4887 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4889 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4890 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4891 Xstrdup_a(str, strg, return -1);
4893 if (str[0] != '\0') {
4894 compose_convert_header
4895 (compose, buf, sizeof(buf), str,
4896 strlen("Resent-Cc") + 2, TRUE);
4898 if (first_cc_address) {
4899 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4900 first_cc_address = FALSE;
4902 err |= (fprintf(fp, ",") < 0);
4904 err |= (fprintf(fp, "%s", buf) < 0);
4908 if (!first_cc_address) {
4909 err |= (fprintf(fp, "\n") < 0);
4912 return (err ? -1:0);
4915 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4917 gchar buf[BUFFSIZE];
4919 const gchar *entstr;
4920 /* struct utsname utsbuf; */
4921 gboolean err = FALSE;
4923 g_return_val_if_fail(fp != NULL, -1);
4924 g_return_val_if_fail(compose->account != NULL, -1);
4925 g_return_val_if_fail(compose->account->address != NULL, -1);
4928 get_rfc822_date(buf, sizeof(buf));
4929 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4932 if (compose->account->name && *compose->account->name) {
4933 compose_convert_header
4934 (compose, buf, sizeof(buf), compose->account->name,
4935 strlen("From: "), TRUE);
4936 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4937 buf, compose->account->address) < 0);
4939 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4942 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4943 if (*entstr != '\0') {
4944 Xstrdup_a(str, entstr, return -1);
4947 compose_convert_header(compose, buf, sizeof(buf), str,
4948 strlen("Subject: "), FALSE);
4949 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4953 /* Resent-Message-ID */
4954 if (compose->account->set_domain && compose->account->domain) {
4955 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4956 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4957 g_snprintf(buf, sizeof(buf), "%s",
4958 strchr(compose->account->address, '@') ?
4959 strchr(compose->account->address, '@')+1 :
4960 compose->account->address);
4962 g_snprintf(buf, sizeof(buf), "%s", "");
4965 if (compose->account->gen_msgid) {
4966 generate_msgid(buf, sizeof(buf));
4967 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4968 compose->msgid = g_strdup(buf);
4970 compose->msgid = NULL;
4973 if (compose_redirect_write_headers_from_headerlist(compose, fp))
4976 /* separator between header and body */
4977 err |= (fputs("\n", fp) == EOF);
4979 return (err ? -1:0);
4982 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4986 gchar buf[BUFFSIZE];
4988 gboolean skip = FALSE;
4989 gboolean err = FALSE;
4990 gchar *not_included[]={
4991 "Return-Path:", "Delivered-To:", "Received:",
4992 "Subject:", "X-UIDL:", "AF:",
4993 "NF:", "PS:", "SRH:",
4994 "SFN:", "DSR:", "MID:",
4995 "CFG:", "PT:", "S:",
4996 "RQ:", "SSV:", "NSV:",
4997 "SSH:", "R:", "MAID:",
4998 "NAID:", "RMID:", "FMID:",
4999 "SCF:", "RRCPT:", "NG:",
5000 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5001 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5002 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5003 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5004 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5007 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5008 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5012 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5014 for (i = 0; not_included[i] != NULL; i++) {
5015 if (g_ascii_strncasecmp(buf, not_included[i],
5016 strlen(not_included[i])) == 0) {
5023 if (fputs(buf, fdest) == -1)
5026 if (!prefs_common.redirect_keep_from) {
5027 if (g_ascii_strncasecmp(buf, "From:",
5028 strlen("From:")) == 0) {
5029 err |= (fputs(" (by way of ", fdest) == EOF);
5030 if (compose->account->name
5031 && *compose->account->name) {
5032 compose_convert_header
5033 (compose, buf, sizeof(buf),
5034 compose->account->name,
5037 err |= (fprintf(fdest, "%s <%s>",
5039 compose->account->address) < 0);
5041 err |= (fprintf(fdest, "%s",
5042 compose->account->address) < 0);
5043 err |= (fputs(")", fdest) == EOF);
5047 if (fputs("\n", fdest) == -1)
5054 if (compose_redirect_write_headers(compose, fdest))
5057 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5058 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5071 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5073 GtkTextBuffer *buffer;
5074 GtkTextIter start, end;
5077 const gchar *out_codeset;
5078 EncodingType encoding = ENC_UNKNOWN;
5079 MimeInfo *mimemsg, *mimetext;
5081 const gchar *src_codeset = CS_INTERNAL;
5083 if (action == COMPOSE_WRITE_FOR_SEND)
5084 attach_parts = TRUE;
5086 /* create message MimeInfo */
5087 mimemsg = procmime_mimeinfo_new();
5088 mimemsg->type = MIMETYPE_MESSAGE;
5089 mimemsg->subtype = g_strdup("rfc822");
5090 mimemsg->content = MIMECONTENT_MEM;
5091 mimemsg->tmp = TRUE; /* must free content later */
5092 mimemsg->data.mem = compose_get_header(compose);
5094 /* Create text part MimeInfo */
5095 /* get all composed text */
5096 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5097 gtk_text_buffer_get_start_iter(buffer, &start);
5098 gtk_text_buffer_get_end_iter(buffer, &end);
5099 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5101 out_codeset = conv_get_charset_str(compose->out_encoding);
5103 if (!out_codeset && is_ascii_str(chars)) {
5104 out_codeset = CS_US_ASCII;
5105 } else if (prefs_common.outgoing_fallback_to_ascii &&
5106 is_ascii_str(chars)) {
5107 out_codeset = CS_US_ASCII;
5108 encoding = ENC_7BIT;
5112 gchar *test_conv_global_out = NULL;
5113 gchar *test_conv_reply = NULL;
5115 /* automatic mode. be automatic. */
5116 codeconv_set_strict(TRUE);
5118 out_codeset = conv_get_outgoing_charset_str();
5120 debug_print("trying to convert to %s\n", out_codeset);
5121 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5124 if (!test_conv_global_out && compose->orig_charset
5125 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5126 out_codeset = compose->orig_charset;
5127 debug_print("failure; trying to convert to %s\n", out_codeset);
5128 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5131 if (!test_conv_global_out && !test_conv_reply) {
5133 out_codeset = CS_INTERNAL;
5134 debug_print("failure; finally using %s\n", out_codeset);
5136 g_free(test_conv_global_out);
5137 g_free(test_conv_reply);
5138 codeconv_set_strict(FALSE);
5141 if (encoding == ENC_UNKNOWN) {
5142 if (prefs_common.encoding_method == CTE_BASE64)
5143 encoding = ENC_BASE64;
5144 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5145 encoding = ENC_QUOTED_PRINTABLE;
5146 else if (prefs_common.encoding_method == CTE_8BIT)
5147 encoding = ENC_8BIT;
5149 encoding = procmime_get_encoding_for_charset(out_codeset);
5152 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5153 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5155 if (action == COMPOSE_WRITE_FOR_SEND) {
5156 codeconv_set_strict(TRUE);
5157 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5158 codeconv_set_strict(FALSE);
5164 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5165 "to the specified %s charset.\n"
5166 "Send it as %s?"), out_codeset, src_codeset);
5167 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5168 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5171 if (aval != G_ALERTALTERNATE) {
5176 out_codeset = src_codeset;
5182 out_codeset = src_codeset;
5187 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5188 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5189 strstr(buf, "\nFrom ") != NULL) {
5190 encoding = ENC_QUOTED_PRINTABLE;
5194 mimetext = procmime_mimeinfo_new();
5195 mimetext->content = MIMECONTENT_MEM;
5196 mimetext->tmp = TRUE; /* must free content later */
5197 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5198 * and free the data, which we need later. */
5199 mimetext->data.mem = g_strdup(buf);
5200 mimetext->type = MIMETYPE_TEXT;
5201 mimetext->subtype = g_strdup("plain");
5202 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5203 g_strdup(out_codeset));
5205 /* protect trailing spaces when signing message */
5206 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5207 privacy_system_can_sign(compose->privacy_system)) {
5208 encoding = ENC_QUOTED_PRINTABLE;
5211 debug_print("main text: %zd bytes encoded as %s in %d\n",
5212 strlen(buf), out_codeset, encoding);
5214 /* check for line length limit */
5215 if (action == COMPOSE_WRITE_FOR_SEND &&
5216 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5217 check_line_length(buf, 1000, &line) < 0) {
5221 msg = g_strdup_printf
5222 (_("Line %d exceeds the line length limit (998 bytes).\n"
5223 "The contents of the message might be broken on the way to the delivery.\n"
5225 "Send it anyway?"), line + 1);
5226 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5228 if (aval != G_ALERTALTERNATE) {
5234 if (encoding != ENC_UNKNOWN)
5235 procmime_encode_content(mimetext, encoding);
5237 /* append attachment parts */
5238 if (compose_use_attach(compose) && attach_parts) {
5239 MimeInfo *mimempart;
5240 gchar *boundary = NULL;
5241 mimempart = procmime_mimeinfo_new();
5242 mimempart->content = MIMECONTENT_EMPTY;
5243 mimempart->type = MIMETYPE_MULTIPART;
5244 mimempart->subtype = g_strdup("mixed");
5248 boundary = generate_mime_boundary(NULL);
5249 } while (strstr(buf, boundary) != NULL);
5251 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5254 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5256 g_node_append(mimempart->node, mimetext->node);
5257 g_node_append(mimemsg->node, mimempart->node);
5259 compose_add_attachments(compose, mimempart);
5261 g_node_append(mimemsg->node, mimetext->node);
5265 /* sign message if sending */
5266 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5267 privacy_system_can_sign(compose->privacy_system))
5268 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5271 procmime_write_mimeinfo(mimemsg, fp);
5273 procmime_mimeinfo_free_all(mimemsg);
5278 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5280 GtkTextBuffer *buffer;
5281 GtkTextIter start, end;
5286 if ((fp = g_fopen(file, "wb")) == NULL) {
5287 FILE_OP_ERROR(file, "fopen");
5291 /* chmod for security */
5292 if (change_file_mode_rw(fp, file) < 0) {
5293 FILE_OP_ERROR(file, "chmod");
5294 g_warning("can't change file mode\n");
5297 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5298 gtk_text_buffer_get_start_iter(buffer, &start);
5299 gtk_text_buffer_get_end_iter(buffer, &end);
5300 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5302 chars = conv_codeset_strdup
5303 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5306 if (!chars) return -1;
5309 len = strlen(chars);
5310 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5311 FILE_OP_ERROR(file, "fwrite");
5320 if (fclose(fp) == EOF) {
5321 FILE_OP_ERROR(file, "fclose");
5328 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5331 MsgInfo *msginfo = compose->targetinfo;
5333 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5334 if (!msginfo) return -1;
5336 if (!force && MSG_IS_LOCKED(msginfo->flags))
5339 item = msginfo->folder;
5340 g_return_val_if_fail(item != NULL, -1);
5342 if (procmsg_msg_exist(msginfo) &&
5343 (folder_has_parent_of_type(item, F_QUEUE) ||
5344 folder_has_parent_of_type(item, F_DRAFT)
5345 || msginfo == compose->autosaved_draft)) {
5346 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5347 g_warning("can't remove the old message\n");
5350 debug_print("removed reedit target %d\n", msginfo->msgnum);
5357 static void compose_remove_draft(Compose *compose)
5360 MsgInfo *msginfo = compose->targetinfo;
5361 drafts = account_get_special_folder(compose->account, F_DRAFT);
5363 if (procmsg_msg_exist(msginfo)) {
5364 folder_item_remove_msg(drafts, msginfo->msgnum);
5369 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5370 gboolean remove_reedit_target)
5372 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5375 static gboolean compose_warn_encryption(Compose *compose)
5377 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5378 AlertValue val = G_ALERTALTERNATE;
5380 if (warning == NULL)
5383 val = alertpanel_full(_("Encryption warning"), warning,
5384 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5385 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5386 if (val & G_ALERTDISABLE) {
5387 val &= ~G_ALERTDISABLE;
5388 if (val == G_ALERTALTERNATE)
5389 privacy_inhibit_encrypt_warning(compose->privacy_system,
5393 if (val == G_ALERTALTERNATE) {
5400 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5401 gchar **msgpath, gboolean check_subject,
5402 gboolean remove_reedit_target)
5409 static gboolean lock = FALSE;
5410 PrefsAccount *mailac = NULL, *newsac = NULL;
5411 gboolean err = FALSE;
5413 debug_print("queueing message...\n");
5414 g_return_val_if_fail(compose->account != NULL, -1);
5418 if (compose_check_entries(compose, check_subject) == FALSE) {
5420 if (compose->batch) {
5421 gtk_widget_show_all(compose->window);
5426 if (!compose->to_list && !compose->newsgroup_list) {
5427 g_warning("can't get recipient list.");
5432 if (compose->to_list) {
5433 if (compose->account->protocol != A_NNTP)
5434 mailac = compose->account;
5435 else if (cur_account && cur_account->protocol != A_NNTP)
5436 mailac = cur_account;
5437 else if (!(mailac = compose_current_mail_account())) {
5439 alertpanel_error(_("No account for sending mails available!"));
5444 if (compose->newsgroup_list) {
5445 if (compose->account->protocol == A_NNTP)
5446 newsac = compose->account;
5447 else if (!newsac->protocol != A_NNTP) {
5449 alertpanel_error(_("No account for posting news available!"));
5454 /* write queue header */
5455 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5456 G_DIR_SEPARATOR, compose, (guint) rand());
5457 debug_print("queuing to %s\n", tmp);
5458 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5459 FILE_OP_ERROR(tmp, "fopen");
5465 if (change_file_mode_rw(fp, tmp) < 0) {
5466 FILE_OP_ERROR(tmp, "chmod");
5467 g_warning("can't change file mode\n");
5470 /* queueing variables */
5471 err |= (fprintf(fp, "AF:\n") < 0);
5472 err |= (fprintf(fp, "NF:0\n") < 0);
5473 err |= (fprintf(fp, "PS:10\n") < 0);
5474 err |= (fprintf(fp, "SRH:1\n") < 0);
5475 err |= (fprintf(fp, "SFN:\n") < 0);
5476 err |= (fprintf(fp, "DSR:\n") < 0);
5478 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5480 err |= (fprintf(fp, "MID:\n") < 0);
5481 err |= (fprintf(fp, "CFG:\n") < 0);
5482 err |= (fprintf(fp, "PT:0\n") < 0);
5483 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5484 err |= (fprintf(fp, "RQ:\n") < 0);
5486 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5488 err |= (fprintf(fp, "SSV:\n") < 0);
5490 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5492 err |= (fprintf(fp, "NSV:\n") < 0);
5493 err |= (fprintf(fp, "SSH:\n") < 0);
5494 /* write recepient list */
5495 if (compose->to_list) {
5496 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5497 for (cur = compose->to_list->next; cur != NULL;
5499 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5500 err |= (fprintf(fp, "\n") < 0);
5502 /* write newsgroup list */
5503 if (compose->newsgroup_list) {
5504 err |= (fprintf(fp, "NG:") < 0);
5505 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5506 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5507 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5508 err |= (fprintf(fp, "\n") < 0);
5510 /* Sylpheed account IDs */
5512 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5514 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5517 if (compose->privacy_system != NULL) {
5518 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5519 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5520 if (compose->use_encryption) {
5522 if (!compose_warn_encryption(compose)) {
5529 if (mailac && mailac->encrypt_to_self) {
5530 GSList *tmp_list = g_slist_copy(compose->to_list);
5531 tmp_list = g_slist_append(tmp_list, compose->account->address);
5532 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5533 g_slist_free(tmp_list);
5535 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5537 if (encdata != NULL) {
5538 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5539 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5540 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5542 } /* else we finally dont want to encrypt */
5544 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5545 /* and if encdata was null, it means there's been a problem in
5557 /* Save copy folder */
5558 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5559 gchar *savefolderid;
5561 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5562 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5563 g_free(savefolderid);
5565 /* Save copy folder */
5566 if (compose->return_receipt) {
5567 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5569 /* Message-ID of message replying to */
5570 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5573 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5574 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5577 /* Message-ID of message forwarding to */
5578 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5581 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5582 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5586 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5587 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5589 /* end of headers */
5590 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5592 if (compose->redirect_filename != NULL) {
5593 if (compose_redirect_write_to_file(compose, fp) < 0) {
5602 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5607 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5611 g_warning("failed to write queue message\n");
5618 if (fclose(fp) == EOF) {
5619 FILE_OP_ERROR(tmp, "fclose");
5626 if (item && *item) {
5629 queue = account_get_special_folder(compose->account, F_QUEUE);
5632 g_warning("can't find queue folder\n");
5638 folder_item_scan(queue);
5639 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5640 g_warning("can't queue the message\n");
5647 if (msgpath == NULL) {
5653 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5654 compose_remove_reedit_target(compose, FALSE);
5657 if ((msgnum != NULL) && (item != NULL)) {
5665 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5668 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5670 struct stat statbuf;
5671 gchar *type, *subtype;
5672 GtkTreeModel *model;
5675 model = gtk_tree_view_get_model(tree_view);
5677 if (!gtk_tree_model_get_iter_first(model, &iter))
5680 gtk_tree_model_get(model, &iter,
5684 mimepart = procmime_mimeinfo_new();
5685 mimepart->content = MIMECONTENT_FILE;
5686 mimepart->data.filename = g_strdup(ainfo->file);
5687 mimepart->tmp = FALSE; /* or we destroy our attachment */
5688 mimepart->offset = 0;
5690 g_stat(ainfo->file, &statbuf);
5691 mimepart->length = statbuf.st_size;
5693 type = g_strdup(ainfo->content_type);
5695 if (!strchr(type, '/')) {
5697 type = g_strdup("application/octet-stream");
5700 subtype = strchr(type, '/') + 1;
5701 *(subtype - 1) = '\0';
5702 mimepart->type = procmime_get_media_type(type);
5703 mimepart->subtype = g_strdup(subtype);
5706 if (mimepart->type == MIMETYPE_MESSAGE &&
5707 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5708 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5711 g_hash_table_insert(mimepart->typeparameters,
5712 g_strdup("name"), g_strdup(ainfo->name));
5713 g_hash_table_insert(mimepart->dispositionparameters,
5714 g_strdup("filename"), g_strdup(ainfo->name));
5715 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5719 if (compose->use_signing) {
5720 if (ainfo->encoding == ENC_7BIT)
5721 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5722 else if (ainfo->encoding == ENC_8BIT)
5723 ainfo->encoding = ENC_BASE64;
5726 procmime_encode_content(mimepart, ainfo->encoding);
5728 g_node_append(parent->node, mimepart->node);
5729 } while (gtk_tree_model_iter_next(model, &iter));
5732 #define IS_IN_CUSTOM_HEADER(header) \
5733 (compose->account->add_customhdr && \
5734 custom_header_find(compose->account->customhdr_list, header) != NULL)
5736 static void compose_add_headerfield_from_headerlist(Compose *compose,
5738 const gchar *fieldname,
5739 const gchar *seperator)
5741 gchar *str, *fieldname_w_colon;
5742 gboolean add_field = FALSE;
5744 ComposeHeaderEntry *headerentry;
5745 const gchar *headerentryname;
5746 const gchar *trans_fieldname;
5749 if (IS_IN_CUSTOM_HEADER(fieldname))
5752 debug_print("Adding %s-fields\n", fieldname);
5754 fieldstr = g_string_sized_new(64);
5756 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5757 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5759 for (list = compose->header_list; list; list = list->next) {
5760 headerentry = ((ComposeHeaderEntry *)list->data);
5761 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5763 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5764 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5766 if (str[0] != '\0') {
5768 g_string_append(fieldstr, seperator);
5769 g_string_append(fieldstr, str);
5778 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5779 compose_convert_header
5780 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5781 strlen(fieldname) + 2, TRUE);
5782 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5786 g_free(fieldname_w_colon);
5787 g_string_free(fieldstr, TRUE);
5792 static gchar *compose_get_header(Compose *compose)
5794 gchar buf[BUFFSIZE];
5795 const gchar *entry_str;
5799 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5801 gchar *from_name = NULL, *from_address = NULL;
5804 g_return_val_if_fail(compose->account != NULL, NULL);
5805 g_return_val_if_fail(compose->account->address != NULL, NULL);
5807 header = g_string_sized_new(64);
5810 get_rfc822_date(buf, sizeof(buf));
5811 g_string_append_printf(header, "Date: %s\n", buf);
5815 if (compose->account->name && *compose->account->name) {
5817 QUOTE_IF_REQUIRED(buf, compose->account->name);
5818 tmp = g_strdup_printf("%s <%s>",
5819 buf, compose->account->address);
5821 tmp = g_strdup_printf("%s",
5822 compose->account->address);
5824 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5825 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5827 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5828 from_address = g_strdup(compose->account->address);
5830 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5831 /* extract name and address */
5832 if (strstr(spec, " <") && strstr(spec, ">")) {
5833 from_address = g_strdup(strrchr(spec, '<')+1);
5834 *(strrchr(from_address, '>')) = '\0';
5835 from_name = g_strdup(spec);
5836 *(strrchr(from_name, '<')) = '\0';
5839 from_address = g_strdup(spec);
5846 if (from_name && *from_name) {
5847 compose_convert_header
5848 (compose, buf, sizeof(buf), from_name,
5849 strlen("From: "), TRUE);
5850 QUOTE_IF_REQUIRED(name, buf);
5852 g_string_append_printf(header, "From: %s <%s>\n",
5853 name, from_address);
5855 g_string_append_printf(header, "From: %s\n", from_address);
5858 g_free(from_address);
5861 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5864 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5867 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5870 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5873 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5875 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5878 compose_convert_header(compose, buf, sizeof(buf), str,
5879 strlen("Subject: "), FALSE);
5880 g_string_append_printf(header, "Subject: %s\n", buf);
5886 if (compose->account->set_domain && compose->account->domain) {
5887 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5888 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5889 g_snprintf(buf, sizeof(buf), "%s",
5890 strchr(compose->account->address, '@') ?
5891 strchr(compose->account->address, '@')+1 :
5892 compose->account->address);
5894 g_snprintf(buf, sizeof(buf), "%s", "");
5897 if (compose->account->gen_msgid) {
5898 generate_msgid(buf, sizeof(buf));
5899 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5900 compose->msgid = g_strdup(buf);
5902 compose->msgid = NULL;
5905 if (compose->remove_references == FALSE) {
5907 if (compose->inreplyto && compose->to_list)
5908 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5911 if (compose->references)
5912 g_string_append_printf(header, "References: %s\n", compose->references);
5916 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5919 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5922 if (compose->account->organization &&
5923 strlen(compose->account->organization) &&
5924 !IS_IN_CUSTOM_HEADER("Organization")) {
5925 compose_convert_header(compose, buf, sizeof(buf),
5926 compose->account->organization,
5927 strlen("Organization: "), FALSE);
5928 g_string_append_printf(header, "Organization: %s\n", buf);
5931 /* Program version and system info */
5932 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5933 !compose->newsgroup_list) {
5934 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5936 gtk_major_version, gtk_minor_version, gtk_micro_version,
5939 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5940 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5942 gtk_major_version, gtk_minor_version, gtk_micro_version,
5946 /* custom headers */
5947 if (compose->account->add_customhdr) {
5950 for (cur = compose->account->customhdr_list; cur != NULL;
5952 CustomHeader *chdr = (CustomHeader *)cur->data;
5954 if (custom_header_is_allowed(chdr->name)) {
5955 compose_convert_header
5956 (compose, buf, sizeof(buf),
5957 chdr->value ? chdr->value : "",
5958 strlen(chdr->name) + 2, FALSE);
5959 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5964 /* Automatic Faces and X-Faces */
5965 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
5966 g_string_append_printf(header, "X-Face: %s\n", buf);
5968 else if (get_default_xface (buf, sizeof(buf)) == 0) {
5969 g_string_append_printf(header, "X-Face: %s\n", buf);
5971 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
5972 g_string_append_printf(header, "Face: %s\n", buf);
5974 else if (get_default_face (buf, sizeof(buf)) == 0) {
5975 g_string_append_printf(header, "Face: %s\n", buf);
5979 switch (compose->priority) {
5980 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5981 "X-Priority: 1 (Highest)\n");
5983 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5984 "X-Priority: 2 (High)\n");
5986 case PRIORITY_NORMAL: break;
5987 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5988 "X-Priority: 4 (Low)\n");
5990 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5991 "X-Priority: 5 (Lowest)\n");
5993 default: debug_print("compose: priority unknown : %d\n",
5997 /* Request Return Receipt */
5998 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5999 if (compose->return_receipt) {
6000 if (compose->account->name
6001 && *compose->account->name) {
6002 compose_convert_header(compose, buf, sizeof(buf),
6003 compose->account->name,
6004 strlen("Disposition-Notification-To: "),
6006 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6008 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6012 /* get special headers */
6013 for (list = compose->header_list; list; list = list->next) {
6014 ComposeHeaderEntry *headerentry;
6017 gchar *headername_wcolon;
6018 const gchar *headername_trans;
6021 gboolean standard_header = FALSE;
6023 headerentry = ((ComposeHeaderEntry *)list->data);
6025 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6027 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6032 if (!strstr(tmp, ":")) {
6033 headername_wcolon = g_strconcat(tmp, ":", NULL);
6034 headername = g_strdup(tmp);
6036 headername_wcolon = g_strdup(tmp);
6037 headername = g_strdup(strtok(tmp, ":"));
6041 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6042 Xstrdup_a(headervalue, entry_str, return NULL);
6043 subst_char(headervalue, '\r', ' ');
6044 subst_char(headervalue, '\n', ' ');
6045 string = std_headers;
6046 while (*string != NULL) {
6047 headername_trans = prefs_common_translated_header_name(*string);
6048 if (!strcmp(headername_trans, headername_wcolon))
6049 standard_header = TRUE;
6052 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6053 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6056 g_free(headername_wcolon);
6060 g_string_free(header, FALSE);
6065 #undef IS_IN_CUSTOM_HEADER
6067 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6068 gint header_len, gboolean addr_field)
6070 gchar *tmpstr = NULL;
6071 const gchar *out_codeset = NULL;
6073 g_return_if_fail(src != NULL);
6074 g_return_if_fail(dest != NULL);
6076 if (len < 1) return;
6078 tmpstr = g_strdup(src);
6080 subst_char(tmpstr, '\n', ' ');
6081 subst_char(tmpstr, '\r', ' ');
6084 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6085 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6086 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6091 codeconv_set_strict(TRUE);
6092 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6093 conv_get_charset_str(compose->out_encoding));
6094 codeconv_set_strict(FALSE);
6096 if (!dest || *dest == '\0') {
6097 gchar *test_conv_global_out = NULL;
6098 gchar *test_conv_reply = NULL;
6100 /* automatic mode. be automatic. */
6101 codeconv_set_strict(TRUE);
6103 out_codeset = conv_get_outgoing_charset_str();
6105 debug_print("trying to convert to %s\n", out_codeset);
6106 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6109 if (!test_conv_global_out && compose->orig_charset
6110 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6111 out_codeset = compose->orig_charset;
6112 debug_print("failure; trying to convert to %s\n", out_codeset);
6113 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6116 if (!test_conv_global_out && !test_conv_reply) {
6118 out_codeset = CS_INTERNAL;
6119 debug_print("finally using %s\n", out_codeset);
6121 g_free(test_conv_global_out);
6122 g_free(test_conv_reply);
6123 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6125 codeconv_set_strict(FALSE);
6130 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6134 g_return_if_fail(user_data != NULL);
6136 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6137 g_strstrip(address);
6138 if (*address != '\0') {
6139 gchar *name = procheader_get_fromname(address);
6140 extract_address(address);
6141 addressbook_add_contact(name, address, NULL, NULL);
6146 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6148 GtkWidget *menuitem;
6151 g_return_if_fail(menu != NULL);
6152 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6154 menuitem = gtk_separator_menu_item_new();
6155 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6156 gtk_widget_show(menuitem);
6158 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6159 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6161 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6162 g_strstrip(address);
6163 if (*address == '\0') {
6164 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6167 g_signal_connect(G_OBJECT(menuitem), "activate",
6168 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6169 gtk_widget_show(menuitem);
6172 static void compose_create_header_entry(Compose *compose)
6174 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6179 const gchar *header = NULL;
6180 ComposeHeaderEntry *headerentry;
6181 gboolean standard_header = FALSE;
6182 #if !(GTK_CHECK_VERSION(2,12,0))
6183 GtkTooltips *tips = compose->tooltips;
6186 headerentry = g_new0(ComposeHeaderEntry, 1);
6189 combo = gtk_combo_box_entry_new_text();
6191 while(*string != NULL) {
6192 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6193 (gchar*)prefs_common_translated_header_name(*string));
6196 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6197 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6198 G_CALLBACK(compose_grab_focus_cb), compose);
6199 gtk_widget_show(combo);
6200 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6201 compose->header_nextrow, compose->header_nextrow+1,
6202 GTK_SHRINK, GTK_FILL, 0, 0);
6203 if (compose->header_last) {
6204 const gchar *last_header_entry = gtk_entry_get_text(
6205 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6207 while (*string != NULL) {
6208 if (!strcmp(*string, last_header_entry))
6209 standard_header = TRUE;
6212 if (standard_header)
6213 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6215 if (!compose->header_last || !standard_header) {
6216 switch(compose->account->protocol) {
6218 header = prefs_common_translated_header_name("Newsgroups:");
6221 header = prefs_common_translated_header_name("To:");
6226 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6228 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6229 G_CALLBACK(compose_grab_focus_cb), compose);
6232 entry = gtk_entry_new();
6233 gtk_widget_show(entry);
6234 CLAWS_SET_TIP(entry,
6235 _("Use <tab> to autocomplete from addressbook"));
6236 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6237 compose->header_nextrow, compose->header_nextrow+1,
6238 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6240 g_signal_connect(G_OBJECT(entry), "key-press-event",
6241 G_CALLBACK(compose_headerentry_key_press_event_cb),
6243 g_signal_connect(G_OBJECT(entry), "changed",
6244 G_CALLBACK(compose_headerentry_changed_cb),
6246 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6247 G_CALLBACK(compose_grab_focus_cb), compose);
6250 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6251 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6252 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6253 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6254 G_CALLBACK(compose_header_drag_received_cb),
6256 g_signal_connect(G_OBJECT(entry), "drag-drop",
6257 G_CALLBACK(compose_drag_drop),
6259 g_signal_connect(G_OBJECT(entry), "populate-popup",
6260 G_CALLBACK(compose_entry_popup_extend),
6263 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6265 headerentry->compose = compose;
6266 headerentry->combo = combo;
6267 headerentry->entry = entry;
6268 headerentry->headernum = compose->header_nextrow;
6270 compose->header_nextrow++;
6271 compose->header_last = headerentry;
6272 compose->header_list =
6273 g_slist_append(compose->header_list,
6277 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6279 ComposeHeaderEntry *last_header;
6281 last_header = compose->header_last;
6283 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6284 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6287 static void compose_remove_header_entries(Compose *compose)
6290 for (list = compose->header_list; list; list = list->next) {
6291 ComposeHeaderEntry *headerentry =
6292 (ComposeHeaderEntry *)list->data;
6293 gtk_widget_destroy(headerentry->combo);
6294 gtk_widget_destroy(headerentry->entry);
6295 g_free(headerentry);
6297 compose->header_last = NULL;
6298 g_slist_free(compose->header_list);
6299 compose->header_list = NULL;
6300 compose->header_nextrow = 1;
6301 compose_create_header_entry(compose);
6304 static GtkWidget *compose_create_header(Compose *compose)
6306 GtkWidget *from_optmenu_hbox;
6307 GtkWidget *header_scrolledwin;
6308 GtkWidget *header_table;
6312 /* header labels and entries */
6313 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6314 gtk_widget_show(header_scrolledwin);
6315 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6317 header_table = gtk_table_new(2, 2, FALSE);
6318 gtk_widget_show(header_table);
6319 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6320 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6321 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6324 /* option menu for selecting accounts */
6325 from_optmenu_hbox = compose_account_option_menu_create(compose);
6326 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6327 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6330 compose->header_table = header_table;
6331 compose->header_list = NULL;
6332 compose->header_nextrow = count;
6334 compose_create_header_entry(compose);
6336 compose->table = NULL;
6338 return header_scrolledwin ;
6341 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6343 Compose *compose = (Compose *)data;
6344 GdkEventButton event;
6347 event.time = gtk_get_current_event_time();
6349 return attach_button_pressed(compose->attach_clist, &event, compose);
6352 static GtkWidget *compose_create_attach(Compose *compose)
6354 GtkWidget *attach_scrwin;
6355 GtkWidget *attach_clist;
6357 GtkListStore *store;
6358 GtkCellRenderer *renderer;
6359 GtkTreeViewColumn *column;
6360 GtkTreeSelection *selection;
6362 /* attachment list */
6363 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6364 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6365 GTK_POLICY_AUTOMATIC,
6366 GTK_POLICY_AUTOMATIC);
6367 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6369 store = gtk_list_store_new(N_ATTACH_COLS,
6374 G_TYPE_AUTO_POINTER,
6376 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6377 (GTK_TREE_MODEL(store)));
6378 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6379 g_object_unref(store);
6381 renderer = gtk_cell_renderer_text_new();
6382 column = gtk_tree_view_column_new_with_attributes
6383 (_("Mime type"), renderer, "text",
6384 COL_MIMETYPE, NULL);
6385 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6387 renderer = gtk_cell_renderer_text_new();
6388 column = gtk_tree_view_column_new_with_attributes
6389 (_("Size"), renderer, "text",
6391 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6393 renderer = gtk_cell_renderer_text_new();
6394 column = gtk_tree_view_column_new_with_attributes
6395 (_("Name"), renderer, "text",
6397 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6399 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6400 prefs_common.use_stripes_everywhere);
6401 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6402 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6404 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6405 G_CALLBACK(attach_selected), compose);
6406 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6407 G_CALLBACK(attach_button_pressed), compose);
6409 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6410 G_CALLBACK(popup_attach_button_pressed), compose);
6412 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6413 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6414 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6415 G_CALLBACK(popup_attach_button_pressed), compose);
6417 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6418 G_CALLBACK(attach_key_pressed), compose);
6421 gtk_drag_dest_set(attach_clist,
6422 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6423 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6424 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6425 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6426 G_CALLBACK(compose_attach_drag_received_cb),
6428 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6429 G_CALLBACK(compose_drag_drop),
6432 compose->attach_scrwin = attach_scrwin;
6433 compose->attach_clist = attach_clist;
6435 return attach_scrwin;
6438 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6439 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6441 static GtkWidget *compose_create_others(Compose *compose)
6444 GtkWidget *savemsg_checkbtn;
6445 GtkWidget *savemsg_entry;
6446 GtkWidget *savemsg_select;
6449 gchar *folderidentifier;
6451 /* Table for settings */
6452 table = gtk_table_new(3, 1, FALSE);
6453 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6454 gtk_widget_show(table);
6455 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6458 /* Save Message to folder */
6459 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6460 gtk_widget_show(savemsg_checkbtn);
6461 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6462 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6463 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6465 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6466 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6468 savemsg_entry = gtk_entry_new();
6469 gtk_widget_show(savemsg_entry);
6470 gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6471 gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6472 g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6473 G_CALLBACK(compose_grab_focus_cb), compose);
6474 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6475 folderidentifier = folder_item_get_identifier(account_get_special_folder
6476 (compose->account, F_OUTBOX));
6477 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6478 g_free(folderidentifier);
6481 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6482 gtk_widget_show(savemsg_select);
6483 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6484 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6485 G_CALLBACK(compose_savemsg_select_cb),
6490 compose->savemsg_checkbtn = savemsg_checkbtn;
6491 compose->savemsg_entry = savemsg_entry;
6496 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6498 gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6499 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6502 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6507 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6510 path = folder_item_get_identifier(dest);
6512 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6516 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6517 GdkAtom clip, GtkTextIter *insert_place);
6520 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6524 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6526 if (event->button == 3) {
6528 GtkTextIter sel_start, sel_end;
6529 gboolean stuff_selected;
6531 /* move the cursor to allow GtkAspell to check the word
6532 * under the mouse */
6533 if (event->x && event->y) {
6534 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6535 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6537 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6540 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6541 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6544 stuff_selected = gtk_text_buffer_get_selection_bounds(
6546 &sel_start, &sel_end);
6548 gtk_text_buffer_place_cursor (buffer, &iter);
6549 /* reselect stuff */
6551 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6552 gtk_text_buffer_select_range(buffer,
6553 &sel_start, &sel_end);
6555 return FALSE; /* pass the event so that the right-click goes through */
6558 if (event->button == 2) {
6563 /* get the middle-click position to paste at the correct place */
6564 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6565 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6567 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6570 entry_paste_clipboard(compose, text,
6571 prefs_common.linewrap_pastes,
6572 GDK_SELECTION_PRIMARY, &iter);
6580 static void compose_spell_menu_changed(void *data)
6582 Compose *compose = (Compose *)data;
6584 GtkWidget *menuitem;
6585 GtkWidget *parent_item;
6586 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6589 if (compose->gtkaspell == NULL)
6592 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6593 "/Menu/Spelling/Options");
6595 /* setting the submenu removes /Spelling/Options from the factory
6596 * so we need to save it */
6598 if (parent_item == NULL) {
6599 parent_item = compose->aspell_options_menu;
6600 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6602 compose->aspell_options_menu = parent_item;
6604 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6606 spell_menu = g_slist_reverse(spell_menu);
6607 for (items = spell_menu;
6608 items; items = items->next) {
6609 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6610 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6611 gtk_widget_show(GTK_WIDGET(menuitem));
6613 g_slist_free(spell_menu);
6615 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6620 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6622 Compose *compose = (Compose *)data;
6623 GdkEventButton event;
6626 event.time = gtk_get_current_event_time();
6630 return text_clicked(compose->text, &event, compose);
6633 static gboolean compose_force_window_origin = TRUE;
6634 static Compose *compose_create(PrefsAccount *account,
6643 GtkWidget *handlebox;
6645 GtkWidget *notebook;
6647 GtkWidget *attach_hbox;
6648 GtkWidget *attach_lab1;
6649 GtkWidget *attach_lab2;
6654 GtkWidget *subject_hbox;
6655 GtkWidget *subject_frame;
6656 GtkWidget *subject_entry;
6660 GtkWidget *edit_vbox;
6661 GtkWidget *ruler_hbox;
6663 GtkWidget *scrolledwin;
6665 GtkTextBuffer *buffer;
6666 GtkClipboard *clipboard;
6669 UndoMain *undostruct;
6671 gchar *titles[N_ATTACH_COLS];
6672 GtkWidget *popupmenu;
6673 GtkWidget *tmpl_menu;
6674 GtkActionGroup *action_group = NULL;
6677 GtkAspell * gtkaspell = NULL;
6680 static GdkGeometry geometry;
6682 g_return_val_if_fail(account != NULL, NULL);
6684 debug_print("Creating compose window...\n");
6685 compose = g_new0(Compose, 1);
6687 titles[COL_MIMETYPE] = _("MIME type");
6688 titles[COL_SIZE] = _("Size");
6689 titles[COL_NAME] = _("Name");
6691 compose->batch = batch;
6692 compose->account = account;
6693 compose->folder = folder;
6695 compose->mutex = g_mutex_new();
6696 compose->set_cursor_pos = -1;
6698 #if !(GTK_CHECK_VERSION(2,12,0))
6699 compose->tooltips = tips;
6702 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6704 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6705 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6707 if (!geometry.max_width) {
6708 geometry.max_width = gdk_screen_width();
6709 geometry.max_height = gdk_screen_height();
6712 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6713 &geometry, GDK_HINT_MAX_SIZE);
6714 if (!geometry.min_width) {
6715 geometry.min_width = 600;
6716 geometry.min_height = 480;
6718 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6719 &geometry, GDK_HINT_MIN_SIZE);
6721 #ifndef GENERIC_UMPC
6722 if (compose_force_window_origin)
6723 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6724 prefs_common.compose_y);
6726 g_signal_connect(G_OBJECT(window), "delete_event",
6727 G_CALLBACK(compose_delete_cb), compose);
6728 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6729 gtk_widget_realize(window);
6731 gtkut_widget_set_composer_icon(window);
6733 vbox = gtk_vbox_new(FALSE, 0);
6734 gtk_container_add(GTK_CONTAINER(window), vbox);
6736 compose->ui_manager = gtk_ui_manager_new();
6737 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6738 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6739 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6740 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6741 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6742 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6743 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6744 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6745 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6746 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6749 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6751 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6754 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6755 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6757 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6759 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6760 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6761 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6764 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6765 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6766 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6767 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6768 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6769 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6770 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6771 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6772 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6773 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6776 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6777 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6778 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6780 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6781 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6782 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6784 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6785 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6786 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6787 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6789 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6791 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6792 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6793 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6794 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6795 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6796 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6797 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6798 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6799 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6800 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6801 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6802 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6803 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6804 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6805 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6807 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6809 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6810 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6811 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6812 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6815 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6817 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6821 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6822 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6826 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6830 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6837 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6849 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6863 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6865 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6866 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)
6867 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)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6870 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6873 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)
6874 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)
6876 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6879 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)
6880 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6882 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6883 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)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6888 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6889 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)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6894 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6895 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)
6896 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)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6901 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6908 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)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6916 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6924 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6926 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6927 gtk_widget_show_all(menubar);
6929 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6931 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6933 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6936 if (prefs_common.toolbar_detachable) {
6937 handlebox = gtk_handle_box_new();
6939 handlebox = gtk_hbox_new(FALSE, 0);
6941 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6943 gtk_widget_realize(handlebox);
6945 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6948 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6952 vbox2 = gtk_vbox_new(FALSE, 2);
6953 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6954 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6957 notebook = gtk_notebook_new();
6958 gtk_widget_set_size_request(notebook, -1, 130);
6959 gtk_widget_show(notebook);
6961 /* header labels and entries */
6962 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6963 compose_create_header(compose),
6964 gtk_label_new_with_mnemonic(_("Hea_der")));
6965 /* attachment list */
6966 attach_hbox = gtk_hbox_new(FALSE, 0);
6967 gtk_widget_show(attach_hbox);
6969 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6970 gtk_widget_show(attach_lab1);
6971 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6973 attach_lab2 = gtk_label_new("");
6974 gtk_widget_show(attach_lab2);
6975 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6977 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6978 compose_create_attach(compose),
6981 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6982 compose_create_others(compose),
6983 gtk_label_new_with_mnemonic(_("Othe_rs")));
6986 subject_hbox = gtk_hbox_new(FALSE, 0);
6987 gtk_widget_show(subject_hbox);
6989 subject_frame = gtk_frame_new(NULL);
6990 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6991 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6992 gtk_widget_show(subject_frame);
6994 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6995 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6996 gtk_widget_show(subject);
6998 label = gtk_label_new(_("Subject:"));
6999 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7000 gtk_widget_show(label);
7002 subject_entry = gtk_entry_new();
7003 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7004 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7005 G_CALLBACK(compose_grab_focus_cb), compose);
7006 gtk_widget_show(subject_entry);
7007 compose->subject_entry = subject_entry;
7008 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7010 edit_vbox = gtk_vbox_new(FALSE, 0);
7012 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7015 ruler_hbox = gtk_hbox_new(FALSE, 0);
7016 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7018 ruler = gtk_shruler_new();
7019 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7020 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7024 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7025 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7026 GTK_POLICY_AUTOMATIC,
7027 GTK_POLICY_AUTOMATIC);
7028 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7030 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7031 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7033 text = gtk_text_view_new();
7034 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7035 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7036 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7037 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7038 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7040 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7042 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7043 G_CALLBACK(compose_edit_size_alloc),
7045 g_signal_connect(G_OBJECT(buffer), "changed",
7046 G_CALLBACK(compose_changed_cb), compose);
7047 g_signal_connect(G_OBJECT(text), "grab_focus",
7048 G_CALLBACK(compose_grab_focus_cb), compose);
7049 g_signal_connect(G_OBJECT(buffer), "insert_text",
7050 G_CALLBACK(text_inserted), compose);
7051 g_signal_connect(G_OBJECT(text), "button_press_event",
7052 G_CALLBACK(text_clicked), compose);
7054 g_signal_connect(G_OBJECT(text), "popup-menu",
7055 G_CALLBACK(compose_popup_menu), compose);
7057 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7058 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7059 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7060 G_CALLBACK(compose_popup_menu), compose);
7062 g_signal_connect(G_OBJECT(subject_entry), "changed",
7063 G_CALLBACK(compose_changed_cb), compose);
7066 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7067 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7068 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7069 g_signal_connect(G_OBJECT(text), "drag_data_received",
7070 G_CALLBACK(compose_insert_drag_received_cb),
7072 g_signal_connect(G_OBJECT(text), "drag-drop",
7073 G_CALLBACK(compose_drag_drop),
7075 gtk_widget_show_all(vbox);
7077 /* pane between attach clist and text */
7078 paned = gtk_vpaned_new();
7079 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7081 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7082 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7084 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7086 gtk_paned_add1(GTK_PANED(paned), notebook);
7087 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7088 gtk_widget_show_all(paned);
7091 if (prefs_common.textfont) {
7092 PangoFontDescription *font_desc;
7094 font_desc = pango_font_description_from_string
7095 (prefs_common.textfont);
7097 gtk_widget_modify_font(text, font_desc);
7098 pango_font_description_free(font_desc);
7102 gtk_action_group_add_actions(action_group, compose_popup_entries,
7103 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7104 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7105 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7106 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7107 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7108 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7109 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7111 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7113 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7114 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7115 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7117 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7119 undostruct = undo_init(text);
7120 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7123 address_completion_start(window);
7125 compose->window = window;
7126 compose->vbox = vbox;
7127 compose->menubar = menubar;
7128 compose->handlebox = handlebox;
7130 compose->vbox2 = vbox2;
7132 compose->paned = paned;
7134 compose->attach_label = attach_lab2;
7136 compose->notebook = notebook;
7137 compose->edit_vbox = edit_vbox;
7138 compose->ruler_hbox = ruler_hbox;
7139 compose->ruler = ruler;
7140 compose->scrolledwin = scrolledwin;
7141 compose->text = text;
7143 compose->focused_editable = NULL;
7145 compose->popupmenu = popupmenu;
7147 compose->tmpl_menu = tmpl_menu;
7149 compose->mode = mode;
7150 compose->rmode = mode;
7152 compose->targetinfo = NULL;
7153 compose->replyinfo = NULL;
7154 compose->fwdinfo = NULL;
7156 compose->replyto = NULL;
7158 compose->bcc = NULL;
7159 compose->followup_to = NULL;
7161 compose->ml_post = NULL;
7163 compose->inreplyto = NULL;
7164 compose->references = NULL;
7165 compose->msgid = NULL;
7166 compose->boundary = NULL;
7168 compose->autowrap = prefs_common.autowrap;
7169 compose->autoindent = prefs_common.auto_indent;
7170 compose->use_signing = FALSE;
7171 compose->use_encryption = FALSE;
7172 compose->privacy_system = NULL;
7174 compose->modified = FALSE;
7176 compose->return_receipt = FALSE;
7178 compose->to_list = NULL;
7179 compose->newsgroup_list = NULL;
7181 compose->undostruct = undostruct;
7183 compose->sig_str = NULL;
7185 compose->exteditor_file = NULL;
7186 compose->exteditor_pid = -1;
7187 compose->exteditor_tag = -1;
7188 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7191 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7192 if (mode != COMPOSE_REDIRECT) {
7193 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7194 strcmp(prefs_common.dictionary, "")) {
7195 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7196 prefs_common.alt_dictionary,
7197 conv_get_locale_charset_str(),
7198 prefs_common.misspelled_col,
7199 prefs_common.check_while_typing,
7200 prefs_common.recheck_when_changing_dict,
7201 prefs_common.use_alternate,
7202 prefs_common.use_both_dicts,
7203 GTK_TEXT_VIEW(text),
7204 GTK_WINDOW(compose->window),
7205 compose_spell_menu_changed,
7208 alertpanel_error(_("Spell checker could not "
7210 gtkaspell_checkers_strerror());
7211 gtkaspell_checkers_reset_error();
7213 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7217 compose->gtkaspell = gtkaspell;
7218 compose_spell_menu_changed(compose);
7221 compose_select_account(compose, account, TRUE);
7223 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7224 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7226 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7227 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7229 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7230 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7232 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7233 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7235 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7236 if (account->protocol != A_NNTP)
7237 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7238 prefs_common_translated_header_name("To:"));
7240 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7241 prefs_common_translated_header_name("Newsgroups:"));
7243 addressbook_set_target_compose(compose);
7245 if (mode != COMPOSE_REDIRECT)
7246 compose_set_template_menu(compose);
7248 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7251 compose_list = g_list_append(compose_list, compose);
7253 if (!prefs_common.show_ruler)
7254 gtk_widget_hide(ruler_hbox);
7256 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7259 compose->priority = PRIORITY_NORMAL;
7260 compose_update_priority_menu_item(compose);
7262 compose_set_out_encoding(compose);
7265 compose_update_actions_menu(compose);
7267 /* Privacy Systems menu */
7268 compose_update_privacy_systems_menu(compose);
7270 activate_privacy_system(compose, account, TRUE);
7271 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7273 gtk_widget_realize(window);
7275 gtk_widget_show(window);
7277 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7278 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7285 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7290 GtkWidget *optmenubox;
7293 GtkWidget *from_name = NULL;
7294 #if !(GTK_CHECK_VERSION(2,12,0))
7295 GtkTooltips *tips = compose->tooltips;
7298 gint num = 0, def_menu = 0;
7300 accounts = account_get_list();
7301 g_return_val_if_fail(accounts != NULL, NULL);
7303 optmenubox = gtk_event_box_new();
7304 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7305 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7307 hbox = gtk_hbox_new(FALSE, 6);
7308 from_name = gtk_entry_new();
7310 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7311 G_CALLBACK(compose_grab_focus_cb), compose);
7313 for (; accounts != NULL; accounts = accounts->next, num++) {
7314 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7315 gchar *name, *from = NULL;
7317 if (ac == compose->account) def_menu = num;
7319 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7322 if (ac == compose->account) {
7323 if (ac->name && *ac->name) {
7325 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7326 from = g_strdup_printf("%s <%s>",
7328 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7330 from = g_strdup_printf("%s",
7332 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7335 COMBOBOX_ADD(menu, name, ac->account_id);
7340 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7342 g_signal_connect(G_OBJECT(optmenu), "changed",
7343 G_CALLBACK(account_activated),
7345 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7346 G_CALLBACK(compose_entry_popup_extend),
7349 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7350 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7352 CLAWS_SET_TIP(optmenubox,
7353 _("Account to use for this email"));
7354 CLAWS_SET_TIP(from_name,
7355 _("Sender address to be used"));
7357 compose->from_name = from_name;
7362 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7364 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7365 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7366 Compose *compose = (Compose *) data;
7368 compose->priority = value;
7372 static void compose_reply_change_mode(Compose *compose,
7375 gboolean was_modified = compose->modified;
7377 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7379 g_return_if_fail(compose->replyinfo != NULL);
7381 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7383 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7385 if (action == COMPOSE_REPLY_TO_ALL)
7387 if (action == COMPOSE_REPLY_TO_SENDER)
7389 if (action == COMPOSE_REPLY_TO_LIST)
7392 compose_remove_header_entries(compose);
7393 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7394 if (compose->account->set_autocc && compose->account->auto_cc)
7395 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7397 if (compose->account->set_autobcc && compose->account->auto_bcc)
7398 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7400 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7401 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7402 compose_show_first_last_header(compose, TRUE);
7403 compose->modified = was_modified;
7404 compose_set_title(compose);
7407 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7409 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7410 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7411 Compose *compose = (Compose *) data;
7414 compose_reply_change_mode(compose, value);
7417 static void compose_update_priority_menu_item(Compose * compose)
7419 GtkWidget *menuitem = NULL;
7420 switch (compose->priority) {
7421 case PRIORITY_HIGHEST:
7422 menuitem = gtk_ui_manager_get_widget
7423 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7426 menuitem = gtk_ui_manager_get_widget
7427 (compose->ui_manager, "/Menu/Options/Priority/High");
7429 case PRIORITY_NORMAL:
7430 menuitem = gtk_ui_manager_get_widget
7431 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7434 menuitem = gtk_ui_manager_get_widget
7435 (compose->ui_manager, "/Menu/Options/Priority/Low");
7437 case PRIORITY_LOWEST:
7438 menuitem = gtk_ui_manager_get_widget
7439 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7442 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7445 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7447 Compose *compose = (Compose *) data;
7449 gboolean can_sign = FALSE, can_encrypt = FALSE;
7451 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7453 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7456 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7457 g_free(compose->privacy_system);
7458 compose->privacy_system = NULL;
7459 if (systemid != NULL) {
7460 compose->privacy_system = g_strdup(systemid);
7462 can_sign = privacy_system_can_sign(systemid);
7463 can_encrypt = privacy_system_can_encrypt(systemid);
7466 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7472 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7474 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7475 GtkWidget *menuitem = NULL;
7477 gboolean can_sign = FALSE, can_encrypt = FALSE;
7478 gboolean found = FALSE;
7480 if (compose->privacy_system != NULL) {
7482 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7483 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7484 g_return_if_fail(menuitem != NULL);
7486 amenu = GTK_MENU_SHELL(menuitem)->children;
7488 while (amenu != NULL) {
7489 GList *alist = amenu->next;
7491 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7492 if (systemid != NULL) {
7493 if (strcmp(systemid, compose->privacy_system) == 0 &&
7494 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7495 menuitem = GTK_WIDGET(amenu->data);
7497 can_sign = privacy_system_can_sign(systemid);
7498 can_encrypt = privacy_system_can_encrypt(systemid);
7502 } else if (strlen(compose->privacy_system) == 0 &&
7503 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7504 menuitem = GTK_WIDGET(amenu->data);
7507 can_encrypt = FALSE;
7514 if (menuitem != NULL)
7515 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7517 if (warn && !found && strlen(compose->privacy_system)) {
7518 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7519 "will not be able to sign or encrypt this message."),
7520 compose->privacy_system);
7524 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7525 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7528 static void compose_set_out_encoding(Compose *compose)
7530 CharSet out_encoding;
7531 const gchar *branch = NULL;
7532 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7534 switch(out_encoding) {
7535 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7536 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7537 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7538 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7539 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7540 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7541 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7542 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7543 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7544 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7545 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7546 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7547 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7548 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7549 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7550 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7551 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7552 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7553 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7554 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7555 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7556 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7557 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7558 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7559 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7560 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7561 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7562 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7563 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7564 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7565 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7566 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7568 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7571 static void compose_set_template_menu(Compose *compose)
7573 GSList *tmpl_list, *cur;
7577 tmpl_list = template_get_config();
7579 menu = gtk_menu_new();
7581 gtk_menu_set_accel_group (GTK_MENU (menu),
7582 gtk_ui_manager_get_accel_group(compose->ui_manager));
7583 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7584 Template *tmpl = (Template *)cur->data;
7585 gchar *accel_path = NULL;
7586 item = gtk_menu_item_new_with_label(tmpl->name);
7587 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7588 g_signal_connect(G_OBJECT(item), "activate",
7589 G_CALLBACK(compose_template_activate_cb),
7591 g_object_set_data(G_OBJECT(item), "template", tmpl);
7592 gtk_widget_show(item);
7593 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7594 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7598 gtk_widget_show(menu);
7599 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7602 void compose_update_actions_menu(Compose *compose)
7604 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7607 static void compose_update_privacy_systems_menu(Compose *compose)
7609 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7610 GSList *systems, *cur;
7612 GtkWidget *system_none;
7614 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7615 GtkWidget *privacy_menu = gtk_menu_new();
7617 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7618 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7620 g_signal_connect(G_OBJECT(system_none), "activate",
7621 G_CALLBACK(compose_set_privacy_system_cb), compose);
7623 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7624 gtk_widget_show(system_none);
7626 systems = privacy_get_system_ids();
7627 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7628 gchar *systemid = cur->data;
7630 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7631 widget = gtk_radio_menu_item_new_with_label(group,
7632 privacy_system_get_name(systemid));
7633 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7634 g_strdup(systemid), g_free);
7635 g_signal_connect(G_OBJECT(widget), "activate",
7636 G_CALLBACK(compose_set_privacy_system_cb), compose);
7638 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7639 gtk_widget_show(widget);
7642 g_slist_free(systems);
7643 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7644 gtk_widget_show_all(privacy_menu);
7645 gtk_widget_show_all(privacy_menuitem);
7648 void compose_reflect_prefs_all(void)
7653 for (cur = compose_list; cur != NULL; cur = cur->next) {
7654 compose = (Compose *)cur->data;
7655 compose_set_template_menu(compose);
7659 void compose_reflect_prefs_pixmap_theme(void)
7664 for (cur = compose_list; cur != NULL; cur = cur->next) {
7665 compose = (Compose *)cur->data;
7666 toolbar_update(TOOLBAR_COMPOSE, compose);
7670 static const gchar *compose_quote_char_from_context(Compose *compose)
7672 const gchar *qmark = NULL;
7674 g_return_val_if_fail(compose != NULL, NULL);
7676 switch (compose->mode) {
7677 /* use forward-specific quote char */
7678 case COMPOSE_FORWARD:
7679 case COMPOSE_FORWARD_AS_ATTACH:
7680 case COMPOSE_FORWARD_INLINE:
7681 if (compose->folder && compose->folder->prefs &&
7682 compose->folder->prefs->forward_with_format)
7683 qmark = compose->folder->prefs->forward_quotemark;
7684 else if (compose->account->forward_with_format)
7685 qmark = compose->account->forward_quotemark;
7687 qmark = prefs_common.fw_quotemark;
7690 /* use reply-specific quote char in all other modes */
7692 if (compose->folder && compose->folder->prefs &&
7693 compose->folder->prefs->reply_with_format)
7694 qmark = compose->folder->prefs->reply_quotemark;
7695 else if (compose->account->reply_with_format)
7696 qmark = compose->account->reply_quotemark;
7698 qmark = prefs_common.quotemark;
7702 if (qmark == NULL || *qmark == '\0')
7708 static void compose_template_apply(Compose *compose, Template *tmpl,
7712 GtkTextBuffer *buffer;
7716 gchar *parsed_str = NULL;
7717 gint cursor_pos = 0;
7718 const gchar *err_msg = _("Template body format error at line %d.");
7721 /* process the body */
7723 text = GTK_TEXT_VIEW(compose->text);
7724 buffer = gtk_text_view_get_buffer(text);
7727 qmark = compose_quote_char_from_context(compose);
7729 if (compose->replyinfo != NULL) {
7732 gtk_text_buffer_set_text(buffer, "", -1);
7733 mark = gtk_text_buffer_get_insert(buffer);
7734 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7736 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7737 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7739 } else if (compose->fwdinfo != NULL) {
7742 gtk_text_buffer_set_text(buffer, "", -1);
7743 mark = gtk_text_buffer_get_insert(buffer);
7744 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7746 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7747 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7750 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7752 GtkTextIter start, end;
7755 gtk_text_buffer_get_start_iter(buffer, &start);
7756 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7757 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7759 /* clear the buffer now */
7761 gtk_text_buffer_set_text(buffer, "", -1);
7763 parsed_str = compose_quote_fmt(compose, dummyinfo,
7764 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7765 procmsg_msginfo_free( dummyinfo );
7771 gtk_text_buffer_set_text(buffer, "", -1);
7772 mark = gtk_text_buffer_get_insert(buffer);
7773 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7776 if (replace && parsed_str && compose->account->auto_sig)
7777 compose_insert_sig(compose, FALSE);
7779 if (replace && parsed_str) {
7780 gtk_text_buffer_get_start_iter(buffer, &iter);
7781 gtk_text_buffer_place_cursor(buffer, &iter);
7785 cursor_pos = quote_fmt_get_cursor_pos();
7786 compose->set_cursor_pos = cursor_pos;
7787 if (cursor_pos == -1)
7789 gtk_text_buffer_get_start_iter(buffer, &iter);
7790 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7791 gtk_text_buffer_place_cursor(buffer, &iter);
7794 /* process the other fields */
7796 compose_template_apply_fields(compose, tmpl);
7797 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7798 quote_fmt_reset_vartable();
7799 compose_changed_cb(NULL, compose);
7802 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7804 MsgInfo* dummyinfo = NULL;
7805 MsgInfo *msginfo = NULL;
7808 if (compose->replyinfo != NULL)
7809 msginfo = compose->replyinfo;
7810 else if (compose->fwdinfo != NULL)
7811 msginfo = compose->fwdinfo;
7813 dummyinfo = compose_msginfo_new_from_compose(compose);
7814 msginfo = dummyinfo;
7817 if (tmpl->from && *tmpl->from != '\0') {
7819 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7820 compose->gtkaspell);
7822 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7824 quote_fmt_scan_string(tmpl->from);
7827 buf = quote_fmt_get_buffer();
7829 alertpanel_error(_("Template From format error."));
7831 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7835 if (tmpl->to && *tmpl->to != '\0') {
7837 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7838 compose->gtkaspell);
7840 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7842 quote_fmt_scan_string(tmpl->to);
7845 buf = quote_fmt_get_buffer();
7847 alertpanel_error(_("Template To format error."));
7849 compose_entry_append(compose, buf, COMPOSE_TO);
7853 if (tmpl->cc && *tmpl->cc != '\0') {
7855 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7856 compose->gtkaspell);
7858 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7860 quote_fmt_scan_string(tmpl->cc);
7863 buf = quote_fmt_get_buffer();
7865 alertpanel_error(_("Template Cc format error."));
7867 compose_entry_append(compose, buf, COMPOSE_CC);
7871 if (tmpl->bcc && *tmpl->bcc != '\0') {
7873 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7874 compose->gtkaspell);
7876 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7878 quote_fmt_scan_string(tmpl->bcc);
7881 buf = quote_fmt_get_buffer();
7883 alertpanel_error(_("Template Bcc format error."));
7885 compose_entry_append(compose, buf, COMPOSE_BCC);
7889 /* process the subject */
7890 if (tmpl->subject && *tmpl->subject != '\0') {
7892 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7893 compose->gtkaspell);
7895 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7897 quote_fmt_scan_string(tmpl->subject);
7900 buf = quote_fmt_get_buffer();
7902 alertpanel_error(_("Template subject format error."));
7904 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7908 procmsg_msginfo_free( dummyinfo );
7911 static void compose_destroy(Compose *compose)
7913 GtkTextBuffer *buffer;
7914 GtkClipboard *clipboard;
7916 compose_list = g_list_remove(compose_list, compose);
7918 if (compose->updating) {
7919 debug_print("danger, not destroying anything now\n");
7920 compose->deferred_destroy = TRUE;
7923 /* NOTE: address_completion_end() does nothing with the window
7924 * however this may change. */
7925 address_completion_end(compose->window);
7927 slist_free_strings(compose->to_list);
7928 g_slist_free(compose->to_list);
7929 slist_free_strings(compose->newsgroup_list);
7930 g_slist_free(compose->newsgroup_list);
7931 slist_free_strings(compose->header_list);
7932 g_slist_free(compose->header_list);
7934 procmsg_msginfo_free(compose->targetinfo);
7935 procmsg_msginfo_free(compose->replyinfo);
7936 procmsg_msginfo_free(compose->fwdinfo);
7938 g_free(compose->replyto);
7939 g_free(compose->cc);
7940 g_free(compose->bcc);
7941 g_free(compose->newsgroups);
7942 g_free(compose->followup_to);
7944 g_free(compose->ml_post);
7946 g_free(compose->inreplyto);
7947 g_free(compose->references);
7948 g_free(compose->msgid);
7949 g_free(compose->boundary);
7951 g_free(compose->redirect_filename);
7952 if (compose->undostruct)
7953 undo_destroy(compose->undostruct);
7955 g_free(compose->sig_str);
7957 g_free(compose->exteditor_file);
7959 g_free(compose->orig_charset);
7961 g_free(compose->privacy_system);
7963 if (addressbook_get_target_compose() == compose)
7964 addressbook_set_target_compose(NULL);
7967 if (compose->gtkaspell) {
7968 gtkaspell_delete(compose->gtkaspell);
7969 compose->gtkaspell = NULL;
7973 if (!compose->batch) {
7974 prefs_common.compose_width = compose->scrolledwin->allocation.width;
7975 prefs_common.compose_height = compose->window->allocation.height;
7978 if (!gtk_widget_get_parent(compose->paned))
7979 gtk_widget_destroy(compose->paned);
7980 gtk_widget_destroy(compose->popupmenu);
7982 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7983 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7984 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7986 gtk_widget_destroy(compose->window);
7987 toolbar_destroy(compose->toolbar);
7988 g_free(compose->toolbar);
7989 g_mutex_free(compose->mutex);
7993 static void compose_attach_info_free(AttachInfo *ainfo)
7995 g_free(ainfo->file);
7996 g_free(ainfo->content_type);
7997 g_free(ainfo->name);
8001 static void compose_attach_update_label(Compose *compose)
8006 GtkTreeModel *model;
8011 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8012 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8013 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8017 while(gtk_tree_model_iter_next(model, &iter))
8020 text = g_strdup_printf("(%d)", i);
8021 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8025 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8027 Compose *compose = (Compose *)data;
8028 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8029 GtkTreeSelection *selection;
8031 GtkTreeModel *model;
8033 selection = gtk_tree_view_get_selection(tree_view);
8034 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8039 for (cur = sel; cur != NULL; cur = cur->next) {
8040 GtkTreePath *path = cur->data;
8041 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8044 gtk_tree_path_free(path);
8047 for (cur = sel; cur != NULL; cur = cur->next) {
8048 GtkTreeRowReference *ref = cur->data;
8049 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8052 if (gtk_tree_model_get_iter(model, &iter, path))
8053 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8055 gtk_tree_path_free(path);
8056 gtk_tree_row_reference_free(ref);
8060 compose_attach_update_label(compose);
8063 static struct _AttachProperty
8066 GtkWidget *mimetype_entry;
8067 GtkWidget *encoding_optmenu;
8068 GtkWidget *path_entry;
8069 GtkWidget *filename_entry;
8071 GtkWidget *cancel_btn;
8074 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8076 gtk_tree_path_free((GtkTreePath *)ptr);
8079 static void compose_attach_property(GtkAction *action, gpointer data)
8081 Compose *compose = (Compose *)data;
8082 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8084 GtkComboBox *optmenu;
8085 GtkTreeSelection *selection;
8087 GtkTreeModel *model;
8090 static gboolean cancelled;
8092 /* only if one selected */
8093 selection = gtk_tree_view_get_selection(tree_view);
8094 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8097 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8101 path = (GtkTreePath *) sel->data;
8102 gtk_tree_model_get_iter(model, &iter, path);
8103 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8106 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8112 if (!attach_prop.window)
8113 compose_attach_property_create(&cancelled);
8114 gtk_widget_grab_focus(attach_prop.ok_btn);
8115 gtk_widget_show(attach_prop.window);
8116 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8118 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8119 if (ainfo->encoding == ENC_UNKNOWN)
8120 combobox_select_by_data(optmenu, ENC_BASE64);
8122 combobox_select_by_data(optmenu, ainfo->encoding);
8124 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8125 ainfo->content_type ? ainfo->content_type : "");
8126 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8127 ainfo->file ? ainfo->file : "");
8128 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8129 ainfo->name ? ainfo->name : "");
8132 const gchar *entry_text;
8134 gchar *cnttype = NULL;
8141 gtk_widget_hide(attach_prop.window);
8146 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8147 if (*entry_text != '\0') {
8150 text = g_strstrip(g_strdup(entry_text));
8151 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8152 cnttype = g_strdup(text);
8155 alertpanel_error(_("Invalid MIME type."));
8161 ainfo->encoding = combobox_get_active_data(optmenu);
8163 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8164 if (*entry_text != '\0') {
8165 if (is_file_exist(entry_text) &&
8166 (size = get_file_size(entry_text)) > 0)
8167 file = g_strdup(entry_text);
8170 (_("File doesn't exist or is empty."));
8176 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8177 if (*entry_text != '\0') {
8178 g_free(ainfo->name);
8179 ainfo->name = g_strdup(entry_text);
8183 g_free(ainfo->content_type);
8184 ainfo->content_type = cnttype;
8187 g_free(ainfo->file);
8191 ainfo->size = (goffset)size;
8193 /* update tree store */
8194 text = to_human_readable(ainfo->size);
8195 gtk_tree_model_get_iter(model, &iter, path);
8196 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8197 COL_MIMETYPE, ainfo->content_type,
8199 COL_NAME, ainfo->name,
8205 gtk_tree_path_free(path);
8208 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8210 label = gtk_label_new(str); \
8211 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8212 GTK_FILL, 0, 0, 0); \
8213 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8215 entry = gtk_entry_new(); \
8216 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8217 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8220 static void compose_attach_property_create(gboolean *cancelled)
8226 GtkWidget *mimetype_entry;
8229 GtkListStore *optmenu_menu;
8230 GtkWidget *path_entry;
8231 GtkWidget *filename_entry;
8234 GtkWidget *cancel_btn;
8235 GList *mime_type_list, *strlist;
8238 debug_print("Creating attach_property window...\n");
8240 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8241 gtk_widget_set_size_request(window, 480, -1);
8242 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8243 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8244 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8245 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8246 g_signal_connect(G_OBJECT(window), "delete_event",
8247 G_CALLBACK(attach_property_delete_event),
8249 g_signal_connect(G_OBJECT(window), "key_press_event",
8250 G_CALLBACK(attach_property_key_pressed),
8253 vbox = gtk_vbox_new(FALSE, 8);
8254 gtk_container_add(GTK_CONTAINER(window), vbox);
8256 table = gtk_table_new(4, 2, FALSE);
8257 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8258 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8259 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8261 label = gtk_label_new(_("MIME type"));
8262 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8264 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8265 mimetype_entry = gtk_combo_box_entry_new_text();
8266 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8267 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8269 /* stuff with list */
8270 mime_type_list = procmime_get_mime_type_list();
8272 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8273 MimeType *type = (MimeType *) mime_type_list->data;
8276 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8278 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8281 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8282 (GCompareFunc)strcmp2);
8285 for (mime_type_list = strlist; mime_type_list != NULL;
8286 mime_type_list = mime_type_list->next) {
8287 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8288 g_free(mime_type_list->data);
8290 g_list_free(strlist);
8291 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8292 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8294 label = gtk_label_new(_("Encoding"));
8295 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8297 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8299 hbox = gtk_hbox_new(FALSE, 0);
8300 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8301 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8303 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8304 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8306 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8307 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8308 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8309 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8310 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8312 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8314 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8315 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8317 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8318 &ok_btn, GTK_STOCK_OK,
8320 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8321 gtk_widget_grab_default(ok_btn);
8323 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8324 G_CALLBACK(attach_property_ok),
8326 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8327 G_CALLBACK(attach_property_cancel),
8330 gtk_widget_show_all(vbox);
8332 attach_prop.window = window;
8333 attach_prop.mimetype_entry = mimetype_entry;
8334 attach_prop.encoding_optmenu = optmenu;
8335 attach_prop.path_entry = path_entry;
8336 attach_prop.filename_entry = filename_entry;
8337 attach_prop.ok_btn = ok_btn;
8338 attach_prop.cancel_btn = cancel_btn;
8341 #undef SET_LABEL_AND_ENTRY
8343 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8349 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8355 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8356 gboolean *cancelled)
8364 static gboolean attach_property_key_pressed(GtkWidget *widget,
8366 gboolean *cancelled)
8368 if (event && event->keyval == GDK_Escape) {
8375 static void compose_exec_ext_editor(Compose *compose)
8382 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8383 G_DIR_SEPARATOR, compose);
8385 if (pipe(pipe_fds) < 0) {
8391 if ((pid = fork()) < 0) {
8398 /* close the write side of the pipe */
8401 compose->exteditor_file = g_strdup(tmp);
8402 compose->exteditor_pid = pid;
8404 compose_set_ext_editor_sensitive(compose, FALSE);
8407 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8409 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8411 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8415 } else { /* process-monitoring process */
8421 /* close the read side of the pipe */
8424 if (compose_write_body_to_file(compose, tmp) < 0) {
8425 fd_write_all(pipe_fds[1], "2\n", 2);
8429 pid_ed = compose_exec_ext_editor_real(tmp);
8431 fd_write_all(pipe_fds[1], "1\n", 2);
8435 /* wait until editor is terminated */
8436 waitpid(pid_ed, NULL, 0);
8438 fd_write_all(pipe_fds[1], "0\n", 2);
8445 #endif /* G_OS_UNIX */
8449 static gint compose_exec_ext_editor_real(const gchar *file)
8456 g_return_val_if_fail(file != NULL, -1);
8458 if ((pid = fork()) < 0) {
8463 if (pid != 0) return pid;
8465 /* grandchild process */
8467 if (setpgid(0, getppid()))
8470 if (prefs_common_get_ext_editor_cmd() &&
8471 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8472 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8473 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8475 if (prefs_common_get_ext_editor_cmd())
8476 g_warning("External editor command line is invalid: '%s'\n",
8477 prefs_common_get_ext_editor_cmd());
8478 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8481 cmdline = strsplit_with_quote(buf, " ", 1024);
8482 execvp(cmdline[0], cmdline);
8485 g_strfreev(cmdline);
8490 static gboolean compose_ext_editor_kill(Compose *compose)
8492 pid_t pgid = compose->exteditor_pid * -1;
8495 ret = kill(pgid, 0);
8497 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8501 msg = g_strdup_printf
8502 (_("The external editor is still working.\n"
8503 "Force terminating the process?\n"
8504 "process group id: %d"), -pgid);
8505 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8506 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8510 if (val == G_ALERTALTERNATE) {
8511 g_source_remove(compose->exteditor_tag);
8512 g_io_channel_shutdown(compose->exteditor_ch,
8514 g_io_channel_unref(compose->exteditor_ch);
8516 if (kill(pgid, SIGTERM) < 0) perror("kill");
8517 waitpid(compose->exteditor_pid, NULL, 0);
8519 g_warning("Terminated process group id: %d", -pgid);
8520 g_warning("Temporary file: %s",
8521 compose->exteditor_file);
8523 compose_set_ext_editor_sensitive(compose, TRUE);
8525 g_free(compose->exteditor_file);
8526 compose->exteditor_file = NULL;
8527 compose->exteditor_pid = -1;
8528 compose->exteditor_ch = NULL;
8529 compose->exteditor_tag = -1;
8537 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8541 Compose *compose = (Compose *)data;
8544 debug_print(_("Compose: input from monitoring process\n"));
8546 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8548 g_io_channel_shutdown(source, FALSE, NULL);
8549 g_io_channel_unref(source);
8551 waitpid(compose->exteditor_pid, NULL, 0);
8553 if (buf[0] == '0') { /* success */
8554 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8555 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8557 gtk_text_buffer_set_text(buffer, "", -1);
8558 compose_insert_file(compose, compose->exteditor_file);
8559 compose_changed_cb(NULL, compose);
8561 if (claws_unlink(compose->exteditor_file) < 0)
8562 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8563 } else if (buf[0] == '1') { /* failed */
8564 g_warning("Couldn't exec external editor\n");
8565 if (claws_unlink(compose->exteditor_file) < 0)
8566 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8567 } else if (buf[0] == '2') {
8568 g_warning("Couldn't write to file\n");
8569 } else if (buf[0] == '3') {
8570 g_warning("Pipe read failed\n");
8573 compose_set_ext_editor_sensitive(compose, TRUE);
8575 g_free(compose->exteditor_file);
8576 compose->exteditor_file = NULL;
8577 compose->exteditor_pid = -1;
8578 compose->exteditor_ch = NULL;
8579 compose->exteditor_tag = -1;
8584 static void compose_set_ext_editor_sensitive(Compose *compose,
8587 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8588 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8589 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8590 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8591 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8592 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8595 gtk_widget_set_sensitive(compose->text, sensitive);
8596 if (compose->toolbar->send_btn)
8597 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8598 if (compose->toolbar->sendl_btn)
8599 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8600 if (compose->toolbar->draft_btn)
8601 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8602 if (compose->toolbar->insert_btn)
8603 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8604 if (compose->toolbar->sig_btn)
8605 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8606 if (compose->toolbar->exteditor_btn)
8607 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8608 if (compose->toolbar->linewrap_current_btn)
8609 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8610 if (compose->toolbar->linewrap_all_btn)
8611 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8613 #endif /* G_OS_UNIX */
8616 * compose_undo_state_changed:
8618 * Change the sensivity of the menuentries undo and redo
8620 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8621 gint redo_state, gpointer data)
8623 Compose *compose = (Compose *)data;
8625 switch (undo_state) {
8626 case UNDO_STATE_TRUE:
8627 if (!undostruct->undo_state) {
8628 undostruct->undo_state = TRUE;
8629 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8632 case UNDO_STATE_FALSE:
8633 if (undostruct->undo_state) {
8634 undostruct->undo_state = FALSE;
8635 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8638 case UNDO_STATE_UNCHANGED:
8640 case UNDO_STATE_REFRESH:
8641 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8644 g_warning("Undo state not recognized");
8648 switch (redo_state) {
8649 case UNDO_STATE_TRUE:
8650 if (!undostruct->redo_state) {
8651 undostruct->redo_state = TRUE;
8652 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8655 case UNDO_STATE_FALSE:
8656 if (undostruct->redo_state) {
8657 undostruct->redo_state = FALSE;
8658 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8661 case UNDO_STATE_UNCHANGED:
8663 case UNDO_STATE_REFRESH:
8664 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8667 g_warning("Redo state not recognized");
8672 /* callback functions */
8674 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8675 * includes "non-client" (windows-izm) in calculation, so this calculation
8676 * may not be accurate.
8678 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8679 GtkAllocation *allocation,
8680 GtkSHRuler *shruler)
8682 if (prefs_common.show_ruler) {
8683 gint char_width = 0, char_height = 0;
8684 gint line_width_in_chars;
8686 gtkut_get_font_size(GTK_WIDGET(widget),
8687 &char_width, &char_height);
8688 line_width_in_chars =
8689 (allocation->width - allocation->x) / char_width;
8691 /* got the maximum */
8692 gtk_ruler_set_range(GTK_RULER(shruler),
8693 0.0, line_width_in_chars, 0,
8694 /*line_width_in_chars*/ char_width);
8700 static void account_activated(GtkComboBox *optmenu, gpointer data)
8702 Compose *compose = (Compose *)data;
8705 gchar *folderidentifier;
8706 gint account_id = 0;
8710 /* Get ID of active account in the combo box */
8711 menu = gtk_combo_box_get_model(optmenu);
8712 gtk_combo_box_get_active_iter(optmenu, &iter);
8713 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8715 ac = account_find_from_id(account_id);
8716 g_return_if_fail(ac != NULL);
8718 if (ac != compose->account)
8719 compose_select_account(compose, ac, FALSE);
8721 /* Set message save folder */
8722 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8723 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8725 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8726 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8728 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8729 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8730 folderidentifier = folder_item_get_identifier(account_get_special_folder
8731 (compose->account, F_OUTBOX));
8732 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8733 g_free(folderidentifier);
8737 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8738 GtkTreeViewColumn *column, Compose *compose)
8740 compose_attach_property(NULL, compose);
8743 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8746 Compose *compose = (Compose *)data;
8747 GtkTreeSelection *attach_selection;
8748 gint attach_nr_selected;
8750 if (!event) return FALSE;
8752 if (event->button == 3) {
8753 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8754 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8756 if (attach_nr_selected > 0)
8758 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8759 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8761 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8762 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8765 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8766 NULL, NULL, event->button, event->time);
8773 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8776 Compose *compose = (Compose *)data;
8778 if (!event) return FALSE;
8780 switch (event->keyval) {
8782 compose_attach_remove_selected(NULL, compose);
8788 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8790 toolbar_comp_set_sensitive(compose, allow);
8791 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8792 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8794 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8796 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8797 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8798 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8800 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8804 static void compose_send_cb(GtkAction *action, gpointer data)
8806 Compose *compose = (Compose *)data;
8808 if (prefs_common.work_offline &&
8809 !inc_offline_should_override(TRUE,
8810 _("Claws Mail needs network access in order "
8811 "to send this email.")))
8814 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8815 g_source_remove(compose->draft_timeout_tag);
8816 compose->draft_timeout_tag = -1;
8819 compose_send(compose);
8822 static void compose_send_later_cb(GtkAction *action, gpointer data)
8824 Compose *compose = (Compose *)data;
8828 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8832 compose_close(compose);
8833 } else if (val == -1) {
8834 alertpanel_error(_("Could not queue message."));
8835 } else if (val == -2) {
8836 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8837 } else if (val == -3) {
8838 if (privacy_peek_error())
8839 alertpanel_error(_("Could not queue message for sending:\n\n"
8840 "Signature failed: %s"), privacy_get_error());
8841 } else if (val == -4) {
8842 alertpanel_error(_("Could not queue message for sending:\n\n"
8843 "Charset conversion failed."));
8844 } else if (val == -5) {
8845 alertpanel_error(_("Could not queue message for sending:\n\n"
8846 "Couldn't get recipient encryption key."));
8847 } else if (val == -6) {
8850 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8853 #define DRAFTED_AT_EXIT "drafted_at_exit"
8854 static void compose_register_draft(MsgInfo *info)
8856 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8857 DRAFTED_AT_EXIT, NULL);
8858 FILE *fp = g_fopen(filepath, "ab");
8861 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8869 gboolean compose_draft (gpointer data, guint action)
8871 Compose *compose = (Compose *)data;
8875 MsgFlags flag = {0, 0};
8876 static gboolean lock = FALSE;
8877 MsgInfo *newmsginfo;
8879 gboolean target_locked = FALSE;
8880 gboolean err = FALSE;
8882 if (lock) return FALSE;
8884 if (compose->sending)
8887 draft = account_get_special_folder(compose->account, F_DRAFT);
8888 g_return_val_if_fail(draft != NULL, FALSE);
8890 if (!g_mutex_trylock(compose->mutex)) {
8891 /* we don't want to lock the mutex once it's available,
8892 * because as the only other part of compose.c locking
8893 * it is compose_close - which means once unlocked,
8894 * the compose struct will be freed */
8895 debug_print("couldn't lock mutex, probably sending\n");
8901 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8902 G_DIR_SEPARATOR, compose);
8903 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8904 FILE_OP_ERROR(tmp, "fopen");
8908 /* chmod for security */
8909 if (change_file_mode_rw(fp, tmp) < 0) {
8910 FILE_OP_ERROR(tmp, "chmod");
8911 g_warning("can't change file mode\n");
8914 /* Save draft infos */
8915 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8916 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8918 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8919 gchar *savefolderid;
8921 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8922 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8923 g_free(savefolderid);
8925 if (compose->return_receipt) {
8926 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8928 if (compose->privacy_system) {
8929 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8930 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8931 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8934 /* Message-ID of message replying to */
8935 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8938 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8939 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8942 /* Message-ID of message forwarding to */
8943 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8946 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8947 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8951 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
8952 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
8954 /* end of headers */
8955 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8962 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8966 if (fclose(fp) == EOF) {
8970 if (compose->targetinfo) {
8971 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8972 flag.perm_flags = target_locked?MSG_LOCKED:0;
8974 flag.tmp_flags = MSG_DRAFT;
8976 folder_item_scan(draft);
8977 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8978 MsgInfo *tmpinfo = NULL;
8979 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8980 if (compose->msgid) {
8981 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8984 msgnum = tmpinfo->msgnum;
8985 procmsg_msginfo_free(tmpinfo);
8986 debug_print("got draft msgnum %d from scanning\n", msgnum);
8988 debug_print("didn't get draft msgnum after scanning\n");
8991 debug_print("got draft msgnum %d from adding\n", msgnum);
8997 if (action != COMPOSE_AUTO_SAVE) {
8998 if (action != COMPOSE_DRAFT_FOR_EXIT)
8999 alertpanel_error(_("Could not save draft."));
9002 gtkut_window_popup(compose->window);
9003 val = alertpanel_full(_("Could not save draft"),
9004 _("Could not save draft.\n"
9005 "Do you want to cancel exit or discard this email?"),
9006 _("_Cancel exit"), _("_Discard email"), NULL,
9007 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9008 if (val == G_ALERTALTERNATE) {
9010 g_mutex_unlock(compose->mutex); /* must be done before closing */
9011 compose_close(compose);
9015 g_mutex_unlock(compose->mutex); /* must be done before closing */
9024 if (compose->mode == COMPOSE_REEDIT) {
9025 compose_remove_reedit_target(compose, TRUE);
9028 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9031 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9033 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9035 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9036 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9037 procmsg_msginfo_set_flags(newmsginfo, 0,
9038 MSG_HAS_ATTACHMENT);
9040 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9041 compose_register_draft(newmsginfo);
9043 procmsg_msginfo_free(newmsginfo);
9046 folder_item_scan(draft);
9048 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9050 g_mutex_unlock(compose->mutex); /* must be done before closing */
9051 compose_close(compose);
9057 path = folder_item_fetch_msg(draft, msgnum);
9059 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9062 if (g_stat(path, &s) < 0) {
9063 FILE_OP_ERROR(path, "stat");
9069 procmsg_msginfo_free(compose->targetinfo);
9070 compose->targetinfo = procmsg_msginfo_new();
9071 compose->targetinfo->msgnum = msgnum;
9072 compose->targetinfo->size = (goffset)s.st_size;
9073 compose->targetinfo->mtime = s.st_mtime;
9074 compose->targetinfo->folder = draft;
9076 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9077 compose->mode = COMPOSE_REEDIT;
9079 if (action == COMPOSE_AUTO_SAVE) {
9080 compose->autosaved_draft = compose->targetinfo;
9082 compose->modified = FALSE;
9083 compose_set_title(compose);
9087 g_mutex_unlock(compose->mutex);
9091 void compose_clear_exit_drafts(void)
9093 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9094 DRAFTED_AT_EXIT, NULL);
9095 if (is_file_exist(filepath))
9096 claws_unlink(filepath);
9101 void compose_reopen_exit_drafts(void)
9103 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9104 DRAFTED_AT_EXIT, NULL);
9105 FILE *fp = g_fopen(filepath, "rb");
9109 while (fgets(buf, sizeof(buf), fp)) {
9110 gchar **parts = g_strsplit(buf, "\t", 2);
9111 const gchar *folder = parts[0];
9112 int msgnum = parts[1] ? atoi(parts[1]):-1;
9114 if (folder && *folder && msgnum > -1) {
9115 FolderItem *item = folder_find_item_from_identifier(folder);
9116 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9118 compose_reedit(info, FALSE);
9125 compose_clear_exit_drafts();
9128 static void compose_save_cb(GtkAction *action, gpointer data)
9130 compose_draft(data, COMPOSE_KEEP_EDITING);
9133 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9135 if (compose && file_list) {
9138 for ( tmp = file_list; tmp; tmp = tmp->next) {
9139 gchar *file = (gchar *) tmp->data;
9140 gchar *utf8_filename = conv_filename_to_utf8(file);
9141 compose_attach_append(compose, file, utf8_filename, NULL);
9142 compose_changed_cb(NULL, compose);
9147 g_free(utf8_filename);
9152 static void compose_attach_cb(GtkAction *action, gpointer data)
9154 Compose *compose = (Compose *)data;
9157 if (compose->redirect_filename != NULL)
9160 file_list = filesel_select_multiple_files_open(_("Select file"));
9163 compose_attach_from_list(compose, file_list, TRUE);
9164 g_list_free(file_list);
9168 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9170 Compose *compose = (Compose *)data;
9173 file_list = filesel_select_multiple_files_open(_("Select file"));
9178 for ( tmp = file_list; tmp; tmp = tmp->next) {
9179 gchar *file = (gchar *) tmp->data;
9180 gchar *filedup = g_strdup(file);
9181 gchar *shortfile = g_path_get_basename(filedup);
9182 ComposeInsertResult res;
9184 res = compose_insert_file(compose, file);
9185 if (res == COMPOSE_INSERT_READ_ERROR) {
9186 alertpanel_error(_("File '%s' could not be read."), shortfile);
9187 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9188 alertpanel_error(_("File '%s' contained invalid characters\n"
9189 "for the current encoding, insertion may be incorrect."), shortfile);
9195 g_list_free(file_list);
9199 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9201 Compose *compose = (Compose *)data;
9203 compose_insert_sig(compose, FALSE);
9206 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9210 Compose *compose = (Compose *)data;
9212 gtkut_widget_get_uposition(widget, &x, &y);
9213 if (!compose->batch) {
9214 prefs_common.compose_x = x;
9215 prefs_common.compose_y = y;
9217 if (compose->sending || compose->updating)
9219 compose_close_cb(NULL, compose);
9223 void compose_close_toolbar(Compose *compose)
9225 compose_close_cb(NULL, compose);
9228 static void compose_close_cb(GtkAction *action, gpointer data)
9230 Compose *compose = (Compose *)data;
9234 if (compose->exteditor_tag != -1) {
9235 if (!compose_ext_editor_kill(compose))
9240 if (compose->modified) {
9241 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9242 if (!g_mutex_trylock(compose->mutex)) {
9243 /* we don't want to lock the mutex once it's available,
9244 * because as the only other part of compose.c locking
9245 * it is compose_close - which means once unlocked,
9246 * the compose struct will be freed */
9247 debug_print("couldn't lock mutex, probably sending\n");
9251 val = alertpanel(_("Discard message"),
9252 _("This message has been modified. Discard it?"),
9253 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9255 val = alertpanel(_("Save changes"),
9256 _("This message has been modified. Save the latest changes?"),
9257 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9259 g_mutex_unlock(compose->mutex);
9261 case G_ALERTDEFAULT:
9262 if (prefs_common.autosave && !reedit)
9263 compose_remove_draft(compose);
9265 case G_ALERTALTERNATE:
9266 compose_draft(data, COMPOSE_QUIT_EDITING);
9273 compose_close(compose);
9276 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9278 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9279 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9280 Compose *compose = (Compose *) data;
9283 compose->out_encoding = (CharSet)value;
9286 static void compose_address_cb(GtkAction *action, gpointer data)
9288 Compose *compose = (Compose *)data;
9290 addressbook_open(compose);
9293 static void about_show_cb(GtkAction *action, gpointer data)
9298 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9300 Compose *compose = (Compose *)data;
9305 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9306 g_return_if_fail(tmpl != NULL);
9308 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9310 val = alertpanel(_("Apply template"), msg,
9311 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9314 if (val == G_ALERTDEFAULT)
9315 compose_template_apply(compose, tmpl, TRUE);
9316 else if (val == G_ALERTALTERNATE)
9317 compose_template_apply(compose, tmpl, FALSE);
9320 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9322 Compose *compose = (Compose *)data;
9324 compose_exec_ext_editor(compose);
9327 static void compose_undo_cb(GtkAction *action, gpointer data)
9329 Compose *compose = (Compose *)data;
9330 gboolean prev_autowrap = compose->autowrap;
9332 compose->autowrap = FALSE;
9333 undo_undo(compose->undostruct);
9334 compose->autowrap = prev_autowrap;
9337 static void compose_redo_cb(GtkAction *action, gpointer data)
9339 Compose *compose = (Compose *)data;
9340 gboolean prev_autowrap = compose->autowrap;
9342 compose->autowrap = FALSE;
9343 undo_redo(compose->undostruct);
9344 compose->autowrap = prev_autowrap;
9347 static void entry_cut_clipboard(GtkWidget *entry)
9349 if (GTK_IS_EDITABLE(entry))
9350 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9351 else if (GTK_IS_TEXT_VIEW(entry))
9352 gtk_text_buffer_cut_clipboard(
9353 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9354 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9358 static void entry_copy_clipboard(GtkWidget *entry)
9360 if (GTK_IS_EDITABLE(entry))
9361 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9362 else if (GTK_IS_TEXT_VIEW(entry))
9363 gtk_text_buffer_copy_clipboard(
9364 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9365 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9368 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9369 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9371 if (GTK_IS_TEXT_VIEW(entry)) {
9372 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9373 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9374 GtkTextIter start_iter, end_iter;
9376 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9378 if (contents == NULL)
9381 /* we shouldn't delete the selection when middle-click-pasting, or we
9382 * can't mid-click-paste our own selection */
9383 if (clip != GDK_SELECTION_PRIMARY) {
9384 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9385 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9388 if (insert_place == NULL) {
9389 /* if insert_place isn't specified, insert at the cursor.
9390 * used for Ctrl-V pasting */
9391 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9392 start = gtk_text_iter_get_offset(&start_iter);
9393 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9395 /* if insert_place is specified, paste here.
9396 * used for mid-click-pasting */
9397 start = gtk_text_iter_get_offset(insert_place);
9398 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9402 /* paste unwrapped: mark the paste so it's not wrapped later */
9403 end = start + strlen(contents);
9404 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9405 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9406 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9407 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9408 /* rewrap paragraph now (after a mid-click-paste) */
9409 mark_start = gtk_text_buffer_get_insert(buffer);
9410 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9411 gtk_text_iter_backward_char(&start_iter);
9412 compose_beautify_paragraph(compose, &start_iter, TRUE);
9414 } else if (GTK_IS_EDITABLE(entry))
9415 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9417 compose->modified = TRUE;
9420 static void entry_allsel(GtkWidget *entry)
9422 if (GTK_IS_EDITABLE(entry))
9423 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9424 else if (GTK_IS_TEXT_VIEW(entry)) {
9425 GtkTextIter startiter, enditer;
9426 GtkTextBuffer *textbuf;
9428 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9429 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9430 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9432 gtk_text_buffer_move_mark_by_name(textbuf,
9433 "selection_bound", &startiter);
9434 gtk_text_buffer_move_mark_by_name(textbuf,
9435 "insert", &enditer);
9439 static void compose_cut_cb(GtkAction *action, gpointer data)
9441 Compose *compose = (Compose *)data;
9442 if (compose->focused_editable
9443 #ifndef GENERIC_UMPC
9444 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9447 entry_cut_clipboard(compose->focused_editable);
9450 static void compose_copy_cb(GtkAction *action, gpointer data)
9452 Compose *compose = (Compose *)data;
9453 if (compose->focused_editable
9454 #ifndef GENERIC_UMPC
9455 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9458 entry_copy_clipboard(compose->focused_editable);
9461 static void compose_paste_cb(GtkAction *action, gpointer data)
9463 Compose *compose = (Compose *)data;
9465 GtkTextBuffer *buffer;
9467 if (compose->focused_editable &&
9468 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9469 entry_paste_clipboard(compose, compose->focused_editable,
9470 prefs_common.linewrap_pastes,
9471 GDK_SELECTION_CLIPBOARD, NULL);
9475 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9477 Compose *compose = (Compose *)data;
9478 gint wrap_quote = prefs_common.linewrap_quote;
9479 if (compose->focused_editable
9480 #ifndef GENERIC_UMPC
9481 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9484 /* let text_insert() (called directly or at a later time
9485 * after the gtk_editable_paste_clipboard) know that
9486 * text is to be inserted as a quotation. implemented
9487 * by using a simple refcount... */
9488 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9489 G_OBJECT(compose->focused_editable),
9490 "paste_as_quotation"));
9491 g_object_set_data(G_OBJECT(compose->focused_editable),
9492 "paste_as_quotation",
9493 GINT_TO_POINTER(paste_as_quotation + 1));
9494 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9495 entry_paste_clipboard(compose, compose->focused_editable,
9496 prefs_common.linewrap_pastes,
9497 GDK_SELECTION_CLIPBOARD, NULL);
9498 prefs_common.linewrap_quote = wrap_quote;
9502 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9504 Compose *compose = (Compose *)data;
9506 GtkTextBuffer *buffer;
9508 if (compose->focused_editable
9509 #ifndef GENERIC_UMPC
9510 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9513 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9514 GDK_SELECTION_CLIPBOARD, NULL);
9518 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9520 Compose *compose = (Compose *)data;
9522 GtkTextBuffer *buffer;
9524 if (compose->focused_editable
9525 #ifndef GENERIC_UMPC
9526 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9529 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9530 GDK_SELECTION_CLIPBOARD, NULL);
9534 static void compose_allsel_cb(GtkAction *action, gpointer data)
9536 Compose *compose = (Compose *)data;
9537 if (compose->focused_editable
9538 #ifndef GENERIC_UMPC
9539 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9542 entry_allsel(compose->focused_editable);
9545 static void textview_move_beginning_of_line (GtkTextView *text)
9547 GtkTextBuffer *buffer;
9551 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9553 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9554 mark = gtk_text_buffer_get_insert(buffer);
9555 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9556 gtk_text_iter_set_line_offset(&ins, 0);
9557 gtk_text_buffer_place_cursor(buffer, &ins);
9560 static void textview_move_forward_character (GtkTextView *text)
9562 GtkTextBuffer *buffer;
9566 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9568 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9569 mark = gtk_text_buffer_get_insert(buffer);
9570 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9571 if (gtk_text_iter_forward_cursor_position(&ins))
9572 gtk_text_buffer_place_cursor(buffer, &ins);
9575 static void textview_move_backward_character (GtkTextView *text)
9577 GtkTextBuffer *buffer;
9581 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9583 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9584 mark = gtk_text_buffer_get_insert(buffer);
9585 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9586 if (gtk_text_iter_backward_cursor_position(&ins))
9587 gtk_text_buffer_place_cursor(buffer, &ins);
9590 static void textview_move_forward_word (GtkTextView *text)
9592 GtkTextBuffer *buffer;
9597 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9599 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9600 mark = gtk_text_buffer_get_insert(buffer);
9601 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9602 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9603 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9604 gtk_text_iter_backward_word_start(&ins);
9605 gtk_text_buffer_place_cursor(buffer, &ins);
9609 static void textview_move_backward_word (GtkTextView *text)
9611 GtkTextBuffer *buffer;
9616 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9618 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9619 mark = gtk_text_buffer_get_insert(buffer);
9620 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9621 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9622 if (gtk_text_iter_backward_word_starts(&ins, 1))
9623 gtk_text_buffer_place_cursor(buffer, &ins);
9626 static void textview_move_end_of_line (GtkTextView *text)
9628 GtkTextBuffer *buffer;
9632 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9634 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9635 mark = gtk_text_buffer_get_insert(buffer);
9636 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9637 if (gtk_text_iter_forward_to_line_end(&ins))
9638 gtk_text_buffer_place_cursor(buffer, &ins);
9641 static void textview_move_next_line (GtkTextView *text)
9643 GtkTextBuffer *buffer;
9648 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9650 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9651 mark = gtk_text_buffer_get_insert(buffer);
9652 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9653 offset = gtk_text_iter_get_line_offset(&ins);
9654 if (gtk_text_iter_forward_line(&ins)) {
9655 gtk_text_iter_set_line_offset(&ins, offset);
9656 gtk_text_buffer_place_cursor(buffer, &ins);
9660 static void textview_move_previous_line (GtkTextView *text)
9662 GtkTextBuffer *buffer;
9667 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9669 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9670 mark = gtk_text_buffer_get_insert(buffer);
9671 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9672 offset = gtk_text_iter_get_line_offset(&ins);
9673 if (gtk_text_iter_backward_line(&ins)) {
9674 gtk_text_iter_set_line_offset(&ins, offset);
9675 gtk_text_buffer_place_cursor(buffer, &ins);
9679 static void textview_delete_forward_character (GtkTextView *text)
9681 GtkTextBuffer *buffer;
9683 GtkTextIter ins, end_iter;
9685 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9687 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9688 mark = gtk_text_buffer_get_insert(buffer);
9689 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9691 if (gtk_text_iter_forward_char(&end_iter)) {
9692 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9696 static void textview_delete_backward_character (GtkTextView *text)
9698 GtkTextBuffer *buffer;
9700 GtkTextIter ins, end_iter;
9702 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9704 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9705 mark = gtk_text_buffer_get_insert(buffer);
9706 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9708 if (gtk_text_iter_backward_char(&end_iter)) {
9709 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9713 static void textview_delete_forward_word (GtkTextView *text)
9715 GtkTextBuffer *buffer;
9717 GtkTextIter ins, end_iter;
9719 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9721 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9722 mark = gtk_text_buffer_get_insert(buffer);
9723 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9725 if (gtk_text_iter_forward_word_end(&end_iter)) {
9726 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9730 static void textview_delete_backward_word (GtkTextView *text)
9732 GtkTextBuffer *buffer;
9734 GtkTextIter ins, end_iter;
9736 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9738 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9739 mark = gtk_text_buffer_get_insert(buffer);
9740 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9742 if (gtk_text_iter_backward_word_start(&end_iter)) {
9743 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9747 static void textview_delete_line (GtkTextView *text)
9749 GtkTextBuffer *buffer;
9751 GtkTextIter ins, start_iter, end_iter;
9753 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9755 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9756 mark = gtk_text_buffer_get_insert(buffer);
9757 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9760 gtk_text_iter_set_line_offset(&start_iter, 0);
9763 if (gtk_text_iter_ends_line(&end_iter)){
9764 if (!gtk_text_iter_forward_char(&end_iter))
9765 gtk_text_iter_backward_char(&start_iter);
9768 gtk_text_iter_forward_to_line_end(&end_iter);
9769 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9772 static void textview_delete_to_line_end (GtkTextView *text)
9774 GtkTextBuffer *buffer;
9776 GtkTextIter ins, end_iter;
9778 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9780 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9781 mark = gtk_text_buffer_get_insert(buffer);
9782 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9784 if (gtk_text_iter_ends_line(&end_iter))
9785 gtk_text_iter_forward_char(&end_iter);
9787 gtk_text_iter_forward_to_line_end(&end_iter);
9788 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9791 #define DO_ACTION(name, act) { \
9792 if(!strcmp(name, a_name)) { \
9796 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9798 const gchar *a_name = gtk_action_get_name(action);
9799 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9800 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9801 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9802 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9803 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9804 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9805 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9806 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9807 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9808 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9809 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9810 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9811 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9812 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9816 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9818 Compose *compose = (Compose *)data;
9819 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9820 ComposeCallAdvancedAction action = -1;
9822 action = compose_call_advanced_action_from_path(gaction);
9825 void (*do_action) (GtkTextView *text);
9826 } action_table[] = {
9827 {textview_move_beginning_of_line},
9828 {textview_move_forward_character},
9829 {textview_move_backward_character},
9830 {textview_move_forward_word},
9831 {textview_move_backward_word},
9832 {textview_move_end_of_line},
9833 {textview_move_next_line},
9834 {textview_move_previous_line},
9835 {textview_delete_forward_character},
9836 {textview_delete_backward_character},
9837 {textview_delete_forward_word},
9838 {textview_delete_backward_word},
9839 {textview_delete_line},
9840 {textview_delete_to_line_end}
9843 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9845 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9846 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9847 if (action_table[action].do_action)
9848 action_table[action].do_action(text);
9850 g_warning("Not implemented yet.");
9854 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9858 if (GTK_IS_EDITABLE(widget)) {
9859 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9860 gtk_editable_set_position(GTK_EDITABLE(widget),
9863 if (widget->parent && widget->parent->parent
9864 && widget->parent->parent->parent) {
9865 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9866 gint y = widget->allocation.y;
9867 gint height = widget->allocation.height;
9868 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9869 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9871 if (y < (int)shown->value) {
9872 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9874 if (y + height > (int)shown->value + (int)shown->page_size) {
9875 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9876 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9877 y + height - (int)shown->page_size - 1);
9879 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9880 (int)shown->upper - (int)shown->page_size - 1);
9887 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9888 compose->focused_editable = widget;
9891 if (GTK_IS_TEXT_VIEW(widget)
9892 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9893 g_object_ref(compose->notebook);
9894 g_object_ref(compose->edit_vbox);
9895 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9896 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9897 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9898 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9899 g_object_unref(compose->notebook);
9900 g_object_unref(compose->edit_vbox);
9901 g_signal_handlers_block_by_func(G_OBJECT(widget),
9902 G_CALLBACK(compose_grab_focus_cb),
9904 gtk_widget_grab_focus(widget);
9905 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9906 G_CALLBACK(compose_grab_focus_cb),
9908 } else if (!GTK_IS_TEXT_VIEW(widget)
9909 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9910 g_object_ref(compose->notebook);
9911 g_object_ref(compose->edit_vbox);
9912 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9913 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9914 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9915 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9916 g_object_unref(compose->notebook);
9917 g_object_unref(compose->edit_vbox);
9918 g_signal_handlers_block_by_func(G_OBJECT(widget),
9919 G_CALLBACK(compose_grab_focus_cb),
9921 gtk_widget_grab_focus(widget);
9922 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9923 G_CALLBACK(compose_grab_focus_cb),
9929 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9931 compose->modified = TRUE;
9932 #ifndef GENERIC_UMPC
9933 compose_set_title(compose);
9937 static void compose_wrap_cb(GtkAction *action, gpointer data)
9939 Compose *compose = (Compose *)data;
9940 compose_beautify_paragraph(compose, NULL, TRUE);
9943 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9945 Compose *compose = (Compose *)data;
9946 compose_wrap_all_full(compose, TRUE);
9949 static void compose_find_cb(GtkAction *action, gpointer data)
9951 Compose *compose = (Compose *)data;
9953 message_search_compose(compose);
9956 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9959 Compose *compose = (Compose *)data;
9960 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9961 if (compose->autowrap)
9962 compose_wrap_all_full(compose, TRUE);
9963 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9966 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
9969 Compose *compose = (Compose *)data;
9970 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9973 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9975 Compose *compose = (Compose *)data;
9977 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9980 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
9982 Compose *compose = (Compose *)data;
9984 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9987 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
9989 g_free(compose->privacy_system);
9991 compose->privacy_system = g_strdup(account->default_privacy_system);
9992 compose_update_privacy_system_menu_item(compose, warn);
9995 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
9997 Compose *compose = (Compose *)data;
9999 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10000 gtk_widget_show(compose->ruler_hbox);
10001 prefs_common.show_ruler = TRUE;
10003 gtk_widget_hide(compose->ruler_hbox);
10004 gtk_widget_queue_resize(compose->edit_vbox);
10005 prefs_common.show_ruler = FALSE;
10009 static void compose_attach_drag_received_cb (GtkWidget *widget,
10010 GdkDragContext *context,
10013 GtkSelectionData *data,
10016 gpointer user_data)
10018 Compose *compose = (Compose *)user_data;
10021 if (gdk_atom_name(data->type) &&
10022 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10023 && gtk_drag_get_source_widget(context) !=
10024 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10025 list = uri_list_extract_filenames((const gchar *)data->data);
10026 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10027 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10028 compose_attach_append
10029 (compose, (const gchar *)tmp->data,
10030 utf8_filename, NULL);
10031 g_free(utf8_filename);
10033 if (list) compose_changed_cb(NULL, compose);
10034 list_free_strings(list);
10036 } else if (gtk_drag_get_source_widget(context)
10037 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10038 /* comes from our summaryview */
10039 SummaryView * summaryview = NULL;
10040 GSList * list = NULL, *cur = NULL;
10042 if (mainwindow_get_mainwindow())
10043 summaryview = mainwindow_get_mainwindow()->summaryview;
10046 list = summary_get_selected_msg_list(summaryview);
10048 for (cur = list; cur; cur = cur->next) {
10049 MsgInfo *msginfo = (MsgInfo *)cur->data;
10050 gchar *file = NULL;
10052 file = procmsg_get_message_file_full(msginfo,
10055 compose_attach_append(compose, (const gchar *)file,
10056 (const gchar *)file, "message/rfc822");
10060 g_slist_free(list);
10064 static gboolean compose_drag_drop(GtkWidget *widget,
10065 GdkDragContext *drag_context,
10067 guint time, gpointer user_data)
10069 /* not handling this signal makes compose_insert_drag_received_cb
10074 static void compose_insert_drag_received_cb (GtkWidget *widget,
10075 GdkDragContext *drag_context,
10078 GtkSelectionData *data,
10081 gpointer user_data)
10083 Compose *compose = (Compose *)user_data;
10086 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10088 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10089 AlertValue val = G_ALERTDEFAULT;
10091 list = uri_list_extract_filenames((const gchar *)data->data);
10093 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10094 /* Assume a list of no files, and data has ://, is a remote link */
10095 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10096 gchar *tmpfile = get_tmp_file();
10097 str_write_to_file(tmpdata, tmpfile);
10099 compose_insert_file(compose, tmpfile);
10100 claws_unlink(tmpfile);
10102 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10103 compose_beautify_paragraph(compose, NULL, TRUE);
10106 switch (prefs_common.compose_dnd_mode) {
10107 case COMPOSE_DND_ASK:
10108 val = alertpanel_full(_("Insert or attach?"),
10109 _("Do you want to insert the contents of the file(s) "
10110 "into the message body, or attach it to the email?"),
10111 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10112 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10114 case COMPOSE_DND_INSERT:
10115 val = G_ALERTALTERNATE;
10117 case COMPOSE_DND_ATTACH:
10118 val = G_ALERTOTHER;
10121 /* unexpected case */
10122 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10125 if (val & G_ALERTDISABLE) {
10126 val &= ~G_ALERTDISABLE;
10127 /* remember what action to perform by default, only if we don't click Cancel */
10128 if (val == G_ALERTALTERNATE)
10129 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10130 else if (val == G_ALERTOTHER)
10131 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10134 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10135 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10136 list_free_strings(list);
10139 } else if (val == G_ALERTOTHER) {
10140 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10141 list_free_strings(list);
10146 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10147 compose_insert_file(compose, (const gchar *)tmp->data);
10149 list_free_strings(list);
10151 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10154 #if GTK_CHECK_VERSION(2, 8, 0)
10155 /* do nothing, handled by GTK */
10157 gchar *tmpfile = get_tmp_file();
10158 str_write_to_file((const gchar *)data->data, tmpfile);
10159 compose_insert_file(compose, tmpfile);
10160 claws_unlink(tmpfile);
10162 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10166 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10169 static void compose_header_drag_received_cb (GtkWidget *widget,
10170 GdkDragContext *drag_context,
10173 GtkSelectionData *data,
10176 gpointer user_data)
10178 GtkEditable *entry = (GtkEditable *)user_data;
10179 gchar *email = (gchar *)data->data;
10181 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10184 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10185 gchar *decoded=g_new(gchar, strlen(email));
10188 email += strlen("mailto:");
10189 decode_uri(decoded, email); /* will fit */
10190 gtk_editable_delete_text(entry, 0, -1);
10191 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10192 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10196 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10199 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10201 Compose *compose = (Compose *)data;
10203 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10204 compose->return_receipt = TRUE;
10206 compose->return_receipt = FALSE;
10209 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10211 Compose *compose = (Compose *)data;
10213 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10214 compose->remove_references = TRUE;
10216 compose->remove_references = FALSE;
10219 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10220 GdkEventKey *event,
10221 ComposeHeaderEntry *headerentry)
10223 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10224 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10225 !(event->state & GDK_MODIFIER_MASK) &&
10226 (event->keyval == GDK_BackSpace) &&
10227 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10228 gtk_container_remove
10229 (GTK_CONTAINER(headerentry->compose->header_table),
10230 headerentry->combo);
10231 gtk_container_remove
10232 (GTK_CONTAINER(headerentry->compose->header_table),
10233 headerentry->entry);
10234 headerentry->compose->header_list =
10235 g_slist_remove(headerentry->compose->header_list,
10237 g_free(headerentry);
10238 } else if (event->keyval == GDK_Tab) {
10239 if (headerentry->compose->header_last == headerentry) {
10240 /* Override default next focus, and give it to subject_entry
10241 * instead of notebook tabs
10243 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10244 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10251 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10252 ComposeHeaderEntry *headerentry)
10254 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10255 compose_create_header_entry(headerentry->compose);
10256 g_signal_handlers_disconnect_matched
10257 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10258 0, 0, NULL, NULL, headerentry);
10260 /* Automatically scroll down */
10261 compose_show_first_last_header(headerentry->compose, FALSE);
10267 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10269 GtkAdjustment *vadj;
10271 g_return_if_fail(compose);
10272 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10273 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10275 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10276 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10277 gtk_adjustment_changed(vadj);
10280 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10281 const gchar *text, gint len, Compose *compose)
10283 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10284 (G_OBJECT(compose->text), "paste_as_quotation"));
10287 g_return_if_fail(text != NULL);
10289 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10290 G_CALLBACK(text_inserted),
10292 if (paste_as_quotation) {
10294 const gchar *qmark;
10296 GtkTextIter start_iter;
10299 len = strlen(text);
10301 new_text = g_strndup(text, len);
10303 qmark = compose_quote_char_from_context(compose);
10305 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10306 gtk_text_buffer_place_cursor(buffer, iter);
10308 pos = gtk_text_iter_get_offset(iter);
10310 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10311 _("Quote format error at line %d."));
10312 quote_fmt_reset_vartable();
10314 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10315 GINT_TO_POINTER(paste_as_quotation - 1));
10317 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10318 gtk_text_buffer_place_cursor(buffer, iter);
10319 gtk_text_buffer_delete_mark(buffer, mark);
10321 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10322 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10323 compose_beautify_paragraph(compose, &start_iter, FALSE);
10324 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10325 gtk_text_buffer_delete_mark(buffer, mark);
10327 if (strcmp(text, "\n") || compose->automatic_break
10328 || gtk_text_iter_starts_line(iter)) {
10329 GtkTextIter before_ins;
10330 gtk_text_buffer_insert(buffer, iter, text, len);
10331 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10332 before_ins = *iter;
10333 gtk_text_iter_backward_chars(&before_ins, len);
10334 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10337 /* check if the preceding is just whitespace or quote */
10338 GtkTextIter start_line;
10339 gchar *tmp = NULL, *quote = NULL;
10340 gint quote_len = 0, is_normal = 0;
10341 start_line = *iter;
10342 gtk_text_iter_set_line_offset(&start_line, 0);
10343 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10346 if (*tmp == '\0') {
10349 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10357 gtk_text_buffer_insert(buffer, iter, text, len);
10359 gtk_text_buffer_insert_with_tags_by_name(buffer,
10360 iter, text, len, "no_join", NULL);
10365 if (!paste_as_quotation) {
10366 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10367 compose_beautify_paragraph(compose, iter, FALSE);
10368 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10369 gtk_text_buffer_delete_mark(buffer, mark);
10372 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10373 G_CALLBACK(text_inserted),
10375 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10377 if (prefs_common.autosave &&
10378 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10379 compose->draft_timeout_tag != -2 /* disabled while loading */)
10380 compose->draft_timeout_tag = g_timeout_add
10381 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10383 static gint compose_defer_auto_save_draft(Compose *compose)
10385 compose->draft_timeout_tag = -1;
10386 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10391 static void compose_check_all(GtkAction *action, gpointer data)
10393 Compose *compose = (Compose *)data;
10394 if (compose->gtkaspell)
10395 gtkaspell_check_all(compose->gtkaspell);
10398 static void compose_highlight_all(GtkAction *action, gpointer data)
10400 Compose *compose = (Compose *)data;
10401 if (compose->gtkaspell)
10402 gtkaspell_highlight_all(compose->gtkaspell);
10405 static void compose_check_backwards(GtkAction *action, gpointer data)
10407 Compose *compose = (Compose *)data;
10408 if (compose->gtkaspell)
10409 gtkaspell_check_backwards(compose->gtkaspell);
10411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10415 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10417 Compose *compose = (Compose *)data;
10418 if (compose->gtkaspell)
10419 gtkaspell_check_forwards_go(compose->gtkaspell);
10421 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10427 *\brief Guess originating forward account from MsgInfo and several
10428 * "common preference" settings. Return NULL if no guess.
10430 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10432 PrefsAccount *account = NULL;
10434 g_return_val_if_fail(msginfo, NULL);
10435 g_return_val_if_fail(msginfo->folder, NULL);
10436 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10438 if (msginfo->folder->prefs->enable_default_account)
10439 account = account_find_from_id(msginfo->folder->prefs->default_account);
10442 account = msginfo->folder->folder->account;
10444 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10446 Xstrdup_a(to, msginfo->to, return NULL);
10447 extract_address(to);
10448 account = account_find_from_address(to, FALSE);
10451 if (!account && prefs_common.forward_account_autosel) {
10452 gchar cc[BUFFSIZE];
10453 if (!procheader_get_header_from_msginfo
10454 (msginfo, cc,sizeof cc , "Cc:")) {
10455 gchar *buf = cc + strlen("Cc:");
10456 extract_address(buf);
10457 account = account_find_from_address(buf, FALSE);
10461 if (!account && prefs_common.forward_account_autosel) {
10462 gchar deliveredto[BUFFSIZE];
10463 if (!procheader_get_header_from_msginfo
10464 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10465 gchar *buf = deliveredto + strlen("Delivered-To:");
10466 extract_address(buf);
10467 account = account_find_from_address(buf, FALSE);
10474 gboolean compose_close(Compose *compose)
10478 if (!g_mutex_trylock(compose->mutex)) {
10479 /* we have to wait for the (possibly deferred by auto-save)
10480 * drafting to be done, before destroying the compose under
10482 debug_print("waiting for drafting to finish...\n");
10483 compose_allow_user_actions(compose, FALSE);
10484 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10487 g_return_val_if_fail(compose, FALSE);
10488 gtkut_widget_get_uposition(compose->window, &x, &y);
10489 if (!compose->batch) {
10490 prefs_common.compose_x = x;
10491 prefs_common.compose_y = y;
10493 g_mutex_unlock(compose->mutex);
10494 compose_destroy(compose);
10499 * Add entry field for each address in list.
10500 * \param compose E-Mail composition object.
10501 * \param listAddress List of (formatted) E-Mail addresses.
10503 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10506 node = listAddress;
10508 addr = ( gchar * ) node->data;
10509 compose_entry_append( compose, addr, COMPOSE_TO );
10510 node = g_list_next( node );
10514 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10515 guint action, gboolean opening_multiple)
10517 gchar *body = NULL;
10518 GSList *new_msglist = NULL;
10519 MsgInfo *tmp_msginfo = NULL;
10520 gboolean originally_enc = FALSE;
10521 gboolean originally_sig = FALSE;
10522 Compose *compose = NULL;
10524 g_return_if_fail(msgview != NULL);
10526 g_return_if_fail(msginfo_list != NULL);
10528 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10529 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10530 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10532 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10533 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10534 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10535 orig_msginfo, mimeinfo);
10536 if (tmp_msginfo != NULL) {
10537 new_msglist = g_slist_append(NULL, tmp_msginfo);
10539 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10540 privacy_msginfo_get_signed_state(orig_msginfo);
10541 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10543 tmp_msginfo->folder = orig_msginfo->folder;
10544 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10545 if (orig_msginfo->tags)
10546 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10551 if (!opening_multiple)
10552 body = messageview_get_selection(msgview);
10555 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10556 procmsg_msginfo_free(tmp_msginfo);
10557 g_slist_free(new_msglist);
10559 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10561 if (compose && originally_enc) {
10562 compose_force_encryption(compose, compose->account, FALSE);
10565 if (compose && originally_sig && compose->account->default_sign_reply) {
10566 compose_force_signing(compose, compose->account);
10572 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10575 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10576 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10577 GSList *cur = msginfo_list;
10578 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10579 "messages. Opening the windows "
10580 "could take some time. Do you "
10581 "want to continue?"),
10582 g_slist_length(msginfo_list));
10583 if (g_slist_length(msginfo_list) > 9
10584 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10585 != G_ALERTALTERNATE) {
10590 /* We'll open multiple compose windows */
10591 /* let the WM place the next windows */
10592 compose_force_window_origin = FALSE;
10593 for (; cur; cur = cur->next) {
10595 tmplist.data = cur->data;
10596 tmplist.next = NULL;
10597 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10599 compose_force_window_origin = TRUE;
10601 /* forwarding multiple mails as attachments is done via a
10602 * single compose window */
10603 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10607 void compose_set_position(Compose *compose, gint pos)
10609 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10611 gtkut_text_view_set_position(text, pos);
10614 gboolean compose_search_string(Compose *compose,
10615 const gchar *str, gboolean case_sens)
10617 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10619 return gtkut_text_view_search_string(text, str, case_sens);
10622 gboolean compose_search_string_backward(Compose *compose,
10623 const gchar *str, gboolean case_sens)
10625 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10627 return gtkut_text_view_search_string_backward(text, str, case_sens);
10630 /* allocate a msginfo structure and populate its data from a compose data structure */
10631 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10633 MsgInfo *newmsginfo;
10635 gchar buf[BUFFSIZE];
10637 g_return_val_if_fail( compose != NULL, NULL );
10639 newmsginfo = procmsg_msginfo_new();
10642 get_rfc822_date(buf, sizeof(buf));
10643 newmsginfo->date = g_strdup(buf);
10646 if (compose->from_name) {
10647 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10648 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10652 if (compose->subject_entry)
10653 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10655 /* to, cc, reply-to, newsgroups */
10656 for (list = compose->header_list; list; list = list->next) {
10657 gchar *header = gtk_editable_get_chars(
10659 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10660 gchar *entry = gtk_editable_get_chars(
10661 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10663 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10664 if ( newmsginfo->to == NULL ) {
10665 newmsginfo->to = g_strdup(entry);
10666 } else if (entry && *entry) {
10667 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10668 g_free(newmsginfo->to);
10669 newmsginfo->to = tmp;
10672 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10673 if ( newmsginfo->cc == NULL ) {
10674 newmsginfo->cc = g_strdup(entry);
10675 } else if (entry && *entry) {
10676 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10677 g_free(newmsginfo->cc);
10678 newmsginfo->cc = tmp;
10681 if ( strcasecmp(header,
10682 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10683 if ( newmsginfo->newsgroups == NULL ) {
10684 newmsginfo->newsgroups = g_strdup(entry);
10685 } else if (entry && *entry) {
10686 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10687 g_free(newmsginfo->newsgroups);
10688 newmsginfo->newsgroups = tmp;
10696 /* other data is unset */
10702 /* update compose's dictionaries from folder dict settings */
10703 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10704 FolderItem *folder_item)
10706 g_return_if_fail(compose != NULL);
10708 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10709 FolderItemPrefs *prefs = folder_item->prefs;
10711 if (prefs->enable_default_dictionary)
10712 gtkaspell_change_dict(compose->gtkaspell,
10713 prefs->default_dictionary, FALSE);
10714 if (folder_item->prefs->enable_default_alt_dictionary)
10715 gtkaspell_change_alt_dict(compose->gtkaspell,
10716 prefs->default_alt_dictionary);
10717 if (prefs->enable_default_dictionary
10718 || prefs->enable_default_alt_dictionary)
10719 compose_spell_menu_changed(compose);