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 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
892 if (folderidentifier) {
893 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
894 prefs_common.compose_save_to_history = add_history(
895 prefs_common.compose_save_to_history, folderidentifier);
896 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
897 prefs_common.compose_save_to_history);
900 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
901 if (folderidentifier)
902 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
904 gtk_entry_set_text(GTK_ENTRY(entry), "");
907 static gchar *compose_get_save_to(Compose *compose)
910 gchar *result = NULL;
911 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
912 result = gtk_editable_get_chars(entry, 0, -1);
915 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
916 prefs_common.compose_save_to_history = add_history(
917 prefs_common.compose_save_to_history, result);
918 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
919 prefs_common.compose_save_to_history);
924 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
925 GPtrArray *attach_files, GList *listAddress )
928 GtkTextView *textview;
929 GtkTextBuffer *textbuf;
931 const gchar *subject_format = NULL;
932 const gchar *body_format = NULL;
933 gchar *mailto_from = NULL;
934 PrefsAccount *mailto_account = NULL;
935 MsgInfo* dummyinfo = NULL;
937 /* check if mailto defines a from */
938 if (mailto && *mailto != '\0') {
939 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
940 /* mailto defines a from, check if we can get account prefs from it,
941 if not, the account prefs will be guessed using other ways, but we'll keep
944 mailto_account = account_find_from_address(mailto_from, TRUE);
946 account = mailto_account;
949 /* if no account prefs set from mailto, set if from folder prefs (if any) */
950 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
951 account = account_find_from_id(item->prefs->default_account);
953 /* if no account prefs set, fallback to the current one */
954 if (!account) account = cur_account;
955 g_return_val_if_fail(account != NULL, NULL);
957 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
959 /* override from name if mailto asked for it */
961 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
964 /* override from name according to folder properties */
965 if (item && item->prefs &&
966 item->prefs->compose_with_format &&
967 item->prefs->compose_override_from_format &&
968 *item->prefs->compose_override_from_format != '\0') {
973 dummyinfo = compose_msginfo_new_from_compose(compose);
975 /* decode \-escape sequences in the internal representation of the quote format */
976 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
977 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
980 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
983 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
985 quote_fmt_scan_string(tmp);
988 buf = quote_fmt_get_buffer();
990 alertpanel_error(_("New message From format error."));
992 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
993 quote_fmt_reset_vartable();
998 compose->replyinfo = NULL;
999 compose->fwdinfo = NULL;
1001 textview = GTK_TEXT_VIEW(compose->text);
1002 textbuf = gtk_text_view_get_buffer(textview);
1003 compose_create_tags(textview, compose);
1005 undo_block(compose->undostruct);
1007 compose_set_dictionaries_from_folder_prefs(compose, item);
1010 if (account->auto_sig)
1011 compose_insert_sig(compose, FALSE);
1012 gtk_text_buffer_get_start_iter(textbuf, &iter);
1013 gtk_text_buffer_place_cursor(textbuf, &iter);
1015 if (account->protocol != A_NNTP) {
1016 if (mailto && *mailto != '\0') {
1017 compose_entries_set(compose, mailto, COMPOSE_TO);
1019 } else if (item && item->prefs->enable_default_to) {
1020 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1021 compose_entry_mark_default_to(compose, item->prefs->default_to);
1023 if (item && item->ret_rcpt) {
1024 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1027 if (mailto && *mailto != '\0') {
1028 if (!strchr(mailto, '@'))
1029 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1031 compose_entries_set(compose, mailto, COMPOSE_TO);
1032 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1033 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1036 * CLAWS: just don't allow return receipt request, even if the user
1037 * may want to send an email. simple but foolproof.
1039 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1041 compose_add_field_list( compose, listAddress );
1043 if (item && item->prefs && item->prefs->compose_with_format) {
1044 subject_format = item->prefs->compose_subject_format;
1045 body_format = item->prefs->compose_body_format;
1046 } else if (account->compose_with_format) {
1047 subject_format = account->compose_subject_format;
1048 body_format = account->compose_body_format;
1049 } else if (prefs_common.compose_with_format) {
1050 subject_format = prefs_common.compose_subject_format;
1051 body_format = prefs_common.compose_body_format;
1054 if (subject_format || body_format) {
1057 && *subject_format != '\0' )
1059 gchar *subject = NULL;
1064 dummyinfo = compose_msginfo_new_from_compose(compose);
1066 /* decode \-escape sequences in the internal representation of the quote format */
1067 tmp = malloc(strlen(subject_format)+1);
1068 pref_get_unescaped_pref(tmp, subject_format);
1070 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1072 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1073 compose->gtkaspell);
1075 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1077 quote_fmt_scan_string(tmp);
1080 buf = quote_fmt_get_buffer();
1082 alertpanel_error(_("New message subject format error."));
1084 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1085 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1086 quote_fmt_reset_vartable();
1093 && *body_format != '\0' )
1096 GtkTextBuffer *buffer;
1097 GtkTextIter start, end;
1101 dummyinfo = compose_msginfo_new_from_compose(compose);
1103 text = GTK_TEXT_VIEW(compose->text);
1104 buffer = gtk_text_view_get_buffer(text);
1105 gtk_text_buffer_get_start_iter(buffer, &start);
1106 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1107 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1109 compose_quote_fmt(compose, dummyinfo,
1111 NULL, tmp, FALSE, TRUE,
1112 _("New message body format error at line %d."));
1113 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1114 quote_fmt_reset_vartable();
1120 procmsg_msginfo_free( dummyinfo );
1126 for (i = 0; i < attach_files->len; i++) {
1127 file = g_ptr_array_index(attach_files, i);
1128 compose_attach_append(compose, file, file, NULL);
1132 compose_show_first_last_header(compose, TRUE);
1134 /* Set save folder */
1135 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1136 gchar *folderidentifier;
1138 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1139 folderidentifier = folder_item_get_identifier(item);
1140 compose_set_save_to(compose, folderidentifier);
1141 g_free(folderidentifier);
1144 gtk_widget_grab_focus(compose->header_last->entry);
1146 undo_unblock(compose->undostruct);
1148 if (prefs_common.auto_exteditor)
1149 compose_exec_ext_editor(compose);
1151 compose->draft_timeout_tag = -1;
1152 SCROLL_TO_CURSOR(compose);
1154 compose->modified = FALSE;
1155 compose_set_title(compose);
1159 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1160 gboolean override_pref)
1162 gchar *privacy = NULL;
1164 g_return_if_fail(compose != NULL);
1165 g_return_if_fail(account != NULL);
1167 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1170 if (account->default_privacy_system
1171 && strlen(account->default_privacy_system)) {
1172 privacy = account->default_privacy_system;
1174 GSList *privacy_avail = privacy_get_system_ids();
1175 if (privacy_avail && g_slist_length(privacy_avail)) {
1176 privacy = (gchar *)(privacy_avail->data);
1179 if (privacy != NULL) {
1180 if (compose->privacy_system == NULL)
1181 compose->privacy_system = g_strdup(privacy);
1182 else if (*(compose->privacy_system) == '\0') {
1183 g_free(compose->privacy_system);
1184 compose->privacy_system = g_strdup(privacy);
1186 compose_update_privacy_system_menu_item(compose, FALSE);
1187 compose_use_encryption(compose, TRUE);
1191 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1193 gchar *privacy = NULL;
1195 if (account->default_privacy_system
1196 && strlen(account->default_privacy_system)) {
1197 privacy = account->default_privacy_system;
1199 GSList *privacy_avail = privacy_get_system_ids();
1200 if (privacy_avail && g_slist_length(privacy_avail)) {
1201 privacy = (gchar *)(privacy_avail->data);
1204 if (privacy != NULL) {
1205 if (compose->privacy_system == NULL)
1206 compose->privacy_system = g_strdup(privacy);
1207 compose_update_privacy_system_menu_item(compose, FALSE);
1208 compose_use_signing(compose, TRUE);
1212 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1216 Compose *compose = NULL;
1218 g_return_val_if_fail(msginfo_list != NULL, NULL);
1220 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1221 g_return_val_if_fail(msginfo != NULL, NULL);
1223 list_len = g_slist_length(msginfo_list);
1227 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1228 FALSE, prefs_common.default_reply_list, FALSE, body);
1230 case COMPOSE_REPLY_WITH_QUOTE:
1231 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1232 FALSE, prefs_common.default_reply_list, FALSE, body);
1234 case COMPOSE_REPLY_WITHOUT_QUOTE:
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1236 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1238 case COMPOSE_REPLY_TO_SENDER:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1240 FALSE, FALSE, TRUE, body);
1242 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1243 compose = compose_followup_and_reply_to(msginfo,
1244 COMPOSE_QUOTE_CHECK,
1245 FALSE, FALSE, body);
1247 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1248 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1249 FALSE, FALSE, TRUE, body);
1251 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1252 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1253 FALSE, FALSE, TRUE, NULL);
1255 case COMPOSE_REPLY_TO_ALL:
1256 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1257 TRUE, FALSE, FALSE, body);
1259 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1260 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1261 TRUE, FALSE, FALSE, body);
1263 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1264 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1265 TRUE, FALSE, FALSE, NULL);
1267 case COMPOSE_REPLY_TO_LIST:
1268 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1269 FALSE, TRUE, FALSE, body);
1271 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1272 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1273 FALSE, TRUE, FALSE, body);
1275 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1276 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1277 FALSE, TRUE, FALSE, NULL);
1279 case COMPOSE_FORWARD:
1280 if (prefs_common.forward_as_attachment) {
1281 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1284 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1288 case COMPOSE_FORWARD_INLINE:
1289 /* check if we reply to more than one Message */
1290 if (list_len == 1) {
1291 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1294 /* more messages FALL THROUGH */
1295 case COMPOSE_FORWARD_AS_ATTACH:
1296 compose = compose_forward_multiple(NULL, msginfo_list);
1298 case COMPOSE_REDIRECT:
1299 compose = compose_redirect(NULL, msginfo, FALSE);
1302 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1305 if (compose == NULL) {
1306 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1310 compose->rmode = mode;
1311 switch (compose->rmode) {
1313 case COMPOSE_REPLY_WITH_QUOTE:
1314 case COMPOSE_REPLY_WITHOUT_QUOTE:
1315 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1316 debug_print("reply mode Normal\n");
1317 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1318 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1320 case COMPOSE_REPLY_TO_SENDER:
1321 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1322 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1323 debug_print("reply mode Sender\n");
1324 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1326 case COMPOSE_REPLY_TO_ALL:
1327 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1328 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1329 debug_print("reply mode All\n");
1330 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1332 case COMPOSE_REPLY_TO_LIST:
1333 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1334 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1335 debug_print("reply mode List\n");
1336 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1344 static Compose *compose_reply(MsgInfo *msginfo,
1345 ComposeQuoteMode quote_mode,
1351 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1352 to_sender, FALSE, body);
1355 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1356 ComposeQuoteMode quote_mode,
1361 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1362 to_sender, TRUE, body);
1365 static void compose_extract_original_charset(Compose *compose)
1367 MsgInfo *info = NULL;
1368 if (compose->replyinfo) {
1369 info = compose->replyinfo;
1370 } else if (compose->fwdinfo) {
1371 info = compose->fwdinfo;
1372 } else if (compose->targetinfo) {
1373 info = compose->targetinfo;
1376 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1377 MimeInfo *partinfo = mimeinfo;
1378 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1379 partinfo = procmime_mimeinfo_next(partinfo);
1381 compose->orig_charset =
1382 g_strdup(procmime_mimeinfo_get_parameter(
1383 partinfo, "charset"));
1385 procmime_mimeinfo_free_all(mimeinfo);
1389 #define SIGNAL_BLOCK(buffer) { \
1390 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1391 G_CALLBACK(compose_changed_cb), \
1393 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1394 G_CALLBACK(text_inserted), \
1398 #define SIGNAL_UNBLOCK(buffer) { \
1399 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1400 G_CALLBACK(compose_changed_cb), \
1402 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1403 G_CALLBACK(text_inserted), \
1407 static Compose *compose_generic_reply(MsgInfo *msginfo,
1408 ComposeQuoteMode quote_mode,
1409 gboolean to_all, gboolean to_ml,
1411 gboolean followup_and_reply_to,
1415 PrefsAccount *account = NULL;
1416 GtkTextView *textview;
1417 GtkTextBuffer *textbuf;
1418 gboolean quote = FALSE;
1419 const gchar *qmark = NULL;
1420 const gchar *body_fmt = NULL;
1422 g_return_val_if_fail(msginfo != NULL, NULL);
1423 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1425 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1427 g_return_val_if_fail(account != NULL, NULL);
1429 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1431 compose->updating = TRUE;
1433 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1436 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1437 if (!compose->replyinfo)
1438 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1440 compose_extract_original_charset(compose);
1442 if (msginfo->folder && msginfo->folder->ret_rcpt)
1443 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1445 /* Set save folder */
1446 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1447 gchar *folderidentifier;
1449 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1450 folderidentifier = folder_item_get_identifier(msginfo->folder);
1451 compose_set_save_to(compose, folderidentifier);
1452 g_free(folderidentifier);
1455 if (compose_parse_header(compose, msginfo) < 0) {
1456 compose->updating = FALSE;
1457 compose_destroy(compose);
1461 /* override from name according to folder properties */
1462 if (msginfo->folder && msginfo->folder->prefs &&
1463 msginfo->folder->prefs->reply_with_format &&
1464 msginfo->folder->prefs->reply_override_from_format &&
1465 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1470 /* decode \-escape sequences in the internal representation of the quote format */
1471 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1472 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1475 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1476 compose->gtkaspell);
1478 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1480 quote_fmt_scan_string(tmp);
1483 buf = quote_fmt_get_buffer();
1485 alertpanel_error(_("Message reply From format error."));
1487 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1488 quote_fmt_reset_vartable();
1493 textview = (GTK_TEXT_VIEW(compose->text));
1494 textbuf = gtk_text_view_get_buffer(textview);
1495 compose_create_tags(textview, compose);
1497 undo_block(compose->undostruct);
1499 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1502 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1503 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1504 /* use the reply format of folder (if enabled), or the account's one
1505 (if enabled) or fallback to the global reply format, which is always
1506 enabled (even if empty), and use the relevant quotemark */
1508 if (msginfo->folder && msginfo->folder->prefs &&
1509 msginfo->folder->prefs->reply_with_format) {
1510 qmark = msginfo->folder->prefs->reply_quotemark;
1511 body_fmt = msginfo->folder->prefs->reply_body_format;
1513 } else if (account->reply_with_format) {
1514 qmark = account->reply_quotemark;
1515 body_fmt = account->reply_body_format;
1518 qmark = prefs_common.quotemark;
1519 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1520 body_fmt = gettext(prefs_common.quotefmt);
1527 /* empty quotemark is not allowed */
1528 if (qmark == NULL || *qmark == '\0')
1530 compose_quote_fmt(compose, compose->replyinfo,
1531 body_fmt, qmark, body, FALSE, TRUE,
1532 _("Message reply format error at line %d."));
1533 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1534 quote_fmt_reset_vartable();
1537 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1538 compose_force_encryption(compose, account, FALSE);
1541 privacy_msginfo_get_signed_state(compose->replyinfo);
1542 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1543 compose_force_signing(compose, account);
1546 SIGNAL_BLOCK(textbuf);
1548 if (account->auto_sig)
1549 compose_insert_sig(compose, FALSE);
1551 compose_wrap_all(compose);
1553 SIGNAL_UNBLOCK(textbuf);
1555 gtk_widget_grab_focus(compose->text);
1557 undo_unblock(compose->undostruct);
1559 if (prefs_common.auto_exteditor)
1560 compose_exec_ext_editor(compose);
1562 compose->modified = FALSE;
1563 compose_set_title(compose);
1565 compose->updating = FALSE;
1566 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1567 SCROLL_TO_CURSOR(compose);
1569 if (compose->deferred_destroy) {
1570 compose_destroy(compose);
1577 #define INSERT_FW_HEADER(var, hdr) \
1578 if (msginfo->var && *msginfo->var) { \
1579 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1580 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1581 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1584 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1585 gboolean as_attach, const gchar *body,
1586 gboolean no_extedit,
1590 GtkTextView *textview;
1591 GtkTextBuffer *textbuf;
1594 g_return_val_if_fail(msginfo != NULL, NULL);
1595 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1598 !(account = compose_guess_forward_account_from_msginfo
1600 account = cur_account;
1602 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1604 compose->updating = TRUE;
1605 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1606 if (!compose->fwdinfo)
1607 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1609 compose_extract_original_charset(compose);
1611 if (msginfo->subject && *msginfo->subject) {
1612 gchar *buf, *buf2, *p;
1614 buf = p = g_strdup(msginfo->subject);
1615 p += subject_get_prefix_length(p);
1616 memmove(buf, p, strlen(p) + 1);
1618 buf2 = g_strdup_printf("Fw: %s", buf);
1619 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1625 /* override from name according to folder properties */
1626 if (msginfo->folder && msginfo->folder->prefs &&
1627 msginfo->folder->prefs->forward_with_format &&
1628 msginfo->folder->prefs->forward_override_from_format &&
1629 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1633 MsgInfo *full_msginfo = NULL;
1636 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1638 full_msginfo = procmsg_msginfo_copy(msginfo);
1640 /* decode \-escape sequences in the internal representation of the quote format */
1641 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1642 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1645 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1646 compose->gtkaspell);
1648 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1650 quote_fmt_scan_string(tmp);
1653 buf = quote_fmt_get_buffer();
1655 alertpanel_error(_("Message forward From format error."));
1657 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1658 quote_fmt_reset_vartable();
1661 procmsg_msginfo_free(full_msginfo);
1664 textview = GTK_TEXT_VIEW(compose->text);
1665 textbuf = gtk_text_view_get_buffer(textview);
1666 compose_create_tags(textview, compose);
1668 undo_block(compose->undostruct);
1672 msgfile = procmsg_get_message_file(msginfo);
1673 if (!is_file_exist(msgfile))
1674 g_warning("%s: file not exist\n", msgfile);
1676 compose_attach_append(compose, msgfile, msgfile,
1681 const gchar *qmark = NULL;
1682 const gchar *body_fmt = NULL;
1683 MsgInfo *full_msginfo;
1685 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1686 body_fmt = gettext(prefs_common.fw_quotefmt);
1690 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1692 full_msginfo = procmsg_msginfo_copy(msginfo);
1694 /* use the forward format of folder (if enabled), or the account's one
1695 (if enabled) or fallback to the global forward format, which is always
1696 enabled (even if empty), and use the relevant quotemark */
1697 if (msginfo->folder && msginfo->folder->prefs &&
1698 msginfo->folder->prefs->forward_with_format) {
1699 qmark = msginfo->folder->prefs->forward_quotemark;
1700 body_fmt = msginfo->folder->prefs->forward_body_format;
1702 } else if (account->forward_with_format) {
1703 qmark = account->forward_quotemark;
1704 body_fmt = account->forward_body_format;
1707 qmark = prefs_common.fw_quotemark;
1708 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1709 body_fmt = gettext(prefs_common.fw_quotefmt);
1714 /* empty quotemark is not allowed */
1715 if (qmark == NULL || *qmark == '\0')
1718 compose_quote_fmt(compose, full_msginfo,
1719 body_fmt, qmark, body, FALSE, TRUE,
1720 _("Message forward format error at line %d."));
1721 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1722 quote_fmt_reset_vartable();
1723 compose_attach_parts(compose, msginfo);
1725 procmsg_msginfo_free(full_msginfo);
1728 SIGNAL_BLOCK(textbuf);
1730 if (account->auto_sig)
1731 compose_insert_sig(compose, FALSE);
1733 compose_wrap_all(compose);
1735 SIGNAL_UNBLOCK(textbuf);
1737 gtk_text_buffer_get_start_iter(textbuf, &iter);
1738 gtk_text_buffer_place_cursor(textbuf, &iter);
1740 gtk_widget_grab_focus(compose->header_last->entry);
1742 if (!no_extedit && prefs_common.auto_exteditor)
1743 compose_exec_ext_editor(compose);
1746 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1747 gchar *folderidentifier;
1749 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1750 folderidentifier = folder_item_get_identifier(msginfo->folder);
1751 compose_set_save_to(compose, folderidentifier);
1752 g_free(folderidentifier);
1755 undo_unblock(compose->undostruct);
1757 compose->modified = FALSE;
1758 compose_set_title(compose);
1760 compose->updating = FALSE;
1761 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1762 SCROLL_TO_CURSOR(compose);
1764 if (compose->deferred_destroy) {
1765 compose_destroy(compose);
1772 #undef INSERT_FW_HEADER
1774 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1777 GtkTextView *textview;
1778 GtkTextBuffer *textbuf;
1782 gboolean single_mail = TRUE;
1784 g_return_val_if_fail(msginfo_list != NULL, NULL);
1786 if (g_slist_length(msginfo_list) > 1)
1787 single_mail = FALSE;
1789 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1790 if (((MsgInfo *)msginfo->data)->folder == NULL)
1793 /* guess account from first selected message */
1795 !(account = compose_guess_forward_account_from_msginfo
1796 (msginfo_list->data)))
1797 account = cur_account;
1799 g_return_val_if_fail(account != NULL, NULL);
1801 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1802 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1803 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1806 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1808 compose->updating = TRUE;
1810 /* override from name according to folder properties */
1811 if (msginfo_list->data) {
1812 MsgInfo *msginfo = msginfo_list->data;
1814 if (msginfo->folder && msginfo->folder->prefs &&
1815 msginfo->folder->prefs->forward_with_format &&
1816 msginfo->folder->prefs->forward_override_from_format &&
1817 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1822 /* decode \-escape sequences in the internal representation of the quote format */
1823 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1824 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1827 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1828 compose->gtkaspell);
1830 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1832 quote_fmt_scan_string(tmp);
1835 buf = quote_fmt_get_buffer();
1837 alertpanel_error(_("Message forward From format error."));
1839 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1840 quote_fmt_reset_vartable();
1846 textview = GTK_TEXT_VIEW(compose->text);
1847 textbuf = gtk_text_view_get_buffer(textview);
1848 compose_create_tags(textview, compose);
1850 undo_block(compose->undostruct);
1851 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1852 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1854 if (!is_file_exist(msgfile))
1855 g_warning("%s: file not exist\n", msgfile);
1857 compose_attach_append(compose, msgfile, msgfile,
1863 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1864 if (info->subject && *info->subject) {
1865 gchar *buf, *buf2, *p;
1867 buf = p = g_strdup(info->subject);
1868 p += subject_get_prefix_length(p);
1869 memmove(buf, p, strlen(p) + 1);
1871 buf2 = g_strdup_printf("Fw: %s", buf);
1872 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1878 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1879 _("Fw: multiple emails"));
1882 SIGNAL_BLOCK(textbuf);
1884 if (account->auto_sig)
1885 compose_insert_sig(compose, FALSE);
1887 compose_wrap_all(compose);
1889 SIGNAL_UNBLOCK(textbuf);
1891 gtk_text_buffer_get_start_iter(textbuf, &iter);
1892 gtk_text_buffer_place_cursor(textbuf, &iter);
1894 gtk_widget_grab_focus(compose->header_last->entry);
1895 undo_unblock(compose->undostruct);
1896 compose->modified = FALSE;
1897 compose_set_title(compose);
1899 compose->updating = FALSE;
1900 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1901 SCROLL_TO_CURSOR(compose);
1903 if (compose->deferred_destroy) {
1904 compose_destroy(compose);
1911 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1913 GtkTextIter start = *iter;
1914 GtkTextIter end_iter;
1915 int start_pos = gtk_text_iter_get_offset(&start);
1917 if (!compose->account->sig_sep)
1920 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1921 start_pos+strlen(compose->account->sig_sep));
1923 /* check sig separator */
1924 str = gtk_text_iter_get_text(&start, &end_iter);
1925 if (!strcmp(str, compose->account->sig_sep)) {
1927 /* check end of line (\n) */
1928 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1929 start_pos+strlen(compose->account->sig_sep));
1930 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1931 start_pos+strlen(compose->account->sig_sep)+1);
1932 tmp = gtk_text_iter_get_text(&start, &end_iter);
1933 if (!strcmp(tmp,"\n")) {
1945 static void compose_colorize_signature(Compose *compose)
1947 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1949 GtkTextIter end_iter;
1950 gtk_text_buffer_get_start_iter(buffer, &iter);
1951 while (gtk_text_iter_forward_line(&iter))
1952 if (compose_is_sig_separator(compose, buffer, &iter)) {
1953 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1954 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1958 #define BLOCK_WRAP() { \
1959 prev_autowrap = compose->autowrap; \
1960 buffer = gtk_text_view_get_buffer( \
1961 GTK_TEXT_VIEW(compose->text)); \
1962 compose->autowrap = FALSE; \
1964 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1965 G_CALLBACK(compose_changed_cb), \
1967 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1968 G_CALLBACK(text_inserted), \
1971 #define UNBLOCK_WRAP() { \
1972 compose->autowrap = prev_autowrap; \
1973 if (compose->autowrap) { \
1974 gint old = compose->draft_timeout_tag; \
1975 compose->draft_timeout_tag = -2; \
1976 compose_wrap_all(compose); \
1977 compose->draft_timeout_tag = old; \
1980 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1981 G_CALLBACK(compose_changed_cb), \
1983 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1984 G_CALLBACK(text_inserted), \
1988 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1990 Compose *compose = NULL;
1991 PrefsAccount *account = NULL;
1992 GtkTextView *textview;
1993 GtkTextBuffer *textbuf;
1997 gchar buf[BUFFSIZE];
1998 gboolean use_signing = FALSE;
1999 gboolean use_encryption = FALSE;
2000 gchar *privacy_system = NULL;
2001 int priority = PRIORITY_NORMAL;
2002 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2003 gboolean autowrap = prefs_common.autowrap;
2004 gboolean autoindent = prefs_common.auto_indent;
2006 g_return_val_if_fail(msginfo != NULL, NULL);
2007 g_return_val_if_fail(msginfo->folder != NULL, NULL);
2009 if (compose_put_existing_to_front(msginfo)) {
2013 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2014 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2015 gchar queueheader_buf[BUFFSIZE];
2018 /* Select Account from queue headers */
2019 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2020 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2021 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2022 account = account_find_from_id(id);
2024 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2025 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2026 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2027 account = account_find_from_id(id);
2029 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2030 sizeof(queueheader_buf), "NAID:")) {
2031 id = atoi(&queueheader_buf[strlen("NAID:")]);
2032 account = account_find_from_id(id);
2034 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2035 sizeof(queueheader_buf), "MAID:")) {
2036 id = atoi(&queueheader_buf[strlen("MAID:")]);
2037 account = account_find_from_id(id);
2039 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2040 sizeof(queueheader_buf), "S:")) {
2041 account = account_find_from_address(queueheader_buf, FALSE);
2043 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2044 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2045 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2046 use_signing = param;
2049 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2050 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2051 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2052 use_signing = param;
2055 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2056 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2057 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2058 use_encryption = param;
2060 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2061 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2062 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2063 use_encryption = param;
2065 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2066 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2067 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2070 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2071 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2072 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2075 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2076 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2077 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2079 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2080 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2081 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2083 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2084 sizeof(queueheader_buf), "X-Priority: ")) {
2085 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2088 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2089 sizeof(queueheader_buf), "RMID:")) {
2090 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2091 if (tokens[0] && tokens[1] && tokens[2]) {
2092 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2093 if (orig_item != NULL) {
2094 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2099 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2100 sizeof(queueheader_buf), "FMID:")) {
2101 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2102 if (tokens[0] && tokens[1] && tokens[2]) {
2103 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2104 if (orig_item != NULL) {
2105 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2111 account = msginfo->folder->folder->account;
2114 if (!account && prefs_common.reedit_account_autosel) {
2115 gchar from[BUFFSIZE];
2116 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2117 extract_address(from);
2118 account = account_find_from_address(from, FALSE);
2122 account = cur_account;
2124 g_return_val_if_fail(account != NULL, NULL);
2126 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2128 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2129 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2130 compose->autowrap = autowrap;
2131 compose->replyinfo = replyinfo;
2132 compose->fwdinfo = fwdinfo;
2134 compose->updating = TRUE;
2135 compose->priority = priority;
2137 if (privacy_system != NULL) {
2138 compose->privacy_system = privacy_system;
2139 compose_use_signing(compose, use_signing);
2140 compose_use_encryption(compose, use_encryption);
2141 compose_update_privacy_system_menu_item(compose, FALSE);
2143 activate_privacy_system(compose, account, FALSE);
2146 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2148 compose_extract_original_charset(compose);
2150 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2151 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2152 gchar queueheader_buf[BUFFSIZE];
2154 /* Set message save folder */
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2156 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2157 compose_set_save_to(compose, &queueheader_buf[4]);
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2160 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2162 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2167 if (compose_parse_header(compose, msginfo) < 0) {
2168 compose->updating = FALSE;
2169 compose_destroy(compose);
2172 compose_reedit_set_entry(compose, msginfo);
2174 textview = GTK_TEXT_VIEW(compose->text);
2175 textbuf = gtk_text_view_get_buffer(textview);
2176 compose_create_tags(textview, compose);
2178 mark = gtk_text_buffer_get_insert(textbuf);
2179 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2181 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2182 G_CALLBACK(compose_changed_cb),
2185 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2186 fp = procmime_get_first_encrypted_text_content(msginfo);
2188 compose_force_encryption(compose, account, TRUE);
2191 fp = procmime_get_first_text_content(msginfo);
2194 g_warning("Can't get text part\n");
2198 gboolean prev_autowrap = compose->autowrap;
2199 GtkTextBuffer *buffer = textbuf;
2201 while (fgets(buf, sizeof(buf), fp) != NULL) {
2203 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2209 compose_attach_parts(compose, msginfo);
2211 compose_colorize_signature(compose);
2213 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2214 G_CALLBACK(compose_changed_cb),
2217 gtk_widget_grab_focus(compose->text);
2219 if (prefs_common.auto_exteditor) {
2220 compose_exec_ext_editor(compose);
2222 compose->modified = FALSE;
2223 compose_set_title(compose);
2225 compose->updating = FALSE;
2226 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2227 SCROLL_TO_CURSOR(compose);
2229 if (compose->deferred_destroy) {
2230 compose_destroy(compose);
2234 compose->sig_str = account_get_signature_str(compose->account);
2239 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2246 g_return_val_if_fail(msginfo != NULL, NULL);
2249 account = account_get_reply_account(msginfo,
2250 prefs_common.reply_account_autosel);
2251 g_return_val_if_fail(account != NULL, NULL);
2253 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2255 compose->updating = TRUE;
2257 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2258 compose->replyinfo = NULL;
2259 compose->fwdinfo = NULL;
2261 compose_show_first_last_header(compose, TRUE);
2263 gtk_widget_grab_focus(compose->header_last->entry);
2265 filename = procmsg_get_message_file(msginfo);
2267 if (filename == NULL) {
2268 compose->updating = FALSE;
2269 compose_destroy(compose);
2274 compose->redirect_filename = filename;
2276 /* Set save folder */
2277 item = msginfo->folder;
2278 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2279 gchar *folderidentifier;
2281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2282 folderidentifier = folder_item_get_identifier(item);
2283 compose_set_save_to(compose, folderidentifier);
2284 g_free(folderidentifier);
2287 compose_attach_parts(compose, msginfo);
2289 if (msginfo->subject)
2290 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2292 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2294 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2295 _("Message redirect format error at line %d."));
2296 quote_fmt_reset_vartable();
2297 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2299 compose_colorize_signature(compose);
2302 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2303 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2304 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2306 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2307 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2308 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2309 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2310 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2311 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2312 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2313 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2315 if (compose->toolbar->draft_btn)
2316 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2317 if (compose->toolbar->insert_btn)
2318 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2319 if (compose->toolbar->attach_btn)
2320 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2321 if (compose->toolbar->sig_btn)
2322 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2323 if (compose->toolbar->exteditor_btn)
2324 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2325 if (compose->toolbar->linewrap_current_btn)
2326 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2327 if (compose->toolbar->linewrap_all_btn)
2328 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2330 compose->modified = FALSE;
2331 compose_set_title(compose);
2332 compose->updating = FALSE;
2333 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2334 SCROLL_TO_CURSOR(compose);
2336 if (compose->deferred_destroy) {
2337 compose_destroy(compose);
2344 GList *compose_get_compose_list(void)
2346 return compose_list;
2349 void compose_entry_append(Compose *compose, const gchar *address,
2350 ComposeEntryType type)
2352 const gchar *header;
2354 gboolean in_quote = FALSE;
2355 if (!address || *address == '\0') return;
2362 header = N_("Bcc:");
2364 case COMPOSE_REPLYTO:
2365 header = N_("Reply-To:");
2367 case COMPOSE_NEWSGROUPS:
2368 header = N_("Newsgroups:");
2370 case COMPOSE_FOLLOWUPTO:
2371 header = N_( "Followup-To:");
2378 header = prefs_common_translated_header_name(header);
2380 cur = begin = (gchar *)address;
2382 /* we separate the line by commas, but not if we're inside a quoted
2384 while (*cur != '\0') {
2386 in_quote = !in_quote;
2387 if (*cur == ',' && !in_quote) {
2388 gchar *tmp = g_strdup(begin);
2390 tmp[cur-begin]='\0';
2393 while (*tmp == ' ' || *tmp == '\t')
2395 compose_add_header_entry(compose, header, tmp);
2402 gchar *tmp = g_strdup(begin);
2404 tmp[cur-begin]='\0';
2407 while (*tmp == ' ' || *tmp == '\t')
2409 compose_add_header_entry(compose, header, tmp);
2414 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2416 static GdkColor yellow;
2417 static GdkColor black;
2418 static gboolean yellow_initialised = FALSE;
2422 if (!yellow_initialised) {
2423 gdk_color_parse("#f5f6be", &yellow);
2424 gdk_color_parse("#000000", &black);
2425 yellow_initialised = gdk_colormap_alloc_color(
2426 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2427 yellow_initialised &= gdk_colormap_alloc_color(
2428 gdk_colormap_get_system(), &black, FALSE, TRUE);
2431 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2432 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2433 if (gtk_entry_get_text(entry) &&
2434 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2435 if (yellow_initialised) {
2436 gtk_widget_modify_base(
2437 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2438 GTK_STATE_NORMAL, &yellow);
2439 gtk_widget_modify_text(
2440 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2441 GTK_STATE_NORMAL, &black);
2447 void compose_toolbar_cb(gint action, gpointer data)
2449 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2450 Compose *compose = (Compose*)toolbar_item->parent;
2452 g_return_if_fail(compose != NULL);
2456 compose_send_cb(NULL, compose);
2459 compose_send_later_cb(NULL, compose);
2462 compose_draft(compose, COMPOSE_QUIT_EDITING);
2465 compose_insert_file_cb(NULL, compose);
2468 compose_attach_cb(NULL, compose);
2471 compose_insert_sig(compose, FALSE);
2474 compose_ext_editor_cb(NULL, compose);
2476 case A_LINEWRAP_CURRENT:
2477 compose_beautify_paragraph(compose, NULL, TRUE);
2479 case A_LINEWRAP_ALL:
2480 compose_wrap_all_full(compose, TRUE);
2483 compose_address_cb(NULL, compose);
2486 case A_CHECK_SPELLING:
2487 compose_check_all(NULL, compose);
2495 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2500 gchar *subject = NULL;
2504 gchar **attach = NULL;
2506 /* get mailto parts but skip from */
2507 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2510 compose_entry_append(compose, to, to_type);
2512 compose_entry_append(compose, cc, COMPOSE_CC);
2514 compose_entry_append(compose, bcc, COMPOSE_BCC);
2516 if (!g_utf8_validate (subject, -1, NULL)) {
2517 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2518 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2521 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2525 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2526 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2529 gboolean prev_autowrap = compose->autowrap;
2531 compose->autowrap = FALSE;
2533 mark = gtk_text_buffer_get_insert(buffer);
2534 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2536 if (!g_utf8_validate (body, -1, NULL)) {
2537 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2538 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2541 gtk_text_buffer_insert(buffer, &iter, body, -1);
2543 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2545 compose->autowrap = prev_autowrap;
2546 if (compose->autowrap)
2547 compose_wrap_all(compose);
2551 gint i = 0, att = 0;
2552 gchar *warn_files = NULL;
2553 while (attach[i] != NULL) {
2554 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2555 if (utf8_filename) {
2556 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2557 gchar *tmp = g_strdup_printf("%s%s\n",
2558 warn_files?warn_files:"",
2564 g_free(utf8_filename);
2566 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2571 alertpanel_notice(ngettext(
2572 "The following file has been attached: \n%s",
2573 "The following files have been attached: \n%s", att), warn_files);
2585 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2587 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2588 {"Cc:", NULL, TRUE},
2589 {"References:", NULL, FALSE},
2590 {"Bcc:", NULL, TRUE},
2591 {"Newsgroups:", NULL, TRUE},
2592 {"Followup-To:", NULL, TRUE},
2593 {"List-Post:", NULL, FALSE},
2594 {"X-Priority:", NULL, FALSE},
2595 {NULL, NULL, FALSE}};
2611 g_return_val_if_fail(msginfo != NULL, -1);
2613 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2614 procheader_get_header_fields(fp, hentry);
2617 if (hentry[H_REPLY_TO].body != NULL) {
2618 if (hentry[H_REPLY_TO].body[0] != '\0') {
2620 conv_unmime_header(hentry[H_REPLY_TO].body,
2623 g_free(hentry[H_REPLY_TO].body);
2624 hentry[H_REPLY_TO].body = NULL;
2626 if (hentry[H_CC].body != NULL) {
2627 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2628 g_free(hentry[H_CC].body);
2629 hentry[H_CC].body = NULL;
2631 if (hentry[H_REFERENCES].body != NULL) {
2632 if (compose->mode == COMPOSE_REEDIT)
2633 compose->references = hentry[H_REFERENCES].body;
2635 compose->references = compose_parse_references
2636 (hentry[H_REFERENCES].body, msginfo->msgid);
2637 g_free(hentry[H_REFERENCES].body);
2639 hentry[H_REFERENCES].body = NULL;
2641 if (hentry[H_BCC].body != NULL) {
2642 if (compose->mode == COMPOSE_REEDIT)
2644 conv_unmime_header(hentry[H_BCC].body, NULL);
2645 g_free(hentry[H_BCC].body);
2646 hentry[H_BCC].body = NULL;
2648 if (hentry[H_NEWSGROUPS].body != NULL) {
2649 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2650 hentry[H_NEWSGROUPS].body = NULL;
2652 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2653 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2654 compose->followup_to =
2655 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2658 g_free(hentry[H_FOLLOWUP_TO].body);
2659 hentry[H_FOLLOWUP_TO].body = NULL;
2661 if (hentry[H_LIST_POST].body != NULL) {
2664 extract_address(hentry[H_LIST_POST].body);
2665 if (hentry[H_LIST_POST].body[0] != '\0') {
2666 scan_mailto_url(hentry[H_LIST_POST].body,
2667 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2669 g_free(compose->ml_post);
2670 compose->ml_post = to;
2673 g_free(hentry[H_LIST_POST].body);
2674 hentry[H_LIST_POST].body = NULL;
2677 /* CLAWS - X-Priority */
2678 if (compose->mode == COMPOSE_REEDIT)
2679 if (hentry[H_X_PRIORITY].body != NULL) {
2682 priority = atoi(hentry[H_X_PRIORITY].body);
2683 g_free(hentry[H_X_PRIORITY].body);
2685 hentry[H_X_PRIORITY].body = NULL;
2687 if (priority < PRIORITY_HIGHEST ||
2688 priority > PRIORITY_LOWEST)
2689 priority = PRIORITY_NORMAL;
2691 compose->priority = priority;
2694 if (compose->mode == COMPOSE_REEDIT) {
2695 if (msginfo->inreplyto && *msginfo->inreplyto)
2696 compose->inreplyto = g_strdup(msginfo->inreplyto);
2700 if (msginfo->msgid && *msginfo->msgid)
2701 compose->inreplyto = g_strdup(msginfo->msgid);
2703 if (!compose->references) {
2704 if (msginfo->msgid && *msginfo->msgid) {
2705 if (msginfo->inreplyto && *msginfo->inreplyto)
2706 compose->references =
2707 g_strdup_printf("<%s>\n\t<%s>",
2711 compose->references =
2712 g_strconcat("<", msginfo->msgid, ">",
2714 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2715 compose->references =
2716 g_strconcat("<", msginfo->inreplyto, ">",
2724 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2726 GSList *ref_id_list, *cur;
2730 ref_id_list = references_list_append(NULL, ref);
2731 if (!ref_id_list) return NULL;
2732 if (msgid && *msgid)
2733 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2738 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2739 /* "<" + Message-ID + ">" + CR+LF+TAB */
2740 len += strlen((gchar *)cur->data) + 5;
2742 if (len > MAX_REFERENCES_LEN) {
2743 /* remove second message-ID */
2744 if (ref_id_list && ref_id_list->next &&
2745 ref_id_list->next->next) {
2746 g_free(ref_id_list->next->data);
2747 ref_id_list = g_slist_remove
2748 (ref_id_list, ref_id_list->next->data);
2750 slist_free_strings(ref_id_list);
2751 g_slist_free(ref_id_list);
2758 new_ref = g_string_new("");
2759 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2760 if (new_ref->len > 0)
2761 g_string_append(new_ref, "\n\t");
2762 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2765 slist_free_strings(ref_id_list);
2766 g_slist_free(ref_id_list);
2768 new_ref_str = new_ref->str;
2769 g_string_free(new_ref, FALSE);
2774 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2775 const gchar *fmt, const gchar *qmark,
2776 const gchar *body, gboolean rewrap,
2777 gboolean need_unescape,
2778 const gchar *err_msg)
2780 MsgInfo* dummyinfo = NULL;
2781 gchar *quote_str = NULL;
2783 gboolean prev_autowrap;
2784 const gchar *trimmed_body = body;
2785 gint cursor_pos = -1;
2786 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2787 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2792 SIGNAL_BLOCK(buffer);
2795 dummyinfo = compose_msginfo_new_from_compose(compose);
2796 msginfo = dummyinfo;
2799 if (qmark != NULL) {
2801 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2802 compose->gtkaspell);
2804 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2806 quote_fmt_scan_string(qmark);
2809 buf = quote_fmt_get_buffer();
2811 alertpanel_error(_("Quote mark format error."));
2813 Xstrdup_a(quote_str, buf, goto error)
2816 if (fmt && *fmt != '\0') {
2819 while (*trimmed_body == '\n')
2823 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2824 compose->gtkaspell);
2826 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2828 if (need_unescape) {
2831 /* decode \-escape sequences in the internal representation of the quote format */
2832 tmp = malloc(strlen(fmt)+1);
2833 pref_get_unescaped_pref(tmp, fmt);
2834 quote_fmt_scan_string(tmp);
2838 quote_fmt_scan_string(fmt);
2842 buf = quote_fmt_get_buffer();
2844 gint line = quote_fmt_get_line();
2845 alertpanel_error(err_msg, line);
2851 prev_autowrap = compose->autowrap;
2852 compose->autowrap = FALSE;
2854 mark = gtk_text_buffer_get_insert(buffer);
2855 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2856 if (g_utf8_validate(buf, -1, NULL)) {
2857 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2859 gchar *tmpout = NULL;
2860 tmpout = conv_codeset_strdup
2861 (buf, conv_get_locale_charset_str_no_utf8(),
2863 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2865 tmpout = g_malloc(strlen(buf)*2+1);
2866 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2868 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2872 cursor_pos = quote_fmt_get_cursor_pos();
2873 if (cursor_pos == -1)
2874 cursor_pos = gtk_text_iter_get_offset(&iter);
2875 compose->set_cursor_pos = cursor_pos;
2877 gtk_text_buffer_get_start_iter(buffer, &iter);
2878 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2879 gtk_text_buffer_place_cursor(buffer, &iter);
2881 compose->autowrap = prev_autowrap;
2882 if (compose->autowrap && rewrap)
2883 compose_wrap_all(compose);
2890 SIGNAL_UNBLOCK(buffer);
2892 procmsg_msginfo_free( dummyinfo );
2897 /* if ml_post is of type addr@host and from is of type
2898 * addr-anything@host, return TRUE
2900 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2902 gchar *left_ml = NULL;
2903 gchar *right_ml = NULL;
2904 gchar *left_from = NULL;
2905 gchar *right_from = NULL;
2906 gboolean result = FALSE;
2908 if (!ml_post || !from)
2911 left_ml = g_strdup(ml_post);
2912 if (strstr(left_ml, "@")) {
2913 right_ml = strstr(left_ml, "@")+1;
2914 *(strstr(left_ml, "@")) = '\0';
2917 left_from = g_strdup(from);
2918 if (strstr(left_from, "@")) {
2919 right_from = strstr(left_from, "@")+1;
2920 *(strstr(left_from, "@")) = '\0';
2923 if (left_ml && left_from && right_ml && right_from
2924 && !strncmp(left_from, left_ml, strlen(left_ml))
2925 && !strcmp(right_from, right_ml)) {
2934 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2936 gchar *my_addr1, *my_addr2;
2938 if (!addr1 || !addr2)
2941 Xstrdup_a(my_addr1, addr1, return FALSE);
2942 Xstrdup_a(my_addr2, addr2, return FALSE);
2944 extract_address(my_addr1);
2945 extract_address(my_addr2);
2947 return !strcasecmp(my_addr1, my_addr2);
2950 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2951 gboolean to_all, gboolean to_ml,
2953 gboolean followup_and_reply_to)
2955 GSList *cc_list = NULL;
2958 gchar *replyto = NULL;
2959 GHashTable *to_table;
2961 gboolean reply_to_ml = FALSE;
2962 gboolean default_reply_to = FALSE;
2964 g_return_if_fail(compose->account != NULL);
2965 g_return_if_fail(msginfo != NULL);
2967 reply_to_ml = to_ml && compose->ml_post;
2969 default_reply_to = msginfo->folder &&
2970 msginfo->folder->prefs->enable_default_reply_to;
2972 if (compose->account->protocol != A_NNTP) {
2973 if (reply_to_ml && !default_reply_to) {
2975 gboolean is_subscr = is_subscription(compose->ml_post,
2978 /* normal answer to ml post with a reply-to */
2979 compose_entry_append(compose,
2982 if (compose->replyto
2983 && !same_address(compose->ml_post, compose->replyto))
2984 compose_entry_append(compose,
2988 /* answer to subscription confirmation */
2989 if (compose->replyto)
2990 compose_entry_append(compose,
2993 else if (msginfo->from)
2994 compose_entry_append(compose,
2999 else if (!(to_all || to_sender) && default_reply_to) {
3000 compose_entry_append(compose,
3001 msginfo->folder->prefs->default_reply_to,
3003 compose_entry_mark_default_to(compose,
3004 msginfo->folder->prefs->default_reply_to);
3009 Xstrdup_a(tmp1, msginfo->from, return);
3010 extract_address(tmp1);
3011 if (to_all || to_sender ||
3012 !account_find_from_address(tmp1, FALSE))
3013 compose_entry_append(compose,
3014 (compose->replyto && !to_sender)
3015 ? compose->replyto :
3016 msginfo->from ? msginfo->from : "",
3018 else if (!to_all && !to_sender) {
3019 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3020 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3021 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3022 if (compose->replyto) {
3023 compose_entry_append(compose,
3027 compose_entry_append(compose,
3028 msginfo->from ? msginfo->from : "",
3032 /* replying to own mail, use original recp */
3033 compose_entry_append(compose,
3034 msginfo->to ? msginfo->to : "",
3036 compose_entry_append(compose,
3037 msginfo->cc ? msginfo->cc : "",
3043 if (to_sender || (compose->followup_to &&
3044 !strncmp(compose->followup_to, "poster", 6)))
3045 compose_entry_append
3047 (compose->replyto ? compose->replyto :
3048 msginfo->from ? msginfo->from : ""),
3051 else if (followup_and_reply_to || to_all) {
3052 compose_entry_append
3054 (compose->replyto ? compose->replyto :
3055 msginfo->from ? msginfo->from : ""),
3058 compose_entry_append
3060 compose->followup_to ? compose->followup_to :
3061 compose->newsgroups ? compose->newsgroups : "",
3062 COMPOSE_NEWSGROUPS);
3065 compose_entry_append
3067 compose->followup_to ? compose->followup_to :
3068 compose->newsgroups ? compose->newsgroups : "",
3069 COMPOSE_NEWSGROUPS);
3072 if (msginfo->subject && *msginfo->subject) {
3076 buf = p = g_strdup(msginfo->subject);
3077 p += subject_get_prefix_length(p);
3078 memmove(buf, p, strlen(p) + 1);
3080 buf2 = g_strdup_printf("Re: %s", buf);
3081 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3086 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3088 if (to_ml && compose->ml_post) return;
3089 if (!to_all || compose->account->protocol == A_NNTP) return;
3091 if (compose->replyto) {
3092 Xstrdup_a(replyto, compose->replyto, return);
3093 extract_address(replyto);
3095 if (msginfo->from) {
3096 Xstrdup_a(from, msginfo->from, return);
3097 extract_address(from);
3100 if (replyto && from)
3101 cc_list = address_list_append_with_comments(cc_list, from);
3102 if (to_all && msginfo->folder &&
3103 msginfo->folder->prefs->enable_default_reply_to)
3104 cc_list = address_list_append_with_comments(cc_list,
3105 msginfo->folder->prefs->default_reply_to);
3106 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3107 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3109 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3111 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3112 if (compose->account) {
3113 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3114 GINT_TO_POINTER(1));
3116 /* remove address on To: and that of current account */
3117 for (cur = cc_list; cur != NULL; ) {
3118 GSList *next = cur->next;
3121 addr = g_utf8_strdown(cur->data, -1);
3122 extract_address(addr);
3124 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3125 cc_list = g_slist_remove(cc_list, cur->data);
3127 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3131 hash_free_strings(to_table);
3132 g_hash_table_destroy(to_table);
3135 for (cur = cc_list; cur != NULL; cur = cur->next)
3136 compose_entry_append(compose, (gchar *)cur->data,
3138 slist_free_strings(cc_list);
3139 g_slist_free(cc_list);
3144 #define SET_ENTRY(entry, str) \
3147 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3150 #define SET_ADDRESS(type, str) \
3153 compose_entry_append(compose, str, type); \
3156 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3158 g_return_if_fail(msginfo != NULL);
3160 SET_ENTRY(subject_entry, msginfo->subject);
3161 SET_ENTRY(from_name, msginfo->from);
3162 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3163 SET_ADDRESS(COMPOSE_CC, compose->cc);
3164 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3165 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3166 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3167 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3169 compose_update_priority_menu_item(compose);
3170 compose_update_privacy_system_menu_item(compose, FALSE);
3171 compose_show_first_last_header(compose, TRUE);
3177 static void compose_insert_sig(Compose *compose, gboolean replace)
3179 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3180 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3182 GtkTextIter iter, iter_end;
3183 gint cur_pos, ins_pos;
3184 gboolean prev_autowrap;
3185 gboolean found = FALSE;
3186 gboolean exists = FALSE;
3188 g_return_if_fail(compose->account != NULL);
3192 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3193 G_CALLBACK(compose_changed_cb),
3196 mark = gtk_text_buffer_get_insert(buffer);
3197 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3198 cur_pos = gtk_text_iter_get_offset (&iter);
3201 gtk_text_buffer_get_end_iter(buffer, &iter);
3203 exists = (compose->sig_str != NULL);
3206 GtkTextIter first_iter, start_iter, end_iter;
3208 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3210 if (!exists || compose->sig_str[0] == '\0')
3213 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3214 compose->signature_tag);
3217 /* include previous \n\n */
3218 gtk_text_iter_backward_chars(&first_iter, 1);
3219 start_iter = first_iter;
3220 end_iter = first_iter;
3222 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3223 compose->signature_tag);
3224 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3225 compose->signature_tag);
3227 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3233 g_free(compose->sig_str);
3234 compose->sig_str = account_get_signature_str(compose->account);
3236 cur_pos = gtk_text_iter_get_offset(&iter);
3238 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3239 g_free(compose->sig_str);
3240 compose->sig_str = NULL;
3242 if (compose->sig_inserted == FALSE)
3243 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3244 compose->sig_inserted = TRUE;
3246 cur_pos = gtk_text_iter_get_offset(&iter);
3247 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3249 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3250 gtk_text_iter_forward_chars(&iter, 1);
3251 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3252 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3254 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3255 cur_pos = gtk_text_buffer_get_char_count (buffer);
3258 /* put the cursor where it should be
3259 * either where the quote_fmt says, either where it was */
3260 if (compose->set_cursor_pos < 0)
3261 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3263 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3264 compose->set_cursor_pos);
3266 compose->set_cursor_pos = -1;
3267 gtk_text_buffer_place_cursor(buffer, &iter);
3268 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3269 G_CALLBACK(compose_changed_cb),
3275 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3278 GtkTextBuffer *buffer;
3281 const gchar *cur_encoding;
3282 gchar buf[BUFFSIZE];
3285 gboolean prev_autowrap;
3286 gboolean badtxt = FALSE;
3288 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3290 if ((fp = g_fopen(file, "rb")) == NULL) {
3291 FILE_OP_ERROR(file, "fopen");
3292 return COMPOSE_INSERT_READ_ERROR;
3295 prev_autowrap = compose->autowrap;
3296 compose->autowrap = FALSE;
3298 text = GTK_TEXT_VIEW(compose->text);
3299 buffer = gtk_text_view_get_buffer(text);
3300 mark = gtk_text_buffer_get_insert(buffer);
3301 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3303 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3304 G_CALLBACK(text_inserted),
3307 cur_encoding = conv_get_locale_charset_str_no_utf8();
3309 while (fgets(buf, sizeof(buf), fp) != NULL) {
3312 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3313 str = g_strdup(buf);
3315 str = conv_codeset_strdup
3316 (buf, cur_encoding, CS_INTERNAL);
3319 /* strip <CR> if DOS/Windows file,
3320 replace <CR> with <LF> if Macintosh file. */
3323 if (len > 0 && str[len - 1] != '\n') {
3325 if (str[len] == '\r') str[len] = '\n';
3328 gtk_text_buffer_insert(buffer, &iter, str, -1);
3332 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3333 G_CALLBACK(text_inserted),
3335 compose->autowrap = prev_autowrap;
3336 if (compose->autowrap)
3337 compose_wrap_all(compose);
3342 return COMPOSE_INSERT_INVALID_CHARACTER;
3344 return COMPOSE_INSERT_SUCCESS;
3347 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3348 const gchar *filename,
3349 const gchar *content_type)
3357 GtkListStore *store;
3359 gboolean has_binary = FALSE;
3361 if (!is_file_exist(file)) {
3362 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3363 gboolean result = FALSE;
3364 if (file_from_uri && is_file_exist(file_from_uri)) {
3365 result = compose_attach_append(
3366 compose, file_from_uri,
3370 g_free(file_from_uri);
3373 alertpanel_error("File %s doesn't exist\n", filename);
3376 if ((size = get_file_size(file)) < 0) {
3377 alertpanel_error("Can't get file size of %s\n", filename);
3381 alertpanel_error(_("File %s is empty."), filename);
3384 if ((fp = g_fopen(file, "rb")) == NULL) {
3385 alertpanel_error(_("Can't read %s."), filename);
3390 ainfo = g_new0(AttachInfo, 1);
3391 auto_ainfo = g_auto_pointer_new_with_free
3392 (ainfo, (GFreeFunc) compose_attach_info_free);
3393 ainfo->file = g_strdup(file);
3396 ainfo->content_type = g_strdup(content_type);
3397 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3399 MsgFlags flags = {0, 0};
3401 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3402 ainfo->encoding = ENC_7BIT;
3404 ainfo->encoding = ENC_8BIT;
3406 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3407 if (msginfo && msginfo->subject)
3408 name = g_strdup(msginfo->subject);
3410 name = g_path_get_basename(filename ? filename : file);
3412 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3414 procmsg_msginfo_free(msginfo);
3416 if (!g_ascii_strncasecmp(content_type, "text", 4))
3417 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3419 ainfo->encoding = ENC_BASE64;
3420 name = g_path_get_basename(filename ? filename : file);
3421 ainfo->name = g_strdup(name);
3425 ainfo->content_type = procmime_get_mime_type(file);
3426 if (!ainfo->content_type) {
3427 ainfo->content_type =
3428 g_strdup("application/octet-stream");
3429 ainfo->encoding = ENC_BASE64;
3430 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3432 procmime_get_encoding_for_text_file(file, &has_binary);
3434 ainfo->encoding = ENC_BASE64;
3435 name = g_path_get_basename(filename ? filename : file);
3436 ainfo->name = g_strdup(name);
3440 if (ainfo->name != NULL
3441 && !strcmp(ainfo->name, ".")) {
3442 g_free(ainfo->name);
3446 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3447 g_free(ainfo->content_type);
3448 ainfo->content_type = g_strdup("application/octet-stream");
3451 ainfo->size = (goffset)size;
3452 size_text = to_human_readable((goffset)size);
3454 store = GTK_LIST_STORE(gtk_tree_view_get_model
3455 (GTK_TREE_VIEW(compose->attach_clist)));
3457 gtk_list_store_append(store, &iter);
3458 gtk_list_store_set(store, &iter,
3459 COL_MIMETYPE, ainfo->content_type,
3460 COL_SIZE, size_text,
3461 COL_NAME, ainfo->name,
3463 COL_AUTODATA, auto_ainfo,
3466 g_auto_pointer_free(auto_ainfo);
3467 compose_attach_update_label(compose);
3471 static void compose_use_signing(Compose *compose, gboolean use_signing)
3473 compose->use_signing = use_signing;
3474 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3477 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3479 compose->use_encryption = use_encryption;
3480 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3483 #define NEXT_PART_NOT_CHILD(info) \
3485 node = info->node; \
3486 while (node->children) \
3487 node = g_node_last_child(node); \
3488 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3491 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3495 MimeInfo *firsttext = NULL;
3496 MimeInfo *encrypted = NULL;
3499 const gchar *partname = NULL;
3501 mimeinfo = procmime_scan_message(msginfo);
3502 if (!mimeinfo) return;
3504 if (mimeinfo->node->children == NULL) {
3505 procmime_mimeinfo_free_all(mimeinfo);
3509 /* find first content part */
3510 child = (MimeInfo *) mimeinfo->node->children->data;
3511 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3512 child = (MimeInfo *)child->node->children->data;
3514 if (child->type == MIMETYPE_TEXT) {
3516 debug_print("First text part found\n");
3517 } else if (compose->mode == COMPOSE_REEDIT &&
3518 child->type == MIMETYPE_APPLICATION &&
3519 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3520 encrypted = (MimeInfo *)child->node->parent->data;
3523 child = (MimeInfo *) mimeinfo->node->children->data;
3524 while (child != NULL) {
3527 if (child == encrypted) {
3528 /* skip this part of tree */
3529 NEXT_PART_NOT_CHILD(child);
3533 if (child->type == MIMETYPE_MULTIPART) {
3534 /* get the actual content */
3535 child = procmime_mimeinfo_next(child);
3539 if (child == firsttext) {
3540 child = procmime_mimeinfo_next(child);
3544 outfile = procmime_get_tmp_file_name(child);
3545 if ((err = procmime_get_part(outfile, child)) < 0)
3546 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3548 gchar *content_type;
3550 content_type = procmime_get_content_type_str(child->type, child->subtype);
3552 /* if we meet a pgp signature, we don't attach it, but
3553 * we force signing. */
3554 if ((strcmp(content_type, "application/pgp-signature") &&
3555 strcmp(content_type, "application/pkcs7-signature") &&
3556 strcmp(content_type, "application/x-pkcs7-signature"))
3557 || compose->mode == COMPOSE_REDIRECT) {
3558 partname = procmime_mimeinfo_get_parameter(child, "filename");
3559 if (partname == NULL)
3560 partname = procmime_mimeinfo_get_parameter(child, "name");
3561 if (partname == NULL)
3563 compose_attach_append(compose, outfile,
3564 partname, content_type);
3566 compose_force_signing(compose, compose->account);
3568 g_free(content_type);
3571 NEXT_PART_NOT_CHILD(child);
3573 procmime_mimeinfo_free_all(mimeinfo);
3576 #undef NEXT_PART_NOT_CHILD
3581 WAIT_FOR_INDENT_CHAR,
3582 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3585 /* return indent length, we allow:
3586 indent characters followed by indent characters or spaces/tabs,
3587 alphabets and numbers immediately followed by indent characters,
3588 and the repeating sequences of the above
3589 If quote ends with multiple spaces, only the first one is included. */
3590 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3591 const GtkTextIter *start, gint *len)
3593 GtkTextIter iter = *start;
3597 IndentState state = WAIT_FOR_INDENT_CHAR;
3600 gint alnum_count = 0;
3601 gint space_count = 0;
3604 if (prefs_common.quote_chars == NULL) {
3608 while (!gtk_text_iter_ends_line(&iter)) {
3609 wc = gtk_text_iter_get_char(&iter);
3610 if (g_unichar_iswide(wc))
3612 clen = g_unichar_to_utf8(wc, ch);
3616 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3617 is_space = g_unichar_isspace(wc);
3619 if (state == WAIT_FOR_INDENT_CHAR) {
3620 if (!is_indent && !g_unichar_isalnum(wc))
3623 quote_len += alnum_count + space_count + 1;
3624 alnum_count = space_count = 0;
3625 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3628 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3629 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3633 else if (is_indent) {
3634 quote_len += alnum_count + space_count + 1;
3635 alnum_count = space_count = 0;
3638 state = WAIT_FOR_INDENT_CHAR;
3642 gtk_text_iter_forward_char(&iter);
3645 if (quote_len > 0 && space_count > 0)
3651 if (quote_len > 0) {
3653 gtk_text_iter_forward_chars(&iter, quote_len);
3654 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3660 /* return >0 if the line is itemized */
3661 static int compose_itemized_length(GtkTextBuffer *buffer,
3662 const GtkTextIter *start)
3664 GtkTextIter iter = *start;
3669 if (gtk_text_iter_ends_line(&iter))
3674 wc = gtk_text_iter_get_char(&iter);
3675 if (!g_unichar_isspace(wc))
3677 gtk_text_iter_forward_char(&iter);
3678 if (gtk_text_iter_ends_line(&iter))
3682 clen = g_unichar_to_utf8(wc, ch);
3686 if (!strchr("*-+", ch[0]))
3689 gtk_text_iter_forward_char(&iter);
3690 if (gtk_text_iter_ends_line(&iter))
3692 wc = gtk_text_iter_get_char(&iter);
3693 if (g_unichar_isspace(wc)) {
3699 /* return the string at the start of the itemization */
3700 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3701 const GtkTextIter *start)
3703 GtkTextIter iter = *start;
3706 GString *item_chars = g_string_new("");
3709 if (gtk_text_iter_ends_line(&iter))
3714 wc = gtk_text_iter_get_char(&iter);
3715 if (!g_unichar_isspace(wc))
3717 gtk_text_iter_forward_char(&iter);
3718 if (gtk_text_iter_ends_line(&iter))
3720 g_string_append_unichar(item_chars, wc);
3723 str = item_chars->str;
3724 g_string_free(item_chars, FALSE);
3728 /* return the number of spaces at a line's start */
3729 static int compose_left_offset_length(GtkTextBuffer *buffer,
3730 const GtkTextIter *start)
3732 GtkTextIter iter = *start;
3735 if (gtk_text_iter_ends_line(&iter))
3739 wc = gtk_text_iter_get_char(&iter);
3740 if (!g_unichar_isspace(wc))
3743 gtk_text_iter_forward_char(&iter);
3744 if (gtk_text_iter_ends_line(&iter))
3748 gtk_text_iter_forward_char(&iter);
3749 if (gtk_text_iter_ends_line(&iter))
3754 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3755 const GtkTextIter *start,
3756 GtkTextIter *break_pos,
3760 GtkTextIter iter = *start, line_end = *start;
3761 PangoLogAttr *attrs;
3768 gboolean can_break = FALSE;
3769 gboolean do_break = FALSE;
3770 gboolean was_white = FALSE;
3771 gboolean prev_dont_break = FALSE;
3773 gtk_text_iter_forward_to_line_end(&line_end);
3774 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3775 len = g_utf8_strlen(str, -1);
3779 g_warning("compose_get_line_break_pos: len = 0!\n");
3783 /* g_print("breaking line: %d: %s (len = %d)\n",
3784 gtk_text_iter_get_line(&iter), str, len); */
3786 attrs = g_new(PangoLogAttr, len + 1);
3788 pango_default_break(str, -1, NULL, attrs, len + 1);
3792 /* skip quote and leading spaces */
3793 for (i = 0; *p != '\0' && i < len; i++) {
3796 wc = g_utf8_get_char(p);
3797 if (i >= quote_len && !g_unichar_isspace(wc))
3799 if (g_unichar_iswide(wc))
3801 else if (*p == '\t')
3805 p = g_utf8_next_char(p);
3808 for (; *p != '\0' && i < len; i++) {
3809 PangoLogAttr *attr = attrs + i;
3813 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3816 was_white = attr->is_white;
3818 /* don't wrap URI */
3819 if ((uri_len = get_uri_len(p)) > 0) {
3821 if (pos > 0 && col > max_col) {
3831 wc = g_utf8_get_char(p);
3832 if (g_unichar_iswide(wc)) {
3834 if (prev_dont_break && can_break && attr->is_line_break)
3836 } else if (*p == '\t')
3840 if (pos > 0 && col > max_col) {
3845 if (*p == '-' || *p == '/')
3846 prev_dont_break = TRUE;
3848 prev_dont_break = FALSE;
3850 p = g_utf8_next_char(p);
3854 debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3859 *break_pos = *start;
3860 gtk_text_iter_set_line_offset(break_pos, pos);
3865 static gboolean compose_join_next_line(Compose *compose,
3866 GtkTextBuffer *buffer,
3868 const gchar *quote_str)
3870 GtkTextIter iter_ = *iter, cur, prev, next, end;
3871 PangoLogAttr attrs[3];
3873 gchar *next_quote_str;
3876 gboolean keep_cursor = FALSE;
3878 if (!gtk_text_iter_forward_line(&iter_) ||
3879 gtk_text_iter_ends_line(&iter_)) {
3882 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3884 if ((quote_str || next_quote_str) &&
3885 strcmp2(quote_str, next_quote_str) != 0) {
3886 g_free(next_quote_str);
3889 g_free(next_quote_str);
3892 if (quote_len > 0) {
3893 gtk_text_iter_forward_chars(&end, quote_len);
3894 if (gtk_text_iter_ends_line(&end)) {
3899 /* don't join itemized lines */
3900 if (compose_itemized_length(buffer, &end) > 0) {
3904 /* don't join signature separator */
3905 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3908 /* delete quote str */
3910 gtk_text_buffer_delete(buffer, &iter_, &end);
3912 /* don't join line breaks put by the user */
3914 gtk_text_iter_backward_char(&cur);
3915 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3916 gtk_text_iter_forward_char(&cur);
3920 gtk_text_iter_forward_char(&cur);
3921 /* delete linebreak and extra spaces */
3922 while (gtk_text_iter_backward_char(&cur)) {
3923 wc1 = gtk_text_iter_get_char(&cur);
3924 if (!g_unichar_isspace(wc1))
3929 while (!gtk_text_iter_ends_line(&cur)) {
3930 wc1 = gtk_text_iter_get_char(&cur);
3931 if (!g_unichar_isspace(wc1))
3933 gtk_text_iter_forward_char(&cur);
3936 if (!gtk_text_iter_equal(&prev, &next)) {
3939 mark = gtk_text_buffer_get_insert(buffer);
3940 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3941 if (gtk_text_iter_equal(&prev, &cur))
3943 gtk_text_buffer_delete(buffer, &prev, &next);
3947 /* insert space if required */
3948 gtk_text_iter_backward_char(&prev);
3949 wc1 = gtk_text_iter_get_char(&prev);
3950 wc2 = gtk_text_iter_get_char(&next);
3951 gtk_text_iter_forward_char(&next);
3952 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3953 pango_default_break(str, -1, NULL, attrs, 3);
3954 if (!attrs[1].is_line_break ||
3955 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3956 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3958 gtk_text_iter_backward_char(&iter_);
3959 gtk_text_buffer_place_cursor(buffer, &iter_);
3968 #define ADD_TXT_POS(bp_, ep_, pti_) \
3969 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3970 last = last->next; \
3971 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3972 last->next = NULL; \
3974 g_warning("alloc error scanning URIs\n"); \
3977 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3979 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3980 GtkTextBuffer *buffer;
3981 GtkTextIter iter, break_pos, end_of_line;
3982 gchar *quote_str = NULL;
3984 gboolean wrap_quote = prefs_common.linewrap_quote;
3985 gboolean prev_autowrap = compose->autowrap;
3986 gint startq_offset = -1, noq_offset = -1;
3987 gint uri_start = -1, uri_stop = -1;
3988 gint nouri_start = -1, nouri_stop = -1;
3989 gint num_blocks = 0;
3990 gint quotelevel = -1;
3991 gboolean modified = force;
3992 gboolean removed = FALSE;
3993 gboolean modified_before_remove = FALSE;
3995 gboolean start = TRUE;
3996 gint itemized_len = 0, rem_item_len = 0;
3997 gchar *itemized_chars = NULL;
3998 gboolean item_continuation = FALSE;
4003 if (compose->draft_timeout_tag == -2) {
4007 compose->autowrap = FALSE;
4009 buffer = gtk_text_view_get_buffer(text);
4010 undo_wrapping(compose->undostruct, TRUE);
4015 mark = gtk_text_buffer_get_insert(buffer);
4016 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4020 if (compose->draft_timeout_tag == -2) {
4021 if (gtk_text_iter_ends_line(&iter)) {
4022 while (gtk_text_iter_ends_line(&iter) &&
4023 gtk_text_iter_forward_line(&iter))
4026 while (gtk_text_iter_backward_line(&iter)) {
4027 if (gtk_text_iter_ends_line(&iter)) {
4028 gtk_text_iter_forward_line(&iter);
4034 /* move to line start */
4035 gtk_text_iter_set_line_offset(&iter, 0);
4038 itemized_len = compose_itemized_length(buffer, &iter);
4040 if (!itemized_len) {
4041 itemized_len = compose_left_offset_length(buffer, &iter);
4042 item_continuation = TRUE;
4046 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4048 /* go until paragraph end (empty line) */
4049 while (start || !gtk_text_iter_ends_line(&iter)) {
4050 gchar *scanpos = NULL;
4051 /* parse table - in order of priority */
4053 const gchar *needle; /* token */
4055 /* token search function */
4056 gchar *(*search) (const gchar *haystack,
4057 const gchar *needle);
4058 /* part parsing function */
4059 gboolean (*parse) (const gchar *start,
4060 const gchar *scanpos,
4064 /* part to URI function */
4065 gchar *(*build_uri) (const gchar *bp,
4069 static struct table parser[] = {
4070 {"http://", strcasestr, get_uri_part, make_uri_string},
4071 {"https://", strcasestr, get_uri_part, make_uri_string},
4072 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4073 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4074 {"www.", strcasestr, get_uri_part, make_http_string},
4075 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4076 {"@", strcasestr, get_email_part, make_email_string}
4078 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4079 gint last_index = PARSE_ELEMS;
4081 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4085 if (!prev_autowrap && num_blocks == 0) {
4087 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4088 G_CALLBACK(text_inserted),
4091 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4094 uri_start = uri_stop = -1;
4096 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4099 debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4100 if (startq_offset == -1)
4101 startq_offset = gtk_text_iter_get_offset(&iter);
4102 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4103 if (quotelevel > 2) {
4104 /* recycle colors */
4105 if (prefs_common.recycle_quote_colors)
4114 if (startq_offset == -1)
4115 noq_offset = gtk_text_iter_get_offset(&iter);
4119 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4122 if (gtk_text_iter_ends_line(&iter)) {
4124 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4125 prefs_common.linewrap_len,
4127 GtkTextIter prev, next, cur;
4128 if (prev_autowrap != FALSE || force) {
4129 compose->automatic_break = TRUE;
4131 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4132 compose->automatic_break = FALSE;
4133 if (itemized_len && compose->autoindent) {
4134 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4135 if (!item_continuation)
4136 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4138 } else if (quote_str && wrap_quote) {
4139 compose->automatic_break = TRUE;
4141 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4142 compose->automatic_break = FALSE;
4143 if (itemized_len && compose->autoindent) {
4144 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4145 if (!item_continuation)
4146 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4150 /* remove trailing spaces */
4152 rem_item_len = itemized_len;
4153 while (compose->autoindent && rem_item_len-- > 0)
4154 gtk_text_iter_backward_char(&cur);
4155 gtk_text_iter_backward_char(&cur);
4158 while (!gtk_text_iter_starts_line(&cur)) {
4161 gtk_text_iter_backward_char(&cur);
4162 wc = gtk_text_iter_get_char(&cur);
4163 if (!g_unichar_isspace(wc))
4167 if (!gtk_text_iter_equal(&prev, &next)) {
4168 gtk_text_buffer_delete(buffer, &prev, &next);
4170 gtk_text_iter_forward_char(&break_pos);
4174 gtk_text_buffer_insert(buffer, &break_pos,
4178 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4180 /* move iter to current line start */
4181 gtk_text_iter_set_line_offset(&iter, 0);
4188 /* move iter to next line start */
4194 if (!prev_autowrap && num_blocks > 0) {
4196 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4197 G_CALLBACK(text_inserted),
4201 while (!gtk_text_iter_ends_line(&end_of_line)) {
4202 gtk_text_iter_forward_char(&end_of_line);
4204 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4206 nouri_start = gtk_text_iter_get_offset(&iter);
4207 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4209 walk_pos = gtk_text_iter_get_offset(&iter);
4210 /* FIXME: this looks phony. scanning for anything in the parse table */
4211 for (n = 0; n < PARSE_ELEMS; n++) {
4214 tmp = parser[n].search(walk, parser[n].needle);
4216 if (scanpos == NULL || tmp < scanpos) {
4225 /* check if URI can be parsed */
4226 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4227 (const gchar **)&ep, FALSE)
4228 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4232 strlen(parser[last_index].needle);
4235 uri_start = walk_pos + (bp - o_walk);
4236 uri_stop = walk_pos + (ep - o_walk);
4240 gtk_text_iter_forward_line(&iter);
4243 if (startq_offset != -1) {
4244 GtkTextIter startquote, endquote;
4245 gtk_text_buffer_get_iter_at_offset(
4246 buffer, &startquote, startq_offset);
4249 switch (quotelevel) {
4251 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4252 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4253 gtk_text_buffer_apply_tag_by_name(
4254 buffer, "quote0", &startquote, &endquote);
4255 gtk_text_buffer_remove_tag_by_name(
4256 buffer, "quote1", &startquote, &endquote);
4257 gtk_text_buffer_remove_tag_by_name(
4258 buffer, "quote2", &startquote, &endquote);
4263 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4264 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4265 gtk_text_buffer_apply_tag_by_name(
4266 buffer, "quote1", &startquote, &endquote);
4267 gtk_text_buffer_remove_tag_by_name(
4268 buffer, "quote0", &startquote, &endquote);
4269 gtk_text_buffer_remove_tag_by_name(
4270 buffer, "quote2", &startquote, &endquote);
4275 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4276 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4277 gtk_text_buffer_apply_tag_by_name(
4278 buffer, "quote2", &startquote, &endquote);
4279 gtk_text_buffer_remove_tag_by_name(
4280 buffer, "quote0", &startquote, &endquote);
4281 gtk_text_buffer_remove_tag_by_name(
4282 buffer, "quote1", &startquote, &endquote);
4288 } else if (noq_offset != -1) {
4289 GtkTextIter startnoquote, endnoquote;
4290 gtk_text_buffer_get_iter_at_offset(
4291 buffer, &startnoquote, noq_offset);
4294 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4295 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4296 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4297 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4298 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4299 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4300 gtk_text_buffer_remove_tag_by_name(
4301 buffer, "quote0", &startnoquote, &endnoquote);
4302 gtk_text_buffer_remove_tag_by_name(
4303 buffer, "quote1", &startnoquote, &endnoquote);
4304 gtk_text_buffer_remove_tag_by_name(
4305 buffer, "quote2", &startnoquote, &endnoquote);
4311 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4312 GtkTextIter nouri_start_iter, nouri_end_iter;
4313 gtk_text_buffer_get_iter_at_offset(
4314 buffer, &nouri_start_iter, nouri_start);
4315 gtk_text_buffer_get_iter_at_offset(
4316 buffer, &nouri_end_iter, nouri_stop);
4317 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4318 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4319 gtk_text_buffer_remove_tag_by_name(
4320 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4321 modified_before_remove = modified;
4326 if (uri_start >= 0 && uri_stop > 0) {
4327 GtkTextIter uri_start_iter, uri_end_iter, back;
4328 gtk_text_buffer_get_iter_at_offset(
4329 buffer, &uri_start_iter, uri_start);
4330 gtk_text_buffer_get_iter_at_offset(
4331 buffer, &uri_end_iter, uri_stop);
4332 back = uri_end_iter;
4333 gtk_text_iter_backward_char(&back);
4334 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4335 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4336 gtk_text_buffer_apply_tag_by_name(
4337 buffer, "link", &uri_start_iter, &uri_end_iter);
4339 if (removed && !modified_before_remove) {
4345 debug_print("not modified, out after %d lines\n", lines);
4349 debug_print("modified, out after %d lines\n", lines);
4351 g_free(itemized_chars);
4354 undo_wrapping(compose->undostruct, FALSE);
4355 compose->autowrap = prev_autowrap;
4360 void compose_action_cb(void *data)
4362 Compose *compose = (Compose *)data;
4363 compose_wrap_all(compose);
4366 static void compose_wrap_all(Compose *compose)
4368 compose_wrap_all_full(compose, FALSE);
4371 static void compose_wrap_all_full(Compose *compose, gboolean force)
4373 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4374 GtkTextBuffer *buffer;
4376 gboolean modified = TRUE;
4378 buffer = gtk_text_view_get_buffer(text);
4380 gtk_text_buffer_get_start_iter(buffer, &iter);
4381 while (!gtk_text_iter_is_end(&iter) && modified)
4382 modified = compose_beautify_paragraph(compose, &iter, force);
4386 static void compose_set_title(Compose *compose)
4392 edited = compose->modified ? _(" [Edited]") : "";
4394 subject = gtk_editable_get_chars(
4395 GTK_EDITABLE(compose->subject_entry), 0, -1);
4397 #ifndef GENERIC_UMPC
4398 if (subject && strlen(subject))
4399 str = g_strdup_printf(_("%s - Compose message%s"),
4402 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4404 str = g_strdup(_("Compose message"));
4407 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4413 * compose_current_mail_account:
4415 * Find a current mail account (the currently selected account, or the
4416 * default account, if a news account is currently selected). If a
4417 * mail account cannot be found, display an error message.
4419 * Return value: Mail account, or NULL if not found.
4421 static PrefsAccount *
4422 compose_current_mail_account(void)
4426 if (cur_account && cur_account->protocol != A_NNTP)
4429 ac = account_get_default();
4430 if (!ac || ac->protocol == A_NNTP) {
4431 alertpanel_error(_("Account for sending mail is not specified.\n"
4432 "Please select a mail account before sending."));
4439 #define QUOTE_IF_REQUIRED(out, str) \
4441 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4445 len = strlen(str) + 3; \
4446 if ((__tmp = alloca(len)) == NULL) { \
4447 g_warning("can't allocate memory\n"); \
4448 g_string_free(header, TRUE); \
4451 g_snprintf(__tmp, len, "\"%s\"", str); \
4456 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4457 g_warning("can't allocate memory\n"); \
4458 g_string_free(header, TRUE); \
4461 strcpy(__tmp, str); \
4467 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4469 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4473 len = strlen(str) + 3; \
4474 if ((__tmp = alloca(len)) == NULL) { \
4475 g_warning("can't allocate memory\n"); \
4478 g_snprintf(__tmp, len, "\"%s\"", str); \
4483 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4484 g_warning("can't allocate memory\n"); \
4487 strcpy(__tmp, str); \
4493 static void compose_select_account(Compose *compose, PrefsAccount *account,
4498 g_return_if_fail(account != NULL);
4500 compose->account = account;
4502 if (account->name && *account->name) {
4504 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4505 from = g_strdup_printf("%s <%s>",
4506 buf, account->address);
4507 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4509 from = g_strdup_printf("<%s>",
4511 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4516 compose_set_title(compose);
4518 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4519 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4522 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4523 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4525 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4527 activate_privacy_system(compose, account, FALSE);
4529 if (!init && compose->mode != COMPOSE_REDIRECT) {
4530 undo_block(compose->undostruct);
4531 compose_insert_sig(compose, TRUE);
4532 undo_unblock(compose->undostruct);
4536 /* use account's dict info if set */
4537 if (compose->gtkaspell) {
4538 if (account->enable_default_dictionary)
4539 gtkaspell_change_dict(compose->gtkaspell,
4540 account->default_dictionary, FALSE);
4541 if (account->enable_default_alt_dictionary)
4542 gtkaspell_change_alt_dict(compose->gtkaspell,
4543 account->default_alt_dictionary);
4544 if (account->enable_default_dictionary
4545 || account->enable_default_alt_dictionary)
4546 compose_spell_menu_changed(compose);
4551 gboolean compose_check_for_valid_recipient(Compose *compose) {
4552 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4553 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4554 gboolean recipient_found = FALSE;
4558 /* free to and newsgroup list */
4559 slist_free_strings(compose->to_list);
4560 g_slist_free(compose->to_list);
4561 compose->to_list = NULL;
4563 slist_free_strings(compose->newsgroup_list);
4564 g_slist_free(compose->newsgroup_list);
4565 compose->newsgroup_list = NULL;
4567 /* search header entries for to and newsgroup entries */
4568 for (list = compose->header_list; list; list = list->next) {
4571 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4572 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4575 if (entry[0] != '\0') {
4576 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4577 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4578 compose->to_list = address_list_append(compose->to_list, entry);
4579 recipient_found = TRUE;
4582 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4583 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4584 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4585 recipient_found = TRUE;
4592 return recipient_found;
4595 static gboolean compose_check_for_set_recipients(Compose *compose)
4597 if (compose->account->set_autocc && compose->account->auto_cc) {
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_cc)
4609 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4619 if (compose->batch) {
4620 gtk_widget_show_all(compose->window);
4622 aval = alertpanel(_("Send"),
4623 _("The only recipient is the default CC address. Send anyway?"),
4624 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4625 if (aval != G_ALERTALTERNATE)
4629 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4630 gboolean found_other = FALSE;
4632 /* search header entries for to and newsgroup entries */
4633 for (list = compose->header_list; list; list = list->next) {
4636 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4637 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4640 if (strcmp(entry, compose->account->auto_bcc)
4641 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4651 if (compose->batch) {
4652 gtk_widget_show_all(compose->window);
4654 aval = alertpanel(_("Send"),
4655 _("The only recipient is the default BCC address. Send anyway?"),
4656 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4657 if (aval != G_ALERTALTERNATE)
4664 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4668 if (compose_check_for_valid_recipient(compose) == FALSE) {
4669 if (compose->batch) {
4670 gtk_widget_show_all(compose->window);
4672 alertpanel_error(_("Recipient is not specified."));
4676 if (compose_check_for_set_recipients(compose) == FALSE) {
4680 if (!compose->batch) {
4681 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4682 if (*str == '\0' && check_everything == TRUE &&
4683 compose->mode != COMPOSE_REDIRECT) {
4685 gchar *button_label;
4688 if (compose->sending)
4689 button_label = _("+_Send");
4691 button_label = _("+_Queue");
4692 message = g_strdup_printf(_("Subject is empty. %s"),
4693 compose->sending?_("Send it anyway?"):
4694 _("Queue it anyway?"));
4696 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4697 GTK_STOCK_CANCEL, button_label, NULL);
4699 if (aval != G_ALERTALTERNATE)
4704 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4710 gint compose_send(Compose *compose)
4713 FolderItem *folder = NULL;
4715 gchar *msgpath = NULL;
4716 gboolean discard_window = FALSE;
4717 gchar *errstr = NULL;
4718 gchar *tmsgid = NULL;
4719 MainWindow *mainwin = mainwindow_get_mainwindow();
4720 gboolean queued_removed = FALSE;
4722 if (prefs_common.send_dialog_invisible
4723 || compose->batch == TRUE)
4724 discard_window = TRUE;
4726 compose_allow_user_actions (compose, FALSE);
4727 compose->sending = TRUE;
4729 if (compose_check_entries(compose, TRUE) == FALSE) {
4730 if (compose->batch) {
4731 gtk_widget_show_all(compose->window);
4737 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4740 if (compose->batch) {
4741 gtk_widget_show_all(compose->window);
4744 alertpanel_error(_("Could not queue message for sending:\n\n"
4745 "Charset conversion failed."));
4746 } else if (val == -5) {
4747 alertpanel_error(_("Could not queue message for sending:\n\n"
4748 "Couldn't get recipient encryption key."));
4749 } else if (val == -6) {
4751 } else if (val == -3) {
4752 if (privacy_peek_error())
4753 alertpanel_error(_("Could not queue message for sending:\n\n"
4754 "Signature failed: %s"), privacy_get_error());
4755 } else if (val == -2 && errno != 0) {
4756 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4758 alertpanel_error(_("Could not queue message for sending."));
4763 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4764 if (discard_window) {
4765 compose->sending = FALSE;
4766 compose_close(compose);
4767 /* No more compose access in the normal codepath
4768 * after this point! */
4773 alertpanel_error(_("The message was queued but could not be "
4774 "sent.\nUse \"Send queued messages\" from "
4775 "the main window to retry."));
4776 if (!discard_window) {
4783 if (msgpath == NULL) {
4784 msgpath = folder_item_fetch_msg(folder, msgnum);
4785 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4788 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4789 claws_unlink(msgpath);
4792 if (!discard_window) {
4794 if (!queued_removed)
4795 folder_item_remove_msg(folder, msgnum);
4796 folder_item_scan(folder);
4798 /* make sure we delete that */
4799 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4801 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4802 folder_item_remove_msg(folder, tmp->msgnum);
4803 procmsg_msginfo_free(tmp);
4810 if (!queued_removed)
4811 folder_item_remove_msg(folder, msgnum);
4812 folder_item_scan(folder);
4814 /* make sure we delete that */
4815 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4817 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4818 folder_item_remove_msg(folder, tmp->msgnum);
4819 procmsg_msginfo_free(tmp);
4822 if (!discard_window) {
4823 compose->sending = FALSE;
4824 compose_allow_user_actions (compose, TRUE);
4825 compose_close(compose);
4829 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4830 "the main window to retry."), errstr);
4833 alertpanel_error_log(_("The message was queued but could not be "
4834 "sent.\nUse \"Send queued messages\" from "
4835 "the main window to retry."));
4837 if (!discard_window) {
4846 toolbar_main_set_sensitive(mainwin);
4847 main_window_set_menu_sensitive(mainwin);
4853 compose_allow_user_actions (compose, TRUE);
4854 compose->sending = FALSE;
4855 compose->modified = TRUE;
4856 toolbar_main_set_sensitive(mainwin);
4857 main_window_set_menu_sensitive(mainwin);
4862 static gboolean compose_use_attach(Compose *compose)
4864 GtkTreeModel *model = gtk_tree_view_get_model
4865 (GTK_TREE_VIEW(compose->attach_clist));
4866 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4869 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4872 gchar buf[BUFFSIZE];
4874 gboolean first_to_address;
4875 gboolean first_cc_address;
4877 ComposeHeaderEntry *headerentry;
4878 const gchar *headerentryname;
4879 const gchar *cc_hdr;
4880 const gchar *to_hdr;
4881 gboolean err = FALSE;
4883 debug_print("Writing redirect header\n");
4885 cc_hdr = prefs_common_translated_header_name("Cc:");
4886 to_hdr = prefs_common_translated_header_name("To:");
4888 first_to_address = TRUE;
4889 for (list = compose->header_list; list; list = list->next) {
4890 headerentry = ((ComposeHeaderEntry *)list->data);
4891 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4893 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4894 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4895 Xstrdup_a(str, entstr, return -1);
4897 if (str[0] != '\0') {
4898 compose_convert_header
4899 (compose, buf, sizeof(buf), str,
4900 strlen("Resent-To") + 2, TRUE);
4902 if (first_to_address) {
4903 err |= (fprintf(fp, "Resent-To: ") < 0);
4904 first_to_address = FALSE;
4906 err |= (fprintf(fp, ",") < 0);
4908 err |= (fprintf(fp, "%s", buf) < 0);
4912 if (!first_to_address) {
4913 err |= (fprintf(fp, "\n") < 0);
4916 first_cc_address = TRUE;
4917 for (list = compose->header_list; list; list = list->next) {
4918 headerentry = ((ComposeHeaderEntry *)list->data);
4919 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4921 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4922 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4923 Xstrdup_a(str, strg, return -1);
4925 if (str[0] != '\0') {
4926 compose_convert_header
4927 (compose, buf, sizeof(buf), str,
4928 strlen("Resent-Cc") + 2, TRUE);
4930 if (first_cc_address) {
4931 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4932 first_cc_address = FALSE;
4934 err |= (fprintf(fp, ",") < 0);
4936 err |= (fprintf(fp, "%s", buf) < 0);
4940 if (!first_cc_address) {
4941 err |= (fprintf(fp, "\n") < 0);
4944 return (err ? -1:0);
4947 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4949 gchar buf[BUFFSIZE];
4951 const gchar *entstr;
4952 /* struct utsname utsbuf; */
4953 gboolean err = FALSE;
4955 g_return_val_if_fail(fp != NULL, -1);
4956 g_return_val_if_fail(compose->account != NULL, -1);
4957 g_return_val_if_fail(compose->account->address != NULL, -1);
4960 get_rfc822_date(buf, sizeof(buf));
4961 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4964 if (compose->account->name && *compose->account->name) {
4965 compose_convert_header
4966 (compose, buf, sizeof(buf), compose->account->name,
4967 strlen("From: "), TRUE);
4968 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4969 buf, compose->account->address) < 0);
4971 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4974 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4975 if (*entstr != '\0') {
4976 Xstrdup_a(str, entstr, return -1);
4979 compose_convert_header(compose, buf, sizeof(buf), str,
4980 strlen("Subject: "), FALSE);
4981 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4985 /* Resent-Message-ID */
4986 if (compose->account->set_domain && compose->account->domain) {
4987 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
4988 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4989 g_snprintf(buf, sizeof(buf), "%s",
4990 strchr(compose->account->address, '@') ?
4991 strchr(compose->account->address, '@')+1 :
4992 compose->account->address);
4994 g_snprintf(buf, sizeof(buf), "%s", "");
4997 if (compose->account->gen_msgid) {
4998 generate_msgid(buf, sizeof(buf));
4999 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5000 compose->msgid = g_strdup(buf);
5002 compose->msgid = NULL;
5005 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5008 /* separator between header and body */
5009 err |= (fputs("\n", fp) == EOF);
5011 return (err ? -1:0);
5014 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5018 gchar buf[BUFFSIZE];
5020 gboolean skip = FALSE;
5021 gboolean err = FALSE;
5022 gchar *not_included[]={
5023 "Return-Path:", "Delivered-To:", "Received:",
5024 "Subject:", "X-UIDL:", "AF:",
5025 "NF:", "PS:", "SRH:",
5026 "SFN:", "DSR:", "MID:",
5027 "CFG:", "PT:", "S:",
5028 "RQ:", "SSV:", "NSV:",
5029 "SSH:", "R:", "MAID:",
5030 "NAID:", "RMID:", "FMID:",
5031 "SCF:", "RRCPT:", "NG:",
5032 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5033 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5034 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5035 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5036 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5039 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5040 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5044 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5046 for (i = 0; not_included[i] != NULL; i++) {
5047 if (g_ascii_strncasecmp(buf, not_included[i],
5048 strlen(not_included[i])) == 0) {
5055 if (fputs(buf, fdest) == -1)
5058 if (!prefs_common.redirect_keep_from) {
5059 if (g_ascii_strncasecmp(buf, "From:",
5060 strlen("From:")) == 0) {
5061 err |= (fputs(" (by way of ", fdest) == EOF);
5062 if (compose->account->name
5063 && *compose->account->name) {
5064 compose_convert_header
5065 (compose, buf, sizeof(buf),
5066 compose->account->name,
5069 err |= (fprintf(fdest, "%s <%s>",
5071 compose->account->address) < 0);
5073 err |= (fprintf(fdest, "%s",
5074 compose->account->address) < 0);
5075 err |= (fputs(")", fdest) == EOF);
5079 if (fputs("\n", fdest) == -1)
5086 if (compose_redirect_write_headers(compose, fdest))
5089 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5090 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5103 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5105 GtkTextBuffer *buffer;
5106 GtkTextIter start, end;
5109 const gchar *out_codeset;
5110 EncodingType encoding = ENC_UNKNOWN;
5111 MimeInfo *mimemsg, *mimetext;
5113 const gchar *src_codeset = CS_INTERNAL;
5114 gchar *from_addr = NULL;
5115 gchar *from_name = NULL;
5117 if (action == COMPOSE_WRITE_FOR_SEND)
5118 attach_parts = TRUE;
5120 /* create message MimeInfo */
5121 mimemsg = procmime_mimeinfo_new();
5122 mimemsg->type = MIMETYPE_MESSAGE;
5123 mimemsg->subtype = g_strdup("rfc822");
5124 mimemsg->content = MIMECONTENT_MEM;
5125 mimemsg->tmp = TRUE; /* must free content later */
5126 mimemsg->data.mem = compose_get_header(compose);
5128 /* Create text part MimeInfo */
5129 /* get all composed text */
5130 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5131 gtk_text_buffer_get_start_iter(buffer, &start);
5132 gtk_text_buffer_get_end_iter(buffer, &end);
5133 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5135 out_codeset = conv_get_charset_str(compose->out_encoding);
5137 if (!out_codeset && is_ascii_str(chars)) {
5138 out_codeset = CS_US_ASCII;
5139 } else if (prefs_common.outgoing_fallback_to_ascii &&
5140 is_ascii_str(chars)) {
5141 out_codeset = CS_US_ASCII;
5142 encoding = ENC_7BIT;
5146 gchar *test_conv_global_out = NULL;
5147 gchar *test_conv_reply = NULL;
5149 /* automatic mode. be automatic. */
5150 codeconv_set_strict(TRUE);
5152 out_codeset = conv_get_outgoing_charset_str();
5154 debug_print("trying to convert to %s\n", out_codeset);
5155 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5158 if (!test_conv_global_out && compose->orig_charset
5159 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5160 out_codeset = compose->orig_charset;
5161 debug_print("failure; trying to convert to %s\n", out_codeset);
5162 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5165 if (!test_conv_global_out && !test_conv_reply) {
5167 out_codeset = CS_INTERNAL;
5168 debug_print("failure; finally using %s\n", out_codeset);
5170 g_free(test_conv_global_out);
5171 g_free(test_conv_reply);
5172 codeconv_set_strict(FALSE);
5175 if (encoding == ENC_UNKNOWN) {
5176 if (prefs_common.encoding_method == CTE_BASE64)
5177 encoding = ENC_BASE64;
5178 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5179 encoding = ENC_QUOTED_PRINTABLE;
5180 else if (prefs_common.encoding_method == CTE_8BIT)
5181 encoding = ENC_8BIT;
5183 encoding = procmime_get_encoding_for_charset(out_codeset);
5186 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5187 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5189 if (action == COMPOSE_WRITE_FOR_SEND) {
5190 codeconv_set_strict(TRUE);
5191 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5192 codeconv_set_strict(FALSE);
5198 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5199 "to the specified %s charset.\n"
5200 "Send it as %s?"), out_codeset, src_codeset);
5201 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5202 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5205 if (aval != G_ALERTALTERNATE) {
5210 out_codeset = src_codeset;
5216 out_codeset = src_codeset;
5221 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5222 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5223 strstr(buf, "\nFrom ") != NULL) {
5224 encoding = ENC_QUOTED_PRINTABLE;
5228 mimetext = procmime_mimeinfo_new();
5229 mimetext->content = MIMECONTENT_MEM;
5230 mimetext->tmp = TRUE; /* must free content later */
5231 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5232 * and free the data, which we need later. */
5233 mimetext->data.mem = g_strdup(buf);
5234 mimetext->type = MIMETYPE_TEXT;
5235 mimetext->subtype = g_strdup("plain");
5236 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5237 g_strdup(out_codeset));
5239 /* protect trailing spaces when signing message */
5240 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5241 privacy_system_can_sign(compose->privacy_system)) {
5242 encoding = ENC_QUOTED_PRINTABLE;
5245 debug_print("main text: %zd bytes encoded as %s in %d\n",
5246 strlen(buf), out_codeset, encoding);
5248 /* check for line length limit */
5249 if (action == COMPOSE_WRITE_FOR_SEND &&
5250 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5251 check_line_length(buf, 1000, &line) < 0) {
5255 msg = g_strdup_printf
5256 (_("Line %d exceeds the line length limit (998 bytes).\n"
5257 "The contents of the message might be broken on the way to the delivery.\n"
5259 "Send it anyway?"), line + 1);
5260 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5262 if (aval != G_ALERTALTERNATE) {
5268 if (encoding != ENC_UNKNOWN)
5269 procmime_encode_content(mimetext, encoding);
5271 /* append attachment parts */
5272 if (compose_use_attach(compose) && attach_parts) {
5273 MimeInfo *mimempart;
5274 gchar *boundary = NULL;
5275 mimempart = procmime_mimeinfo_new();
5276 mimempart->content = MIMECONTENT_EMPTY;
5277 mimempart->type = MIMETYPE_MULTIPART;
5278 mimempart->subtype = g_strdup("mixed");
5282 boundary = generate_mime_boundary(NULL);
5283 } while (strstr(buf, boundary) != NULL);
5285 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5288 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5290 g_node_append(mimempart->node, mimetext->node);
5291 g_node_append(mimemsg->node, mimempart->node);
5293 compose_add_attachments(compose, mimempart);
5295 g_node_append(mimemsg->node, mimetext->node);
5299 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5300 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5301 /* extract name and address */
5302 if (strstr(spec, " <") && strstr(spec, ">")) {
5303 from_addr = g_strdup(strrchr(spec, '<')+1);
5304 *(strrchr(from_addr, '>')) = '\0';
5305 from_name = g_strdup(spec);
5306 *(strrchr(from_name, '<')) = '\0';
5313 /* sign message if sending */
5314 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5315 privacy_system_can_sign(compose->privacy_system))
5316 if (!privacy_sign(compose->privacy_system, mimemsg,
5317 compose->account, from_addr)) {
5324 procmime_write_mimeinfo(mimemsg, fp);
5326 procmime_mimeinfo_free_all(mimemsg);
5331 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5333 GtkTextBuffer *buffer;
5334 GtkTextIter start, end;
5339 if ((fp = g_fopen(file, "wb")) == NULL) {
5340 FILE_OP_ERROR(file, "fopen");
5344 /* chmod for security */
5345 if (change_file_mode_rw(fp, file) < 0) {
5346 FILE_OP_ERROR(file, "chmod");
5347 g_warning("can't change file mode\n");
5350 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5351 gtk_text_buffer_get_start_iter(buffer, &start);
5352 gtk_text_buffer_get_end_iter(buffer, &end);
5353 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5355 chars = conv_codeset_strdup
5356 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5359 if (!chars) return -1;
5362 len = strlen(chars);
5363 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5364 FILE_OP_ERROR(file, "fwrite");
5373 if (fclose(fp) == EOF) {
5374 FILE_OP_ERROR(file, "fclose");
5381 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5384 MsgInfo *msginfo = compose->targetinfo;
5386 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5387 if (!msginfo) return -1;
5389 if (!force && MSG_IS_LOCKED(msginfo->flags))
5392 item = msginfo->folder;
5393 g_return_val_if_fail(item != NULL, -1);
5395 if (procmsg_msg_exist(msginfo) &&
5396 (folder_has_parent_of_type(item, F_QUEUE) ||
5397 folder_has_parent_of_type(item, F_DRAFT)
5398 || msginfo == compose->autosaved_draft)) {
5399 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5400 g_warning("can't remove the old message\n");
5403 debug_print("removed reedit target %d\n", msginfo->msgnum);
5410 static void compose_remove_draft(Compose *compose)
5413 MsgInfo *msginfo = compose->targetinfo;
5414 drafts = account_get_special_folder(compose->account, F_DRAFT);
5416 if (procmsg_msg_exist(msginfo)) {
5417 folder_item_remove_msg(drafts, msginfo->msgnum);
5422 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5423 gboolean remove_reedit_target)
5425 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5428 static gboolean compose_warn_encryption(Compose *compose)
5430 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5431 AlertValue val = G_ALERTALTERNATE;
5433 if (warning == NULL)
5436 val = alertpanel_full(_("Encryption warning"), warning,
5437 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5438 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5439 if (val & G_ALERTDISABLE) {
5440 val &= ~G_ALERTDISABLE;
5441 if (val == G_ALERTALTERNATE)
5442 privacy_inhibit_encrypt_warning(compose->privacy_system,
5446 if (val == G_ALERTALTERNATE) {
5453 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5454 gchar **msgpath, gboolean check_subject,
5455 gboolean remove_reedit_target)
5462 static gboolean lock = FALSE;
5463 PrefsAccount *mailac = NULL, *newsac = NULL;
5464 gboolean err = FALSE;
5466 debug_print("queueing message...\n");
5467 g_return_val_if_fail(compose->account != NULL, -1);
5471 if (compose_check_entries(compose, check_subject) == FALSE) {
5473 if (compose->batch) {
5474 gtk_widget_show_all(compose->window);
5479 if (!compose->to_list && !compose->newsgroup_list) {
5480 g_warning("can't get recipient list.");
5485 if (compose->to_list) {
5486 if (compose->account->protocol != A_NNTP)
5487 mailac = compose->account;
5488 else if (cur_account && cur_account->protocol != A_NNTP)
5489 mailac = cur_account;
5490 else if (!(mailac = compose_current_mail_account())) {
5492 alertpanel_error(_("No account for sending mails available!"));
5497 if (compose->newsgroup_list) {
5498 if (compose->account->protocol == A_NNTP)
5499 newsac = compose->account;
5500 else if (!newsac->protocol != A_NNTP) {
5502 alertpanel_error(_("No account for posting news available!"));
5507 /* write queue header */
5508 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5509 G_DIR_SEPARATOR, compose, (guint) rand());
5510 debug_print("queuing to %s\n", tmp);
5511 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5512 FILE_OP_ERROR(tmp, "fopen");
5518 if (change_file_mode_rw(fp, tmp) < 0) {
5519 FILE_OP_ERROR(tmp, "chmod");
5520 g_warning("can't change file mode\n");
5523 /* queueing variables */
5524 err |= (fprintf(fp, "AF:\n") < 0);
5525 err |= (fprintf(fp, "NF:0\n") < 0);
5526 err |= (fprintf(fp, "PS:10\n") < 0);
5527 err |= (fprintf(fp, "SRH:1\n") < 0);
5528 err |= (fprintf(fp, "SFN:\n") < 0);
5529 err |= (fprintf(fp, "DSR:\n") < 0);
5531 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5533 err |= (fprintf(fp, "MID:\n") < 0);
5534 err |= (fprintf(fp, "CFG:\n") < 0);
5535 err |= (fprintf(fp, "PT:0\n") < 0);
5536 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5537 err |= (fprintf(fp, "RQ:\n") < 0);
5539 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5541 err |= (fprintf(fp, "SSV:\n") < 0);
5543 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5545 err |= (fprintf(fp, "NSV:\n") < 0);
5546 err |= (fprintf(fp, "SSH:\n") < 0);
5547 /* write recepient list */
5548 if (compose->to_list) {
5549 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5550 for (cur = compose->to_list->next; cur != NULL;
5552 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5553 err |= (fprintf(fp, "\n") < 0);
5555 /* write newsgroup list */
5556 if (compose->newsgroup_list) {
5557 err |= (fprintf(fp, "NG:") < 0);
5558 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5559 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5560 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5561 err |= (fprintf(fp, "\n") < 0);
5563 /* Sylpheed account IDs */
5565 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5567 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5570 if (compose->privacy_system != NULL) {
5571 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5572 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5573 if (compose->use_encryption) {
5575 if (!compose_warn_encryption(compose)) {
5582 if (mailac && mailac->encrypt_to_self) {
5583 GSList *tmp_list = g_slist_copy(compose->to_list);
5584 tmp_list = g_slist_append(tmp_list, compose->account->address);
5585 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5586 g_slist_free(tmp_list);
5588 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5590 if (encdata != NULL) {
5591 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5592 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5593 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5595 } /* else we finally dont want to encrypt */
5597 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5598 /* and if encdata was null, it means there's been a problem in
5610 /* Save copy folder */
5611 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5612 gchar *savefolderid;
5614 savefolderid = compose_get_save_to(compose);
5615 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5616 g_free(savefolderid);
5618 /* Save copy folder */
5619 if (compose->return_receipt) {
5620 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5622 /* Message-ID of message replying to */
5623 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5626 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5627 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5630 /* Message-ID of message forwarding to */
5631 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5634 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5635 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5639 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5640 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5642 /* end of headers */
5643 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5645 if (compose->redirect_filename != NULL) {
5646 if (compose_redirect_write_to_file(compose, fp) < 0) {
5655 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5660 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5664 g_warning("failed to write queue message\n");
5671 if (fclose(fp) == EOF) {
5672 FILE_OP_ERROR(tmp, "fclose");
5679 if (item && *item) {
5682 queue = account_get_special_folder(compose->account, F_QUEUE);
5685 g_warning("can't find queue folder\n");
5691 folder_item_scan(queue);
5692 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5693 g_warning("can't queue the message\n");
5700 if (msgpath == NULL) {
5706 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5707 compose_remove_reedit_target(compose, FALSE);
5710 if ((msgnum != NULL) && (item != NULL)) {
5718 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5721 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5723 struct stat statbuf;
5724 gchar *type, *subtype;
5725 GtkTreeModel *model;
5728 model = gtk_tree_view_get_model(tree_view);
5730 if (!gtk_tree_model_get_iter_first(model, &iter))
5733 gtk_tree_model_get(model, &iter,
5737 mimepart = procmime_mimeinfo_new();
5738 mimepart->content = MIMECONTENT_FILE;
5739 mimepart->data.filename = g_strdup(ainfo->file);
5740 mimepart->tmp = FALSE; /* or we destroy our attachment */
5741 mimepart->offset = 0;
5743 g_stat(ainfo->file, &statbuf);
5744 mimepart->length = statbuf.st_size;
5746 type = g_strdup(ainfo->content_type);
5748 if (!strchr(type, '/')) {
5750 type = g_strdup("application/octet-stream");
5753 subtype = strchr(type, '/') + 1;
5754 *(subtype - 1) = '\0';
5755 mimepart->type = procmime_get_media_type(type);
5756 mimepart->subtype = g_strdup(subtype);
5759 if (mimepart->type == MIMETYPE_MESSAGE &&
5760 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5761 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5764 if (mimepart->type == MIMETYPE_APPLICATION &&
5765 !strcmp2(mimepart->subtype, "octet-stream"))
5766 g_hash_table_insert(mimepart->typeparameters,
5767 g_strdup("name"), g_strdup(ainfo->name));
5768 g_hash_table_insert(mimepart->dispositionparameters,
5769 g_strdup("filename"), g_strdup(ainfo->name));
5770 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5774 if (compose->use_signing) {
5775 if (ainfo->encoding == ENC_7BIT)
5776 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5777 else if (ainfo->encoding == ENC_8BIT)
5778 ainfo->encoding = ENC_BASE64;
5781 procmime_encode_content(mimepart, ainfo->encoding);
5783 g_node_append(parent->node, mimepart->node);
5784 } while (gtk_tree_model_iter_next(model, &iter));
5787 #define IS_IN_CUSTOM_HEADER(header) \
5788 (compose->account->add_customhdr && \
5789 custom_header_find(compose->account->customhdr_list, header) != NULL)
5791 static void compose_add_headerfield_from_headerlist(Compose *compose,
5793 const gchar *fieldname,
5794 const gchar *seperator)
5796 gchar *str, *fieldname_w_colon;
5797 gboolean add_field = FALSE;
5799 ComposeHeaderEntry *headerentry;
5800 const gchar *headerentryname;
5801 const gchar *trans_fieldname;
5804 if (IS_IN_CUSTOM_HEADER(fieldname))
5807 debug_print("Adding %s-fields\n", fieldname);
5809 fieldstr = g_string_sized_new(64);
5811 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5812 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5814 for (list = compose->header_list; list; list = list->next) {
5815 headerentry = ((ComposeHeaderEntry *)list->data);
5816 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5818 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5819 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5821 if (str[0] != '\0') {
5823 g_string_append(fieldstr, seperator);
5824 g_string_append(fieldstr, str);
5833 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5834 compose_convert_header
5835 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5836 strlen(fieldname) + 2, TRUE);
5837 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5841 g_free(fieldname_w_colon);
5842 g_string_free(fieldstr, TRUE);
5847 static gchar *compose_get_header(Compose *compose)
5849 gchar buf[BUFFSIZE];
5850 const gchar *entry_str;
5854 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5856 gchar *from_name = NULL, *from_address = NULL;
5859 g_return_val_if_fail(compose->account != NULL, NULL);
5860 g_return_val_if_fail(compose->account->address != NULL, NULL);
5862 header = g_string_sized_new(64);
5865 get_rfc822_date(buf, sizeof(buf));
5866 g_string_append_printf(header, "Date: %s\n", buf);
5870 if (compose->account->name && *compose->account->name) {
5872 QUOTE_IF_REQUIRED(buf, compose->account->name);
5873 tmp = g_strdup_printf("%s <%s>",
5874 buf, compose->account->address);
5876 tmp = g_strdup_printf("%s",
5877 compose->account->address);
5879 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5880 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5882 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5883 from_address = g_strdup(compose->account->address);
5885 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5886 /* extract name and address */
5887 if (strstr(spec, " <") && strstr(spec, ">")) {
5888 from_address = g_strdup(strrchr(spec, '<')+1);
5889 *(strrchr(from_address, '>')) = '\0';
5890 from_name = g_strdup(spec);
5891 *(strrchr(from_name, '<')) = '\0';
5894 from_address = g_strdup(spec);
5901 if (from_name && *from_name) {
5902 compose_convert_header
5903 (compose, buf, sizeof(buf), from_name,
5904 strlen("From: "), TRUE);
5905 QUOTE_IF_REQUIRED(name, buf);
5907 g_string_append_printf(header, "From: %s <%s>\n",
5908 name, from_address);
5910 g_string_append_printf(header, "From: %s\n", from_address);
5913 g_free(from_address);
5916 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5919 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5922 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5925 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5928 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5930 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5933 compose_convert_header(compose, buf, sizeof(buf), str,
5934 strlen("Subject: "), FALSE);
5935 g_string_append_printf(header, "Subject: %s\n", buf);
5941 if (compose->account->set_domain && compose->account->domain) {
5942 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5943 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5944 g_snprintf(buf, sizeof(buf), "%s",
5945 strchr(compose->account->address, '@') ?
5946 strchr(compose->account->address, '@')+1 :
5947 compose->account->address);
5949 g_snprintf(buf, sizeof(buf), "%s", "");
5952 if (compose->account->gen_msgid) {
5953 generate_msgid(buf, sizeof(buf));
5954 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5955 compose->msgid = g_strdup(buf);
5957 compose->msgid = NULL;
5960 if (compose->remove_references == FALSE) {
5962 if (compose->inreplyto && compose->to_list)
5963 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5966 if (compose->references)
5967 g_string_append_printf(header, "References: %s\n", compose->references);
5971 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5974 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5977 if (compose->account->organization &&
5978 strlen(compose->account->organization) &&
5979 !IS_IN_CUSTOM_HEADER("Organization")) {
5980 compose_convert_header(compose, buf, sizeof(buf),
5981 compose->account->organization,
5982 strlen("Organization: "), FALSE);
5983 g_string_append_printf(header, "Organization: %s\n", buf);
5986 /* Program version and system info */
5987 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5988 !compose->newsgroup_list) {
5989 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5991 gtk_major_version, gtk_minor_version, gtk_micro_version,
5994 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5995 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5997 gtk_major_version, gtk_minor_version, gtk_micro_version,
6001 /* custom headers */
6002 if (compose->account->add_customhdr) {
6005 for (cur = compose->account->customhdr_list; cur != NULL;
6007 CustomHeader *chdr = (CustomHeader *)cur->data;
6009 if (custom_header_is_allowed(chdr->name)) {
6010 compose_convert_header
6011 (compose, buf, sizeof(buf),
6012 chdr->value ? chdr->value : "",
6013 strlen(chdr->name) + 2, FALSE);
6014 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6019 /* Automatic Faces and X-Faces */
6020 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6021 g_string_append_printf(header, "X-Face: %s\n", buf);
6023 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6024 g_string_append_printf(header, "X-Face: %s\n", buf);
6026 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6027 g_string_append_printf(header, "Face: %s\n", buf);
6029 else if (get_default_face (buf, sizeof(buf)) == 0) {
6030 g_string_append_printf(header, "Face: %s\n", buf);
6034 switch (compose->priority) {
6035 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6036 "X-Priority: 1 (Highest)\n");
6038 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6039 "X-Priority: 2 (High)\n");
6041 case PRIORITY_NORMAL: break;
6042 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6043 "X-Priority: 4 (Low)\n");
6045 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6046 "X-Priority: 5 (Lowest)\n");
6048 default: debug_print("compose: priority unknown : %d\n",
6052 /* Request Return Receipt */
6053 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6054 if (compose->return_receipt) {
6055 if (compose->account->name
6056 && *compose->account->name) {
6057 compose_convert_header(compose, buf, sizeof(buf),
6058 compose->account->name,
6059 strlen("Disposition-Notification-To: "),
6061 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6063 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6067 /* get special headers */
6068 for (list = compose->header_list; list; list = list->next) {
6069 ComposeHeaderEntry *headerentry;
6072 gchar *headername_wcolon;
6073 const gchar *headername_trans;
6076 gboolean standard_header = FALSE;
6078 headerentry = ((ComposeHeaderEntry *)list->data);
6080 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6082 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6087 if (!strstr(tmp, ":")) {
6088 headername_wcolon = g_strconcat(tmp, ":", NULL);
6089 headername = g_strdup(tmp);
6091 headername_wcolon = g_strdup(tmp);
6092 headername = g_strdup(strtok(tmp, ":"));
6096 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6097 Xstrdup_a(headervalue, entry_str, return NULL);
6098 subst_char(headervalue, '\r', ' ');
6099 subst_char(headervalue, '\n', ' ');
6100 string = std_headers;
6101 while (*string != NULL) {
6102 headername_trans = prefs_common_translated_header_name(*string);
6103 if (!strcmp(headername_trans, headername_wcolon))
6104 standard_header = TRUE;
6107 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6108 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6111 g_free(headername_wcolon);
6115 g_string_free(header, FALSE);
6120 #undef IS_IN_CUSTOM_HEADER
6122 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6123 gint header_len, gboolean addr_field)
6125 gchar *tmpstr = NULL;
6126 const gchar *out_codeset = NULL;
6128 g_return_if_fail(src != NULL);
6129 g_return_if_fail(dest != NULL);
6131 if (len < 1) return;
6133 tmpstr = g_strdup(src);
6135 subst_char(tmpstr, '\n', ' ');
6136 subst_char(tmpstr, '\r', ' ');
6139 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6140 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6141 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6146 codeconv_set_strict(TRUE);
6147 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6148 conv_get_charset_str(compose->out_encoding));
6149 codeconv_set_strict(FALSE);
6151 if (!dest || *dest == '\0') {
6152 gchar *test_conv_global_out = NULL;
6153 gchar *test_conv_reply = NULL;
6155 /* automatic mode. be automatic. */
6156 codeconv_set_strict(TRUE);
6158 out_codeset = conv_get_outgoing_charset_str();
6160 debug_print("trying to convert to %s\n", out_codeset);
6161 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6164 if (!test_conv_global_out && compose->orig_charset
6165 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6166 out_codeset = compose->orig_charset;
6167 debug_print("failure; trying to convert to %s\n", out_codeset);
6168 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6171 if (!test_conv_global_out && !test_conv_reply) {
6173 out_codeset = CS_INTERNAL;
6174 debug_print("finally using %s\n", out_codeset);
6176 g_free(test_conv_global_out);
6177 g_free(test_conv_reply);
6178 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6180 codeconv_set_strict(FALSE);
6185 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6189 g_return_if_fail(user_data != NULL);
6191 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6192 g_strstrip(address);
6193 if (*address != '\0') {
6194 gchar *name = procheader_get_fromname(address);
6195 extract_address(address);
6196 addressbook_add_contact(name, address, NULL, NULL);
6201 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6203 GtkWidget *menuitem;
6206 g_return_if_fail(menu != NULL);
6207 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6209 menuitem = gtk_separator_menu_item_new();
6210 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6211 gtk_widget_show(menuitem);
6213 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6214 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6216 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6217 g_strstrip(address);
6218 if (*address == '\0') {
6219 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6222 g_signal_connect(G_OBJECT(menuitem), "activate",
6223 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6224 gtk_widget_show(menuitem);
6227 static void compose_create_header_entry(Compose *compose)
6229 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6234 const gchar *header = NULL;
6235 ComposeHeaderEntry *headerentry;
6236 gboolean standard_header = FALSE;
6237 #if !(GTK_CHECK_VERSION(2,12,0))
6238 GtkTooltips *tips = compose->tooltips;
6241 headerentry = g_new0(ComposeHeaderEntry, 1);
6244 combo = gtk_combo_box_entry_new_text();
6246 while(*string != NULL) {
6247 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6248 (gchar*)prefs_common_translated_header_name(*string));
6251 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6252 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6253 G_CALLBACK(compose_grab_focus_cb), compose);
6254 gtk_widget_show(combo);
6255 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6256 compose->header_nextrow, compose->header_nextrow+1,
6257 GTK_SHRINK, GTK_FILL, 0, 0);
6258 if (compose->header_last) {
6259 const gchar *last_header_entry = gtk_entry_get_text(
6260 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6262 while (*string != NULL) {
6263 if (!strcmp(*string, last_header_entry))
6264 standard_header = TRUE;
6267 if (standard_header)
6268 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6270 if (!compose->header_last || !standard_header) {
6271 switch(compose->account->protocol) {
6273 header = prefs_common_translated_header_name("Newsgroups:");
6276 header = prefs_common_translated_header_name("To:");
6281 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6283 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6284 G_CALLBACK(compose_grab_focus_cb), compose);
6287 entry = gtk_entry_new();
6288 gtk_widget_show(entry);
6289 CLAWS_SET_TIP(entry,
6290 _("Use <tab> to autocomplete from addressbook"));
6291 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6292 compose->header_nextrow, compose->header_nextrow+1,
6293 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6295 g_signal_connect(G_OBJECT(entry), "key-press-event",
6296 G_CALLBACK(compose_headerentry_key_press_event_cb),
6298 g_signal_connect(G_OBJECT(entry), "changed",
6299 G_CALLBACK(compose_headerentry_changed_cb),
6301 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6302 G_CALLBACK(compose_grab_focus_cb), compose);
6305 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6306 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6307 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6308 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6309 G_CALLBACK(compose_header_drag_received_cb),
6311 g_signal_connect(G_OBJECT(entry), "drag-drop",
6312 G_CALLBACK(compose_drag_drop),
6314 g_signal_connect(G_OBJECT(entry), "populate-popup",
6315 G_CALLBACK(compose_entry_popup_extend),
6318 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6320 headerentry->compose = compose;
6321 headerentry->combo = combo;
6322 headerentry->entry = entry;
6323 headerentry->headernum = compose->header_nextrow;
6325 compose->header_nextrow++;
6326 compose->header_last = headerentry;
6327 compose->header_list =
6328 g_slist_append(compose->header_list,
6332 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6334 ComposeHeaderEntry *last_header;
6336 last_header = compose->header_last;
6338 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6339 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6342 static void compose_remove_header_entries(Compose *compose)
6345 for (list = compose->header_list; list; list = list->next) {
6346 ComposeHeaderEntry *headerentry =
6347 (ComposeHeaderEntry *)list->data;
6348 gtk_widget_destroy(headerentry->combo);
6349 gtk_widget_destroy(headerentry->entry);
6350 g_free(headerentry);
6352 compose->header_last = NULL;
6353 g_slist_free(compose->header_list);
6354 compose->header_list = NULL;
6355 compose->header_nextrow = 1;
6356 compose_create_header_entry(compose);
6359 static GtkWidget *compose_create_header(Compose *compose)
6361 GtkWidget *from_optmenu_hbox;
6362 GtkWidget *header_scrolledwin;
6363 GtkWidget *header_table;
6367 /* header labels and entries */
6368 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6369 gtk_widget_show(header_scrolledwin);
6370 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6372 header_table = gtk_table_new(2, 2, FALSE);
6373 gtk_widget_show(header_table);
6374 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6375 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6376 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6379 /* option menu for selecting accounts */
6380 from_optmenu_hbox = compose_account_option_menu_create(compose);
6381 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6382 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6385 compose->header_table = header_table;
6386 compose->header_list = NULL;
6387 compose->header_nextrow = count;
6389 compose_create_header_entry(compose);
6391 compose->table = NULL;
6393 return header_scrolledwin ;
6396 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6398 Compose *compose = (Compose *)data;
6399 GdkEventButton event;
6402 event.time = gtk_get_current_event_time();
6404 return attach_button_pressed(compose->attach_clist, &event, compose);
6407 static GtkWidget *compose_create_attach(Compose *compose)
6409 GtkWidget *attach_scrwin;
6410 GtkWidget *attach_clist;
6412 GtkListStore *store;
6413 GtkCellRenderer *renderer;
6414 GtkTreeViewColumn *column;
6415 GtkTreeSelection *selection;
6417 /* attachment list */
6418 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6419 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6420 GTK_POLICY_AUTOMATIC,
6421 GTK_POLICY_AUTOMATIC);
6422 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6424 store = gtk_list_store_new(N_ATTACH_COLS,
6429 G_TYPE_AUTO_POINTER,
6431 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6432 (GTK_TREE_MODEL(store)));
6433 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6434 g_object_unref(store);
6436 renderer = gtk_cell_renderer_text_new();
6437 column = gtk_tree_view_column_new_with_attributes
6438 (_("Mime type"), renderer, "text",
6439 COL_MIMETYPE, NULL);
6440 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6442 renderer = gtk_cell_renderer_text_new();
6443 column = gtk_tree_view_column_new_with_attributes
6444 (_("Size"), renderer, "text",
6446 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6448 renderer = gtk_cell_renderer_text_new();
6449 column = gtk_tree_view_column_new_with_attributes
6450 (_("Name"), renderer, "text",
6452 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6454 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6455 prefs_common.use_stripes_everywhere);
6456 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6457 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6459 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6460 G_CALLBACK(attach_selected), compose);
6461 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6462 G_CALLBACK(attach_button_pressed), compose);
6464 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6465 G_CALLBACK(popup_attach_button_pressed), compose);
6467 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6468 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6469 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6470 G_CALLBACK(popup_attach_button_pressed), compose);
6472 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6473 G_CALLBACK(attach_key_pressed), compose);
6476 gtk_drag_dest_set(attach_clist,
6477 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6478 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6479 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6480 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6481 G_CALLBACK(compose_attach_drag_received_cb),
6483 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6484 G_CALLBACK(compose_drag_drop),
6487 compose->attach_scrwin = attach_scrwin;
6488 compose->attach_clist = attach_clist;
6490 return attach_scrwin;
6493 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6494 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6496 static GtkWidget *compose_create_others(Compose *compose)
6499 GtkWidget *savemsg_checkbtn;
6500 GtkWidget *savemsg_combo;
6501 GtkWidget *savemsg_select;
6504 gchar *folderidentifier;
6506 /* Table for settings */
6507 table = gtk_table_new(3, 1, FALSE);
6508 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6509 gtk_widget_show(table);
6510 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6513 /* Save Message to folder */
6514 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6515 gtk_widget_show(savemsg_checkbtn);
6516 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6517 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6518 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6520 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6521 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6523 savemsg_combo = gtk_combo_box_entry_new_text();
6524 compose->savemsg_checkbtn = savemsg_checkbtn;
6525 compose->savemsg_combo = savemsg_combo;
6526 gtk_widget_show(savemsg_combo);
6528 if (prefs_common.compose_save_to_history)
6529 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6530 prefs_common.compose_save_to_history);
6532 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6533 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6534 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6535 G_CALLBACK(compose_grab_focus_cb), compose);
6536 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6537 folderidentifier = folder_item_get_identifier(account_get_special_folder
6538 (compose->account, F_OUTBOX));
6539 compose_set_save_to(compose, folderidentifier);
6540 g_free(folderidentifier);
6543 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6544 gtk_widget_show(savemsg_select);
6545 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6546 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6547 G_CALLBACK(compose_savemsg_select_cb),
6555 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6557 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6558 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6561 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6566 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6569 path = folder_item_get_identifier(dest);
6571 compose_set_save_to(compose, path);
6575 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6576 GdkAtom clip, GtkTextIter *insert_place);
6579 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6583 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6585 if (event->button == 3) {
6587 GtkTextIter sel_start, sel_end;
6588 gboolean stuff_selected;
6590 /* move the cursor to allow GtkAspell to check the word
6591 * under the mouse */
6592 if (event->x && event->y) {
6593 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6594 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6596 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6599 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6600 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6603 stuff_selected = gtk_text_buffer_get_selection_bounds(
6605 &sel_start, &sel_end);
6607 gtk_text_buffer_place_cursor (buffer, &iter);
6608 /* reselect stuff */
6610 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6611 gtk_text_buffer_select_range(buffer,
6612 &sel_start, &sel_end);
6614 return FALSE; /* pass the event so that the right-click goes through */
6617 if (event->button == 2) {
6622 /* get the middle-click position to paste at the correct place */
6623 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6624 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6626 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6629 entry_paste_clipboard(compose, text,
6630 prefs_common.linewrap_pastes,
6631 GDK_SELECTION_PRIMARY, &iter);
6639 static void compose_spell_menu_changed(void *data)
6641 Compose *compose = (Compose *)data;
6643 GtkWidget *menuitem;
6644 GtkWidget *parent_item;
6645 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6648 if (compose->gtkaspell == NULL)
6651 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6652 "/Menu/Spelling/Options");
6654 /* setting the submenu removes /Spelling/Options from the factory
6655 * so we need to save it */
6657 if (parent_item == NULL) {
6658 parent_item = compose->aspell_options_menu;
6659 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6661 compose->aspell_options_menu = parent_item;
6663 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6665 spell_menu = g_slist_reverse(spell_menu);
6666 for (items = spell_menu;
6667 items; items = items->next) {
6668 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6669 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6670 gtk_widget_show(GTK_WIDGET(menuitem));
6672 g_slist_free(spell_menu);
6674 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6679 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6681 Compose *compose = (Compose *)data;
6682 GdkEventButton event;
6685 event.time = gtk_get_current_event_time();
6689 return text_clicked(compose->text, &event, compose);
6692 static gboolean compose_force_window_origin = TRUE;
6693 static Compose *compose_create(PrefsAccount *account,
6702 GtkWidget *handlebox;
6704 GtkWidget *notebook;
6706 GtkWidget *attach_hbox;
6707 GtkWidget *attach_lab1;
6708 GtkWidget *attach_lab2;
6713 GtkWidget *subject_hbox;
6714 GtkWidget *subject_frame;
6715 GtkWidget *subject_entry;
6719 GtkWidget *edit_vbox;
6720 GtkWidget *ruler_hbox;
6722 GtkWidget *scrolledwin;
6724 GtkTextBuffer *buffer;
6725 GtkClipboard *clipboard;
6728 UndoMain *undostruct;
6730 gchar *titles[N_ATTACH_COLS];
6731 GtkWidget *popupmenu;
6732 GtkWidget *tmpl_menu;
6733 GtkActionGroup *action_group = NULL;
6736 GtkAspell * gtkaspell = NULL;
6739 static GdkGeometry geometry;
6741 g_return_val_if_fail(account != NULL, NULL);
6743 debug_print("Creating compose window...\n");
6744 compose = g_new0(Compose, 1);
6746 titles[COL_MIMETYPE] = _("MIME type");
6747 titles[COL_SIZE] = _("Size");
6748 titles[COL_NAME] = _("Name");
6750 compose->batch = batch;
6751 compose->account = account;
6752 compose->folder = folder;
6754 compose->mutex = g_mutex_new();
6755 compose->set_cursor_pos = -1;
6757 #if !(GTK_CHECK_VERSION(2,12,0))
6758 compose->tooltips = tips;
6761 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6763 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6764 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6766 if (!geometry.max_width) {
6767 geometry.max_width = gdk_screen_width();
6768 geometry.max_height = gdk_screen_height();
6771 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6772 &geometry, GDK_HINT_MAX_SIZE);
6773 if (!geometry.min_width) {
6774 geometry.min_width = 600;
6775 geometry.min_height = 440;
6777 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6778 &geometry, GDK_HINT_MIN_SIZE);
6780 #ifndef GENERIC_UMPC
6781 if (compose_force_window_origin)
6782 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6783 prefs_common.compose_y);
6785 g_signal_connect(G_OBJECT(window), "delete_event",
6786 G_CALLBACK(compose_delete_cb), compose);
6787 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6788 gtk_widget_realize(window);
6790 gtkut_widget_set_composer_icon(window);
6792 vbox = gtk_vbox_new(FALSE, 0);
6793 gtk_container_add(GTK_CONTAINER(window), vbox);
6795 compose->ui_manager = gtk_ui_manager_new();
6796 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6797 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6798 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6799 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6800 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6801 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6802 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6803 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6804 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6805 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6808 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6810 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6813 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6814 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6816 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6818 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6819 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6820 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6824 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6826 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6827 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6830 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6832 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6836 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6837 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6848 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6853 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6857 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6862 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6863 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6864 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6866 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6870 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6874 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6876 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6880 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6881 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6882 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6885 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6890 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6892 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6893 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6902 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6903 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6916 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6924 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6925 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)
6926 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)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6932 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)
6933 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)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6938 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)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6941 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6942 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)
6943 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6945 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6947 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6948 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)
6949 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6951 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6953 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6954 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)
6955 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)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6957 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6959 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6961 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6963 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6967 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)
6969 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6970 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6979 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6983 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6985 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6986 gtk_widget_show_all(menubar);
6988 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6990 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6992 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6995 if (prefs_common.toolbar_detachable) {
6996 handlebox = gtk_handle_box_new();
6998 handlebox = gtk_hbox_new(FALSE, 0);
7000 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7002 gtk_widget_realize(handlebox);
7004 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7007 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7011 vbox2 = gtk_vbox_new(FALSE, 2);
7012 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7013 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7016 notebook = gtk_notebook_new();
7017 gtk_widget_set_size_request(notebook, -1, 130);
7018 gtk_widget_show(notebook);
7020 /* header labels and entries */
7021 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7022 compose_create_header(compose),
7023 gtk_label_new_with_mnemonic(_("Hea_der")));
7024 /* attachment list */
7025 attach_hbox = gtk_hbox_new(FALSE, 0);
7026 gtk_widget_show(attach_hbox);
7028 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7029 gtk_widget_show(attach_lab1);
7030 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7032 attach_lab2 = gtk_label_new("");
7033 gtk_widget_show(attach_lab2);
7034 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7036 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7037 compose_create_attach(compose),
7040 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7041 compose_create_others(compose),
7042 gtk_label_new_with_mnemonic(_("Othe_rs")));
7045 subject_hbox = gtk_hbox_new(FALSE, 0);
7046 gtk_widget_show(subject_hbox);
7048 subject_frame = gtk_frame_new(NULL);
7049 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7050 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7051 gtk_widget_show(subject_frame);
7053 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7054 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7055 gtk_widget_show(subject);
7057 label = gtk_label_new(_("Subject:"));
7058 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7059 gtk_widget_show(label);
7061 subject_entry = gtk_entry_new();
7062 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7063 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7064 G_CALLBACK(compose_grab_focus_cb), compose);
7065 gtk_widget_show(subject_entry);
7066 compose->subject_entry = subject_entry;
7067 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7069 edit_vbox = gtk_vbox_new(FALSE, 0);
7071 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7074 ruler_hbox = gtk_hbox_new(FALSE, 0);
7075 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7077 ruler = gtk_shruler_new();
7078 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7079 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7083 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7084 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7085 GTK_POLICY_AUTOMATIC,
7086 GTK_POLICY_AUTOMATIC);
7087 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7089 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7090 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7092 text = gtk_text_view_new();
7093 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7094 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7095 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7096 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7097 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7099 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7101 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7102 G_CALLBACK(compose_edit_size_alloc),
7104 g_signal_connect(G_OBJECT(buffer), "changed",
7105 G_CALLBACK(compose_changed_cb), compose);
7106 g_signal_connect(G_OBJECT(text), "grab_focus",
7107 G_CALLBACK(compose_grab_focus_cb), compose);
7108 g_signal_connect(G_OBJECT(buffer), "insert_text",
7109 G_CALLBACK(text_inserted), compose);
7110 g_signal_connect(G_OBJECT(text), "button_press_event",
7111 G_CALLBACK(text_clicked), compose);
7113 g_signal_connect(G_OBJECT(text), "popup-menu",
7114 G_CALLBACK(compose_popup_menu), compose);
7116 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7117 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7118 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7119 G_CALLBACK(compose_popup_menu), compose);
7121 g_signal_connect(G_OBJECT(subject_entry), "changed",
7122 G_CALLBACK(compose_changed_cb), compose);
7125 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7126 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7127 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7128 g_signal_connect(G_OBJECT(text), "drag_data_received",
7129 G_CALLBACK(compose_insert_drag_received_cb),
7131 g_signal_connect(G_OBJECT(text), "drag-drop",
7132 G_CALLBACK(compose_drag_drop),
7134 gtk_widget_show_all(vbox);
7136 /* pane between attach clist and text */
7137 paned = gtk_vpaned_new();
7138 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7140 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7141 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7143 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7145 gtk_paned_add1(GTK_PANED(paned), notebook);
7146 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7147 gtk_widget_show_all(paned);
7150 if (prefs_common.textfont) {
7151 PangoFontDescription *font_desc;
7153 font_desc = pango_font_description_from_string
7154 (prefs_common.textfont);
7156 gtk_widget_modify_font(text, font_desc);
7157 pango_font_description_free(font_desc);
7161 gtk_action_group_add_actions(action_group, compose_popup_entries,
7162 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7170 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7172 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7173 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7174 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7176 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7178 undostruct = undo_init(text);
7179 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7182 address_completion_start(window);
7184 compose->window = window;
7185 compose->vbox = vbox;
7186 compose->menubar = menubar;
7187 compose->handlebox = handlebox;
7189 compose->vbox2 = vbox2;
7191 compose->paned = paned;
7193 compose->attach_label = attach_lab2;
7195 compose->notebook = notebook;
7196 compose->edit_vbox = edit_vbox;
7197 compose->ruler_hbox = ruler_hbox;
7198 compose->ruler = ruler;
7199 compose->scrolledwin = scrolledwin;
7200 compose->text = text;
7202 compose->focused_editable = NULL;
7204 compose->popupmenu = popupmenu;
7206 compose->tmpl_menu = tmpl_menu;
7208 compose->mode = mode;
7209 compose->rmode = mode;
7211 compose->targetinfo = NULL;
7212 compose->replyinfo = NULL;
7213 compose->fwdinfo = NULL;
7215 compose->replyto = NULL;
7217 compose->bcc = NULL;
7218 compose->followup_to = NULL;
7220 compose->ml_post = NULL;
7222 compose->inreplyto = NULL;
7223 compose->references = NULL;
7224 compose->msgid = NULL;
7225 compose->boundary = NULL;
7227 compose->autowrap = prefs_common.autowrap;
7228 compose->autoindent = prefs_common.auto_indent;
7229 compose->use_signing = FALSE;
7230 compose->use_encryption = FALSE;
7231 compose->privacy_system = NULL;
7233 compose->modified = FALSE;
7235 compose->return_receipt = FALSE;
7237 compose->to_list = NULL;
7238 compose->newsgroup_list = NULL;
7240 compose->undostruct = undostruct;
7242 compose->sig_str = NULL;
7244 compose->exteditor_file = NULL;
7245 compose->exteditor_pid = -1;
7246 compose->exteditor_tag = -1;
7247 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7250 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7251 if (mode != COMPOSE_REDIRECT) {
7252 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7253 strcmp(prefs_common.dictionary, "")) {
7254 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7255 prefs_common.alt_dictionary,
7256 conv_get_locale_charset_str(),
7257 prefs_common.misspelled_col,
7258 prefs_common.check_while_typing,
7259 prefs_common.recheck_when_changing_dict,
7260 prefs_common.use_alternate,
7261 prefs_common.use_both_dicts,
7262 GTK_TEXT_VIEW(text),
7263 GTK_WINDOW(compose->window),
7264 compose_spell_menu_changed,
7267 alertpanel_error(_("Spell checker could not "
7269 gtkaspell_checkers_strerror());
7270 gtkaspell_checkers_reset_error();
7272 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7276 compose->gtkaspell = gtkaspell;
7277 compose_spell_menu_changed(compose);
7280 compose_select_account(compose, account, TRUE);
7282 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7283 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7285 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7286 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7288 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7289 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7291 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7292 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7294 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7295 if (account->protocol != A_NNTP)
7296 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7297 prefs_common_translated_header_name("To:"));
7299 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7300 prefs_common_translated_header_name("Newsgroups:"));
7302 addressbook_set_target_compose(compose);
7304 if (mode != COMPOSE_REDIRECT)
7305 compose_set_template_menu(compose);
7307 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7310 compose_list = g_list_append(compose_list, compose);
7312 if (!prefs_common.show_ruler)
7313 gtk_widget_hide(ruler_hbox);
7315 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7318 compose->priority = PRIORITY_NORMAL;
7319 compose_update_priority_menu_item(compose);
7321 compose_set_out_encoding(compose);
7324 compose_update_actions_menu(compose);
7326 /* Privacy Systems menu */
7327 compose_update_privacy_systems_menu(compose);
7329 activate_privacy_system(compose, account, TRUE);
7330 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7332 gtk_widget_realize(window);
7334 gtk_widget_show(window);
7336 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7337 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7344 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7349 GtkWidget *optmenubox;
7352 GtkWidget *from_name = NULL;
7353 #if !(GTK_CHECK_VERSION(2,12,0))
7354 GtkTooltips *tips = compose->tooltips;
7357 gint num = 0, def_menu = 0;
7359 accounts = account_get_list();
7360 g_return_val_if_fail(accounts != NULL, NULL);
7362 optmenubox = gtk_event_box_new();
7363 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7364 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7366 hbox = gtk_hbox_new(FALSE, 6);
7367 from_name = gtk_entry_new();
7369 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7370 G_CALLBACK(compose_grab_focus_cb), compose);
7372 for (; accounts != NULL; accounts = accounts->next, num++) {
7373 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7374 gchar *name, *from = NULL;
7376 if (ac == compose->account) def_menu = num;
7378 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7381 if (ac == compose->account) {
7382 if (ac->name && *ac->name) {
7384 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7385 from = g_strdup_printf("%s <%s>",
7387 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7389 from = g_strdup_printf("%s",
7391 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7394 COMBOBOX_ADD(menu, name, ac->account_id);
7399 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7401 g_signal_connect(G_OBJECT(optmenu), "changed",
7402 G_CALLBACK(account_activated),
7404 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7405 G_CALLBACK(compose_entry_popup_extend),
7408 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7409 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7411 CLAWS_SET_TIP(optmenubox,
7412 _("Account to use for this email"));
7413 CLAWS_SET_TIP(from_name,
7414 _("Sender address to be used"));
7416 compose->from_name = from_name;
7421 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7423 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7424 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7425 Compose *compose = (Compose *) data;
7427 compose->priority = value;
7431 static void compose_reply_change_mode(Compose *compose,
7434 gboolean was_modified = compose->modified;
7436 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7438 g_return_if_fail(compose->replyinfo != NULL);
7440 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7442 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7444 if (action == COMPOSE_REPLY_TO_ALL)
7446 if (action == COMPOSE_REPLY_TO_SENDER)
7448 if (action == COMPOSE_REPLY_TO_LIST)
7451 compose_remove_header_entries(compose);
7452 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7453 if (compose->account->set_autocc && compose->account->auto_cc)
7454 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7456 if (compose->account->set_autobcc && compose->account->auto_bcc)
7457 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7459 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7460 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7461 compose_show_first_last_header(compose, TRUE);
7462 compose->modified = was_modified;
7463 compose_set_title(compose);
7466 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7468 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7469 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7470 Compose *compose = (Compose *) data;
7473 compose_reply_change_mode(compose, value);
7476 static void compose_update_priority_menu_item(Compose * compose)
7478 GtkWidget *menuitem = NULL;
7479 switch (compose->priority) {
7480 case PRIORITY_HIGHEST:
7481 menuitem = gtk_ui_manager_get_widget
7482 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7485 menuitem = gtk_ui_manager_get_widget
7486 (compose->ui_manager, "/Menu/Options/Priority/High");
7488 case PRIORITY_NORMAL:
7489 menuitem = gtk_ui_manager_get_widget
7490 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7493 menuitem = gtk_ui_manager_get_widget
7494 (compose->ui_manager, "/Menu/Options/Priority/Low");
7496 case PRIORITY_LOWEST:
7497 menuitem = gtk_ui_manager_get_widget
7498 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7501 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7504 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7506 Compose *compose = (Compose *) data;
7508 gboolean can_sign = FALSE, can_encrypt = FALSE;
7510 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7512 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7515 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7516 g_free(compose->privacy_system);
7517 compose->privacy_system = NULL;
7518 if (systemid != NULL) {
7519 compose->privacy_system = g_strdup(systemid);
7521 can_sign = privacy_system_can_sign(systemid);
7522 can_encrypt = privacy_system_can_encrypt(systemid);
7525 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7527 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7528 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7531 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7533 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7534 GtkWidget *menuitem = NULL;
7536 gboolean can_sign = FALSE, can_encrypt = FALSE;
7537 gboolean found = FALSE;
7539 if (compose->privacy_system != NULL) {
7541 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7542 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7543 g_return_if_fail(menuitem != NULL);
7545 amenu = GTK_MENU_SHELL(menuitem)->children;
7547 while (amenu != NULL) {
7548 GList *alist = amenu->next;
7550 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7551 if (systemid != NULL) {
7552 if (strcmp(systemid, compose->privacy_system) == 0 &&
7553 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7554 menuitem = GTK_WIDGET(amenu->data);
7556 can_sign = privacy_system_can_sign(systemid);
7557 can_encrypt = privacy_system_can_encrypt(systemid);
7561 } else if (strlen(compose->privacy_system) == 0 &&
7562 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7563 menuitem = GTK_WIDGET(amenu->data);
7566 can_encrypt = FALSE;
7573 if (menuitem != NULL)
7574 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7576 if (warn && !found && strlen(compose->privacy_system)) {
7577 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7578 "will not be able to sign or encrypt this message."),
7579 compose->privacy_system);
7583 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7584 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7587 static void compose_set_out_encoding(Compose *compose)
7589 CharSet out_encoding;
7590 const gchar *branch = NULL;
7591 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7593 switch(out_encoding) {
7594 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7595 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7596 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7597 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7598 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7599 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7600 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7601 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7602 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7603 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7604 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7605 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7606 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7607 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7608 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7609 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7610 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7611 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7612 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7613 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7614 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7615 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7616 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7617 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7618 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7619 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7620 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7621 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7622 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7623 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7624 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7625 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7627 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7630 static void compose_set_template_menu(Compose *compose)
7632 GSList *tmpl_list, *cur;
7636 tmpl_list = template_get_config();
7638 menu = gtk_menu_new();
7640 gtk_menu_set_accel_group (GTK_MENU (menu),
7641 gtk_ui_manager_get_accel_group(compose->ui_manager));
7642 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7643 Template *tmpl = (Template *)cur->data;
7644 gchar *accel_path = NULL;
7645 item = gtk_menu_item_new_with_label(tmpl->name);
7646 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7647 g_signal_connect(G_OBJECT(item), "activate",
7648 G_CALLBACK(compose_template_activate_cb),
7650 g_object_set_data(G_OBJECT(item), "template", tmpl);
7651 gtk_widget_show(item);
7652 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7653 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7657 gtk_widget_show(menu);
7658 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7661 void compose_update_actions_menu(Compose *compose)
7663 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7666 static void compose_update_privacy_systems_menu(Compose *compose)
7668 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7669 GSList *systems, *cur;
7671 GtkWidget *system_none;
7673 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7674 GtkWidget *privacy_menu = gtk_menu_new();
7676 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7677 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7679 g_signal_connect(G_OBJECT(system_none), "activate",
7680 G_CALLBACK(compose_set_privacy_system_cb), compose);
7682 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7683 gtk_widget_show(system_none);
7685 systems = privacy_get_system_ids();
7686 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7687 gchar *systemid = cur->data;
7689 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7690 widget = gtk_radio_menu_item_new_with_label(group,
7691 privacy_system_get_name(systemid));
7692 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7693 g_strdup(systemid), g_free);
7694 g_signal_connect(G_OBJECT(widget), "activate",
7695 G_CALLBACK(compose_set_privacy_system_cb), compose);
7697 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7698 gtk_widget_show(widget);
7701 g_slist_free(systems);
7702 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7703 gtk_widget_show_all(privacy_menu);
7704 gtk_widget_show_all(privacy_menuitem);
7707 void compose_reflect_prefs_all(void)
7712 for (cur = compose_list; cur != NULL; cur = cur->next) {
7713 compose = (Compose *)cur->data;
7714 compose_set_template_menu(compose);
7718 void compose_reflect_prefs_pixmap_theme(void)
7723 for (cur = compose_list; cur != NULL; cur = cur->next) {
7724 compose = (Compose *)cur->data;
7725 toolbar_update(TOOLBAR_COMPOSE, compose);
7729 static const gchar *compose_quote_char_from_context(Compose *compose)
7731 const gchar *qmark = NULL;
7733 g_return_val_if_fail(compose != NULL, NULL);
7735 switch (compose->mode) {
7736 /* use forward-specific quote char */
7737 case COMPOSE_FORWARD:
7738 case COMPOSE_FORWARD_AS_ATTACH:
7739 case COMPOSE_FORWARD_INLINE:
7740 if (compose->folder && compose->folder->prefs &&
7741 compose->folder->prefs->forward_with_format)
7742 qmark = compose->folder->prefs->forward_quotemark;
7743 else if (compose->account->forward_with_format)
7744 qmark = compose->account->forward_quotemark;
7746 qmark = prefs_common.fw_quotemark;
7749 /* use reply-specific quote char in all other modes */
7751 if (compose->folder && compose->folder->prefs &&
7752 compose->folder->prefs->reply_with_format)
7753 qmark = compose->folder->prefs->reply_quotemark;
7754 else if (compose->account->reply_with_format)
7755 qmark = compose->account->reply_quotemark;
7757 qmark = prefs_common.quotemark;
7761 if (qmark == NULL || *qmark == '\0')
7767 static void compose_template_apply(Compose *compose, Template *tmpl,
7771 GtkTextBuffer *buffer;
7775 gchar *parsed_str = NULL;
7776 gint cursor_pos = 0;
7777 const gchar *err_msg = _("Template body format error at line %d.");
7780 /* process the body */
7782 text = GTK_TEXT_VIEW(compose->text);
7783 buffer = gtk_text_view_get_buffer(text);
7786 qmark = compose_quote_char_from_context(compose);
7788 if (compose->replyinfo != NULL) {
7791 gtk_text_buffer_set_text(buffer, "", -1);
7792 mark = gtk_text_buffer_get_insert(buffer);
7793 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7795 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7796 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7798 } else if (compose->fwdinfo != NULL) {
7801 gtk_text_buffer_set_text(buffer, "", -1);
7802 mark = gtk_text_buffer_get_insert(buffer);
7803 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7805 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7806 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7809 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7811 GtkTextIter start, end;
7814 gtk_text_buffer_get_start_iter(buffer, &start);
7815 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7816 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7818 /* clear the buffer now */
7820 gtk_text_buffer_set_text(buffer, "", -1);
7822 parsed_str = compose_quote_fmt(compose, dummyinfo,
7823 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7824 procmsg_msginfo_free( dummyinfo );
7830 gtk_text_buffer_set_text(buffer, "", -1);
7831 mark = gtk_text_buffer_get_insert(buffer);
7832 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7835 if (replace && parsed_str && compose->account->auto_sig)
7836 compose_insert_sig(compose, FALSE);
7838 if (replace && parsed_str) {
7839 gtk_text_buffer_get_start_iter(buffer, &iter);
7840 gtk_text_buffer_place_cursor(buffer, &iter);
7844 cursor_pos = quote_fmt_get_cursor_pos();
7845 compose->set_cursor_pos = cursor_pos;
7846 if (cursor_pos == -1)
7848 gtk_text_buffer_get_start_iter(buffer, &iter);
7849 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7850 gtk_text_buffer_place_cursor(buffer, &iter);
7853 /* process the other fields */
7855 compose_template_apply_fields(compose, tmpl);
7856 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7857 quote_fmt_reset_vartable();
7858 compose_changed_cb(NULL, compose);
7861 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7863 MsgInfo* dummyinfo = NULL;
7864 MsgInfo *msginfo = NULL;
7867 if (compose->replyinfo != NULL)
7868 msginfo = compose->replyinfo;
7869 else if (compose->fwdinfo != NULL)
7870 msginfo = compose->fwdinfo;
7872 dummyinfo = compose_msginfo_new_from_compose(compose);
7873 msginfo = dummyinfo;
7876 if (tmpl->from && *tmpl->from != '\0') {
7878 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7879 compose->gtkaspell);
7881 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7883 quote_fmt_scan_string(tmpl->from);
7886 buf = quote_fmt_get_buffer();
7888 alertpanel_error(_("Template From format error."));
7890 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7894 if (tmpl->to && *tmpl->to != '\0') {
7896 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7897 compose->gtkaspell);
7899 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7901 quote_fmt_scan_string(tmpl->to);
7904 buf = quote_fmt_get_buffer();
7906 alertpanel_error(_("Template To format error."));
7908 compose_entry_append(compose, buf, COMPOSE_TO);
7912 if (tmpl->cc && *tmpl->cc != '\0') {
7914 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7915 compose->gtkaspell);
7917 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7919 quote_fmt_scan_string(tmpl->cc);
7922 buf = quote_fmt_get_buffer();
7924 alertpanel_error(_("Template Cc format error."));
7926 compose_entry_append(compose, buf, COMPOSE_CC);
7930 if (tmpl->bcc && *tmpl->bcc != '\0') {
7932 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7933 compose->gtkaspell);
7935 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7937 quote_fmt_scan_string(tmpl->bcc);
7940 buf = quote_fmt_get_buffer();
7942 alertpanel_error(_("Template Bcc format error."));
7944 compose_entry_append(compose, buf, COMPOSE_BCC);
7948 /* process the subject */
7949 if (tmpl->subject && *tmpl->subject != '\0') {
7951 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7952 compose->gtkaspell);
7954 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7956 quote_fmt_scan_string(tmpl->subject);
7959 buf = quote_fmt_get_buffer();
7961 alertpanel_error(_("Template subject format error."));
7963 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7967 procmsg_msginfo_free( dummyinfo );
7970 static void compose_destroy(Compose *compose)
7972 GtkTextBuffer *buffer;
7973 GtkClipboard *clipboard;
7975 compose_list = g_list_remove(compose_list, compose);
7977 if (compose->updating) {
7978 debug_print("danger, not destroying anything now\n");
7979 compose->deferred_destroy = TRUE;
7982 /* NOTE: address_completion_end() does nothing with the window
7983 * however this may change. */
7984 address_completion_end(compose->window);
7986 slist_free_strings(compose->to_list);
7987 g_slist_free(compose->to_list);
7988 slist_free_strings(compose->newsgroup_list);
7989 g_slist_free(compose->newsgroup_list);
7990 slist_free_strings(compose->header_list);
7991 g_slist_free(compose->header_list);
7993 procmsg_msginfo_free(compose->targetinfo);
7994 procmsg_msginfo_free(compose->replyinfo);
7995 procmsg_msginfo_free(compose->fwdinfo);
7997 g_free(compose->replyto);
7998 g_free(compose->cc);
7999 g_free(compose->bcc);
8000 g_free(compose->newsgroups);
8001 g_free(compose->followup_to);
8003 g_free(compose->ml_post);
8005 g_free(compose->inreplyto);
8006 g_free(compose->references);
8007 g_free(compose->msgid);
8008 g_free(compose->boundary);
8010 g_free(compose->redirect_filename);
8011 if (compose->undostruct)
8012 undo_destroy(compose->undostruct);
8014 g_free(compose->sig_str);
8016 g_free(compose->exteditor_file);
8018 g_free(compose->orig_charset);
8020 g_free(compose->privacy_system);
8022 if (addressbook_get_target_compose() == compose)
8023 addressbook_set_target_compose(NULL);
8026 if (compose->gtkaspell) {
8027 gtkaspell_delete(compose->gtkaspell);
8028 compose->gtkaspell = NULL;
8032 if (!compose->batch) {
8033 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8034 prefs_common.compose_height = compose->window->allocation.height;
8037 if (!gtk_widget_get_parent(compose->paned))
8038 gtk_widget_destroy(compose->paned);
8039 gtk_widget_destroy(compose->popupmenu);
8041 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8042 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8043 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8045 gtk_widget_destroy(compose->window);
8046 toolbar_destroy(compose->toolbar);
8047 g_free(compose->toolbar);
8048 g_mutex_free(compose->mutex);
8052 static void compose_attach_info_free(AttachInfo *ainfo)
8054 g_free(ainfo->file);
8055 g_free(ainfo->content_type);
8056 g_free(ainfo->name);
8060 static void compose_attach_update_label(Compose *compose)
8065 GtkTreeModel *model;
8070 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8071 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8072 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8076 while(gtk_tree_model_iter_next(model, &iter))
8079 text = g_strdup_printf("(%d)", i);
8080 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8084 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8086 Compose *compose = (Compose *)data;
8087 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8088 GtkTreeSelection *selection;
8090 GtkTreeModel *model;
8092 selection = gtk_tree_view_get_selection(tree_view);
8093 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8098 for (cur = sel; cur != NULL; cur = cur->next) {
8099 GtkTreePath *path = cur->data;
8100 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8103 gtk_tree_path_free(path);
8106 for (cur = sel; cur != NULL; cur = cur->next) {
8107 GtkTreeRowReference *ref = cur->data;
8108 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8111 if (gtk_tree_model_get_iter(model, &iter, path))
8112 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8114 gtk_tree_path_free(path);
8115 gtk_tree_row_reference_free(ref);
8119 compose_attach_update_label(compose);
8122 static struct _AttachProperty
8125 GtkWidget *mimetype_entry;
8126 GtkWidget *encoding_optmenu;
8127 GtkWidget *path_entry;
8128 GtkWidget *filename_entry;
8130 GtkWidget *cancel_btn;
8133 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8135 gtk_tree_path_free((GtkTreePath *)ptr);
8138 static void compose_attach_property(GtkAction *action, gpointer data)
8140 Compose *compose = (Compose *)data;
8141 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8143 GtkComboBox *optmenu;
8144 GtkTreeSelection *selection;
8146 GtkTreeModel *model;
8149 static gboolean cancelled;
8151 /* only if one selected */
8152 selection = gtk_tree_view_get_selection(tree_view);
8153 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8156 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8160 path = (GtkTreePath *) sel->data;
8161 gtk_tree_model_get_iter(model, &iter, path);
8162 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8165 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8171 if (!attach_prop.window)
8172 compose_attach_property_create(&cancelled);
8173 gtk_widget_grab_focus(attach_prop.ok_btn);
8174 gtk_widget_show(attach_prop.window);
8175 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8177 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8178 if (ainfo->encoding == ENC_UNKNOWN)
8179 combobox_select_by_data(optmenu, ENC_BASE64);
8181 combobox_select_by_data(optmenu, ainfo->encoding);
8183 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8184 ainfo->content_type ? ainfo->content_type : "");
8185 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8186 ainfo->file ? ainfo->file : "");
8187 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8188 ainfo->name ? ainfo->name : "");
8191 const gchar *entry_text;
8193 gchar *cnttype = NULL;
8200 gtk_widget_hide(attach_prop.window);
8205 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8206 if (*entry_text != '\0') {
8209 text = g_strstrip(g_strdup(entry_text));
8210 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8211 cnttype = g_strdup(text);
8214 alertpanel_error(_("Invalid MIME type."));
8220 ainfo->encoding = combobox_get_active_data(optmenu);
8222 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8223 if (*entry_text != '\0') {
8224 if (is_file_exist(entry_text) &&
8225 (size = get_file_size(entry_text)) > 0)
8226 file = g_strdup(entry_text);
8229 (_("File doesn't exist or is empty."));
8235 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8236 if (*entry_text != '\0') {
8237 g_free(ainfo->name);
8238 ainfo->name = g_strdup(entry_text);
8242 g_free(ainfo->content_type);
8243 ainfo->content_type = cnttype;
8246 g_free(ainfo->file);
8250 ainfo->size = (goffset)size;
8252 /* update tree store */
8253 text = to_human_readable(ainfo->size);
8254 gtk_tree_model_get_iter(model, &iter, path);
8255 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8256 COL_MIMETYPE, ainfo->content_type,
8258 COL_NAME, ainfo->name,
8264 gtk_tree_path_free(path);
8267 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8269 label = gtk_label_new(str); \
8270 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8271 GTK_FILL, 0, 0, 0); \
8272 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8274 entry = gtk_entry_new(); \
8275 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8276 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8279 static void compose_attach_property_create(gboolean *cancelled)
8285 GtkWidget *mimetype_entry;
8288 GtkListStore *optmenu_menu;
8289 GtkWidget *path_entry;
8290 GtkWidget *filename_entry;
8293 GtkWidget *cancel_btn;
8294 GList *mime_type_list, *strlist;
8297 debug_print("Creating attach_property window...\n");
8299 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8300 gtk_widget_set_size_request(window, 480, -1);
8301 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8302 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8303 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8304 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8305 g_signal_connect(G_OBJECT(window), "delete_event",
8306 G_CALLBACK(attach_property_delete_event),
8308 g_signal_connect(G_OBJECT(window), "key_press_event",
8309 G_CALLBACK(attach_property_key_pressed),
8312 vbox = gtk_vbox_new(FALSE, 8);
8313 gtk_container_add(GTK_CONTAINER(window), vbox);
8315 table = gtk_table_new(4, 2, FALSE);
8316 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8317 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8318 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8320 label = gtk_label_new(_("MIME type"));
8321 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8323 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8324 mimetype_entry = gtk_combo_box_entry_new_text();
8325 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8326 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8328 /* stuff with list */
8329 mime_type_list = procmime_get_mime_type_list();
8331 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8332 MimeType *type = (MimeType *) mime_type_list->data;
8335 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8337 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8340 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8341 (GCompareFunc)strcmp2);
8344 for (mime_type_list = strlist; mime_type_list != NULL;
8345 mime_type_list = mime_type_list->next) {
8346 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8347 g_free(mime_type_list->data);
8349 g_list_free(strlist);
8350 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8351 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8353 label = gtk_label_new(_("Encoding"));
8354 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8356 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8358 hbox = gtk_hbox_new(FALSE, 0);
8359 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8360 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8362 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8363 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8365 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8366 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8367 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8368 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8369 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8371 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8373 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8374 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8376 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8377 &ok_btn, GTK_STOCK_OK,
8379 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8380 gtk_widget_grab_default(ok_btn);
8382 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8383 G_CALLBACK(attach_property_ok),
8385 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8386 G_CALLBACK(attach_property_cancel),
8389 gtk_widget_show_all(vbox);
8391 attach_prop.window = window;
8392 attach_prop.mimetype_entry = mimetype_entry;
8393 attach_prop.encoding_optmenu = optmenu;
8394 attach_prop.path_entry = path_entry;
8395 attach_prop.filename_entry = filename_entry;
8396 attach_prop.ok_btn = ok_btn;
8397 attach_prop.cancel_btn = cancel_btn;
8400 #undef SET_LABEL_AND_ENTRY
8402 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8408 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8414 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8415 gboolean *cancelled)
8423 static gboolean attach_property_key_pressed(GtkWidget *widget,
8425 gboolean *cancelled)
8427 if (event && event->keyval == GDK_Escape) {
8431 if (event && event->keyval == GDK_Return) {
8438 static void compose_exec_ext_editor(Compose *compose)
8445 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8446 G_DIR_SEPARATOR, compose);
8448 if (pipe(pipe_fds) < 0) {
8454 if ((pid = fork()) < 0) {
8461 /* close the write side of the pipe */
8464 compose->exteditor_file = g_strdup(tmp);
8465 compose->exteditor_pid = pid;
8467 compose_set_ext_editor_sensitive(compose, FALSE);
8470 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8472 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8474 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8478 } else { /* process-monitoring process */
8484 /* close the read side of the pipe */
8487 if (compose_write_body_to_file(compose, tmp) < 0) {
8488 fd_write_all(pipe_fds[1], "2\n", 2);
8492 pid_ed = compose_exec_ext_editor_real(tmp);
8494 fd_write_all(pipe_fds[1], "1\n", 2);
8498 /* wait until editor is terminated */
8499 waitpid(pid_ed, NULL, 0);
8501 fd_write_all(pipe_fds[1], "0\n", 2);
8508 #endif /* G_OS_UNIX */
8512 static gint compose_exec_ext_editor_real(const gchar *file)
8519 g_return_val_if_fail(file != NULL, -1);
8521 if ((pid = fork()) < 0) {
8526 if (pid != 0) return pid;
8528 /* grandchild process */
8530 if (setpgid(0, getppid()))
8533 if (prefs_common_get_ext_editor_cmd() &&
8534 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8535 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8536 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8538 if (prefs_common_get_ext_editor_cmd())
8539 g_warning("External editor command-line is invalid: '%s'\n",
8540 prefs_common_get_ext_editor_cmd());
8541 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8544 cmdline = strsplit_with_quote(buf, " ", 1024);
8545 execvp(cmdline[0], cmdline);
8548 g_strfreev(cmdline);
8553 static gboolean compose_ext_editor_kill(Compose *compose)
8555 pid_t pgid = compose->exteditor_pid * -1;
8558 ret = kill(pgid, 0);
8560 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8564 msg = g_strdup_printf
8565 (_("The external editor is still working.\n"
8566 "Force terminating the process?\n"
8567 "process group id: %d"), -pgid);
8568 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8569 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8573 if (val == G_ALERTALTERNATE) {
8574 g_source_remove(compose->exteditor_tag);
8575 g_io_channel_shutdown(compose->exteditor_ch,
8577 g_io_channel_unref(compose->exteditor_ch);
8579 if (kill(pgid, SIGTERM) < 0) perror("kill");
8580 waitpid(compose->exteditor_pid, NULL, 0);
8582 g_warning("Terminated process group id: %d", -pgid);
8583 g_warning("Temporary file: %s",
8584 compose->exteditor_file);
8586 compose_set_ext_editor_sensitive(compose, TRUE);
8588 g_free(compose->exteditor_file);
8589 compose->exteditor_file = NULL;
8590 compose->exteditor_pid = -1;
8591 compose->exteditor_ch = NULL;
8592 compose->exteditor_tag = -1;
8600 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8604 Compose *compose = (Compose *)data;
8607 debug_print(_("Compose: input from monitoring process\n"));
8609 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8611 g_io_channel_shutdown(source, FALSE, NULL);
8612 g_io_channel_unref(source);
8614 waitpid(compose->exteditor_pid, NULL, 0);
8616 if (buf[0] == '0') { /* success */
8617 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8618 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8620 gtk_text_buffer_set_text(buffer, "", -1);
8621 compose_insert_file(compose, compose->exteditor_file);
8622 compose_changed_cb(NULL, compose);
8623 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8625 if (claws_unlink(compose->exteditor_file) < 0)
8626 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8627 } else if (buf[0] == '1') { /* failed */
8628 g_warning("Couldn't exec external editor\n");
8629 if (claws_unlink(compose->exteditor_file) < 0)
8630 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8631 } else if (buf[0] == '2') {
8632 g_warning("Couldn't write to file\n");
8633 } else if (buf[0] == '3') {
8634 g_warning("Pipe read failed\n");
8637 compose_set_ext_editor_sensitive(compose, TRUE);
8639 g_free(compose->exteditor_file);
8640 compose->exteditor_file = NULL;
8641 compose->exteditor_pid = -1;
8642 compose->exteditor_ch = NULL;
8643 compose->exteditor_tag = -1;
8648 static void compose_set_ext_editor_sensitive(Compose *compose,
8651 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8652 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8653 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8654 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8655 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8656 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8657 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8659 gtk_widget_set_sensitive(compose->text, sensitive);
8660 if (compose->toolbar->send_btn)
8661 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8662 if (compose->toolbar->sendl_btn)
8663 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8664 if (compose->toolbar->draft_btn)
8665 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8666 if (compose->toolbar->insert_btn)
8667 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8668 if (compose->toolbar->sig_btn)
8669 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8670 if (compose->toolbar->exteditor_btn)
8671 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8672 if (compose->toolbar->linewrap_current_btn)
8673 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8674 if (compose->toolbar->linewrap_all_btn)
8675 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8677 #endif /* G_OS_UNIX */
8680 * compose_undo_state_changed:
8682 * Change the sensivity of the menuentries undo and redo
8684 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8685 gint redo_state, gpointer data)
8687 Compose *compose = (Compose *)data;
8689 switch (undo_state) {
8690 case UNDO_STATE_TRUE:
8691 if (!undostruct->undo_state) {
8692 undostruct->undo_state = TRUE;
8693 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8696 case UNDO_STATE_FALSE:
8697 if (undostruct->undo_state) {
8698 undostruct->undo_state = FALSE;
8699 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8702 case UNDO_STATE_UNCHANGED:
8704 case UNDO_STATE_REFRESH:
8705 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8708 g_warning("Undo state not recognized");
8712 switch (redo_state) {
8713 case UNDO_STATE_TRUE:
8714 if (!undostruct->redo_state) {
8715 undostruct->redo_state = TRUE;
8716 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8719 case UNDO_STATE_FALSE:
8720 if (undostruct->redo_state) {
8721 undostruct->redo_state = FALSE;
8722 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8725 case UNDO_STATE_UNCHANGED:
8727 case UNDO_STATE_REFRESH:
8728 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8731 g_warning("Redo state not recognized");
8736 /* callback functions */
8738 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8739 * includes "non-client" (windows-izm) in calculation, so this calculation
8740 * may not be accurate.
8742 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8743 GtkAllocation *allocation,
8744 GtkSHRuler *shruler)
8746 if (prefs_common.show_ruler) {
8747 gint char_width = 0, char_height = 0;
8748 gint line_width_in_chars;
8750 gtkut_get_font_size(GTK_WIDGET(widget),
8751 &char_width, &char_height);
8752 line_width_in_chars =
8753 (allocation->width - allocation->x) / char_width;
8755 /* got the maximum */
8756 gtk_ruler_set_range(GTK_RULER(shruler),
8757 0.0, line_width_in_chars, 0,
8758 /*line_width_in_chars*/ char_width);
8764 static void account_activated(GtkComboBox *optmenu, gpointer data)
8766 Compose *compose = (Compose *)data;
8769 gchar *folderidentifier;
8770 gint account_id = 0;
8774 /* Get ID of active account in the combo box */
8775 menu = gtk_combo_box_get_model(optmenu);
8776 gtk_combo_box_get_active_iter(optmenu, &iter);
8777 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8779 ac = account_find_from_id(account_id);
8780 g_return_if_fail(ac != NULL);
8782 if (ac != compose->account)
8783 compose_select_account(compose, ac, FALSE);
8785 /* Set message save folder */
8786 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8787 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8789 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8790 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8792 compose_set_save_to(compose, NULL);
8793 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8794 folderidentifier = folder_item_get_identifier(account_get_special_folder
8795 (compose->account, F_OUTBOX));
8796 compose_set_save_to(compose, folderidentifier);
8797 g_free(folderidentifier);
8801 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8802 GtkTreeViewColumn *column, Compose *compose)
8804 compose_attach_property(NULL, compose);
8807 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8810 Compose *compose = (Compose *)data;
8811 GtkTreeSelection *attach_selection;
8812 gint attach_nr_selected;
8814 if (!event) return FALSE;
8816 if (event->button == 3) {
8817 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8818 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8820 if (attach_nr_selected > 0)
8822 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8823 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8825 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8826 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8829 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8830 NULL, NULL, event->button, event->time);
8837 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8840 Compose *compose = (Compose *)data;
8842 if (!event) return FALSE;
8844 switch (event->keyval) {
8846 compose_attach_remove_selected(NULL, compose);
8852 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8854 toolbar_comp_set_sensitive(compose, allow);
8855 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8856 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8858 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8860 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8861 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8862 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8864 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8868 static void compose_send_cb(GtkAction *action, gpointer data)
8870 Compose *compose = (Compose *)data;
8872 if (prefs_common.work_offline &&
8873 !inc_offline_should_override(TRUE,
8874 _("Claws Mail needs network access in order "
8875 "to send this email.")))
8878 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8879 g_source_remove(compose->draft_timeout_tag);
8880 compose->draft_timeout_tag = -1;
8883 compose_send(compose);
8886 static void compose_send_later_cb(GtkAction *action, gpointer data)
8888 Compose *compose = (Compose *)data;
8892 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8896 compose_close(compose);
8897 } else if (val == -1) {
8898 alertpanel_error(_("Could not queue message."));
8899 } else if (val == -2) {
8900 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8901 } else if (val == -3) {
8902 if (privacy_peek_error())
8903 alertpanel_error(_("Could not queue message for sending:\n\n"
8904 "Signature failed: %s"), privacy_get_error());
8905 } else if (val == -4) {
8906 alertpanel_error(_("Could not queue message for sending:\n\n"
8907 "Charset conversion failed."));
8908 } else if (val == -5) {
8909 alertpanel_error(_("Could not queue message for sending:\n\n"
8910 "Couldn't get recipient encryption key."));
8911 } else if (val == -6) {
8914 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8917 #define DRAFTED_AT_EXIT "drafted_at_exit"
8918 static void compose_register_draft(MsgInfo *info)
8920 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8921 DRAFTED_AT_EXIT, NULL);
8922 FILE *fp = g_fopen(filepath, "ab");
8925 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8933 gboolean compose_draft (gpointer data, guint action)
8935 Compose *compose = (Compose *)data;
8939 MsgFlags flag = {0, 0};
8940 static gboolean lock = FALSE;
8941 MsgInfo *newmsginfo;
8943 gboolean target_locked = FALSE;
8944 gboolean err = FALSE;
8946 if (lock) return FALSE;
8948 if (compose->sending)
8951 draft = account_get_special_folder(compose->account, F_DRAFT);
8952 g_return_val_if_fail(draft != NULL, FALSE);
8954 if (!g_mutex_trylock(compose->mutex)) {
8955 /* we don't want to lock the mutex once it's available,
8956 * because as the only other part of compose.c locking
8957 * it is compose_close - which means once unlocked,
8958 * the compose struct will be freed */
8959 debug_print("couldn't lock mutex, probably sending\n");
8965 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8966 G_DIR_SEPARATOR, compose);
8967 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8968 FILE_OP_ERROR(tmp, "fopen");
8972 /* chmod for security */
8973 if (change_file_mode_rw(fp, tmp) < 0) {
8974 FILE_OP_ERROR(tmp, "chmod");
8975 g_warning("can't change file mode\n");
8978 /* Save draft infos */
8979 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8980 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8982 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8983 gchar *savefolderid;
8985 savefolderid = compose_get_save_to(compose);
8986 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8987 g_free(savefolderid);
8989 if (compose->return_receipt) {
8990 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8992 if (compose->privacy_system) {
8993 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8994 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8995 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8998 /* Message-ID of message replying to */
8999 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9002 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9003 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9006 /* Message-ID of message forwarding to */
9007 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9010 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9011 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9015 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9016 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9018 /* end of headers */
9019 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9026 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9030 if (fclose(fp) == EOF) {
9034 if (compose->targetinfo) {
9035 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9036 flag.perm_flags = target_locked?MSG_LOCKED:0;
9038 flag.tmp_flags = MSG_DRAFT;
9040 folder_item_scan(draft);
9041 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9042 MsgInfo *tmpinfo = NULL;
9043 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9044 if (compose->msgid) {
9045 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9048 msgnum = tmpinfo->msgnum;
9049 procmsg_msginfo_free(tmpinfo);
9050 debug_print("got draft msgnum %d from scanning\n", msgnum);
9052 debug_print("didn't get draft msgnum after scanning\n");
9055 debug_print("got draft msgnum %d from adding\n", msgnum);
9061 if (action != COMPOSE_AUTO_SAVE) {
9062 if (action != COMPOSE_DRAFT_FOR_EXIT)
9063 alertpanel_error(_("Could not save draft."));
9066 gtkut_window_popup(compose->window);
9067 val = alertpanel_full(_("Could not save draft"),
9068 _("Could not save draft.\n"
9069 "Do you want to cancel exit or discard this email?"),
9070 _("_Cancel exit"), _("_Discard email"), NULL,
9071 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9072 if (val == G_ALERTALTERNATE) {
9074 g_mutex_unlock(compose->mutex); /* must be done before closing */
9075 compose_close(compose);
9079 g_mutex_unlock(compose->mutex); /* must be done before closing */
9088 if (compose->mode == COMPOSE_REEDIT) {
9089 compose_remove_reedit_target(compose, TRUE);
9092 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9095 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9097 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9099 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9100 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9101 procmsg_msginfo_set_flags(newmsginfo, 0,
9102 MSG_HAS_ATTACHMENT);
9104 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9105 compose_register_draft(newmsginfo);
9107 procmsg_msginfo_free(newmsginfo);
9110 folder_item_scan(draft);
9112 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9114 g_mutex_unlock(compose->mutex); /* must be done before closing */
9115 compose_close(compose);
9121 path = folder_item_fetch_msg(draft, msgnum);
9123 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9126 if (g_stat(path, &s) < 0) {
9127 FILE_OP_ERROR(path, "stat");
9133 procmsg_msginfo_free(compose->targetinfo);
9134 compose->targetinfo = procmsg_msginfo_new();
9135 compose->targetinfo->msgnum = msgnum;
9136 compose->targetinfo->size = (goffset)s.st_size;
9137 compose->targetinfo->mtime = s.st_mtime;
9138 compose->targetinfo->folder = draft;
9140 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9141 compose->mode = COMPOSE_REEDIT;
9143 if (action == COMPOSE_AUTO_SAVE) {
9144 compose->autosaved_draft = compose->targetinfo;
9146 compose->modified = FALSE;
9147 compose_set_title(compose);
9151 g_mutex_unlock(compose->mutex);
9155 void compose_clear_exit_drafts(void)
9157 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9158 DRAFTED_AT_EXIT, NULL);
9159 if (is_file_exist(filepath))
9160 claws_unlink(filepath);
9165 void compose_reopen_exit_drafts(void)
9167 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9168 DRAFTED_AT_EXIT, NULL);
9169 FILE *fp = g_fopen(filepath, "rb");
9173 while (fgets(buf, sizeof(buf), fp)) {
9174 gchar **parts = g_strsplit(buf, "\t", 2);
9175 const gchar *folder = parts[0];
9176 int msgnum = parts[1] ? atoi(parts[1]):-1;
9178 if (folder && *folder && msgnum > -1) {
9179 FolderItem *item = folder_find_item_from_identifier(folder);
9180 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9182 compose_reedit(info, FALSE);
9189 compose_clear_exit_drafts();
9192 static void compose_save_cb(GtkAction *action, gpointer data)
9194 Compose *compose = (Compose *)data;
9195 compose_draft(compose, COMPOSE_KEEP_EDITING);
9196 compose->rmode = COMPOSE_REEDIT;
9199 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9201 if (compose && file_list) {
9204 for ( tmp = file_list; tmp; tmp = tmp->next) {
9205 gchar *file = (gchar *) tmp->data;
9206 gchar *utf8_filename = conv_filename_to_utf8(file);
9207 compose_attach_append(compose, file, utf8_filename, NULL);
9208 compose_changed_cb(NULL, compose);
9213 g_free(utf8_filename);
9218 static void compose_attach_cb(GtkAction *action, gpointer data)
9220 Compose *compose = (Compose *)data;
9223 if (compose->redirect_filename != NULL)
9226 file_list = filesel_select_multiple_files_open(_("Select file"));
9229 compose_attach_from_list(compose, file_list, TRUE);
9230 g_list_free(file_list);
9234 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9236 Compose *compose = (Compose *)data;
9239 file_list = filesel_select_multiple_files_open(_("Select file"));
9244 for ( tmp = file_list; tmp; tmp = tmp->next) {
9245 gchar *file = (gchar *) tmp->data;
9246 gchar *filedup = g_strdup(file);
9247 gchar *shortfile = g_path_get_basename(filedup);
9248 ComposeInsertResult res;
9250 res = compose_insert_file(compose, file);
9251 if (res == COMPOSE_INSERT_READ_ERROR) {
9252 alertpanel_error(_("File '%s' could not be read."), shortfile);
9253 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9254 alertpanel_error(_("File '%s' contained invalid characters\n"
9255 "for the current encoding, insertion may be incorrect."), shortfile);
9261 g_list_free(file_list);
9265 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9267 Compose *compose = (Compose *)data;
9269 compose_insert_sig(compose, FALSE);
9272 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9276 Compose *compose = (Compose *)data;
9278 gtkut_widget_get_uposition(widget, &x, &y);
9279 if (!compose->batch) {
9280 prefs_common.compose_x = x;
9281 prefs_common.compose_y = y;
9283 if (compose->sending || compose->updating)
9285 compose_close_cb(NULL, compose);
9289 void compose_close_toolbar(Compose *compose)
9291 compose_close_cb(NULL, compose);
9294 static void compose_close_cb(GtkAction *action, gpointer data)
9296 Compose *compose = (Compose *)data;
9300 if (compose->exteditor_tag != -1) {
9301 if (!compose_ext_editor_kill(compose))
9306 if (compose->modified) {
9307 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9308 if (!g_mutex_trylock(compose->mutex)) {
9309 /* we don't want to lock the mutex once it's available,
9310 * because as the only other part of compose.c locking
9311 * it is compose_close - which means once unlocked,
9312 * the compose struct will be freed */
9313 debug_print("couldn't lock mutex, probably sending\n");
9317 val = alertpanel(_("Discard message"),
9318 _("This message has been modified. Discard it?"),
9319 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9321 val = alertpanel(_("Save changes"),
9322 _("This message has been modified. Save the latest changes?"),
9323 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9325 g_mutex_unlock(compose->mutex);
9327 case G_ALERTDEFAULT:
9328 if (prefs_common.autosave && !reedit)
9329 compose_remove_draft(compose);
9331 case G_ALERTALTERNATE:
9332 compose_draft(data, COMPOSE_QUIT_EDITING);
9339 compose_close(compose);
9342 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9344 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9345 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9346 Compose *compose = (Compose *) data;
9349 compose->out_encoding = (CharSet)value;
9352 static void compose_address_cb(GtkAction *action, gpointer data)
9354 Compose *compose = (Compose *)data;
9356 addressbook_open(compose);
9359 static void about_show_cb(GtkAction *action, gpointer data)
9364 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9366 Compose *compose = (Compose *)data;
9371 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9372 g_return_if_fail(tmpl != NULL);
9374 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9376 val = alertpanel(_("Apply template"), msg,
9377 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9380 if (val == G_ALERTDEFAULT)
9381 compose_template_apply(compose, tmpl, TRUE);
9382 else if (val == G_ALERTALTERNATE)
9383 compose_template_apply(compose, tmpl, FALSE);
9386 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9388 Compose *compose = (Compose *)data;
9390 compose_exec_ext_editor(compose);
9393 static void compose_undo_cb(GtkAction *action, gpointer data)
9395 Compose *compose = (Compose *)data;
9396 gboolean prev_autowrap = compose->autowrap;
9398 compose->autowrap = FALSE;
9399 undo_undo(compose->undostruct);
9400 compose->autowrap = prev_autowrap;
9403 static void compose_redo_cb(GtkAction *action, gpointer data)
9405 Compose *compose = (Compose *)data;
9406 gboolean prev_autowrap = compose->autowrap;
9408 compose->autowrap = FALSE;
9409 undo_redo(compose->undostruct);
9410 compose->autowrap = prev_autowrap;
9413 static void entry_cut_clipboard(GtkWidget *entry)
9415 if (GTK_IS_EDITABLE(entry))
9416 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9417 else if (GTK_IS_TEXT_VIEW(entry))
9418 gtk_text_buffer_cut_clipboard(
9419 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9420 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9424 static void entry_copy_clipboard(GtkWidget *entry)
9426 if (GTK_IS_EDITABLE(entry))
9427 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9428 else if (GTK_IS_TEXT_VIEW(entry))
9429 gtk_text_buffer_copy_clipboard(
9430 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9431 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9434 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9435 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9437 if (GTK_IS_TEXT_VIEW(entry)) {
9438 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9439 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9440 GtkTextIter start_iter, end_iter;
9442 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9444 if (contents == NULL)
9447 /* we shouldn't delete the selection when middle-click-pasting, or we
9448 * can't mid-click-paste our own selection */
9449 if (clip != GDK_SELECTION_PRIMARY) {
9450 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9451 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9454 if (insert_place == NULL) {
9455 /* if insert_place isn't specified, insert at the cursor.
9456 * used for Ctrl-V pasting */
9457 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9458 start = gtk_text_iter_get_offset(&start_iter);
9459 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9461 /* if insert_place is specified, paste here.
9462 * used for mid-click-pasting */
9463 start = gtk_text_iter_get_offset(insert_place);
9464 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9465 if (prefs_common.primary_paste_unselects)
9466 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9470 /* paste unwrapped: mark the paste so it's not wrapped later */
9471 end = start + strlen(contents);
9472 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9473 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9474 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9475 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9476 /* rewrap paragraph now (after a mid-click-paste) */
9477 mark_start = gtk_text_buffer_get_insert(buffer);
9478 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9479 gtk_text_iter_backward_char(&start_iter);
9480 compose_beautify_paragraph(compose, &start_iter, TRUE);
9482 } else if (GTK_IS_EDITABLE(entry))
9483 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9485 compose->modified = TRUE;
9488 static void entry_allsel(GtkWidget *entry)
9490 if (GTK_IS_EDITABLE(entry))
9491 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9492 else if (GTK_IS_TEXT_VIEW(entry)) {
9493 GtkTextIter startiter, enditer;
9494 GtkTextBuffer *textbuf;
9496 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9497 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9498 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9500 gtk_text_buffer_move_mark_by_name(textbuf,
9501 "selection_bound", &startiter);
9502 gtk_text_buffer_move_mark_by_name(textbuf,
9503 "insert", &enditer);
9507 static void compose_cut_cb(GtkAction *action, gpointer data)
9509 Compose *compose = (Compose *)data;
9510 if (compose->focused_editable
9511 #ifndef GENERIC_UMPC
9512 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9515 entry_cut_clipboard(compose->focused_editable);
9518 static void compose_copy_cb(GtkAction *action, gpointer data)
9520 Compose *compose = (Compose *)data;
9521 if (compose->focused_editable
9522 #ifndef GENERIC_UMPC
9523 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9526 entry_copy_clipboard(compose->focused_editable);
9529 static void compose_paste_cb(GtkAction *action, gpointer data)
9531 Compose *compose = (Compose *)data;
9533 GtkTextBuffer *buffer;
9535 if (compose->focused_editable &&
9536 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9537 entry_paste_clipboard(compose, compose->focused_editable,
9538 prefs_common.linewrap_pastes,
9539 GDK_SELECTION_CLIPBOARD, NULL);
9543 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9545 Compose *compose = (Compose *)data;
9546 gint wrap_quote = prefs_common.linewrap_quote;
9547 if (compose->focused_editable
9548 #ifndef GENERIC_UMPC
9549 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9552 /* let text_insert() (called directly or at a later time
9553 * after the gtk_editable_paste_clipboard) know that
9554 * text is to be inserted as a quotation. implemented
9555 * by using a simple refcount... */
9556 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9557 G_OBJECT(compose->focused_editable),
9558 "paste_as_quotation"));
9559 g_object_set_data(G_OBJECT(compose->focused_editable),
9560 "paste_as_quotation",
9561 GINT_TO_POINTER(paste_as_quotation + 1));
9562 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9563 entry_paste_clipboard(compose, compose->focused_editable,
9564 prefs_common.linewrap_pastes,
9565 GDK_SELECTION_CLIPBOARD, NULL);
9566 prefs_common.linewrap_quote = wrap_quote;
9570 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9572 Compose *compose = (Compose *)data;
9574 GtkTextBuffer *buffer;
9576 if (compose->focused_editable
9577 #ifndef GENERIC_UMPC
9578 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9581 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9582 GDK_SELECTION_CLIPBOARD, NULL);
9586 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9588 Compose *compose = (Compose *)data;
9590 GtkTextBuffer *buffer;
9592 if (compose->focused_editable
9593 #ifndef GENERIC_UMPC
9594 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9597 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9598 GDK_SELECTION_CLIPBOARD, NULL);
9602 static void compose_allsel_cb(GtkAction *action, gpointer data)
9604 Compose *compose = (Compose *)data;
9605 if (compose->focused_editable
9606 #ifndef GENERIC_UMPC
9607 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9610 entry_allsel(compose->focused_editable);
9613 static void textview_move_beginning_of_line (GtkTextView *text)
9615 GtkTextBuffer *buffer;
9619 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9621 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9622 mark = gtk_text_buffer_get_insert(buffer);
9623 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9624 gtk_text_iter_set_line_offset(&ins, 0);
9625 gtk_text_buffer_place_cursor(buffer, &ins);
9628 static void textview_move_forward_character (GtkTextView *text)
9630 GtkTextBuffer *buffer;
9634 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9636 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9637 mark = gtk_text_buffer_get_insert(buffer);
9638 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9639 if (gtk_text_iter_forward_cursor_position(&ins))
9640 gtk_text_buffer_place_cursor(buffer, &ins);
9643 static void textview_move_backward_character (GtkTextView *text)
9645 GtkTextBuffer *buffer;
9649 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9651 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9652 mark = gtk_text_buffer_get_insert(buffer);
9653 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9654 if (gtk_text_iter_backward_cursor_position(&ins))
9655 gtk_text_buffer_place_cursor(buffer, &ins);
9658 static void textview_move_forward_word (GtkTextView *text)
9660 GtkTextBuffer *buffer;
9665 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9667 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9668 mark = gtk_text_buffer_get_insert(buffer);
9669 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9670 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9671 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9672 gtk_text_iter_backward_word_start(&ins);
9673 gtk_text_buffer_place_cursor(buffer, &ins);
9677 static void textview_move_backward_word (GtkTextView *text)
9679 GtkTextBuffer *buffer;
9684 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9686 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9687 mark = gtk_text_buffer_get_insert(buffer);
9688 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9689 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9690 if (gtk_text_iter_backward_word_starts(&ins, 1))
9691 gtk_text_buffer_place_cursor(buffer, &ins);
9694 static void textview_move_end_of_line (GtkTextView *text)
9696 GtkTextBuffer *buffer;
9700 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9702 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9703 mark = gtk_text_buffer_get_insert(buffer);
9704 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9705 if (gtk_text_iter_forward_to_line_end(&ins))
9706 gtk_text_buffer_place_cursor(buffer, &ins);
9709 static void textview_move_next_line (GtkTextView *text)
9711 GtkTextBuffer *buffer;
9716 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9718 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9719 mark = gtk_text_buffer_get_insert(buffer);
9720 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9721 offset = gtk_text_iter_get_line_offset(&ins);
9722 if (gtk_text_iter_forward_line(&ins)) {
9723 gtk_text_iter_set_line_offset(&ins, offset);
9724 gtk_text_buffer_place_cursor(buffer, &ins);
9728 static void textview_move_previous_line (GtkTextView *text)
9730 GtkTextBuffer *buffer;
9735 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9737 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9738 mark = gtk_text_buffer_get_insert(buffer);
9739 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9740 offset = gtk_text_iter_get_line_offset(&ins);
9741 if (gtk_text_iter_backward_line(&ins)) {
9742 gtk_text_iter_set_line_offset(&ins, offset);
9743 gtk_text_buffer_place_cursor(buffer, &ins);
9747 static void textview_delete_forward_character (GtkTextView *text)
9749 GtkTextBuffer *buffer;
9751 GtkTextIter ins, 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);
9759 if (gtk_text_iter_forward_char(&end_iter)) {
9760 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9764 static void textview_delete_backward_character (GtkTextView *text)
9766 GtkTextBuffer *buffer;
9768 GtkTextIter ins, end_iter;
9770 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9772 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9773 mark = gtk_text_buffer_get_insert(buffer);
9774 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9776 if (gtk_text_iter_backward_char(&end_iter)) {
9777 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9781 static void textview_delete_forward_word (GtkTextView *text)
9783 GtkTextBuffer *buffer;
9785 GtkTextIter ins, end_iter;
9787 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9789 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9790 mark = gtk_text_buffer_get_insert(buffer);
9791 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9793 if (gtk_text_iter_forward_word_end(&end_iter)) {
9794 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9798 static void textview_delete_backward_word (GtkTextView *text)
9800 GtkTextBuffer *buffer;
9802 GtkTextIter ins, end_iter;
9804 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9806 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9807 mark = gtk_text_buffer_get_insert(buffer);
9808 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9810 if (gtk_text_iter_backward_word_start(&end_iter)) {
9811 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9815 static void textview_delete_line (GtkTextView *text)
9817 GtkTextBuffer *buffer;
9819 GtkTextIter ins, start_iter, end_iter;
9821 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9823 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9824 mark = gtk_text_buffer_get_insert(buffer);
9825 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9828 gtk_text_iter_set_line_offset(&start_iter, 0);
9831 if (gtk_text_iter_ends_line(&end_iter)){
9832 if (!gtk_text_iter_forward_char(&end_iter))
9833 gtk_text_iter_backward_char(&start_iter);
9836 gtk_text_iter_forward_to_line_end(&end_iter);
9837 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9840 static void textview_delete_to_line_end (GtkTextView *text)
9842 GtkTextBuffer *buffer;
9844 GtkTextIter ins, end_iter;
9846 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9848 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9849 mark = gtk_text_buffer_get_insert(buffer);
9850 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9852 if (gtk_text_iter_ends_line(&end_iter))
9853 gtk_text_iter_forward_char(&end_iter);
9855 gtk_text_iter_forward_to_line_end(&end_iter);
9856 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9859 #define DO_ACTION(name, act) { \
9860 if(!strcmp(name, a_name)) { \
9864 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9866 const gchar *a_name = gtk_action_get_name(action);
9867 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9868 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9869 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9870 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9871 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9872 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9873 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9874 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9875 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9876 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9877 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9878 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9879 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9880 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9884 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9886 Compose *compose = (Compose *)data;
9887 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9888 ComposeCallAdvancedAction action = -1;
9890 action = compose_call_advanced_action_from_path(gaction);
9893 void (*do_action) (GtkTextView *text);
9894 } action_table[] = {
9895 {textview_move_beginning_of_line},
9896 {textview_move_forward_character},
9897 {textview_move_backward_character},
9898 {textview_move_forward_word},
9899 {textview_move_backward_word},
9900 {textview_move_end_of_line},
9901 {textview_move_next_line},
9902 {textview_move_previous_line},
9903 {textview_delete_forward_character},
9904 {textview_delete_backward_character},
9905 {textview_delete_forward_word},
9906 {textview_delete_backward_word},
9907 {textview_delete_line},
9908 {textview_delete_to_line_end}
9911 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9913 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9914 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9915 if (action_table[action].do_action)
9916 action_table[action].do_action(text);
9918 g_warning("Not implemented yet.");
9922 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9926 if (GTK_IS_EDITABLE(widget)) {
9927 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9928 gtk_editable_set_position(GTK_EDITABLE(widget),
9931 if (widget->parent && widget->parent->parent
9932 && widget->parent->parent->parent) {
9933 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9934 gint y = widget->allocation.y;
9935 gint height = widget->allocation.height;
9936 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9937 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9939 if (y < (int)shown->value) {
9940 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9942 if (y + height > (int)shown->value + (int)shown->page_size) {
9943 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9944 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9945 y + height - (int)shown->page_size - 1);
9947 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9948 (int)shown->upper - (int)shown->page_size - 1);
9955 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9956 compose->focused_editable = widget;
9959 if (GTK_IS_TEXT_VIEW(widget)
9960 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9961 g_object_ref(compose->notebook);
9962 g_object_ref(compose->edit_vbox);
9963 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9964 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9965 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9966 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9967 g_object_unref(compose->notebook);
9968 g_object_unref(compose->edit_vbox);
9969 g_signal_handlers_block_by_func(G_OBJECT(widget),
9970 G_CALLBACK(compose_grab_focus_cb),
9972 gtk_widget_grab_focus(widget);
9973 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9974 G_CALLBACK(compose_grab_focus_cb),
9976 } else if (!GTK_IS_TEXT_VIEW(widget)
9977 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9978 g_object_ref(compose->notebook);
9979 g_object_ref(compose->edit_vbox);
9980 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9981 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9982 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9983 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9984 g_object_unref(compose->notebook);
9985 g_object_unref(compose->edit_vbox);
9986 g_signal_handlers_block_by_func(G_OBJECT(widget),
9987 G_CALLBACK(compose_grab_focus_cb),
9989 gtk_widget_grab_focus(widget);
9990 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9991 G_CALLBACK(compose_grab_focus_cb),
9997 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9999 compose->modified = TRUE;
10000 #ifndef GENERIC_UMPC
10001 compose_set_title(compose);
10005 static void compose_wrap_cb(GtkAction *action, gpointer data)
10007 Compose *compose = (Compose *)data;
10008 compose_beautify_paragraph(compose, NULL, TRUE);
10011 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10013 Compose *compose = (Compose *)data;
10014 compose_wrap_all_full(compose, TRUE);
10017 static void compose_find_cb(GtkAction *action, gpointer data)
10019 Compose *compose = (Compose *)data;
10021 message_search_compose(compose);
10024 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10027 Compose *compose = (Compose *)data;
10028 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10029 if (compose->autowrap)
10030 compose_wrap_all_full(compose, TRUE);
10031 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10034 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10037 Compose *compose = (Compose *)data;
10038 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10041 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10043 Compose *compose = (Compose *)data;
10045 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10048 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10050 Compose *compose = (Compose *)data;
10052 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10055 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10057 g_free(compose->privacy_system);
10059 compose->privacy_system = g_strdup(account->default_privacy_system);
10060 compose_update_privacy_system_menu_item(compose, warn);
10063 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10065 Compose *compose = (Compose *)data;
10067 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10068 gtk_widget_show(compose->ruler_hbox);
10069 prefs_common.show_ruler = TRUE;
10071 gtk_widget_hide(compose->ruler_hbox);
10072 gtk_widget_queue_resize(compose->edit_vbox);
10073 prefs_common.show_ruler = FALSE;
10077 static void compose_attach_drag_received_cb (GtkWidget *widget,
10078 GdkDragContext *context,
10081 GtkSelectionData *data,
10084 gpointer user_data)
10086 Compose *compose = (Compose *)user_data;
10089 if (gdk_atom_name(data->type) &&
10090 !strcmp(gdk_atom_name(data->type), "text/uri-list")
10091 && gtk_drag_get_source_widget(context) !=
10092 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10093 list = uri_list_extract_filenames((const gchar *)data->data);
10094 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10095 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10096 compose_attach_append
10097 (compose, (const gchar *)tmp->data,
10098 utf8_filename, NULL);
10099 g_free(utf8_filename);
10101 if (list) compose_changed_cb(NULL, compose);
10102 list_free_strings(list);
10104 } else if (gtk_drag_get_source_widget(context)
10105 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10106 /* comes from our summaryview */
10107 SummaryView * summaryview = NULL;
10108 GSList * list = NULL, *cur = NULL;
10110 if (mainwindow_get_mainwindow())
10111 summaryview = mainwindow_get_mainwindow()->summaryview;
10114 list = summary_get_selected_msg_list(summaryview);
10116 for (cur = list; cur; cur = cur->next) {
10117 MsgInfo *msginfo = (MsgInfo *)cur->data;
10118 gchar *file = NULL;
10120 file = procmsg_get_message_file_full(msginfo,
10123 compose_attach_append(compose, (const gchar *)file,
10124 (const gchar *)file, "message/rfc822");
10128 g_slist_free(list);
10132 static gboolean compose_drag_drop(GtkWidget *widget,
10133 GdkDragContext *drag_context,
10135 guint time, gpointer user_data)
10137 /* not handling this signal makes compose_insert_drag_received_cb
10142 static void compose_insert_drag_received_cb (GtkWidget *widget,
10143 GdkDragContext *drag_context,
10146 GtkSelectionData *data,
10149 gpointer user_data)
10151 Compose *compose = (Compose *)user_data;
10154 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10156 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10157 AlertValue val = G_ALERTDEFAULT;
10159 list = uri_list_extract_filenames((const gchar *)data->data);
10161 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10162 /* Assume a list of no files, and data has ://, is a remote link */
10163 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10164 gchar *tmpfile = get_tmp_file();
10165 str_write_to_file(tmpdata, tmpfile);
10167 compose_insert_file(compose, tmpfile);
10168 claws_unlink(tmpfile);
10170 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10171 compose_beautify_paragraph(compose, NULL, TRUE);
10174 switch (prefs_common.compose_dnd_mode) {
10175 case COMPOSE_DND_ASK:
10176 val = alertpanel_full(_("Insert or attach?"),
10177 _("Do you want to insert the contents of the file(s) "
10178 "into the message body, or attach it to the email?"),
10179 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10180 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10182 case COMPOSE_DND_INSERT:
10183 val = G_ALERTALTERNATE;
10185 case COMPOSE_DND_ATTACH:
10186 val = G_ALERTOTHER;
10189 /* unexpected case */
10190 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10193 if (val & G_ALERTDISABLE) {
10194 val &= ~G_ALERTDISABLE;
10195 /* remember what action to perform by default, only if we don't click Cancel */
10196 if (val == G_ALERTALTERNATE)
10197 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10198 else if (val == G_ALERTOTHER)
10199 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10202 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10203 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10204 list_free_strings(list);
10207 } else if (val == G_ALERTOTHER) {
10208 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10209 list_free_strings(list);
10214 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10215 compose_insert_file(compose, (const gchar *)tmp->data);
10217 list_free_strings(list);
10219 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10222 #if GTK_CHECK_VERSION(2, 8, 0)
10223 /* do nothing, handled by GTK */
10225 gchar *tmpfile = get_tmp_file();
10226 str_write_to_file((const gchar *)data->data, tmpfile);
10227 compose_insert_file(compose, tmpfile);
10228 claws_unlink(tmpfile);
10230 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10234 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10237 static void compose_header_drag_received_cb (GtkWidget *widget,
10238 GdkDragContext *drag_context,
10241 GtkSelectionData *data,
10244 gpointer user_data)
10246 GtkEditable *entry = (GtkEditable *)user_data;
10247 gchar *email = (gchar *)data->data;
10249 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10252 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10253 gchar *decoded=g_new(gchar, strlen(email));
10256 email += strlen("mailto:");
10257 decode_uri(decoded, email); /* will fit */
10258 gtk_editable_delete_text(entry, 0, -1);
10259 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10260 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10264 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10267 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10269 Compose *compose = (Compose *)data;
10271 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10272 compose->return_receipt = TRUE;
10274 compose->return_receipt = FALSE;
10277 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10279 Compose *compose = (Compose *)data;
10281 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10282 compose->remove_references = TRUE;
10284 compose->remove_references = FALSE;
10287 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10288 GdkEventKey *event,
10289 ComposeHeaderEntry *headerentry)
10291 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10292 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10293 !(event->state & GDK_MODIFIER_MASK) &&
10294 (event->keyval == GDK_BackSpace) &&
10295 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10296 gtk_container_remove
10297 (GTK_CONTAINER(headerentry->compose->header_table),
10298 headerentry->combo);
10299 gtk_container_remove
10300 (GTK_CONTAINER(headerentry->compose->header_table),
10301 headerentry->entry);
10302 headerentry->compose->header_list =
10303 g_slist_remove(headerentry->compose->header_list,
10305 g_free(headerentry);
10306 } else if (event->keyval == GDK_Tab) {
10307 if (headerentry->compose->header_last == headerentry) {
10308 /* Override default next focus, and give it to subject_entry
10309 * instead of notebook tabs
10311 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10312 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10319 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10320 ComposeHeaderEntry *headerentry)
10322 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10323 compose_create_header_entry(headerentry->compose);
10324 g_signal_handlers_disconnect_matched
10325 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10326 0, 0, NULL, NULL, headerentry);
10328 /* Automatically scroll down */
10329 compose_show_first_last_header(headerentry->compose, FALSE);
10335 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10337 GtkAdjustment *vadj;
10339 g_return_if_fail(compose);
10340 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10341 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10343 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10344 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10345 gtk_adjustment_changed(vadj);
10348 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10349 const gchar *text, gint len, Compose *compose)
10351 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10352 (G_OBJECT(compose->text), "paste_as_quotation"));
10355 g_return_if_fail(text != NULL);
10357 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10358 G_CALLBACK(text_inserted),
10360 if (paste_as_quotation) {
10362 const gchar *qmark;
10364 GtkTextIter start_iter;
10367 len = strlen(text);
10369 new_text = g_strndup(text, len);
10371 qmark = compose_quote_char_from_context(compose);
10373 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10374 gtk_text_buffer_place_cursor(buffer, iter);
10376 pos = gtk_text_iter_get_offset(iter);
10378 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10379 _("Quote format error at line %d."));
10380 quote_fmt_reset_vartable();
10382 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10383 GINT_TO_POINTER(paste_as_quotation - 1));
10385 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10386 gtk_text_buffer_place_cursor(buffer, iter);
10387 gtk_text_buffer_delete_mark(buffer, mark);
10389 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10390 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10391 compose_beautify_paragraph(compose, &start_iter, FALSE);
10392 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10393 gtk_text_buffer_delete_mark(buffer, mark);
10395 if (strcmp(text, "\n") || compose->automatic_break
10396 || gtk_text_iter_starts_line(iter)) {
10397 GtkTextIter before_ins;
10398 gtk_text_buffer_insert(buffer, iter, text, len);
10399 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10400 before_ins = *iter;
10401 gtk_text_iter_backward_chars(&before_ins, len);
10402 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10405 /* check if the preceding is just whitespace or quote */
10406 GtkTextIter start_line;
10407 gchar *tmp = NULL, *quote = NULL;
10408 gint quote_len = 0, is_normal = 0;
10409 start_line = *iter;
10410 gtk_text_iter_set_line_offset(&start_line, 0);
10411 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10414 if (*tmp == '\0') {
10417 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10425 gtk_text_buffer_insert(buffer, iter, text, len);
10427 gtk_text_buffer_insert_with_tags_by_name(buffer,
10428 iter, text, len, "no_join", NULL);
10433 if (!paste_as_quotation) {
10434 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10435 compose_beautify_paragraph(compose, iter, FALSE);
10436 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10437 gtk_text_buffer_delete_mark(buffer, mark);
10440 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10441 G_CALLBACK(text_inserted),
10443 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10445 if (prefs_common.autosave &&
10446 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10447 compose->draft_timeout_tag != -2 /* disabled while loading */)
10448 compose->draft_timeout_tag = g_timeout_add
10449 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10451 static gint compose_defer_auto_save_draft(Compose *compose)
10453 compose->draft_timeout_tag = -1;
10454 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10459 static void compose_check_all(GtkAction *action, gpointer data)
10461 Compose *compose = (Compose *)data;
10462 if (compose->gtkaspell)
10463 gtkaspell_check_all(compose->gtkaspell);
10466 static void compose_highlight_all(GtkAction *action, gpointer data)
10468 Compose *compose = (Compose *)data;
10469 if (compose->gtkaspell)
10470 gtkaspell_highlight_all(compose->gtkaspell);
10473 static void compose_check_backwards(GtkAction *action, gpointer data)
10475 Compose *compose = (Compose *)data;
10476 if (compose->gtkaspell)
10477 gtkaspell_check_backwards(compose->gtkaspell);
10479 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10483 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10485 Compose *compose = (Compose *)data;
10486 if (compose->gtkaspell)
10487 gtkaspell_check_forwards_go(compose->gtkaspell);
10489 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10495 *\brief Guess originating forward account from MsgInfo and several
10496 * "common preference" settings. Return NULL if no guess.
10498 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10500 PrefsAccount *account = NULL;
10502 g_return_val_if_fail(msginfo, NULL);
10503 g_return_val_if_fail(msginfo->folder, NULL);
10504 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10506 if (msginfo->folder->prefs->enable_default_account)
10507 account = account_find_from_id(msginfo->folder->prefs->default_account);
10510 account = msginfo->folder->folder->account;
10512 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10514 Xstrdup_a(to, msginfo->to, return NULL);
10515 extract_address(to);
10516 account = account_find_from_address(to, FALSE);
10519 if (!account && prefs_common.forward_account_autosel) {
10520 gchar cc[BUFFSIZE];
10521 if (!procheader_get_header_from_msginfo
10522 (msginfo, cc,sizeof cc , "Cc:")) {
10523 gchar *buf = cc + strlen("Cc:");
10524 extract_address(buf);
10525 account = account_find_from_address(buf, FALSE);
10529 if (!account && prefs_common.forward_account_autosel) {
10530 gchar deliveredto[BUFFSIZE];
10531 if (!procheader_get_header_from_msginfo
10532 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10533 gchar *buf = deliveredto + strlen("Delivered-To:");
10534 extract_address(buf);
10535 account = account_find_from_address(buf, FALSE);
10542 gboolean compose_close(Compose *compose)
10546 if (!g_mutex_trylock(compose->mutex)) {
10547 /* we have to wait for the (possibly deferred by auto-save)
10548 * drafting to be done, before destroying the compose under
10550 debug_print("waiting for drafting to finish...\n");
10551 compose_allow_user_actions(compose, FALSE);
10552 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10555 g_return_val_if_fail(compose, FALSE);
10556 gtkut_widget_get_uposition(compose->window, &x, &y);
10557 if (!compose->batch) {
10558 prefs_common.compose_x = x;
10559 prefs_common.compose_y = y;
10561 g_mutex_unlock(compose->mutex);
10562 compose_destroy(compose);
10567 * Add entry field for each address in list.
10568 * \param compose E-Mail composition object.
10569 * \param listAddress List of (formatted) E-Mail addresses.
10571 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10574 node = listAddress;
10576 addr = ( gchar * ) node->data;
10577 compose_entry_append( compose, addr, COMPOSE_TO );
10578 node = g_list_next( node );
10582 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10583 guint action, gboolean opening_multiple)
10585 gchar *body = NULL;
10586 GSList *new_msglist = NULL;
10587 MsgInfo *tmp_msginfo = NULL;
10588 gboolean originally_enc = FALSE;
10589 gboolean originally_sig = FALSE;
10590 Compose *compose = NULL;
10592 g_return_if_fail(msgview != NULL);
10594 g_return_if_fail(msginfo_list != NULL);
10596 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10597 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10598 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10600 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10601 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10602 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10603 orig_msginfo, mimeinfo);
10604 if (tmp_msginfo != NULL) {
10605 new_msglist = g_slist_append(NULL, tmp_msginfo);
10607 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10608 privacy_msginfo_get_signed_state(orig_msginfo);
10609 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10611 tmp_msginfo->folder = orig_msginfo->folder;
10612 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10613 if (orig_msginfo->tags)
10614 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10619 if (!opening_multiple)
10620 body = messageview_get_selection(msgview);
10623 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10624 procmsg_msginfo_free(tmp_msginfo);
10625 g_slist_free(new_msglist);
10627 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10629 if (compose && originally_enc) {
10630 compose_force_encryption(compose, compose->account, FALSE);
10633 if (compose && originally_sig && compose->account->default_sign_reply) {
10634 compose_force_signing(compose, compose->account);
10640 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10643 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10644 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10645 GSList *cur = msginfo_list;
10646 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10647 "messages. Opening the windows "
10648 "could take some time. Do you "
10649 "want to continue?"),
10650 g_slist_length(msginfo_list));
10651 if (g_slist_length(msginfo_list) > 9
10652 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10653 != G_ALERTALTERNATE) {
10658 /* We'll open multiple compose windows */
10659 /* let the WM place the next windows */
10660 compose_force_window_origin = FALSE;
10661 for (; cur; cur = cur->next) {
10663 tmplist.data = cur->data;
10664 tmplist.next = NULL;
10665 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10667 compose_force_window_origin = TRUE;
10669 /* forwarding multiple mails as attachments is done via a
10670 * single compose window */
10671 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10675 void compose_set_position(Compose *compose, gint pos)
10677 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10679 gtkut_text_view_set_position(text, pos);
10682 gboolean compose_search_string(Compose *compose,
10683 const gchar *str, gboolean case_sens)
10685 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10687 return gtkut_text_view_search_string(text, str, case_sens);
10690 gboolean compose_search_string_backward(Compose *compose,
10691 const gchar *str, gboolean case_sens)
10693 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10695 return gtkut_text_view_search_string_backward(text, str, case_sens);
10698 /* allocate a msginfo structure and populate its data from a compose data structure */
10699 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10701 MsgInfo *newmsginfo;
10703 gchar buf[BUFFSIZE];
10705 g_return_val_if_fail( compose != NULL, NULL );
10707 newmsginfo = procmsg_msginfo_new();
10710 get_rfc822_date(buf, sizeof(buf));
10711 newmsginfo->date = g_strdup(buf);
10714 if (compose->from_name) {
10715 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10716 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10720 if (compose->subject_entry)
10721 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10723 /* to, cc, reply-to, newsgroups */
10724 for (list = compose->header_list; list; list = list->next) {
10725 gchar *header = gtk_editable_get_chars(
10727 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10728 gchar *entry = gtk_editable_get_chars(
10729 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10731 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10732 if ( newmsginfo->to == NULL ) {
10733 newmsginfo->to = g_strdup(entry);
10734 } else if (entry && *entry) {
10735 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10736 g_free(newmsginfo->to);
10737 newmsginfo->to = tmp;
10740 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10741 if ( newmsginfo->cc == NULL ) {
10742 newmsginfo->cc = g_strdup(entry);
10743 } else if (entry && *entry) {
10744 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10745 g_free(newmsginfo->cc);
10746 newmsginfo->cc = tmp;
10749 if ( strcasecmp(header,
10750 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10751 if ( newmsginfo->newsgroups == NULL ) {
10752 newmsginfo->newsgroups = g_strdup(entry);
10753 } else if (entry && *entry) {
10754 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10755 g_free(newmsginfo->newsgroups);
10756 newmsginfo->newsgroups = tmp;
10764 /* other data is unset */
10770 /* update compose's dictionaries from folder dict settings */
10771 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10772 FolderItem *folder_item)
10774 g_return_if_fail(compose != NULL);
10776 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10777 FolderItemPrefs *prefs = folder_item->prefs;
10779 if (prefs->enable_default_dictionary)
10780 gtkaspell_change_dict(compose->gtkaspell,
10781 prefs->default_dictionary, FALSE);
10782 if (folder_item->prefs->enable_default_alt_dictionary)
10783 gtkaspell_change_alt_dict(compose->gtkaspell,
10784 prefs->default_alt_dictionary);
10785 if (prefs->enable_default_dictionary
10786 || prefs->enable_default_alt_dictionary)
10787 compose_spell_menu_changed(compose);