2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 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, const gchar *system)
1162 const 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)
1172 else if (account->default_privacy_system
1173 && strlen(account->default_privacy_system)) {
1174 privacy = account->default_privacy_system;
1176 GSList *privacy_avail = privacy_get_system_ids();
1177 if (privacy_avail && g_slist_length(privacy_avail)) {
1178 privacy = (gchar *)(privacy_avail->data);
1181 if (privacy != NULL) {
1183 g_free(compose->privacy_system);
1184 compose->privacy_system = NULL;
1186 if (compose->privacy_system == NULL)
1187 compose->privacy_system = g_strdup(privacy);
1188 else if (*(compose->privacy_system) == '\0') {
1189 g_free(compose->privacy_system);
1190 compose->privacy_system = g_strdup(privacy);
1192 compose_update_privacy_system_menu_item(compose, FALSE);
1193 compose_use_encryption(compose, TRUE);
1197 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1199 gchar *privacy = NULL;
1203 else if (account->default_privacy_system
1204 && strlen(account->default_privacy_system)) {
1205 privacy = account->default_privacy_system;
1207 GSList *privacy_avail = privacy_get_system_ids();
1208 if (privacy_avail && g_slist_length(privacy_avail)) {
1209 privacy = (gchar *)(privacy_avail->data);
1213 if (privacy != NULL) {
1215 g_free(compose->privacy_system);
1216 compose->privacy_system = NULL;
1218 if (compose->privacy_system == NULL)
1219 compose->privacy_system = g_strdup(privacy);
1220 compose_update_privacy_system_menu_item(compose, FALSE);
1221 compose_use_signing(compose, TRUE);
1225 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1229 Compose *compose = NULL;
1231 g_return_val_if_fail(msginfo_list != NULL, NULL);
1233 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1234 g_return_val_if_fail(msginfo != NULL, NULL);
1236 list_len = g_slist_length(msginfo_list);
1240 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1241 FALSE, prefs_common.default_reply_list, FALSE, body);
1243 case COMPOSE_REPLY_WITH_QUOTE:
1244 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1245 FALSE, prefs_common.default_reply_list, FALSE, body);
1247 case COMPOSE_REPLY_WITHOUT_QUOTE:
1248 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1249 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1251 case COMPOSE_REPLY_TO_SENDER:
1252 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1253 FALSE, FALSE, TRUE, body);
1255 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1256 compose = compose_followup_and_reply_to(msginfo,
1257 COMPOSE_QUOTE_CHECK,
1258 FALSE, FALSE, body);
1260 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1261 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1262 FALSE, FALSE, TRUE, body);
1264 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1265 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1266 FALSE, FALSE, TRUE, NULL);
1268 case COMPOSE_REPLY_TO_ALL:
1269 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1270 TRUE, FALSE, FALSE, body);
1272 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1273 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1274 TRUE, FALSE, FALSE, body);
1276 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1277 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1278 TRUE, FALSE, FALSE, NULL);
1280 case COMPOSE_REPLY_TO_LIST:
1281 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1282 FALSE, TRUE, FALSE, body);
1284 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1285 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1286 FALSE, TRUE, FALSE, body);
1288 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1289 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1290 FALSE, TRUE, FALSE, NULL);
1292 case COMPOSE_FORWARD:
1293 if (prefs_common.forward_as_attachment) {
1294 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1297 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1301 case COMPOSE_FORWARD_INLINE:
1302 /* check if we reply to more than one Message */
1303 if (list_len == 1) {
1304 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1307 /* more messages FALL THROUGH */
1308 case COMPOSE_FORWARD_AS_ATTACH:
1309 compose = compose_forward_multiple(NULL, msginfo_list);
1311 case COMPOSE_REDIRECT:
1312 compose = compose_redirect(NULL, msginfo, FALSE);
1315 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1318 if (compose == NULL) {
1319 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1323 compose->rmode = mode;
1324 switch (compose->rmode) {
1326 case COMPOSE_REPLY_WITH_QUOTE:
1327 case COMPOSE_REPLY_WITHOUT_QUOTE:
1328 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1329 debug_print("reply mode Normal\n");
1330 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1331 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1333 case COMPOSE_REPLY_TO_SENDER:
1334 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1335 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1336 debug_print("reply mode Sender\n");
1337 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1339 case COMPOSE_REPLY_TO_ALL:
1340 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1341 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1342 debug_print("reply mode All\n");
1343 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1345 case COMPOSE_REPLY_TO_LIST:
1346 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1347 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1348 debug_print("reply mode List\n");
1349 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1357 static Compose *compose_reply(MsgInfo *msginfo,
1358 ComposeQuoteMode quote_mode,
1364 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1365 to_sender, FALSE, body);
1368 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1369 ComposeQuoteMode quote_mode,
1374 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1375 to_sender, TRUE, body);
1378 static void compose_extract_original_charset(Compose *compose)
1380 MsgInfo *info = NULL;
1381 if (compose->replyinfo) {
1382 info = compose->replyinfo;
1383 } else if (compose->fwdinfo) {
1384 info = compose->fwdinfo;
1385 } else if (compose->targetinfo) {
1386 info = compose->targetinfo;
1389 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1390 MimeInfo *partinfo = mimeinfo;
1391 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1392 partinfo = procmime_mimeinfo_next(partinfo);
1394 compose->orig_charset =
1395 g_strdup(procmime_mimeinfo_get_parameter(
1396 partinfo, "charset"));
1398 procmime_mimeinfo_free_all(mimeinfo);
1402 #define SIGNAL_BLOCK(buffer) { \
1403 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1404 G_CALLBACK(compose_changed_cb), \
1406 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1407 G_CALLBACK(text_inserted), \
1411 #define SIGNAL_UNBLOCK(buffer) { \
1412 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1413 G_CALLBACK(compose_changed_cb), \
1415 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1416 G_CALLBACK(text_inserted), \
1420 static Compose *compose_generic_reply(MsgInfo *msginfo,
1421 ComposeQuoteMode quote_mode,
1422 gboolean to_all, gboolean to_ml,
1424 gboolean followup_and_reply_to,
1428 PrefsAccount *account = NULL;
1429 GtkTextView *textview;
1430 GtkTextBuffer *textbuf;
1431 gboolean quote = FALSE;
1432 const gchar *qmark = NULL;
1433 const gchar *body_fmt = NULL;
1434 gchar *s_system = NULL;
1436 g_return_val_if_fail(msginfo != NULL, NULL);
1437 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1439 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1441 g_return_val_if_fail(account != NULL, NULL);
1443 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1445 compose->updating = TRUE;
1447 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1448 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1450 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1451 if (!compose->replyinfo)
1452 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1454 compose_extract_original_charset(compose);
1456 if (msginfo->folder && msginfo->folder->ret_rcpt)
1457 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1459 /* Set save folder */
1460 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1461 gchar *folderidentifier;
1463 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1464 folderidentifier = folder_item_get_identifier(msginfo->folder);
1465 compose_set_save_to(compose, folderidentifier);
1466 g_free(folderidentifier);
1469 if (compose_parse_header(compose, msginfo) < 0) {
1470 compose->updating = FALSE;
1471 compose_destroy(compose);
1475 /* override from name according to folder properties */
1476 if (msginfo->folder && msginfo->folder->prefs &&
1477 msginfo->folder->prefs->reply_with_format &&
1478 msginfo->folder->prefs->reply_override_from_format &&
1479 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1484 /* decode \-escape sequences in the internal representation of the quote format */
1485 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1486 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1489 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1490 compose->gtkaspell);
1492 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1494 quote_fmt_scan_string(tmp);
1497 buf = quote_fmt_get_buffer();
1499 alertpanel_error(_("Message reply From format error."));
1501 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1502 quote_fmt_reset_vartable();
1507 textview = (GTK_TEXT_VIEW(compose->text));
1508 textbuf = gtk_text_view_get_buffer(textview);
1509 compose_create_tags(textview, compose);
1511 undo_block(compose->undostruct);
1513 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1516 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1517 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1518 /* use the reply format of folder (if enabled), or the account's one
1519 (if enabled) or fallback to the global reply format, which is always
1520 enabled (even if empty), and use the relevant quotemark */
1522 if (msginfo->folder && msginfo->folder->prefs &&
1523 msginfo->folder->prefs->reply_with_format) {
1524 qmark = msginfo->folder->prefs->reply_quotemark;
1525 body_fmt = msginfo->folder->prefs->reply_body_format;
1527 } else if (account->reply_with_format) {
1528 qmark = account->reply_quotemark;
1529 body_fmt = account->reply_body_format;
1532 qmark = prefs_common.quotemark;
1533 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1534 body_fmt = gettext(prefs_common.quotefmt);
1541 /* empty quotemark is not allowed */
1542 if (qmark == NULL || *qmark == '\0')
1544 compose_quote_fmt(compose, compose->replyinfo,
1545 body_fmt, qmark, body, FALSE, TRUE,
1546 _("Message reply format error at line %d."));
1547 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1548 quote_fmt_reset_vartable();
1551 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1552 compose_force_encryption(compose, account, FALSE, s_system);
1555 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1556 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1557 compose_force_signing(compose, account, s_system);
1561 SIGNAL_BLOCK(textbuf);
1563 if (account->auto_sig)
1564 compose_insert_sig(compose, FALSE);
1566 compose_wrap_all(compose);
1568 SIGNAL_UNBLOCK(textbuf);
1570 gtk_widget_grab_focus(compose->text);
1572 undo_unblock(compose->undostruct);
1574 if (prefs_common.auto_exteditor)
1575 compose_exec_ext_editor(compose);
1577 compose->modified = FALSE;
1578 compose_set_title(compose);
1580 compose->updating = FALSE;
1581 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1582 SCROLL_TO_CURSOR(compose);
1584 if (compose->deferred_destroy) {
1585 compose_destroy(compose);
1592 #define INSERT_FW_HEADER(var, hdr) \
1593 if (msginfo->var && *msginfo->var) { \
1594 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1595 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1596 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1599 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1600 gboolean as_attach, const gchar *body,
1601 gboolean no_extedit,
1605 GtkTextView *textview;
1606 GtkTextBuffer *textbuf;
1609 g_return_val_if_fail(msginfo != NULL, NULL);
1610 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1613 !(account = compose_guess_forward_account_from_msginfo
1615 account = cur_account;
1617 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1619 compose->updating = TRUE;
1620 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1621 if (!compose->fwdinfo)
1622 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1624 compose_extract_original_charset(compose);
1626 if (msginfo->subject && *msginfo->subject) {
1627 gchar *buf, *buf2, *p;
1629 buf = p = g_strdup(msginfo->subject);
1630 p += subject_get_prefix_length(p);
1631 memmove(buf, p, strlen(p) + 1);
1633 buf2 = g_strdup_printf("Fw: %s", buf);
1634 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1640 /* override from name according to folder properties */
1641 if (msginfo->folder && msginfo->folder->prefs &&
1642 msginfo->folder->prefs->forward_with_format &&
1643 msginfo->folder->prefs->forward_override_from_format &&
1644 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1648 MsgInfo *full_msginfo = NULL;
1651 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1653 full_msginfo = procmsg_msginfo_copy(msginfo);
1655 /* decode \-escape sequences in the internal representation of the quote format */
1656 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1657 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1660 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1661 compose->gtkaspell);
1663 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1665 quote_fmt_scan_string(tmp);
1668 buf = quote_fmt_get_buffer();
1670 alertpanel_error(_("Message forward From format error."));
1672 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1673 quote_fmt_reset_vartable();
1676 procmsg_msginfo_free(full_msginfo);
1679 textview = GTK_TEXT_VIEW(compose->text);
1680 textbuf = gtk_text_view_get_buffer(textview);
1681 compose_create_tags(textview, compose);
1683 undo_block(compose->undostruct);
1687 msgfile = procmsg_get_message_file(msginfo);
1688 if (!is_file_exist(msgfile))
1689 g_warning("%s: file not exist\n", msgfile);
1691 compose_attach_append(compose, msgfile, msgfile,
1696 const gchar *qmark = NULL;
1697 const gchar *body_fmt = NULL;
1698 MsgInfo *full_msginfo;
1700 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1701 body_fmt = gettext(prefs_common.fw_quotefmt);
1705 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1707 full_msginfo = procmsg_msginfo_copy(msginfo);
1709 /* use the forward format of folder (if enabled), or the account's one
1710 (if enabled) or fallback to the global forward format, which is always
1711 enabled (even if empty), and use the relevant quotemark */
1712 if (msginfo->folder && msginfo->folder->prefs &&
1713 msginfo->folder->prefs->forward_with_format) {
1714 qmark = msginfo->folder->prefs->forward_quotemark;
1715 body_fmt = msginfo->folder->prefs->forward_body_format;
1717 } else if (account->forward_with_format) {
1718 qmark = account->forward_quotemark;
1719 body_fmt = account->forward_body_format;
1722 qmark = prefs_common.fw_quotemark;
1723 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1724 body_fmt = gettext(prefs_common.fw_quotefmt);
1729 /* empty quotemark is not allowed */
1730 if (qmark == NULL || *qmark == '\0')
1733 compose_quote_fmt(compose, full_msginfo,
1734 body_fmt, qmark, body, FALSE, TRUE,
1735 _("Message forward format error at line %d."));
1736 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1737 quote_fmt_reset_vartable();
1738 compose_attach_parts(compose, msginfo);
1740 procmsg_msginfo_free(full_msginfo);
1743 SIGNAL_BLOCK(textbuf);
1745 if (account->auto_sig)
1746 compose_insert_sig(compose, FALSE);
1748 compose_wrap_all(compose);
1750 SIGNAL_UNBLOCK(textbuf);
1752 gtk_text_buffer_get_start_iter(textbuf, &iter);
1753 gtk_text_buffer_place_cursor(textbuf, &iter);
1755 gtk_widget_grab_focus(compose->header_last->entry);
1757 if (!no_extedit && prefs_common.auto_exteditor)
1758 compose_exec_ext_editor(compose);
1761 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1762 gchar *folderidentifier;
1764 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1765 folderidentifier = folder_item_get_identifier(msginfo->folder);
1766 compose_set_save_to(compose, folderidentifier);
1767 g_free(folderidentifier);
1770 undo_unblock(compose->undostruct);
1772 compose->modified = FALSE;
1773 compose_set_title(compose);
1775 compose->updating = FALSE;
1776 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1777 SCROLL_TO_CURSOR(compose);
1779 if (compose->deferred_destroy) {
1780 compose_destroy(compose);
1787 #undef INSERT_FW_HEADER
1789 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1792 GtkTextView *textview;
1793 GtkTextBuffer *textbuf;
1797 gboolean single_mail = TRUE;
1799 g_return_val_if_fail(msginfo_list != NULL, NULL);
1801 if (g_slist_length(msginfo_list) > 1)
1802 single_mail = FALSE;
1804 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1805 if (((MsgInfo *)msginfo->data)->folder == NULL)
1808 /* guess account from first selected message */
1810 !(account = compose_guess_forward_account_from_msginfo
1811 (msginfo_list->data)))
1812 account = cur_account;
1814 g_return_val_if_fail(account != NULL, NULL);
1816 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1817 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1818 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1821 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1823 compose->updating = TRUE;
1825 /* override from name according to folder properties */
1826 if (msginfo_list->data) {
1827 MsgInfo *msginfo = msginfo_list->data;
1829 if (msginfo->folder && msginfo->folder->prefs &&
1830 msginfo->folder->prefs->forward_with_format &&
1831 msginfo->folder->prefs->forward_override_from_format &&
1832 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1837 /* decode \-escape sequences in the internal representation of the quote format */
1838 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1839 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1842 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1843 compose->gtkaspell);
1845 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1847 quote_fmt_scan_string(tmp);
1850 buf = quote_fmt_get_buffer();
1852 alertpanel_error(_("Message forward From format error."));
1854 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1855 quote_fmt_reset_vartable();
1861 textview = GTK_TEXT_VIEW(compose->text);
1862 textbuf = gtk_text_view_get_buffer(textview);
1863 compose_create_tags(textview, compose);
1865 undo_block(compose->undostruct);
1866 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1867 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1869 if (!is_file_exist(msgfile))
1870 g_warning("%s: file not exist\n", msgfile);
1872 compose_attach_append(compose, msgfile, msgfile,
1878 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1879 if (info->subject && *info->subject) {
1880 gchar *buf, *buf2, *p;
1882 buf = p = g_strdup(info->subject);
1883 p += subject_get_prefix_length(p);
1884 memmove(buf, p, strlen(p) + 1);
1886 buf2 = g_strdup_printf("Fw: %s", buf);
1887 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1893 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1894 _("Fw: multiple emails"));
1897 SIGNAL_BLOCK(textbuf);
1899 if (account->auto_sig)
1900 compose_insert_sig(compose, FALSE);
1902 compose_wrap_all(compose);
1904 SIGNAL_UNBLOCK(textbuf);
1906 gtk_text_buffer_get_start_iter(textbuf, &iter);
1907 gtk_text_buffer_place_cursor(textbuf, &iter);
1909 gtk_widget_grab_focus(compose->header_last->entry);
1910 undo_unblock(compose->undostruct);
1911 compose->modified = FALSE;
1912 compose_set_title(compose);
1914 compose->updating = FALSE;
1915 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1916 SCROLL_TO_CURSOR(compose);
1918 if (compose->deferred_destroy) {
1919 compose_destroy(compose);
1926 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1928 GtkTextIter start = *iter;
1929 GtkTextIter end_iter;
1930 int start_pos = gtk_text_iter_get_offset(&start);
1932 if (!compose->account->sig_sep)
1935 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1936 start_pos+strlen(compose->account->sig_sep));
1938 /* check sig separator */
1939 str = gtk_text_iter_get_text(&start, &end_iter);
1940 if (!strcmp(str, compose->account->sig_sep)) {
1942 /* check end of line (\n) */
1943 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1944 start_pos+strlen(compose->account->sig_sep));
1945 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1946 start_pos+strlen(compose->account->sig_sep)+1);
1947 tmp = gtk_text_iter_get_text(&start, &end_iter);
1948 if (!strcmp(tmp,"\n")) {
1960 static void compose_colorize_signature(Compose *compose)
1962 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1964 GtkTextIter end_iter;
1965 gtk_text_buffer_get_start_iter(buffer, &iter);
1966 while (gtk_text_iter_forward_line(&iter))
1967 if (compose_is_sig_separator(compose, buffer, &iter)) {
1968 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1969 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1973 #define BLOCK_WRAP() { \
1974 prev_autowrap = compose->autowrap; \
1975 buffer = gtk_text_view_get_buffer( \
1976 GTK_TEXT_VIEW(compose->text)); \
1977 compose->autowrap = FALSE; \
1979 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1980 G_CALLBACK(compose_changed_cb), \
1982 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1983 G_CALLBACK(text_inserted), \
1986 #define UNBLOCK_WRAP() { \
1987 compose->autowrap = prev_autowrap; \
1988 if (compose->autowrap) { \
1989 gint old = compose->draft_timeout_tag; \
1990 compose->draft_timeout_tag = -2; \
1991 compose_wrap_all(compose); \
1992 compose->draft_timeout_tag = old; \
1995 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1996 G_CALLBACK(compose_changed_cb), \
1998 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1999 G_CALLBACK(text_inserted), \
2003 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2005 Compose *compose = NULL;
2006 PrefsAccount *account = NULL;
2007 GtkTextView *textview;
2008 GtkTextBuffer *textbuf;
2012 gchar buf[BUFFSIZE];
2013 gboolean use_signing = FALSE;
2014 gboolean use_encryption = FALSE;
2015 gchar *privacy_system = NULL;
2016 int priority = PRIORITY_NORMAL;
2017 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2018 gboolean autowrap = prefs_common.autowrap;
2019 gboolean autoindent = prefs_common.auto_indent;
2021 g_return_val_if_fail(msginfo != NULL, NULL);
2022 g_return_val_if_fail(msginfo->folder != NULL, NULL);
2024 if (compose_put_existing_to_front(msginfo)) {
2028 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2029 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2030 gchar queueheader_buf[BUFFSIZE];
2033 /* Select Account from queue headers */
2034 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2035 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2036 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2037 account = account_find_from_id(id);
2039 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2040 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2041 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2042 account = account_find_from_id(id);
2044 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2045 sizeof(queueheader_buf), "NAID:")) {
2046 id = atoi(&queueheader_buf[strlen("NAID:")]);
2047 account = account_find_from_id(id);
2049 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2050 sizeof(queueheader_buf), "MAID:")) {
2051 id = atoi(&queueheader_buf[strlen("MAID:")]);
2052 account = account_find_from_id(id);
2054 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2055 sizeof(queueheader_buf), "S:")) {
2056 account = account_find_from_address(queueheader_buf, FALSE);
2058 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2059 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2060 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2061 use_signing = param;
2064 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2065 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2066 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2067 use_signing = param;
2070 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2071 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2072 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2073 use_encryption = param;
2075 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2076 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2077 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2078 use_encryption = param;
2080 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2081 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2082 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2085 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2086 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2087 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2090 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2091 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2092 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2094 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2095 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2096 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2098 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2099 sizeof(queueheader_buf), "X-Priority: ")) {
2100 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2103 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2104 sizeof(queueheader_buf), "RMID:")) {
2105 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2106 if (tokens[0] && tokens[1] && tokens[2]) {
2107 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2108 if (orig_item != NULL) {
2109 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2114 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2115 sizeof(queueheader_buf), "FMID:")) {
2116 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2117 if (tokens[0] && tokens[1] && tokens[2]) {
2118 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2119 if (orig_item != NULL) {
2120 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2126 account = msginfo->folder->folder->account;
2129 if (!account && prefs_common.reedit_account_autosel) {
2130 gchar from[BUFFSIZE];
2131 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2132 extract_address(from);
2133 account = account_find_from_address(from, FALSE);
2137 account = cur_account;
2139 g_return_val_if_fail(account != NULL, NULL);
2141 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2143 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2144 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2145 compose->autowrap = autowrap;
2146 compose->replyinfo = replyinfo;
2147 compose->fwdinfo = fwdinfo;
2149 compose->updating = TRUE;
2150 compose->priority = priority;
2152 if (privacy_system != NULL) {
2153 compose->privacy_system = privacy_system;
2154 compose_use_signing(compose, use_signing);
2155 compose_use_encryption(compose, use_encryption);
2156 compose_update_privacy_system_menu_item(compose, FALSE);
2158 activate_privacy_system(compose, account, FALSE);
2161 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2163 compose_extract_original_charset(compose);
2165 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2166 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2167 gchar queueheader_buf[BUFFSIZE];
2169 /* Set message save folder */
2170 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2171 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2172 compose_set_save_to(compose, &queueheader_buf[4]);
2174 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2175 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2177 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2182 if (compose_parse_header(compose, msginfo) < 0) {
2183 compose->updating = FALSE;
2184 compose_destroy(compose);
2187 compose_reedit_set_entry(compose, msginfo);
2189 textview = GTK_TEXT_VIEW(compose->text);
2190 textbuf = gtk_text_view_get_buffer(textview);
2191 compose_create_tags(textview, compose);
2193 mark = gtk_text_buffer_get_insert(textbuf);
2194 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2196 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2197 G_CALLBACK(compose_changed_cb),
2200 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2201 fp = procmime_get_first_encrypted_text_content(msginfo);
2203 compose_force_encryption(compose, account, TRUE, NULL);
2206 fp = procmime_get_first_text_content(msginfo);
2209 g_warning("Can't get text part\n");
2213 gboolean prev_autowrap = compose->autowrap;
2214 GtkTextBuffer *buffer = textbuf;
2216 while (fgets(buf, sizeof(buf), fp) != NULL) {
2218 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2224 compose_attach_parts(compose, msginfo);
2226 compose_colorize_signature(compose);
2228 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2229 G_CALLBACK(compose_changed_cb),
2232 gtk_widget_grab_focus(compose->text);
2234 if (prefs_common.auto_exteditor) {
2235 compose_exec_ext_editor(compose);
2237 compose->modified = FALSE;
2238 compose_set_title(compose);
2240 compose->updating = FALSE;
2241 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2242 SCROLL_TO_CURSOR(compose);
2244 if (compose->deferred_destroy) {
2245 compose_destroy(compose);
2249 compose->sig_str = account_get_signature_str(compose->account);
2254 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2261 g_return_val_if_fail(msginfo != NULL, NULL);
2264 account = account_get_reply_account(msginfo,
2265 prefs_common.reply_account_autosel);
2266 g_return_val_if_fail(account != NULL, NULL);
2268 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2270 compose->updating = TRUE;
2272 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2273 compose->replyinfo = NULL;
2274 compose->fwdinfo = NULL;
2276 compose_show_first_last_header(compose, TRUE);
2278 gtk_widget_grab_focus(compose->header_last->entry);
2280 filename = procmsg_get_message_file(msginfo);
2282 if (filename == NULL) {
2283 compose->updating = FALSE;
2284 compose_destroy(compose);
2289 compose->redirect_filename = filename;
2291 /* Set save folder */
2292 item = msginfo->folder;
2293 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2294 gchar *folderidentifier;
2296 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2297 folderidentifier = folder_item_get_identifier(item);
2298 compose_set_save_to(compose, folderidentifier);
2299 g_free(folderidentifier);
2302 compose_attach_parts(compose, msginfo);
2304 if (msginfo->subject)
2305 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2307 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2309 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2310 _("Message redirect format error at line %d."));
2311 quote_fmt_reset_vartable();
2312 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2314 compose_colorize_signature(compose);
2317 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2318 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2319 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2321 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2322 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2323 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2324 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2325 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2326 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2327 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2328 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2330 if (compose->toolbar->draft_btn)
2331 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2332 if (compose->toolbar->insert_btn)
2333 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2334 if (compose->toolbar->attach_btn)
2335 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2336 if (compose->toolbar->sig_btn)
2337 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2338 if (compose->toolbar->exteditor_btn)
2339 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2340 if (compose->toolbar->linewrap_current_btn)
2341 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2342 if (compose->toolbar->linewrap_all_btn)
2343 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2345 compose->modified = FALSE;
2346 compose_set_title(compose);
2347 compose->updating = FALSE;
2348 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2349 SCROLL_TO_CURSOR(compose);
2351 if (compose->deferred_destroy) {
2352 compose_destroy(compose);
2359 GList *compose_get_compose_list(void)
2361 return compose_list;
2364 void compose_entry_append(Compose *compose, const gchar *address,
2365 ComposeEntryType type)
2367 const gchar *header;
2369 gboolean in_quote = FALSE;
2370 if (!address || *address == '\0') return;
2377 header = N_("Bcc:");
2379 case COMPOSE_REPLYTO:
2380 header = N_("Reply-To:");
2382 case COMPOSE_NEWSGROUPS:
2383 header = N_("Newsgroups:");
2385 case COMPOSE_FOLLOWUPTO:
2386 header = N_( "Followup-To:");
2393 header = prefs_common_translated_header_name(header);
2395 cur = begin = (gchar *)address;
2397 /* we separate the line by commas, but not if we're inside a quoted
2399 while (*cur != '\0') {
2401 in_quote = !in_quote;
2402 if (*cur == ',' && !in_quote) {
2403 gchar *tmp = g_strdup(begin);
2405 tmp[cur-begin]='\0';
2408 while (*tmp == ' ' || *tmp == '\t')
2410 compose_add_header_entry(compose, header, tmp);
2417 gchar *tmp = g_strdup(begin);
2419 tmp[cur-begin]='\0';
2422 while (*tmp == ' ' || *tmp == '\t')
2424 compose_add_header_entry(compose, header, tmp);
2429 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2431 static GdkColor yellow;
2432 static GdkColor black;
2433 static gboolean yellow_initialised = FALSE;
2437 if (!yellow_initialised) {
2438 gdk_color_parse("#f5f6be", &yellow);
2439 gdk_color_parse("#000000", &black);
2440 yellow_initialised = gdk_colormap_alloc_color(
2441 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2442 yellow_initialised &= gdk_colormap_alloc_color(
2443 gdk_colormap_get_system(), &black, FALSE, TRUE);
2446 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2447 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2448 if (gtk_entry_get_text(entry) &&
2449 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2450 if (yellow_initialised) {
2451 gtk_widget_modify_base(
2452 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2453 GTK_STATE_NORMAL, &yellow);
2454 gtk_widget_modify_text(
2455 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2456 GTK_STATE_NORMAL, &black);
2462 void compose_toolbar_cb(gint action, gpointer data)
2464 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2465 Compose *compose = (Compose*)toolbar_item->parent;
2467 g_return_if_fail(compose != NULL);
2471 compose_send_cb(NULL, compose);
2474 compose_send_later_cb(NULL, compose);
2477 compose_draft(compose, COMPOSE_QUIT_EDITING);
2480 compose_insert_file_cb(NULL, compose);
2483 compose_attach_cb(NULL, compose);
2486 compose_insert_sig(compose, FALSE);
2489 compose_ext_editor_cb(NULL, compose);
2491 case A_LINEWRAP_CURRENT:
2492 compose_beautify_paragraph(compose, NULL, TRUE);
2494 case A_LINEWRAP_ALL:
2495 compose_wrap_all_full(compose, TRUE);
2498 compose_address_cb(NULL, compose);
2501 case A_CHECK_SPELLING:
2502 compose_check_all(NULL, compose);
2510 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2515 gchar *subject = NULL;
2519 gchar **attach = NULL;
2521 /* get mailto parts but skip from */
2522 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2525 compose_entry_append(compose, to, to_type);
2527 compose_entry_append(compose, cc, COMPOSE_CC);
2529 compose_entry_append(compose, bcc, COMPOSE_BCC);
2531 if (!g_utf8_validate (subject, -1, NULL)) {
2532 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2533 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2536 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2540 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2541 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2544 gboolean prev_autowrap = compose->autowrap;
2546 compose->autowrap = FALSE;
2548 mark = gtk_text_buffer_get_insert(buffer);
2549 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2551 if (!g_utf8_validate (body, -1, NULL)) {
2552 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2553 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2556 gtk_text_buffer_insert(buffer, &iter, body, -1);
2558 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2560 compose->autowrap = prev_autowrap;
2561 if (compose->autowrap)
2562 compose_wrap_all(compose);
2566 gint i = 0, att = 0;
2567 gchar *warn_files = NULL;
2568 while (attach[i] != NULL) {
2569 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2570 if (utf8_filename) {
2571 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2572 gchar *tmp = g_strdup_printf("%s%s\n",
2573 warn_files?warn_files:"",
2579 g_free(utf8_filename);
2581 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2586 alertpanel_notice(ngettext(
2587 "The following file has been attached: \n%s",
2588 "The following files have been attached: \n%s", att), warn_files);
2600 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2602 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2603 {"Cc:", NULL, TRUE},
2604 {"References:", NULL, FALSE},
2605 {"Bcc:", NULL, TRUE},
2606 {"Newsgroups:", NULL, TRUE},
2607 {"Followup-To:", NULL, TRUE},
2608 {"List-Post:", NULL, FALSE},
2609 {"X-Priority:", NULL, FALSE},
2610 {NULL, NULL, FALSE}};
2626 g_return_val_if_fail(msginfo != NULL, -1);
2628 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2629 procheader_get_header_fields(fp, hentry);
2632 if (hentry[H_REPLY_TO].body != NULL) {
2633 if (hentry[H_REPLY_TO].body[0] != '\0') {
2635 conv_unmime_header(hentry[H_REPLY_TO].body,
2638 g_free(hentry[H_REPLY_TO].body);
2639 hentry[H_REPLY_TO].body = NULL;
2641 if (hentry[H_CC].body != NULL) {
2642 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2643 g_free(hentry[H_CC].body);
2644 hentry[H_CC].body = NULL;
2646 if (hentry[H_REFERENCES].body != NULL) {
2647 if (compose->mode == COMPOSE_REEDIT)
2648 compose->references = hentry[H_REFERENCES].body;
2650 compose->references = compose_parse_references
2651 (hentry[H_REFERENCES].body, msginfo->msgid);
2652 g_free(hentry[H_REFERENCES].body);
2654 hentry[H_REFERENCES].body = NULL;
2656 if (hentry[H_BCC].body != NULL) {
2657 if (compose->mode == COMPOSE_REEDIT)
2659 conv_unmime_header(hentry[H_BCC].body, NULL);
2660 g_free(hentry[H_BCC].body);
2661 hentry[H_BCC].body = NULL;
2663 if (hentry[H_NEWSGROUPS].body != NULL) {
2664 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2665 hentry[H_NEWSGROUPS].body = NULL;
2667 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2668 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2669 compose->followup_to =
2670 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2673 g_free(hentry[H_FOLLOWUP_TO].body);
2674 hentry[H_FOLLOWUP_TO].body = NULL;
2676 if (hentry[H_LIST_POST].body != NULL) {
2679 extract_address(hentry[H_LIST_POST].body);
2680 if (hentry[H_LIST_POST].body[0] != '\0') {
2681 scan_mailto_url(hentry[H_LIST_POST].body,
2682 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2684 g_free(compose->ml_post);
2685 compose->ml_post = to;
2688 g_free(hentry[H_LIST_POST].body);
2689 hentry[H_LIST_POST].body = NULL;
2692 /* CLAWS - X-Priority */
2693 if (compose->mode == COMPOSE_REEDIT)
2694 if (hentry[H_X_PRIORITY].body != NULL) {
2697 priority = atoi(hentry[H_X_PRIORITY].body);
2698 g_free(hentry[H_X_PRIORITY].body);
2700 hentry[H_X_PRIORITY].body = NULL;
2702 if (priority < PRIORITY_HIGHEST ||
2703 priority > PRIORITY_LOWEST)
2704 priority = PRIORITY_NORMAL;
2706 compose->priority = priority;
2709 if (compose->mode == COMPOSE_REEDIT) {
2710 if (msginfo->inreplyto && *msginfo->inreplyto)
2711 compose->inreplyto = g_strdup(msginfo->inreplyto);
2715 if (msginfo->msgid && *msginfo->msgid)
2716 compose->inreplyto = g_strdup(msginfo->msgid);
2718 if (!compose->references) {
2719 if (msginfo->msgid && *msginfo->msgid) {
2720 if (msginfo->inreplyto && *msginfo->inreplyto)
2721 compose->references =
2722 g_strdup_printf("<%s>\n\t<%s>",
2726 compose->references =
2727 g_strconcat("<", msginfo->msgid, ">",
2729 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2730 compose->references =
2731 g_strconcat("<", msginfo->inreplyto, ">",
2739 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2741 GSList *ref_id_list, *cur;
2745 ref_id_list = references_list_append(NULL, ref);
2746 if (!ref_id_list) return NULL;
2747 if (msgid && *msgid)
2748 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2753 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2754 /* "<" + Message-ID + ">" + CR+LF+TAB */
2755 len += strlen((gchar *)cur->data) + 5;
2757 if (len > MAX_REFERENCES_LEN) {
2758 /* remove second message-ID */
2759 if (ref_id_list && ref_id_list->next &&
2760 ref_id_list->next->next) {
2761 g_free(ref_id_list->next->data);
2762 ref_id_list = g_slist_remove
2763 (ref_id_list, ref_id_list->next->data);
2765 slist_free_strings(ref_id_list);
2766 g_slist_free(ref_id_list);
2773 new_ref = g_string_new("");
2774 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2775 if (new_ref->len > 0)
2776 g_string_append(new_ref, "\n\t");
2777 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2780 slist_free_strings(ref_id_list);
2781 g_slist_free(ref_id_list);
2783 new_ref_str = new_ref->str;
2784 g_string_free(new_ref, FALSE);
2789 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2790 const gchar *fmt, const gchar *qmark,
2791 const gchar *body, gboolean rewrap,
2792 gboolean need_unescape,
2793 const gchar *err_msg)
2795 MsgInfo* dummyinfo = NULL;
2796 gchar *quote_str = NULL;
2798 gboolean prev_autowrap;
2799 const gchar *trimmed_body = body;
2800 gint cursor_pos = -1;
2801 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2802 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2807 SIGNAL_BLOCK(buffer);
2810 dummyinfo = compose_msginfo_new_from_compose(compose);
2811 msginfo = dummyinfo;
2814 if (qmark != NULL) {
2816 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2817 compose->gtkaspell);
2819 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2821 quote_fmt_scan_string(qmark);
2824 buf = quote_fmt_get_buffer();
2826 alertpanel_error(_("Quote mark format error."));
2828 Xstrdup_a(quote_str, buf, goto error)
2831 if (fmt && *fmt != '\0') {
2834 while (*trimmed_body == '\n')
2838 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2839 compose->gtkaspell);
2841 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2843 if (need_unescape) {
2846 /* decode \-escape sequences in the internal representation of the quote format */
2847 tmp = malloc(strlen(fmt)+1);
2848 pref_get_unescaped_pref(tmp, fmt);
2849 quote_fmt_scan_string(tmp);
2853 quote_fmt_scan_string(fmt);
2857 buf = quote_fmt_get_buffer();
2859 gint line = quote_fmt_get_line();
2860 alertpanel_error(err_msg, line);
2866 prev_autowrap = compose->autowrap;
2867 compose->autowrap = FALSE;
2869 mark = gtk_text_buffer_get_insert(buffer);
2870 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2871 if (g_utf8_validate(buf, -1, NULL)) {
2872 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2874 gchar *tmpout = NULL;
2875 tmpout = conv_codeset_strdup
2876 (buf, conv_get_locale_charset_str_no_utf8(),
2878 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2880 tmpout = g_malloc(strlen(buf)*2+1);
2881 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2883 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2887 cursor_pos = quote_fmt_get_cursor_pos();
2888 if (cursor_pos == -1)
2889 cursor_pos = gtk_text_iter_get_offset(&iter);
2890 compose->set_cursor_pos = cursor_pos;
2892 gtk_text_buffer_get_start_iter(buffer, &iter);
2893 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2894 gtk_text_buffer_place_cursor(buffer, &iter);
2896 compose->autowrap = prev_autowrap;
2897 if (compose->autowrap && rewrap)
2898 compose_wrap_all(compose);
2905 SIGNAL_UNBLOCK(buffer);
2907 procmsg_msginfo_free( dummyinfo );
2912 /* if ml_post is of type addr@host and from is of type
2913 * addr-anything@host, return TRUE
2915 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2917 gchar *left_ml = NULL;
2918 gchar *right_ml = NULL;
2919 gchar *left_from = NULL;
2920 gchar *right_from = NULL;
2921 gboolean result = FALSE;
2923 if (!ml_post || !from)
2926 left_ml = g_strdup(ml_post);
2927 if (strstr(left_ml, "@")) {
2928 right_ml = strstr(left_ml, "@")+1;
2929 *(strstr(left_ml, "@")) = '\0';
2932 left_from = g_strdup(from);
2933 if (strstr(left_from, "@")) {
2934 right_from = strstr(left_from, "@")+1;
2935 *(strstr(left_from, "@")) = '\0';
2938 if (left_ml && left_from && right_ml && right_from
2939 && !strncmp(left_from, left_ml, strlen(left_ml))
2940 && !strcmp(right_from, right_ml)) {
2949 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2951 gchar *my_addr1, *my_addr2;
2953 if (!addr1 || !addr2)
2956 Xstrdup_a(my_addr1, addr1, return FALSE);
2957 Xstrdup_a(my_addr2, addr2, return FALSE);
2959 extract_address(my_addr1);
2960 extract_address(my_addr2);
2962 return !strcasecmp(my_addr1, my_addr2);
2965 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2966 gboolean to_all, gboolean to_ml,
2968 gboolean followup_and_reply_to)
2970 GSList *cc_list = NULL;
2973 gchar *replyto = NULL;
2974 GHashTable *to_table;
2976 gboolean reply_to_ml = FALSE;
2977 gboolean default_reply_to = FALSE;
2979 g_return_if_fail(compose->account != NULL);
2980 g_return_if_fail(msginfo != NULL);
2982 reply_to_ml = to_ml && compose->ml_post;
2984 default_reply_to = msginfo->folder &&
2985 msginfo->folder->prefs->enable_default_reply_to;
2987 if (compose->account->protocol != A_NNTP) {
2988 if (reply_to_ml && !default_reply_to) {
2990 gboolean is_subscr = is_subscription(compose->ml_post,
2993 /* normal answer to ml post with a reply-to */
2994 compose_entry_append(compose,
2997 if (compose->replyto
2998 && !same_address(compose->ml_post, compose->replyto))
2999 compose_entry_append(compose,
3003 /* answer to subscription confirmation */
3004 if (compose->replyto)
3005 compose_entry_append(compose,
3008 else if (msginfo->from)
3009 compose_entry_append(compose,
3014 else if (!(to_all || to_sender) && default_reply_to) {
3015 compose_entry_append(compose,
3016 msginfo->folder->prefs->default_reply_to,
3018 compose_entry_mark_default_to(compose,
3019 msginfo->folder->prefs->default_reply_to);
3024 Xstrdup_a(tmp1, msginfo->from, return);
3025 extract_address(tmp1);
3026 if (to_all || to_sender ||
3027 !account_find_from_address(tmp1, FALSE))
3028 compose_entry_append(compose,
3029 (compose->replyto && !to_sender)
3030 ? compose->replyto :
3031 msginfo->from ? msginfo->from : "",
3033 else if (!to_all && !to_sender) {
3034 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3035 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3036 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3037 if (compose->replyto) {
3038 compose_entry_append(compose,
3042 compose_entry_append(compose,
3043 msginfo->from ? msginfo->from : "",
3047 /* replying to own mail, use original recp */
3048 compose_entry_append(compose,
3049 msginfo->to ? msginfo->to : "",
3051 compose_entry_append(compose,
3052 msginfo->cc ? msginfo->cc : "",
3058 if (to_sender || (compose->followup_to &&
3059 !strncmp(compose->followup_to, "poster", 6)))
3060 compose_entry_append
3062 (compose->replyto ? compose->replyto :
3063 msginfo->from ? msginfo->from : ""),
3066 else if (followup_and_reply_to || to_all) {
3067 compose_entry_append
3069 (compose->replyto ? compose->replyto :
3070 msginfo->from ? msginfo->from : ""),
3073 compose_entry_append
3075 compose->followup_to ? compose->followup_to :
3076 compose->newsgroups ? compose->newsgroups : "",
3077 COMPOSE_NEWSGROUPS);
3080 compose_entry_append
3082 compose->followup_to ? compose->followup_to :
3083 compose->newsgroups ? compose->newsgroups : "",
3084 COMPOSE_NEWSGROUPS);
3087 if (msginfo->subject && *msginfo->subject) {
3091 buf = p = g_strdup(msginfo->subject);
3092 p += subject_get_prefix_length(p);
3093 memmove(buf, p, strlen(p) + 1);
3095 buf2 = g_strdup_printf("Re: %s", buf);
3096 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3101 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3103 if (to_ml && compose->ml_post) return;
3104 if (!to_all || compose->account->protocol == A_NNTP) return;
3106 if (compose->replyto) {
3107 Xstrdup_a(replyto, compose->replyto, return);
3108 extract_address(replyto);
3110 if (msginfo->from) {
3111 Xstrdup_a(from, msginfo->from, return);
3112 extract_address(from);
3115 if (replyto && from)
3116 cc_list = address_list_append_with_comments(cc_list, from);
3117 if (to_all && msginfo->folder &&
3118 msginfo->folder->prefs->enable_default_reply_to)
3119 cc_list = address_list_append_with_comments(cc_list,
3120 msginfo->folder->prefs->default_reply_to);
3121 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3122 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3124 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3126 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3127 if (compose->account) {
3128 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3129 GINT_TO_POINTER(1));
3131 /* remove address on To: and that of current account */
3132 for (cur = cc_list; cur != NULL; ) {
3133 GSList *next = cur->next;
3136 addr = g_utf8_strdown(cur->data, -1);
3137 extract_address(addr);
3139 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3140 cc_list = g_slist_remove(cc_list, cur->data);
3142 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3146 hash_free_strings(to_table);
3147 g_hash_table_destroy(to_table);
3150 for (cur = cc_list; cur != NULL; cur = cur->next)
3151 compose_entry_append(compose, (gchar *)cur->data,
3153 slist_free_strings(cc_list);
3154 g_slist_free(cc_list);
3159 #define SET_ENTRY(entry, str) \
3162 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3165 #define SET_ADDRESS(type, str) \
3168 compose_entry_append(compose, str, type); \
3171 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3173 g_return_if_fail(msginfo != NULL);
3175 SET_ENTRY(subject_entry, msginfo->subject);
3176 SET_ENTRY(from_name, msginfo->from);
3177 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3178 SET_ADDRESS(COMPOSE_CC, compose->cc);
3179 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3180 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3181 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3182 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3184 compose_update_priority_menu_item(compose);
3185 compose_update_privacy_system_menu_item(compose, FALSE);
3186 compose_show_first_last_header(compose, TRUE);
3192 static void compose_insert_sig(Compose *compose, gboolean replace)
3194 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3195 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3197 GtkTextIter iter, iter_end;
3198 gint cur_pos, ins_pos;
3199 gboolean prev_autowrap;
3200 gboolean found = FALSE;
3201 gboolean exists = FALSE;
3203 g_return_if_fail(compose->account != NULL);
3207 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3208 G_CALLBACK(compose_changed_cb),
3211 mark = gtk_text_buffer_get_insert(buffer);
3212 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3213 cur_pos = gtk_text_iter_get_offset (&iter);
3216 gtk_text_buffer_get_end_iter(buffer, &iter);
3218 exists = (compose->sig_str != NULL);
3221 GtkTextIter first_iter, start_iter, end_iter;
3223 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3225 if (!exists || compose->sig_str[0] == '\0')
3228 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3229 compose->signature_tag);
3232 /* include previous \n\n */
3233 gtk_text_iter_backward_chars(&first_iter, 1);
3234 start_iter = first_iter;
3235 end_iter = first_iter;
3237 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3238 compose->signature_tag);
3239 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3240 compose->signature_tag);
3242 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3248 g_free(compose->sig_str);
3249 compose->sig_str = account_get_signature_str(compose->account);
3251 cur_pos = gtk_text_iter_get_offset(&iter);
3253 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3254 g_free(compose->sig_str);
3255 compose->sig_str = NULL;
3257 if (compose->sig_inserted == FALSE)
3258 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3259 compose->sig_inserted = TRUE;
3261 cur_pos = gtk_text_iter_get_offset(&iter);
3262 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3264 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3265 gtk_text_iter_forward_chars(&iter, 1);
3266 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3267 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3269 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3270 cur_pos = gtk_text_buffer_get_char_count (buffer);
3273 /* put the cursor where it should be
3274 * either where the quote_fmt says, either where it was */
3275 if (compose->set_cursor_pos < 0)
3276 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3278 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3279 compose->set_cursor_pos);
3281 compose->set_cursor_pos = -1;
3282 gtk_text_buffer_place_cursor(buffer, &iter);
3283 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3284 G_CALLBACK(compose_changed_cb),
3290 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3293 GtkTextBuffer *buffer;
3296 const gchar *cur_encoding;
3297 gchar buf[BUFFSIZE];
3300 gboolean prev_autowrap;
3301 gboolean badtxt = FALSE;
3303 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3305 if ((fp = g_fopen(file, "rb")) == NULL) {
3306 FILE_OP_ERROR(file, "fopen");
3307 return COMPOSE_INSERT_READ_ERROR;
3310 prev_autowrap = compose->autowrap;
3311 compose->autowrap = FALSE;
3313 text = GTK_TEXT_VIEW(compose->text);
3314 buffer = gtk_text_view_get_buffer(text);
3315 mark = gtk_text_buffer_get_insert(buffer);
3316 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3318 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3319 G_CALLBACK(text_inserted),
3322 cur_encoding = conv_get_locale_charset_str_no_utf8();
3324 while (fgets(buf, sizeof(buf), fp) != NULL) {
3327 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3328 str = g_strdup(buf);
3330 str = conv_codeset_strdup
3331 (buf, cur_encoding, CS_INTERNAL);
3334 /* strip <CR> if DOS/Windows file,
3335 replace <CR> with <LF> if Macintosh file. */
3338 if (len > 0 && str[len - 1] != '\n') {
3340 if (str[len] == '\r') str[len] = '\n';
3343 gtk_text_buffer_insert(buffer, &iter, str, -1);
3347 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3348 G_CALLBACK(text_inserted),
3350 compose->autowrap = prev_autowrap;
3351 if (compose->autowrap)
3352 compose_wrap_all(compose);
3357 return COMPOSE_INSERT_INVALID_CHARACTER;
3359 return COMPOSE_INSERT_SUCCESS;
3362 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3363 const gchar *filename,
3364 const gchar *content_type)
3372 GtkListStore *store;
3374 gboolean has_binary = FALSE;
3376 if (!is_file_exist(file)) {
3377 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3378 gboolean result = FALSE;
3379 if (file_from_uri && is_file_exist(file_from_uri)) {
3380 result = compose_attach_append(
3381 compose, file_from_uri,
3385 g_free(file_from_uri);
3388 alertpanel_error("File %s doesn't exist\n", filename);
3391 if ((size = get_file_size(file)) < 0) {
3392 alertpanel_error("Can't get file size of %s\n", filename);
3396 alertpanel_error(_("File %s is empty."), filename);
3399 if ((fp = g_fopen(file, "rb")) == NULL) {
3400 alertpanel_error(_("Can't read %s."), filename);
3405 ainfo = g_new0(AttachInfo, 1);
3406 auto_ainfo = g_auto_pointer_new_with_free
3407 (ainfo, (GFreeFunc) compose_attach_info_free);
3408 ainfo->file = g_strdup(file);
3411 ainfo->content_type = g_strdup(content_type);
3412 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3414 MsgFlags flags = {0, 0};
3416 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3417 ainfo->encoding = ENC_7BIT;
3419 ainfo->encoding = ENC_8BIT;
3421 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3422 if (msginfo && msginfo->subject)
3423 name = g_strdup(msginfo->subject);
3425 name = g_path_get_basename(filename ? filename : file);
3427 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3429 procmsg_msginfo_free(msginfo);
3431 if (!g_ascii_strncasecmp(content_type, "text", 4))
3432 ainfo->encoding = 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 ainfo->content_type = procmime_get_mime_type(file);
3441 if (!ainfo->content_type) {
3442 ainfo->content_type =
3443 g_strdup("application/octet-stream");
3444 ainfo->encoding = ENC_BASE64;
3445 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3447 procmime_get_encoding_for_text_file(file, &has_binary);
3449 ainfo->encoding = ENC_BASE64;
3450 name = g_path_get_basename(filename ? filename : file);
3451 ainfo->name = g_strdup(name);
3455 if (ainfo->name != NULL
3456 && !strcmp(ainfo->name, ".")) {
3457 g_free(ainfo->name);
3461 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3462 g_free(ainfo->content_type);
3463 ainfo->content_type = g_strdup("application/octet-stream");
3466 ainfo->size = (goffset)size;
3467 size_text = to_human_readable((goffset)size);
3469 store = GTK_LIST_STORE(gtk_tree_view_get_model
3470 (GTK_TREE_VIEW(compose->attach_clist)));
3472 gtk_list_store_append(store, &iter);
3473 gtk_list_store_set(store, &iter,
3474 COL_MIMETYPE, ainfo->content_type,
3475 COL_SIZE, size_text,
3476 COL_NAME, ainfo->name,
3478 COL_AUTODATA, auto_ainfo,
3481 g_auto_pointer_free(auto_ainfo);
3482 compose_attach_update_label(compose);
3486 static void compose_use_signing(Compose *compose, gboolean use_signing)
3488 compose->use_signing = use_signing;
3489 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3492 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3494 compose->use_encryption = use_encryption;
3495 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3498 #define NEXT_PART_NOT_CHILD(info) \
3500 node = info->node; \
3501 while (node->children) \
3502 node = g_node_last_child(node); \
3503 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3506 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3510 MimeInfo *firsttext = NULL;
3511 MimeInfo *encrypted = NULL;
3514 const gchar *partname = NULL;
3516 mimeinfo = procmime_scan_message(msginfo);
3517 if (!mimeinfo) return;
3519 if (mimeinfo->node->children == NULL) {
3520 procmime_mimeinfo_free_all(mimeinfo);
3524 /* find first content part */
3525 child = (MimeInfo *) mimeinfo->node->children->data;
3526 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3527 child = (MimeInfo *)child->node->children->data;
3529 if (child->type == MIMETYPE_TEXT) {
3531 debug_print("First text part found\n");
3532 } else if (compose->mode == COMPOSE_REEDIT &&
3533 child->type == MIMETYPE_APPLICATION &&
3534 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3535 encrypted = (MimeInfo *)child->node->parent->data;
3538 child = (MimeInfo *) mimeinfo->node->children->data;
3539 while (child != NULL) {
3542 if (child == encrypted) {
3543 /* skip this part of tree */
3544 NEXT_PART_NOT_CHILD(child);
3548 if (child->type == MIMETYPE_MULTIPART) {
3549 /* get the actual content */
3550 child = procmime_mimeinfo_next(child);
3554 if (child == firsttext) {
3555 child = procmime_mimeinfo_next(child);
3559 outfile = procmime_get_tmp_file_name(child);
3560 if ((err = procmime_get_part(outfile, child)) < 0)
3561 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3563 gchar *content_type;
3565 content_type = procmime_get_content_type_str(child->type, child->subtype);
3567 /* if we meet a pgp signature, we don't attach it, but
3568 * we force signing. */
3569 if ((strcmp(content_type, "application/pgp-signature") &&
3570 strcmp(content_type, "application/pkcs7-signature") &&
3571 strcmp(content_type, "application/x-pkcs7-signature"))
3572 || compose->mode == COMPOSE_REDIRECT) {
3573 partname = procmime_mimeinfo_get_parameter(child, "filename");
3574 if (partname == NULL)
3575 partname = procmime_mimeinfo_get_parameter(child, "name");
3576 if (partname == NULL)
3578 compose_attach_append(compose, outfile,
3579 partname, content_type);
3581 compose_force_signing(compose, compose->account, NULL);
3583 g_free(content_type);
3586 NEXT_PART_NOT_CHILD(child);
3588 procmime_mimeinfo_free_all(mimeinfo);
3591 #undef NEXT_PART_NOT_CHILD
3596 WAIT_FOR_INDENT_CHAR,
3597 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3600 /* return indent length, we allow:
3601 indent characters followed by indent characters or spaces/tabs,
3602 alphabets and numbers immediately followed by indent characters,
3603 and the repeating sequences of the above
3604 If quote ends with multiple spaces, only the first one is included. */
3605 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3606 const GtkTextIter *start, gint *len)
3608 GtkTextIter iter = *start;
3612 IndentState state = WAIT_FOR_INDENT_CHAR;
3615 gint alnum_count = 0;
3616 gint space_count = 0;
3619 if (prefs_common.quote_chars == NULL) {
3623 while (!gtk_text_iter_ends_line(&iter)) {
3624 wc = gtk_text_iter_get_char(&iter);
3625 if (g_unichar_iswide(wc))
3627 clen = g_unichar_to_utf8(wc, ch);
3631 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3632 is_space = g_unichar_isspace(wc);
3634 if (state == WAIT_FOR_INDENT_CHAR) {
3635 if (!is_indent && !g_unichar_isalnum(wc))
3638 quote_len += alnum_count + space_count + 1;
3639 alnum_count = space_count = 0;
3640 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3643 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3644 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3648 else if (is_indent) {
3649 quote_len += alnum_count + space_count + 1;
3650 alnum_count = space_count = 0;
3653 state = WAIT_FOR_INDENT_CHAR;
3657 gtk_text_iter_forward_char(&iter);
3660 if (quote_len > 0 && space_count > 0)
3666 if (quote_len > 0) {
3668 gtk_text_iter_forward_chars(&iter, quote_len);
3669 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3675 /* return >0 if the line is itemized */
3676 static int compose_itemized_length(GtkTextBuffer *buffer,
3677 const GtkTextIter *start)
3679 GtkTextIter iter = *start;
3684 if (gtk_text_iter_ends_line(&iter))
3689 wc = gtk_text_iter_get_char(&iter);
3690 if (!g_unichar_isspace(wc))
3692 gtk_text_iter_forward_char(&iter);
3693 if (gtk_text_iter_ends_line(&iter))
3697 clen = g_unichar_to_utf8(wc, ch);
3701 if (!strchr("*-+", ch[0]))
3704 gtk_text_iter_forward_char(&iter);
3705 if (gtk_text_iter_ends_line(&iter))
3707 wc = gtk_text_iter_get_char(&iter);
3708 if (g_unichar_isspace(wc)) {
3714 /* return the string at the start of the itemization */
3715 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3716 const GtkTextIter *start)
3718 GtkTextIter iter = *start;
3721 GString *item_chars = g_string_new("");
3724 if (gtk_text_iter_ends_line(&iter))
3729 wc = gtk_text_iter_get_char(&iter);
3730 if (!g_unichar_isspace(wc))
3732 gtk_text_iter_forward_char(&iter);
3733 if (gtk_text_iter_ends_line(&iter))
3735 g_string_append_unichar(item_chars, wc);
3738 str = item_chars->str;
3739 g_string_free(item_chars, FALSE);
3743 /* return the number of spaces at a line's start */
3744 static int compose_left_offset_length(GtkTextBuffer *buffer,
3745 const GtkTextIter *start)
3747 GtkTextIter iter = *start;
3750 if (gtk_text_iter_ends_line(&iter))
3754 wc = gtk_text_iter_get_char(&iter);
3755 if (!g_unichar_isspace(wc))
3758 gtk_text_iter_forward_char(&iter);
3759 if (gtk_text_iter_ends_line(&iter))
3763 gtk_text_iter_forward_char(&iter);
3764 if (gtk_text_iter_ends_line(&iter))
3769 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3770 const GtkTextIter *start,
3771 GtkTextIter *break_pos,
3775 GtkTextIter iter = *start, line_end = *start;
3776 PangoLogAttr *attrs;
3783 gboolean can_break = FALSE;
3784 gboolean do_break = FALSE;
3785 gboolean was_white = FALSE;
3786 gboolean prev_dont_break = FALSE;
3788 gtk_text_iter_forward_to_line_end(&line_end);
3789 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3790 len = g_utf8_strlen(str, -1);
3794 g_warning("compose_get_line_break_pos: len = 0!\n");
3798 /* g_print("breaking line: %d: %s (len = %d)\n",
3799 gtk_text_iter_get_line(&iter), str, len); */
3801 attrs = g_new(PangoLogAttr, len + 1);
3803 pango_default_break(str, -1, NULL, attrs, len + 1);
3807 /* skip quote and leading spaces */
3808 for (i = 0; *p != '\0' && i < len; i++) {
3811 wc = g_utf8_get_char(p);
3812 if (i >= quote_len && !g_unichar_isspace(wc))
3814 if (g_unichar_iswide(wc))
3816 else if (*p == '\t')
3820 p = g_utf8_next_char(p);
3823 for (; *p != '\0' && i < len; i++) {
3824 PangoLogAttr *attr = attrs + i;
3828 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3831 was_white = attr->is_white;
3833 /* don't wrap URI */
3834 if ((uri_len = get_uri_len(p)) > 0) {
3836 if (pos > 0 && col > max_col) {
3846 wc = g_utf8_get_char(p);
3847 if (g_unichar_iswide(wc)) {
3849 if (prev_dont_break && can_break && attr->is_line_break)
3851 } else if (*p == '\t')
3855 if (pos > 0 && col > max_col) {
3860 if (*p == '-' || *p == '/')
3861 prev_dont_break = TRUE;
3863 prev_dont_break = FALSE;
3865 p = g_utf8_next_char(p);
3869 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3874 *break_pos = *start;
3875 gtk_text_iter_set_line_offset(break_pos, pos);
3880 static gboolean compose_join_next_line(Compose *compose,
3881 GtkTextBuffer *buffer,
3883 const gchar *quote_str)
3885 GtkTextIter iter_ = *iter, cur, prev, next, end;
3886 PangoLogAttr attrs[3];
3888 gchar *next_quote_str;
3891 gboolean keep_cursor = FALSE;
3893 if (!gtk_text_iter_forward_line(&iter_) ||
3894 gtk_text_iter_ends_line(&iter_)) {
3897 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
3899 if ((quote_str || next_quote_str) &&
3900 strcmp2(quote_str, next_quote_str) != 0) {
3901 g_free(next_quote_str);
3904 g_free(next_quote_str);
3907 if (quote_len > 0) {
3908 gtk_text_iter_forward_chars(&end, quote_len);
3909 if (gtk_text_iter_ends_line(&end)) {
3914 /* don't join itemized lines */
3915 if (compose_itemized_length(buffer, &end) > 0) {
3919 /* don't join signature separator */
3920 if (compose_is_sig_separator(compose, buffer, &iter_)) {
3923 /* delete quote str */
3925 gtk_text_buffer_delete(buffer, &iter_, &end);
3927 /* don't join line breaks put by the user */
3929 gtk_text_iter_backward_char(&cur);
3930 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3931 gtk_text_iter_forward_char(&cur);
3935 gtk_text_iter_forward_char(&cur);
3936 /* delete linebreak and extra spaces */
3937 while (gtk_text_iter_backward_char(&cur)) {
3938 wc1 = gtk_text_iter_get_char(&cur);
3939 if (!g_unichar_isspace(wc1))
3944 while (!gtk_text_iter_ends_line(&cur)) {
3945 wc1 = gtk_text_iter_get_char(&cur);
3946 if (!g_unichar_isspace(wc1))
3948 gtk_text_iter_forward_char(&cur);
3951 if (!gtk_text_iter_equal(&prev, &next)) {
3954 mark = gtk_text_buffer_get_insert(buffer);
3955 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3956 if (gtk_text_iter_equal(&prev, &cur))
3958 gtk_text_buffer_delete(buffer, &prev, &next);
3962 /* insert space if required */
3963 gtk_text_iter_backward_char(&prev);
3964 wc1 = gtk_text_iter_get_char(&prev);
3965 wc2 = gtk_text_iter_get_char(&next);
3966 gtk_text_iter_forward_char(&next);
3967 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3968 pango_default_break(str, -1, NULL, attrs, 3);
3969 if (!attrs[1].is_line_break ||
3970 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3971 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3973 gtk_text_iter_backward_char(&iter_);
3974 gtk_text_buffer_place_cursor(buffer, &iter_);
3983 #define ADD_TXT_POS(bp_, ep_, pti_) \
3984 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3985 last = last->next; \
3986 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3987 last->next = NULL; \
3989 g_warning("alloc error scanning URIs\n"); \
3992 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3994 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3995 GtkTextBuffer *buffer;
3996 GtkTextIter iter, break_pos, end_of_line;
3997 gchar *quote_str = NULL;
3999 gboolean wrap_quote = prefs_common.linewrap_quote;
4000 gboolean prev_autowrap = compose->autowrap;
4001 gint startq_offset = -1, noq_offset = -1;
4002 gint uri_start = -1, uri_stop = -1;
4003 gint nouri_start = -1, nouri_stop = -1;
4004 gint num_blocks = 0;
4005 gint quotelevel = -1;
4006 gboolean modified = force;
4007 gboolean removed = FALSE;
4008 gboolean modified_before_remove = FALSE;
4010 gboolean start = TRUE;
4011 gint itemized_len = 0, rem_item_len = 0;
4012 gchar *itemized_chars = NULL;
4013 gboolean item_continuation = FALSE;
4018 if (compose->draft_timeout_tag == -2) {
4022 compose->autowrap = FALSE;
4024 buffer = gtk_text_view_get_buffer(text);
4025 undo_wrapping(compose->undostruct, TRUE);
4030 mark = gtk_text_buffer_get_insert(buffer);
4031 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4035 if (compose->draft_timeout_tag == -2) {
4036 if (gtk_text_iter_ends_line(&iter)) {
4037 while (gtk_text_iter_ends_line(&iter) &&
4038 gtk_text_iter_forward_line(&iter))
4041 while (gtk_text_iter_backward_line(&iter)) {
4042 if (gtk_text_iter_ends_line(&iter)) {
4043 gtk_text_iter_forward_line(&iter);
4049 /* move to line start */
4050 gtk_text_iter_set_line_offset(&iter, 0);
4053 itemized_len = compose_itemized_length(buffer, &iter);
4055 if (!itemized_len) {
4056 itemized_len = compose_left_offset_length(buffer, &iter);
4057 item_continuation = TRUE;
4061 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4063 /* go until paragraph end (empty line) */
4064 while (start || !gtk_text_iter_ends_line(&iter)) {
4065 gchar *scanpos = NULL;
4066 /* parse table - in order of priority */
4068 const gchar *needle; /* token */
4070 /* token search function */
4071 gchar *(*search) (const gchar *haystack,
4072 const gchar *needle);
4073 /* part parsing function */
4074 gboolean (*parse) (const gchar *start,
4075 const gchar *scanpos,
4079 /* part to URI function */
4080 gchar *(*build_uri) (const gchar *bp,
4084 static struct table parser[] = {
4085 {"http://", strcasestr, get_uri_part, make_uri_string},
4086 {"https://", strcasestr, get_uri_part, make_uri_string},
4087 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4088 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4089 {"www.", strcasestr, get_uri_part, make_http_string},
4090 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4091 {"@", strcasestr, get_email_part, make_email_string}
4093 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4094 gint last_index = PARSE_ELEMS;
4096 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4100 if (!prev_autowrap && num_blocks == 0) {
4102 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4103 G_CALLBACK(text_inserted),
4106 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4109 uri_start = uri_stop = -1;
4111 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4114 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4115 if (startq_offset == -1)
4116 startq_offset = gtk_text_iter_get_offset(&iter);
4117 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4118 if (quotelevel > 2) {
4119 /* recycle colors */
4120 if (prefs_common.recycle_quote_colors)
4129 if (startq_offset == -1)
4130 noq_offset = gtk_text_iter_get_offset(&iter);
4134 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4137 if (gtk_text_iter_ends_line(&iter)) {
4139 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4140 prefs_common.linewrap_len,
4142 GtkTextIter prev, next, cur;
4143 if (prev_autowrap != FALSE || force) {
4144 compose->automatic_break = TRUE;
4146 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4147 compose->automatic_break = FALSE;
4148 if (itemized_len && compose->autoindent) {
4149 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4150 if (!item_continuation)
4151 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4153 } else if (quote_str && wrap_quote) {
4154 compose->automatic_break = TRUE;
4156 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4157 compose->automatic_break = FALSE;
4158 if (itemized_len && compose->autoindent) {
4159 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4160 if (!item_continuation)
4161 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4165 /* remove trailing spaces */
4167 rem_item_len = itemized_len;
4168 while (compose->autoindent && rem_item_len-- > 0)
4169 gtk_text_iter_backward_char(&cur);
4170 gtk_text_iter_backward_char(&cur);
4173 while (!gtk_text_iter_starts_line(&cur)) {
4176 gtk_text_iter_backward_char(&cur);
4177 wc = gtk_text_iter_get_char(&cur);
4178 if (!g_unichar_isspace(wc))
4182 if (!gtk_text_iter_equal(&prev, &next)) {
4183 gtk_text_buffer_delete(buffer, &prev, &next);
4185 gtk_text_iter_forward_char(&break_pos);
4189 gtk_text_buffer_insert(buffer, &break_pos,
4193 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4195 /* move iter to current line start */
4196 gtk_text_iter_set_line_offset(&iter, 0);
4203 /* move iter to next line start */
4209 if (!prev_autowrap && num_blocks > 0) {
4211 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4212 G_CALLBACK(text_inserted),
4216 while (!gtk_text_iter_ends_line(&end_of_line)) {
4217 gtk_text_iter_forward_char(&end_of_line);
4219 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4221 nouri_start = gtk_text_iter_get_offset(&iter);
4222 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4224 walk_pos = gtk_text_iter_get_offset(&iter);
4225 /* FIXME: this looks phony. scanning for anything in the parse table */
4226 for (n = 0; n < PARSE_ELEMS; n++) {
4229 tmp = parser[n].search(walk, parser[n].needle);
4231 if (scanpos == NULL || tmp < scanpos) {
4240 /* check if URI can be parsed */
4241 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4242 (const gchar **)&ep, FALSE)
4243 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4247 strlen(parser[last_index].needle);
4250 uri_start = walk_pos + (bp - o_walk);
4251 uri_stop = walk_pos + (ep - o_walk);
4255 gtk_text_iter_forward_line(&iter);
4258 if (startq_offset != -1) {
4259 GtkTextIter startquote, endquote;
4260 gtk_text_buffer_get_iter_at_offset(
4261 buffer, &startquote, startq_offset);
4264 switch (quotelevel) {
4266 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4267 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4268 gtk_text_buffer_apply_tag_by_name(
4269 buffer, "quote0", &startquote, &endquote);
4270 gtk_text_buffer_remove_tag_by_name(
4271 buffer, "quote1", &startquote, &endquote);
4272 gtk_text_buffer_remove_tag_by_name(
4273 buffer, "quote2", &startquote, &endquote);
4278 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4279 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4280 gtk_text_buffer_apply_tag_by_name(
4281 buffer, "quote1", &startquote, &endquote);
4282 gtk_text_buffer_remove_tag_by_name(
4283 buffer, "quote0", &startquote, &endquote);
4284 gtk_text_buffer_remove_tag_by_name(
4285 buffer, "quote2", &startquote, &endquote);
4290 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4291 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4292 gtk_text_buffer_apply_tag_by_name(
4293 buffer, "quote2", &startquote, &endquote);
4294 gtk_text_buffer_remove_tag_by_name(
4295 buffer, "quote0", &startquote, &endquote);
4296 gtk_text_buffer_remove_tag_by_name(
4297 buffer, "quote1", &startquote, &endquote);
4303 } else if (noq_offset != -1) {
4304 GtkTextIter startnoquote, endnoquote;
4305 gtk_text_buffer_get_iter_at_offset(
4306 buffer, &startnoquote, noq_offset);
4309 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4310 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4311 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4312 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4313 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4314 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4315 gtk_text_buffer_remove_tag_by_name(
4316 buffer, "quote0", &startnoquote, &endnoquote);
4317 gtk_text_buffer_remove_tag_by_name(
4318 buffer, "quote1", &startnoquote, &endnoquote);
4319 gtk_text_buffer_remove_tag_by_name(
4320 buffer, "quote2", &startnoquote, &endnoquote);
4326 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4327 GtkTextIter nouri_start_iter, nouri_end_iter;
4328 gtk_text_buffer_get_iter_at_offset(
4329 buffer, &nouri_start_iter, nouri_start);
4330 gtk_text_buffer_get_iter_at_offset(
4331 buffer, &nouri_end_iter, nouri_stop);
4332 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4333 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4334 gtk_text_buffer_remove_tag_by_name(
4335 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4336 modified_before_remove = modified;
4341 if (uri_start >= 0 && uri_stop > 0) {
4342 GtkTextIter uri_start_iter, uri_end_iter, back;
4343 gtk_text_buffer_get_iter_at_offset(
4344 buffer, &uri_start_iter, uri_start);
4345 gtk_text_buffer_get_iter_at_offset(
4346 buffer, &uri_end_iter, uri_stop);
4347 back = uri_end_iter;
4348 gtk_text_iter_backward_char(&back);
4349 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4350 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4351 gtk_text_buffer_apply_tag_by_name(
4352 buffer, "link", &uri_start_iter, &uri_end_iter);
4354 if (removed && !modified_before_remove) {
4360 // debug_print("not modified, out after %d lines\n", lines);
4364 // debug_print("modified, out after %d lines\n", lines);
4366 g_free(itemized_chars);
4369 undo_wrapping(compose->undostruct, FALSE);
4370 compose->autowrap = prev_autowrap;
4375 void compose_action_cb(void *data)
4377 Compose *compose = (Compose *)data;
4378 compose_wrap_all(compose);
4381 static void compose_wrap_all(Compose *compose)
4383 compose_wrap_all_full(compose, FALSE);
4386 static void compose_wrap_all_full(Compose *compose, gboolean force)
4388 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4389 GtkTextBuffer *buffer;
4391 gboolean modified = TRUE;
4393 buffer = gtk_text_view_get_buffer(text);
4395 gtk_text_buffer_get_start_iter(buffer, &iter);
4396 while (!gtk_text_iter_is_end(&iter) && modified)
4397 modified = compose_beautify_paragraph(compose, &iter, force);
4401 static void compose_set_title(Compose *compose)
4407 edited = compose->modified ? _(" [Edited]") : "";
4409 subject = gtk_editable_get_chars(
4410 GTK_EDITABLE(compose->subject_entry), 0, -1);
4412 #ifndef GENERIC_UMPC
4413 if (subject && strlen(subject))
4414 str = g_strdup_printf(_("%s - Compose message%s"),
4417 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4419 str = g_strdup(_("Compose message"));
4422 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4428 * compose_current_mail_account:
4430 * Find a current mail account (the currently selected account, or the
4431 * default account, if a news account is currently selected). If a
4432 * mail account cannot be found, display an error message.
4434 * Return value: Mail account, or NULL if not found.
4436 static PrefsAccount *
4437 compose_current_mail_account(void)
4441 if (cur_account && cur_account->protocol != A_NNTP)
4444 ac = account_get_default();
4445 if (!ac || ac->protocol == A_NNTP) {
4446 alertpanel_error(_("Account for sending mail is not specified.\n"
4447 "Please select a mail account before sending."));
4454 #define QUOTE_IF_REQUIRED(out, str) \
4456 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4460 len = strlen(str) + 3; \
4461 if ((__tmp = alloca(len)) == NULL) { \
4462 g_warning("can't allocate memory\n"); \
4463 g_string_free(header, TRUE); \
4466 g_snprintf(__tmp, len, "\"%s\"", str); \
4471 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4472 g_warning("can't allocate memory\n"); \
4473 g_string_free(header, TRUE); \
4476 strcpy(__tmp, str); \
4482 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4484 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4488 len = strlen(str) + 3; \
4489 if ((__tmp = alloca(len)) == NULL) { \
4490 g_warning("can't allocate memory\n"); \
4493 g_snprintf(__tmp, len, "\"%s\"", str); \
4498 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4499 g_warning("can't allocate memory\n"); \
4502 strcpy(__tmp, str); \
4508 static void compose_select_account(Compose *compose, PrefsAccount *account,
4513 g_return_if_fail(account != NULL);
4515 compose->account = account;
4517 if (account->name && *account->name) {
4519 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4520 from = g_strdup_printf("%s <%s>",
4521 buf, account->address);
4522 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4524 from = g_strdup_printf("<%s>",
4526 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4531 compose_set_title(compose);
4533 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4534 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4536 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4537 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4538 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4540 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4542 activate_privacy_system(compose, account, FALSE);
4544 if (!init && compose->mode != COMPOSE_REDIRECT) {
4545 undo_block(compose->undostruct);
4546 compose_insert_sig(compose, TRUE);
4547 undo_unblock(compose->undostruct);
4551 /* use account's dict info if set */
4552 if (compose->gtkaspell) {
4553 if (account->enable_default_dictionary)
4554 gtkaspell_change_dict(compose->gtkaspell,
4555 account->default_dictionary, FALSE);
4556 if (account->enable_default_alt_dictionary)
4557 gtkaspell_change_alt_dict(compose->gtkaspell,
4558 account->default_alt_dictionary);
4559 if (account->enable_default_dictionary
4560 || account->enable_default_alt_dictionary)
4561 compose_spell_menu_changed(compose);
4566 gboolean compose_check_for_valid_recipient(Compose *compose) {
4567 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4568 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4569 gboolean recipient_found = FALSE;
4573 /* free to and newsgroup list */
4574 slist_free_strings(compose->to_list);
4575 g_slist_free(compose->to_list);
4576 compose->to_list = NULL;
4578 slist_free_strings(compose->newsgroup_list);
4579 g_slist_free(compose->newsgroup_list);
4580 compose->newsgroup_list = NULL;
4582 /* search header entries for to and newsgroup entries */
4583 for (list = compose->header_list; list; list = list->next) {
4586 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4587 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4590 if (entry[0] != '\0') {
4591 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4592 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4593 compose->to_list = address_list_append(compose->to_list, entry);
4594 recipient_found = TRUE;
4597 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4598 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4599 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4600 recipient_found = TRUE;
4607 return recipient_found;
4610 static gboolean compose_check_for_set_recipients(Compose *compose)
4612 if (compose->account->set_autocc && compose->account->auto_cc) {
4613 gboolean found_other = FALSE;
4615 /* search header entries for to and newsgroup entries */
4616 for (list = compose->header_list; list; list = list->next) {
4619 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4620 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4623 if (strcmp(entry, compose->account->auto_cc)
4624 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4634 if (compose->batch) {
4635 gtk_widget_show_all(compose->window);
4637 aval = alertpanel(_("Send"),
4638 _("The only recipient is the default CC address. Send anyway?"),
4639 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4640 if (aval != G_ALERTALTERNATE)
4644 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4645 gboolean found_other = FALSE;
4647 /* search header entries for to and newsgroup entries */
4648 for (list = compose->header_list; list; list = list->next) {
4651 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4652 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4655 if (strcmp(entry, compose->account->auto_bcc)
4656 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4666 if (compose->batch) {
4667 gtk_widget_show_all(compose->window);
4669 aval = alertpanel(_("Send"),
4670 _("The only recipient is the default BCC address. Send anyway?"),
4671 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4672 if (aval != G_ALERTALTERNATE)
4679 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4683 if (compose_check_for_valid_recipient(compose) == FALSE) {
4684 if (compose->batch) {
4685 gtk_widget_show_all(compose->window);
4687 alertpanel_error(_("Recipient is not specified."));
4691 if (compose_check_for_set_recipients(compose) == FALSE) {
4695 if (!compose->batch) {
4696 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4697 if (*str == '\0' && check_everything == TRUE &&
4698 compose->mode != COMPOSE_REDIRECT) {
4700 gchar *button_label;
4703 if (compose->sending)
4704 button_label = _("+_Send");
4706 button_label = _("+_Queue");
4707 message = g_strdup_printf(_("Subject is empty. %s"),
4708 compose->sending?_("Send it anyway?"):
4709 _("Queue it anyway?"));
4711 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4712 GTK_STOCK_CANCEL, button_label, NULL);
4714 if (aval != G_ALERTALTERNATE)
4719 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4725 gint compose_send(Compose *compose)
4728 FolderItem *folder = NULL;
4730 gchar *msgpath = NULL;
4731 gboolean discard_window = FALSE;
4732 gchar *errstr = NULL;
4733 gchar *tmsgid = NULL;
4734 MainWindow *mainwin = mainwindow_get_mainwindow();
4735 gboolean queued_removed = FALSE;
4737 if (prefs_common.send_dialog_invisible
4738 || compose->batch == TRUE)
4739 discard_window = TRUE;
4741 compose_allow_user_actions (compose, FALSE);
4742 compose->sending = TRUE;
4744 if (compose_check_entries(compose, TRUE) == FALSE) {
4745 if (compose->batch) {
4746 gtk_widget_show_all(compose->window);
4752 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4755 if (compose->batch) {
4756 gtk_widget_show_all(compose->window);
4759 alertpanel_error(_("Could not queue message for sending:\n\n"
4760 "Charset conversion failed."));
4761 } else if (val == -5) {
4762 alertpanel_error(_("Could not queue message for sending:\n\n"
4763 "Couldn't get recipient encryption key."));
4764 } else if (val == -6) {
4766 } else if (val == -3) {
4767 if (privacy_peek_error())
4768 alertpanel_error(_("Could not queue message for sending:\n\n"
4769 "Signature failed: %s"), privacy_get_error());
4770 } else if (val == -2 && errno != 0) {
4771 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4773 alertpanel_error(_("Could not queue message for sending."));
4778 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4779 if (discard_window) {
4780 compose->sending = FALSE;
4781 compose_close(compose);
4782 /* No more compose access in the normal codepath
4783 * after this point! */
4788 alertpanel_error(_("The message was queued but could not be "
4789 "sent.\nUse \"Send queued messages\" from "
4790 "the main window to retry."));
4791 if (!discard_window) {
4798 if (msgpath == NULL) {
4799 msgpath = folder_item_fetch_msg(folder, msgnum);
4800 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4803 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4804 claws_unlink(msgpath);
4807 if (!discard_window) {
4809 if (!queued_removed)
4810 folder_item_remove_msg(folder, msgnum);
4811 folder_item_scan(folder);
4813 /* make sure we delete that */
4814 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4816 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4817 folder_item_remove_msg(folder, tmp->msgnum);
4818 procmsg_msginfo_free(tmp);
4825 if (!queued_removed)
4826 folder_item_remove_msg(folder, msgnum);
4827 folder_item_scan(folder);
4829 /* make sure we delete that */
4830 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4832 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4833 folder_item_remove_msg(folder, tmp->msgnum);
4834 procmsg_msginfo_free(tmp);
4837 if (!discard_window) {
4838 compose->sending = FALSE;
4839 compose_allow_user_actions (compose, TRUE);
4840 compose_close(compose);
4844 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4845 "the main window to retry."), errstr);
4848 alertpanel_error_log(_("The message was queued but could not be "
4849 "sent.\nUse \"Send queued messages\" from "
4850 "the main window to retry."));
4852 if (!discard_window) {
4861 toolbar_main_set_sensitive(mainwin);
4862 main_window_set_menu_sensitive(mainwin);
4868 compose_allow_user_actions (compose, TRUE);
4869 compose->sending = FALSE;
4870 compose->modified = TRUE;
4871 toolbar_main_set_sensitive(mainwin);
4872 main_window_set_menu_sensitive(mainwin);
4877 static gboolean compose_use_attach(Compose *compose)
4879 GtkTreeModel *model = gtk_tree_view_get_model
4880 (GTK_TREE_VIEW(compose->attach_clist));
4881 return gtk_tree_model_iter_n_children(model, NULL) > 0;
4884 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
4887 gchar buf[BUFFSIZE];
4889 gboolean first_to_address;
4890 gboolean first_cc_address;
4892 ComposeHeaderEntry *headerentry;
4893 const gchar *headerentryname;
4894 const gchar *cc_hdr;
4895 const gchar *to_hdr;
4896 gboolean err = FALSE;
4898 debug_print("Writing redirect header\n");
4900 cc_hdr = prefs_common_translated_header_name("Cc:");
4901 to_hdr = prefs_common_translated_header_name("To:");
4903 first_to_address = TRUE;
4904 for (list = compose->header_list; list; list = list->next) {
4905 headerentry = ((ComposeHeaderEntry *)list->data);
4906 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4908 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4909 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4910 Xstrdup_a(str, entstr, return -1);
4912 if (str[0] != '\0') {
4913 compose_convert_header
4914 (compose, buf, sizeof(buf), str,
4915 strlen("Resent-To") + 2, TRUE);
4917 if (first_to_address) {
4918 err |= (fprintf(fp, "Resent-To: ") < 0);
4919 first_to_address = FALSE;
4921 err |= (fprintf(fp, ",") < 0);
4923 err |= (fprintf(fp, "%s", buf) < 0);
4927 if (!first_to_address) {
4928 err |= (fprintf(fp, "\n") < 0);
4931 first_cc_address = TRUE;
4932 for (list = compose->header_list; list; list = list->next) {
4933 headerentry = ((ComposeHeaderEntry *)list->data);
4934 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4936 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4937 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4938 Xstrdup_a(str, strg, return -1);
4940 if (str[0] != '\0') {
4941 compose_convert_header
4942 (compose, buf, sizeof(buf), str,
4943 strlen("Resent-Cc") + 2, TRUE);
4945 if (first_cc_address) {
4946 err |= (fprintf(fp, "Resent-Cc: ") < 0);
4947 first_cc_address = FALSE;
4949 err |= (fprintf(fp, ",") < 0);
4951 err |= (fprintf(fp, "%s", buf) < 0);
4955 if (!first_cc_address) {
4956 err |= (fprintf(fp, "\n") < 0);
4959 return (err ? -1:0);
4962 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4964 gchar buf[BUFFSIZE];
4966 const gchar *entstr;
4967 /* struct utsname utsbuf; */
4968 gboolean err = FALSE;
4970 g_return_val_if_fail(fp != NULL, -1);
4971 g_return_val_if_fail(compose->account != NULL, -1);
4972 g_return_val_if_fail(compose->account->address != NULL, -1);
4975 get_rfc822_date(buf, sizeof(buf));
4976 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4979 if (compose->account->name && *compose->account->name) {
4980 compose_convert_header
4981 (compose, buf, sizeof(buf), compose->account->name,
4982 strlen("From: "), TRUE);
4983 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4984 buf, compose->account->address) < 0);
4986 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4989 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4990 if (*entstr != '\0') {
4991 Xstrdup_a(str, entstr, return -1);
4994 compose_convert_header(compose, buf, sizeof(buf), str,
4995 strlen("Subject: "), FALSE);
4996 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5000 /* Resent-Message-ID */
5001 if (compose->account->set_domain && compose->account->domain) {
5002 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5003 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5004 g_snprintf(buf, sizeof(buf), "%s",
5005 strchr(compose->account->address, '@') ?
5006 strchr(compose->account->address, '@')+1 :
5007 compose->account->address);
5009 g_snprintf(buf, sizeof(buf), "%s", "");
5012 if (compose->account->gen_msgid) {
5013 generate_msgid(buf, sizeof(buf));
5014 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5015 compose->msgid = g_strdup(buf);
5017 compose->msgid = NULL;
5020 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5023 /* separator between header and body */
5024 err |= (fputs("\n", fp) == EOF);
5026 return (err ? -1:0);
5029 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5033 gchar buf[BUFFSIZE];
5035 gboolean skip = FALSE;
5036 gboolean err = FALSE;
5037 gchar *not_included[]={
5038 "Return-Path:", "Delivered-To:", "Received:",
5039 "Subject:", "X-UIDL:", "AF:",
5040 "NF:", "PS:", "SRH:",
5041 "SFN:", "DSR:", "MID:",
5042 "CFG:", "PT:", "S:",
5043 "RQ:", "SSV:", "NSV:",
5044 "SSH:", "R:", "MAID:",
5045 "NAID:", "RMID:", "FMID:",
5046 "SCF:", "RRCPT:", "NG:",
5047 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5048 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5049 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5050 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5051 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5054 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5055 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5059 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5061 for (i = 0; not_included[i] != NULL; i++) {
5062 if (g_ascii_strncasecmp(buf, not_included[i],
5063 strlen(not_included[i])) == 0) {
5070 if (fputs(buf, fdest) == -1)
5073 if (!prefs_common.redirect_keep_from) {
5074 if (g_ascii_strncasecmp(buf, "From:",
5075 strlen("From:")) == 0) {
5076 err |= (fputs(" (by way of ", fdest) == EOF);
5077 if (compose->account->name
5078 && *compose->account->name) {
5079 compose_convert_header
5080 (compose, buf, sizeof(buf),
5081 compose->account->name,
5084 err |= (fprintf(fdest, "%s <%s>",
5086 compose->account->address) < 0);
5088 err |= (fprintf(fdest, "%s",
5089 compose->account->address) < 0);
5090 err |= (fputs(")", fdest) == EOF);
5094 if (fputs("\n", fdest) == -1)
5101 if (compose_redirect_write_headers(compose, fdest))
5104 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5105 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5118 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5120 GtkTextBuffer *buffer;
5121 GtkTextIter start, end;
5124 const gchar *out_codeset;
5125 EncodingType encoding = ENC_UNKNOWN;
5126 MimeInfo *mimemsg, *mimetext;
5128 const gchar *src_codeset = CS_INTERNAL;
5129 gchar *from_addr = NULL;
5130 gchar *from_name = NULL;
5132 if (action == COMPOSE_WRITE_FOR_SEND)
5133 attach_parts = TRUE;
5135 /* create message MimeInfo */
5136 mimemsg = procmime_mimeinfo_new();
5137 mimemsg->type = MIMETYPE_MESSAGE;
5138 mimemsg->subtype = g_strdup("rfc822");
5139 mimemsg->content = MIMECONTENT_MEM;
5140 mimemsg->tmp = TRUE; /* must free content later */
5141 mimemsg->data.mem = compose_get_header(compose);
5143 /* Create text part MimeInfo */
5144 /* get all composed text */
5145 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5146 gtk_text_buffer_get_start_iter(buffer, &start);
5147 gtk_text_buffer_get_end_iter(buffer, &end);
5148 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5150 out_codeset = conv_get_charset_str(compose->out_encoding);
5152 if (!out_codeset && is_ascii_str(chars)) {
5153 out_codeset = CS_US_ASCII;
5154 } else if (prefs_common.outgoing_fallback_to_ascii &&
5155 is_ascii_str(chars)) {
5156 out_codeset = CS_US_ASCII;
5157 encoding = ENC_7BIT;
5161 gchar *test_conv_global_out = NULL;
5162 gchar *test_conv_reply = NULL;
5164 /* automatic mode. be automatic. */
5165 codeconv_set_strict(TRUE);
5167 out_codeset = conv_get_outgoing_charset_str();
5169 debug_print("trying to convert to %s\n", out_codeset);
5170 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5173 if (!test_conv_global_out && compose->orig_charset
5174 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5175 out_codeset = compose->orig_charset;
5176 debug_print("failure; trying to convert to %s\n", out_codeset);
5177 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5180 if (!test_conv_global_out && !test_conv_reply) {
5182 out_codeset = CS_INTERNAL;
5183 debug_print("failure; finally using %s\n", out_codeset);
5185 g_free(test_conv_global_out);
5186 g_free(test_conv_reply);
5187 codeconv_set_strict(FALSE);
5190 if (encoding == ENC_UNKNOWN) {
5191 if (prefs_common.encoding_method == CTE_BASE64)
5192 encoding = ENC_BASE64;
5193 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5194 encoding = ENC_QUOTED_PRINTABLE;
5195 else if (prefs_common.encoding_method == CTE_8BIT)
5196 encoding = ENC_8BIT;
5198 encoding = procmime_get_encoding_for_charset(out_codeset);
5201 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5202 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5204 if (action == COMPOSE_WRITE_FOR_SEND) {
5205 codeconv_set_strict(TRUE);
5206 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5207 codeconv_set_strict(FALSE);
5213 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5214 "to the specified %s charset.\n"
5215 "Send it as %s?"), out_codeset, src_codeset);
5216 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5217 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5220 if (aval != G_ALERTALTERNATE) {
5225 out_codeset = src_codeset;
5231 out_codeset = src_codeset;
5236 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5237 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5238 strstr(buf, "\nFrom ") != NULL) {
5239 encoding = ENC_QUOTED_PRINTABLE;
5243 mimetext = procmime_mimeinfo_new();
5244 mimetext->content = MIMECONTENT_MEM;
5245 mimetext->tmp = TRUE; /* must free content later */
5246 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5247 * and free the data, which we need later. */
5248 mimetext->data.mem = g_strdup(buf);
5249 mimetext->type = MIMETYPE_TEXT;
5250 mimetext->subtype = g_strdup("plain");
5251 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5252 g_strdup(out_codeset));
5254 /* protect trailing spaces when signing message */
5255 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5256 privacy_system_can_sign(compose->privacy_system)) {
5257 encoding = ENC_QUOTED_PRINTABLE;
5260 debug_print("main text: %zd bytes encoded as %s in %d\n",
5261 strlen(buf), out_codeset, encoding);
5263 /* check for line length limit */
5264 if (action == COMPOSE_WRITE_FOR_SEND &&
5265 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5266 check_line_length(buf, 1000, &line) < 0) {
5270 msg = g_strdup_printf
5271 (_("Line %d exceeds the line length limit (998 bytes).\n"
5272 "The contents of the message might be broken on the way to the delivery.\n"
5274 "Send it anyway?"), line + 1);
5275 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5277 if (aval != G_ALERTALTERNATE) {
5283 if (encoding != ENC_UNKNOWN)
5284 procmime_encode_content(mimetext, encoding);
5286 /* append attachment parts */
5287 if (compose_use_attach(compose) && attach_parts) {
5288 MimeInfo *mimempart;
5289 gchar *boundary = NULL;
5290 mimempart = procmime_mimeinfo_new();
5291 mimempart->content = MIMECONTENT_EMPTY;
5292 mimempart->type = MIMETYPE_MULTIPART;
5293 mimempart->subtype = g_strdup("mixed");
5297 boundary = generate_mime_boundary(NULL);
5298 } while (strstr(buf, boundary) != NULL);
5300 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5303 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5305 g_node_append(mimempart->node, mimetext->node);
5306 g_node_append(mimemsg->node, mimempart->node);
5308 compose_add_attachments(compose, mimempart);
5310 g_node_append(mimemsg->node, mimetext->node);
5314 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5315 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5316 /* extract name and address */
5317 if (strstr(spec, " <") && strstr(spec, ">")) {
5318 from_addr = g_strdup(strrchr(spec, '<')+1);
5319 *(strrchr(from_addr, '>')) = '\0';
5320 from_name = g_strdup(spec);
5321 *(strrchr(from_name, '<')) = '\0';
5328 /* sign message if sending */
5329 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5330 privacy_system_can_sign(compose->privacy_system))
5331 if (!privacy_sign(compose->privacy_system, mimemsg,
5332 compose->account, from_addr)) {
5339 procmime_write_mimeinfo(mimemsg, fp);
5341 procmime_mimeinfo_free_all(mimemsg);
5346 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5348 GtkTextBuffer *buffer;
5349 GtkTextIter start, end;
5354 if ((fp = g_fopen(file, "wb")) == NULL) {
5355 FILE_OP_ERROR(file, "fopen");
5359 /* chmod for security */
5360 if (change_file_mode_rw(fp, file) < 0) {
5361 FILE_OP_ERROR(file, "chmod");
5362 g_warning("can't change file mode\n");
5365 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5366 gtk_text_buffer_get_start_iter(buffer, &start);
5367 gtk_text_buffer_get_end_iter(buffer, &end);
5368 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5370 chars = conv_codeset_strdup
5371 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5374 if (!chars) return -1;
5377 len = strlen(chars);
5378 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5379 FILE_OP_ERROR(file, "fwrite");
5388 if (fclose(fp) == EOF) {
5389 FILE_OP_ERROR(file, "fclose");
5396 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5399 MsgInfo *msginfo = compose->targetinfo;
5401 g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5402 if (!msginfo) return -1;
5404 if (!force && MSG_IS_LOCKED(msginfo->flags))
5407 item = msginfo->folder;
5408 g_return_val_if_fail(item != NULL, -1);
5410 if (procmsg_msg_exist(msginfo) &&
5411 (folder_has_parent_of_type(item, F_QUEUE) ||
5412 folder_has_parent_of_type(item, F_DRAFT)
5413 || msginfo == compose->autosaved_draft)) {
5414 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5415 g_warning("can't remove the old message\n");
5418 debug_print("removed reedit target %d\n", msginfo->msgnum);
5425 static void compose_remove_draft(Compose *compose)
5428 MsgInfo *msginfo = compose->targetinfo;
5429 drafts = account_get_special_folder(compose->account, F_DRAFT);
5431 if (procmsg_msg_exist(msginfo)) {
5432 folder_item_remove_msg(drafts, msginfo->msgnum);
5437 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5438 gboolean remove_reedit_target)
5440 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5443 static gboolean compose_warn_encryption(Compose *compose)
5445 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5446 AlertValue val = G_ALERTALTERNATE;
5448 if (warning == NULL)
5451 val = alertpanel_full(_("Encryption warning"), warning,
5452 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5453 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5454 if (val & G_ALERTDISABLE) {
5455 val &= ~G_ALERTDISABLE;
5456 if (val == G_ALERTALTERNATE)
5457 privacy_inhibit_encrypt_warning(compose->privacy_system,
5461 if (val == G_ALERTALTERNATE) {
5468 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5469 gchar **msgpath, gboolean check_subject,
5470 gboolean remove_reedit_target)
5477 static gboolean lock = FALSE;
5478 PrefsAccount *mailac = NULL, *newsac = NULL;
5479 gboolean err = FALSE;
5481 debug_print("queueing message...\n");
5482 g_return_val_if_fail(compose->account != NULL, -1);
5486 if (compose_check_entries(compose, check_subject) == FALSE) {
5488 if (compose->batch) {
5489 gtk_widget_show_all(compose->window);
5494 if (!compose->to_list && !compose->newsgroup_list) {
5495 g_warning("can't get recipient list.");
5500 if (compose->to_list) {
5501 if (compose->account->protocol != A_NNTP)
5502 mailac = compose->account;
5503 else if (cur_account && cur_account->protocol != A_NNTP)
5504 mailac = cur_account;
5505 else if (!(mailac = compose_current_mail_account())) {
5507 alertpanel_error(_("No account for sending mails available!"));
5512 if (compose->newsgroup_list) {
5513 if (compose->account->protocol == A_NNTP)
5514 newsac = compose->account;
5515 else if (!newsac->protocol != A_NNTP) {
5517 alertpanel_error(_("No account for posting news available!"));
5522 /* write queue header */
5523 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5524 G_DIR_SEPARATOR, compose, (guint) rand());
5525 debug_print("queuing to %s\n", tmp);
5526 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5527 FILE_OP_ERROR(tmp, "fopen");
5533 if (change_file_mode_rw(fp, tmp) < 0) {
5534 FILE_OP_ERROR(tmp, "chmod");
5535 g_warning("can't change file mode\n");
5538 /* queueing variables */
5539 err |= (fprintf(fp, "AF:\n") < 0);
5540 err |= (fprintf(fp, "NF:0\n") < 0);
5541 err |= (fprintf(fp, "PS:10\n") < 0);
5542 err |= (fprintf(fp, "SRH:1\n") < 0);
5543 err |= (fprintf(fp, "SFN:\n") < 0);
5544 err |= (fprintf(fp, "DSR:\n") < 0);
5546 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5548 err |= (fprintf(fp, "MID:\n") < 0);
5549 err |= (fprintf(fp, "CFG:\n") < 0);
5550 err |= (fprintf(fp, "PT:0\n") < 0);
5551 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5552 err |= (fprintf(fp, "RQ:\n") < 0);
5554 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5556 err |= (fprintf(fp, "SSV:\n") < 0);
5558 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5560 err |= (fprintf(fp, "NSV:\n") < 0);
5561 err |= (fprintf(fp, "SSH:\n") < 0);
5562 /* write recepient list */
5563 if (compose->to_list) {
5564 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5565 for (cur = compose->to_list->next; cur != NULL;
5567 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5568 err |= (fprintf(fp, "\n") < 0);
5570 /* write newsgroup list */
5571 if (compose->newsgroup_list) {
5572 err |= (fprintf(fp, "NG:") < 0);
5573 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5574 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5575 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5576 err |= (fprintf(fp, "\n") < 0);
5578 /* Sylpheed account IDs */
5580 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5582 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5585 if (compose->privacy_system != NULL) {
5586 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5587 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5588 if (compose->use_encryption) {
5590 if (!compose_warn_encryption(compose)) {
5597 if (mailac && mailac->encrypt_to_self) {
5598 GSList *tmp_list = g_slist_copy(compose->to_list);
5599 tmp_list = g_slist_append(tmp_list, compose->account->address);
5600 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5601 g_slist_free(tmp_list);
5603 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5605 if (encdata != NULL) {
5606 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5607 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5608 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5610 } /* else we finally dont want to encrypt */
5612 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5613 /* and if encdata was null, it means there's been a problem in
5625 /* Save copy folder */
5626 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5627 gchar *savefolderid;
5629 savefolderid = compose_get_save_to(compose);
5630 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5631 g_free(savefolderid);
5633 /* Save copy folder */
5634 if (compose->return_receipt) {
5635 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5637 /* Message-ID of message replying to */
5638 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5641 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5642 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5645 /* Message-ID of message forwarding to */
5646 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5649 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5650 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5654 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5655 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5657 /* end of headers */
5658 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5660 if (compose->redirect_filename != NULL) {
5661 if (compose_redirect_write_to_file(compose, fp) < 0) {
5670 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5675 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5679 g_warning("failed to write queue message\n");
5686 if (fclose(fp) == EOF) {
5687 FILE_OP_ERROR(tmp, "fclose");
5694 if (item && *item) {
5697 queue = account_get_special_folder(compose->account, F_QUEUE);
5700 g_warning("can't find queue folder\n");
5706 folder_item_scan(queue);
5707 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5708 g_warning("can't queue the message\n");
5715 if (msgpath == NULL) {
5721 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5722 compose_remove_reedit_target(compose, FALSE);
5725 if ((msgnum != NULL) && (item != NULL)) {
5733 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5736 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5738 struct stat statbuf;
5739 gchar *type, *subtype;
5740 GtkTreeModel *model;
5743 model = gtk_tree_view_get_model(tree_view);
5745 if (!gtk_tree_model_get_iter_first(model, &iter))
5748 gtk_tree_model_get(model, &iter,
5752 mimepart = procmime_mimeinfo_new();
5753 mimepart->content = MIMECONTENT_FILE;
5754 mimepart->data.filename = g_strdup(ainfo->file);
5755 mimepart->tmp = FALSE; /* or we destroy our attachment */
5756 mimepart->offset = 0;
5758 g_stat(ainfo->file, &statbuf);
5759 mimepart->length = statbuf.st_size;
5761 type = g_strdup(ainfo->content_type);
5763 if (!strchr(type, '/')) {
5765 type = g_strdup("application/octet-stream");
5768 subtype = strchr(type, '/') + 1;
5769 *(subtype - 1) = '\0';
5770 mimepart->type = procmime_get_media_type(type);
5771 mimepart->subtype = g_strdup(subtype);
5774 if (mimepart->type == MIMETYPE_MESSAGE &&
5775 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5776 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5779 if (mimepart->type == MIMETYPE_APPLICATION &&
5780 !strcmp2(mimepart->subtype, "octet-stream"))
5781 g_hash_table_insert(mimepart->typeparameters,
5782 g_strdup("name"), g_strdup(ainfo->name));
5783 g_hash_table_insert(mimepart->dispositionparameters,
5784 g_strdup("filename"), g_strdup(ainfo->name));
5785 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5789 if (compose->use_signing) {
5790 if (ainfo->encoding == ENC_7BIT)
5791 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5792 else if (ainfo->encoding == ENC_8BIT)
5793 ainfo->encoding = ENC_BASE64;
5796 procmime_encode_content(mimepart, ainfo->encoding);
5798 g_node_append(parent->node, mimepart->node);
5799 } while (gtk_tree_model_iter_next(model, &iter));
5802 #define IS_IN_CUSTOM_HEADER(header) \
5803 (compose->account->add_customhdr && \
5804 custom_header_find(compose->account->customhdr_list, header) != NULL)
5806 static void compose_add_headerfield_from_headerlist(Compose *compose,
5808 const gchar *fieldname,
5809 const gchar *seperator)
5811 gchar *str, *fieldname_w_colon;
5812 gboolean add_field = FALSE;
5814 ComposeHeaderEntry *headerentry;
5815 const gchar *headerentryname;
5816 const gchar *trans_fieldname;
5819 if (IS_IN_CUSTOM_HEADER(fieldname))
5822 debug_print("Adding %s-fields\n", fieldname);
5824 fieldstr = g_string_sized_new(64);
5826 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5827 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5829 for (list = compose->header_list; list; list = list->next) {
5830 headerentry = ((ComposeHeaderEntry *)list->data);
5831 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5833 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5834 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5836 if (str[0] != '\0') {
5838 g_string_append(fieldstr, seperator);
5839 g_string_append(fieldstr, str);
5848 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5849 compose_convert_header
5850 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
5851 strlen(fieldname) + 2, TRUE);
5852 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5856 g_free(fieldname_w_colon);
5857 g_string_free(fieldstr, TRUE);
5862 static gchar *compose_get_header(Compose *compose)
5864 gchar buf[BUFFSIZE];
5865 const gchar *entry_str;
5869 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5871 gchar *from_name = NULL, *from_address = NULL;
5874 g_return_val_if_fail(compose->account != NULL, NULL);
5875 g_return_val_if_fail(compose->account->address != NULL, NULL);
5877 header = g_string_sized_new(64);
5880 get_rfc822_date(buf, sizeof(buf));
5881 g_string_append_printf(header, "Date: %s\n", buf);
5885 if (compose->account->name && *compose->account->name) {
5887 QUOTE_IF_REQUIRED(buf, compose->account->name);
5888 tmp = g_strdup_printf("%s <%s>",
5889 buf, compose->account->address);
5891 tmp = g_strdup_printf("%s",
5892 compose->account->address);
5894 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5895 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5897 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5898 from_address = g_strdup(compose->account->address);
5900 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5901 /* extract name and address */
5902 if (strstr(spec, " <") && strstr(spec, ">")) {
5903 from_address = g_strdup(strrchr(spec, '<')+1);
5904 *(strrchr(from_address, '>')) = '\0';
5905 from_name = g_strdup(spec);
5906 *(strrchr(from_name, '<')) = '\0';
5909 from_address = g_strdup(spec);
5916 if (from_name && *from_name) {
5917 compose_convert_header
5918 (compose, buf, sizeof(buf), from_name,
5919 strlen("From: "), TRUE);
5920 QUOTE_IF_REQUIRED(name, buf);
5922 g_string_append_printf(header, "From: %s <%s>\n",
5923 name, from_address);
5925 g_string_append_printf(header, "From: %s\n", from_address);
5928 g_free(from_address);
5931 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5934 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5937 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5940 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5943 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5945 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5948 compose_convert_header(compose, buf, sizeof(buf), str,
5949 strlen("Subject: "), FALSE);
5950 g_string_append_printf(header, "Subject: %s\n", buf);
5956 if (compose->account->set_domain && compose->account->domain) {
5957 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5958 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5959 g_snprintf(buf, sizeof(buf), "%s",
5960 strchr(compose->account->address, '@') ?
5961 strchr(compose->account->address, '@')+1 :
5962 compose->account->address);
5964 g_snprintf(buf, sizeof(buf), "%s", "");
5967 if (compose->account->gen_msgid) {
5968 generate_msgid(buf, sizeof(buf));
5969 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5970 compose->msgid = g_strdup(buf);
5972 compose->msgid = NULL;
5975 if (compose->remove_references == FALSE) {
5977 if (compose->inreplyto && compose->to_list)
5978 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5981 if (compose->references)
5982 g_string_append_printf(header, "References: %s\n", compose->references);
5986 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5989 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5992 if (compose->account->organization &&
5993 strlen(compose->account->organization) &&
5994 !IS_IN_CUSTOM_HEADER("Organization")) {
5995 compose_convert_header(compose, buf, sizeof(buf),
5996 compose->account->organization,
5997 strlen("Organization: "), FALSE);
5998 g_string_append_printf(header, "Organization: %s\n", buf);
6001 /* Program version and system info */
6002 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6003 !compose->newsgroup_list) {
6004 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6006 gtk_major_version, gtk_minor_version, gtk_micro_version,
6009 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6010 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6012 gtk_major_version, gtk_minor_version, gtk_micro_version,
6016 /* custom headers */
6017 if (compose->account->add_customhdr) {
6020 for (cur = compose->account->customhdr_list; cur != NULL;
6022 CustomHeader *chdr = (CustomHeader *)cur->data;
6024 if (custom_header_is_allowed(chdr->name)) {
6025 compose_convert_header
6026 (compose, buf, sizeof(buf),
6027 chdr->value ? chdr->value : "",
6028 strlen(chdr->name) + 2, FALSE);
6029 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6034 /* Automatic Faces and X-Faces */
6035 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6036 g_string_append_printf(header, "X-Face: %s\n", buf);
6038 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6039 g_string_append_printf(header, "X-Face: %s\n", buf);
6041 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6042 g_string_append_printf(header, "Face: %s\n", buf);
6044 else if (get_default_face (buf, sizeof(buf)) == 0) {
6045 g_string_append_printf(header, "Face: %s\n", buf);
6049 switch (compose->priority) {
6050 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6051 "X-Priority: 1 (Highest)\n");
6053 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6054 "X-Priority: 2 (High)\n");
6056 case PRIORITY_NORMAL: break;
6057 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6058 "X-Priority: 4 (Low)\n");
6060 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6061 "X-Priority: 5 (Lowest)\n");
6063 default: debug_print("compose: priority unknown : %d\n",
6067 /* Request Return Receipt */
6068 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6069 if (compose->return_receipt) {
6070 if (compose->account->name
6071 && *compose->account->name) {
6072 compose_convert_header(compose, buf, sizeof(buf),
6073 compose->account->name,
6074 strlen("Disposition-Notification-To: "),
6076 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6078 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6082 /* get special headers */
6083 for (list = compose->header_list; list; list = list->next) {
6084 ComposeHeaderEntry *headerentry;
6087 gchar *headername_wcolon;
6088 const gchar *headername_trans;
6091 gboolean standard_header = FALSE;
6093 headerentry = ((ComposeHeaderEntry *)list->data);
6095 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6097 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6102 if (!strstr(tmp, ":")) {
6103 headername_wcolon = g_strconcat(tmp, ":", NULL);
6104 headername = g_strdup(tmp);
6106 headername_wcolon = g_strdup(tmp);
6107 headername = g_strdup(strtok(tmp, ":"));
6111 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6112 Xstrdup_a(headervalue, entry_str, return NULL);
6113 subst_char(headervalue, '\r', ' ');
6114 subst_char(headervalue, '\n', ' ');
6115 string = std_headers;
6116 while (*string != NULL) {
6117 headername_trans = prefs_common_translated_header_name(*string);
6118 if (!strcmp(headername_trans, headername_wcolon))
6119 standard_header = TRUE;
6122 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6123 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6126 g_free(headername_wcolon);
6130 g_string_free(header, FALSE);
6135 #undef IS_IN_CUSTOM_HEADER
6137 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6138 gint header_len, gboolean addr_field)
6140 gchar *tmpstr = NULL;
6141 const gchar *out_codeset = NULL;
6143 g_return_if_fail(src != NULL);
6144 g_return_if_fail(dest != NULL);
6146 if (len < 1) return;
6148 tmpstr = g_strdup(src);
6150 subst_char(tmpstr, '\n', ' ');
6151 subst_char(tmpstr, '\r', ' ');
6154 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6155 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6156 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6161 codeconv_set_strict(TRUE);
6162 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6163 conv_get_charset_str(compose->out_encoding));
6164 codeconv_set_strict(FALSE);
6166 if (!dest || *dest == '\0') {
6167 gchar *test_conv_global_out = NULL;
6168 gchar *test_conv_reply = NULL;
6170 /* automatic mode. be automatic. */
6171 codeconv_set_strict(TRUE);
6173 out_codeset = conv_get_outgoing_charset_str();
6175 debug_print("trying to convert to %s\n", out_codeset);
6176 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6179 if (!test_conv_global_out && compose->orig_charset
6180 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6181 out_codeset = compose->orig_charset;
6182 debug_print("failure; trying to convert to %s\n", out_codeset);
6183 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6186 if (!test_conv_global_out && !test_conv_reply) {
6188 out_codeset = CS_INTERNAL;
6189 debug_print("finally using %s\n", out_codeset);
6191 g_free(test_conv_global_out);
6192 g_free(test_conv_reply);
6193 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6195 codeconv_set_strict(FALSE);
6200 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6204 g_return_if_fail(user_data != NULL);
6206 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6207 g_strstrip(address);
6208 if (*address != '\0') {
6209 gchar *name = procheader_get_fromname(address);
6210 extract_address(address);
6211 addressbook_add_contact(name, address, NULL, NULL);
6216 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6218 GtkWidget *menuitem;
6221 g_return_if_fail(menu != NULL);
6222 g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6224 menuitem = gtk_separator_menu_item_new();
6225 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6226 gtk_widget_show(menuitem);
6228 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6229 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6231 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6232 g_strstrip(address);
6233 if (*address == '\0') {
6234 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6237 g_signal_connect(G_OBJECT(menuitem), "activate",
6238 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6239 gtk_widget_show(menuitem);
6242 static void compose_create_header_entry(Compose *compose)
6244 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6249 const gchar *header = NULL;
6250 ComposeHeaderEntry *headerentry;
6251 gboolean standard_header = FALSE;
6252 #if !(GTK_CHECK_VERSION(2,12,0))
6253 GtkTooltips *tips = compose->tooltips;
6256 headerentry = g_new0(ComposeHeaderEntry, 1);
6259 combo = gtk_combo_box_entry_new_text();
6261 while(*string != NULL) {
6262 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6263 (gchar*)prefs_common_translated_header_name(*string));
6266 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6267 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6268 G_CALLBACK(compose_grab_focus_cb), compose);
6269 gtk_widget_show(combo);
6270 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6271 compose->header_nextrow, compose->header_nextrow+1,
6272 GTK_SHRINK, GTK_FILL, 0, 0);
6273 if (compose->header_last) {
6274 const gchar *last_header_entry = gtk_entry_get_text(
6275 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6277 while (*string != NULL) {
6278 if (!strcmp(*string, last_header_entry))
6279 standard_header = TRUE;
6282 if (standard_header)
6283 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6285 if (!compose->header_last || !standard_header) {
6286 switch(compose->account->protocol) {
6288 header = prefs_common_translated_header_name("Newsgroups:");
6291 header = prefs_common_translated_header_name("To:");
6296 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6298 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6299 G_CALLBACK(compose_grab_focus_cb), compose);
6302 entry = gtk_entry_new();
6303 gtk_widget_show(entry);
6304 CLAWS_SET_TIP(entry,
6305 _("Use <tab> to autocomplete from addressbook"));
6306 gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6307 compose->header_nextrow, compose->header_nextrow+1,
6308 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6310 g_signal_connect(G_OBJECT(entry), "key-press-event",
6311 G_CALLBACK(compose_headerentry_key_press_event_cb),
6313 g_signal_connect(G_OBJECT(entry), "changed",
6314 G_CALLBACK(compose_headerentry_changed_cb),
6316 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6317 G_CALLBACK(compose_grab_focus_cb), compose);
6320 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6321 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6322 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6323 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6324 G_CALLBACK(compose_header_drag_received_cb),
6326 g_signal_connect(G_OBJECT(entry), "drag-drop",
6327 G_CALLBACK(compose_drag_drop),
6329 g_signal_connect(G_OBJECT(entry), "populate-popup",
6330 G_CALLBACK(compose_entry_popup_extend),
6333 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6335 headerentry->compose = compose;
6336 headerentry->combo = combo;
6337 headerentry->entry = entry;
6338 headerentry->headernum = compose->header_nextrow;
6340 compose->header_nextrow++;
6341 compose->header_last = headerentry;
6342 compose->header_list =
6343 g_slist_append(compose->header_list,
6347 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text)
6349 ComposeHeaderEntry *last_header;
6351 last_header = compose->header_last;
6353 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6354 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6357 static void compose_remove_header_entries(Compose *compose)
6360 for (list = compose->header_list; list; list = list->next) {
6361 ComposeHeaderEntry *headerentry =
6362 (ComposeHeaderEntry *)list->data;
6363 gtk_widget_destroy(headerentry->combo);
6364 gtk_widget_destroy(headerentry->entry);
6365 g_free(headerentry);
6367 compose->header_last = NULL;
6368 g_slist_free(compose->header_list);
6369 compose->header_list = NULL;
6370 compose->header_nextrow = 1;
6371 compose_create_header_entry(compose);
6374 static GtkWidget *compose_create_header(Compose *compose)
6376 GtkWidget *from_optmenu_hbox;
6377 GtkWidget *header_scrolledwin;
6378 GtkWidget *header_table;
6382 /* header labels and entries */
6383 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6384 gtk_widget_show(header_scrolledwin);
6385 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6387 header_table = gtk_table_new(2, 2, FALSE);
6388 gtk_widget_show(header_table);
6389 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6390 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6391 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6394 /* option menu for selecting accounts */
6395 from_optmenu_hbox = compose_account_option_menu_create(compose);
6396 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6397 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6400 compose->header_table = header_table;
6401 compose->header_list = NULL;
6402 compose->header_nextrow = count;
6404 compose_create_header_entry(compose);
6406 compose->table = NULL;
6408 return header_scrolledwin ;
6411 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6413 Compose *compose = (Compose *)data;
6414 GdkEventButton event;
6417 event.time = gtk_get_current_event_time();
6419 return attach_button_pressed(compose->attach_clist, &event, compose);
6422 static GtkWidget *compose_create_attach(Compose *compose)
6424 GtkWidget *attach_scrwin;
6425 GtkWidget *attach_clist;
6427 GtkListStore *store;
6428 GtkCellRenderer *renderer;
6429 GtkTreeViewColumn *column;
6430 GtkTreeSelection *selection;
6432 /* attachment list */
6433 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6434 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6435 GTK_POLICY_AUTOMATIC,
6436 GTK_POLICY_AUTOMATIC);
6437 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6439 store = gtk_list_store_new(N_ATTACH_COLS,
6444 G_TYPE_AUTO_POINTER,
6446 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6447 (GTK_TREE_MODEL(store)));
6448 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6449 g_object_unref(store);
6451 renderer = gtk_cell_renderer_text_new();
6452 column = gtk_tree_view_column_new_with_attributes
6453 (_("Mime type"), renderer, "text",
6454 COL_MIMETYPE, NULL);
6455 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6457 renderer = gtk_cell_renderer_text_new();
6458 column = gtk_tree_view_column_new_with_attributes
6459 (_("Size"), renderer, "text",
6461 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6463 renderer = gtk_cell_renderer_text_new();
6464 column = gtk_tree_view_column_new_with_attributes
6465 (_("Name"), renderer, "text",
6467 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6469 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6470 prefs_common.use_stripes_everywhere);
6471 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6472 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6474 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6475 G_CALLBACK(attach_selected), compose);
6476 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6477 G_CALLBACK(attach_button_pressed), compose);
6479 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6480 G_CALLBACK(popup_attach_button_pressed), compose);
6482 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6483 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6484 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6485 G_CALLBACK(popup_attach_button_pressed), compose);
6487 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6488 G_CALLBACK(attach_key_pressed), compose);
6491 gtk_drag_dest_set(attach_clist,
6492 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6493 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6494 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6495 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6496 G_CALLBACK(compose_attach_drag_received_cb),
6498 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6499 G_CALLBACK(compose_drag_drop),
6502 compose->attach_scrwin = attach_scrwin;
6503 compose->attach_clist = attach_clist;
6505 return attach_scrwin;
6508 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6509 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6511 static GtkWidget *compose_create_others(Compose *compose)
6514 GtkWidget *savemsg_checkbtn;
6515 GtkWidget *savemsg_combo;
6516 GtkWidget *savemsg_select;
6519 gchar *folderidentifier;
6521 /* Table for settings */
6522 table = gtk_table_new(3, 1, FALSE);
6523 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6524 gtk_widget_show(table);
6525 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6528 /* Save Message to folder */
6529 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6530 gtk_widget_show(savemsg_checkbtn);
6531 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6532 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6533 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6535 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6536 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6538 savemsg_combo = gtk_combo_box_entry_new_text();
6539 compose->savemsg_checkbtn = savemsg_checkbtn;
6540 compose->savemsg_combo = savemsg_combo;
6541 gtk_widget_show(savemsg_combo);
6543 if (prefs_common.compose_save_to_history)
6544 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6545 prefs_common.compose_save_to_history);
6547 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6548 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6549 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6550 G_CALLBACK(compose_grab_focus_cb), compose);
6551 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6552 folderidentifier = folder_item_get_identifier(account_get_special_folder
6553 (compose->account, F_OUTBOX));
6554 compose_set_save_to(compose, folderidentifier);
6555 g_free(folderidentifier);
6558 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6559 gtk_widget_show(savemsg_select);
6560 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6561 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6562 G_CALLBACK(compose_savemsg_select_cb),
6570 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6572 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6573 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6576 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6581 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6584 path = folder_item_get_identifier(dest);
6586 compose_set_save_to(compose, path);
6590 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6591 GdkAtom clip, GtkTextIter *insert_place);
6594 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6598 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6600 if (event->button == 3) {
6602 GtkTextIter sel_start, sel_end;
6603 gboolean stuff_selected;
6605 /* move the cursor to allow GtkAspell to check the word
6606 * under the mouse */
6607 if (event->x && event->y) {
6608 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6609 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6611 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6614 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6615 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6618 stuff_selected = gtk_text_buffer_get_selection_bounds(
6620 &sel_start, &sel_end);
6622 gtk_text_buffer_place_cursor (buffer, &iter);
6623 /* reselect stuff */
6625 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6626 gtk_text_buffer_select_range(buffer,
6627 &sel_start, &sel_end);
6629 return FALSE; /* pass the event so that the right-click goes through */
6632 if (event->button == 2) {
6637 /* get the middle-click position to paste at the correct place */
6638 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6639 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6641 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6644 entry_paste_clipboard(compose, text,
6645 prefs_common.linewrap_pastes,
6646 GDK_SELECTION_PRIMARY, &iter);
6654 static void compose_spell_menu_changed(void *data)
6656 Compose *compose = (Compose *)data;
6658 GtkWidget *menuitem;
6659 GtkWidget *parent_item;
6660 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6663 if (compose->gtkaspell == NULL)
6666 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6667 "/Menu/Spelling/Options");
6669 /* setting the submenu removes /Spelling/Options from the factory
6670 * so we need to save it */
6672 if (parent_item == NULL) {
6673 parent_item = compose->aspell_options_menu;
6674 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6676 compose->aspell_options_menu = parent_item;
6678 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6680 spell_menu = g_slist_reverse(spell_menu);
6681 for (items = spell_menu;
6682 items; items = items->next) {
6683 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6684 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6685 gtk_widget_show(GTK_WIDGET(menuitem));
6687 g_slist_free(spell_menu);
6689 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6694 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6696 Compose *compose = (Compose *)data;
6697 GdkEventButton event;
6700 event.time = gtk_get_current_event_time();
6704 return text_clicked(compose->text, &event, compose);
6707 static gboolean compose_force_window_origin = TRUE;
6708 static Compose *compose_create(PrefsAccount *account,
6717 GtkWidget *handlebox;
6719 GtkWidget *notebook;
6721 GtkWidget *attach_hbox;
6722 GtkWidget *attach_lab1;
6723 GtkWidget *attach_lab2;
6728 GtkWidget *subject_hbox;
6729 GtkWidget *subject_frame;
6730 GtkWidget *subject_entry;
6734 GtkWidget *edit_vbox;
6735 GtkWidget *ruler_hbox;
6737 GtkWidget *scrolledwin;
6739 GtkTextBuffer *buffer;
6740 GtkClipboard *clipboard;
6743 UndoMain *undostruct;
6745 gchar *titles[N_ATTACH_COLS];
6746 GtkWidget *popupmenu;
6747 GtkWidget *tmpl_menu;
6748 GtkActionGroup *action_group = NULL;
6751 GtkAspell * gtkaspell = NULL;
6754 static GdkGeometry geometry;
6756 g_return_val_if_fail(account != NULL, NULL);
6758 debug_print("Creating compose window...\n");
6759 compose = g_new0(Compose, 1);
6761 titles[COL_MIMETYPE] = _("MIME type");
6762 titles[COL_SIZE] = _("Size");
6763 titles[COL_NAME] = _("Name");
6765 compose->batch = batch;
6766 compose->account = account;
6767 compose->folder = folder;
6769 compose->mutex = g_mutex_new();
6770 compose->set_cursor_pos = -1;
6772 #if !(GTK_CHECK_VERSION(2,12,0))
6773 compose->tooltips = tips;
6776 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6778 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6779 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6781 if (!geometry.max_width) {
6782 geometry.max_width = gdk_screen_width();
6783 geometry.max_height = gdk_screen_height();
6786 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6787 &geometry, GDK_HINT_MAX_SIZE);
6788 if (!geometry.min_width) {
6789 geometry.min_width = 600;
6790 geometry.min_height = 440;
6792 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6793 &geometry, GDK_HINT_MIN_SIZE);
6795 #ifndef GENERIC_UMPC
6796 if (compose_force_window_origin)
6797 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
6798 prefs_common.compose_y);
6800 g_signal_connect(G_OBJECT(window), "delete_event",
6801 G_CALLBACK(compose_delete_cb), compose);
6802 MANAGE_WINDOW_SIGNALS_CONNECT(window);
6803 gtk_widget_realize(window);
6805 gtkut_widget_set_composer_icon(window);
6807 vbox = gtk_vbox_new(FALSE, 0);
6808 gtk_container_add(GTK_CONTAINER(window), vbox);
6810 compose->ui_manager = gtk_ui_manager_new();
6811 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6812 G_N_ELEMENTS(compose_entries), (gpointer)compose);
6813 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6814 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6815 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6816 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6817 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6818 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6819 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6820 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6823 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6825 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
6828 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6829 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6831 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6833 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6834 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6835 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6838 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6839 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6840 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6841 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6842 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6843 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6844 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6845 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6846 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6847 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6850 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6851 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6852 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6854 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6855 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6856 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6858 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6859 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6860 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6861 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6863 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6865 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6866 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6867 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6868 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6869 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6870 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6871 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6872 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6873 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6874 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6875 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6876 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6877 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6878 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6879 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6881 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6883 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6884 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6885 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6886 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6887 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
6889 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6891 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6895 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6896 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6897 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6898 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6899 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6900 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6904 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6905 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6906 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6907 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6908 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6910 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6911 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6912 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6913 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6914 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6917 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6918 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6919 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6920 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6921 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6922 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6923 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6925 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6926 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6927 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6928 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6929 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6931 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6933 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6934 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6935 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6936 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6937 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6939 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6940 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)
6941 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)
6942 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6944 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6946 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6947 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)
6948 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)
6950 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6952 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6953 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)
6954 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6956 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6957 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)
6958 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6960 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6962 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6963 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)
6964 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6965 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6966 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6968 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6969 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)
6970 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)
6971 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6972 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6974 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6975 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6976 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6977 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6978 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6980 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6981 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6982 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)
6984 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6985 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6986 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6990 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6991 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6992 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6993 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6994 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6995 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6998 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7000 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7001 gtk_widget_show_all(menubar);
7003 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7005 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7007 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7010 if (prefs_common.toolbar_detachable) {
7011 handlebox = gtk_handle_box_new();
7013 handlebox = gtk_hbox_new(FALSE, 0);
7015 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7017 gtk_widget_realize(handlebox);
7019 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7022 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7026 vbox2 = gtk_vbox_new(FALSE, 2);
7027 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7028 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7031 notebook = gtk_notebook_new();
7032 gtk_widget_set_size_request(notebook, -1, 130);
7033 gtk_widget_show(notebook);
7035 /* header labels and entries */
7036 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7037 compose_create_header(compose),
7038 gtk_label_new_with_mnemonic(_("Hea_der")));
7039 /* attachment list */
7040 attach_hbox = gtk_hbox_new(FALSE, 0);
7041 gtk_widget_show(attach_hbox);
7043 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7044 gtk_widget_show(attach_lab1);
7045 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7047 attach_lab2 = gtk_label_new("");
7048 gtk_widget_show(attach_lab2);
7049 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7051 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7052 compose_create_attach(compose),
7055 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7056 compose_create_others(compose),
7057 gtk_label_new_with_mnemonic(_("Othe_rs")));
7060 subject_hbox = gtk_hbox_new(FALSE, 0);
7061 gtk_widget_show(subject_hbox);
7063 subject_frame = gtk_frame_new(NULL);
7064 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7065 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7066 gtk_widget_show(subject_frame);
7068 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7069 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7070 gtk_widget_show(subject);
7072 label = gtk_label_new(_("Subject:"));
7073 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7074 gtk_widget_show(label);
7076 subject_entry = gtk_entry_new();
7077 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7078 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7079 G_CALLBACK(compose_grab_focus_cb), compose);
7080 gtk_widget_show(subject_entry);
7081 compose->subject_entry = subject_entry;
7082 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7084 edit_vbox = gtk_vbox_new(FALSE, 0);
7086 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7089 ruler_hbox = gtk_hbox_new(FALSE, 0);
7090 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7092 ruler = gtk_shruler_new();
7093 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7094 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7098 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7099 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7100 GTK_POLICY_AUTOMATIC,
7101 GTK_POLICY_AUTOMATIC);
7102 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7104 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7105 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7107 text = gtk_text_view_new();
7108 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7109 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7110 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7111 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7112 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7114 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7116 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7117 G_CALLBACK(compose_edit_size_alloc),
7119 g_signal_connect(G_OBJECT(buffer), "changed",
7120 G_CALLBACK(compose_changed_cb), compose);
7121 g_signal_connect(G_OBJECT(text), "grab_focus",
7122 G_CALLBACK(compose_grab_focus_cb), compose);
7123 g_signal_connect(G_OBJECT(buffer), "insert_text",
7124 G_CALLBACK(text_inserted), compose);
7125 g_signal_connect(G_OBJECT(text), "button_press_event",
7126 G_CALLBACK(text_clicked), compose);
7128 g_signal_connect(G_OBJECT(text), "popup-menu",
7129 G_CALLBACK(compose_popup_menu), compose);
7131 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7132 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7133 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7134 G_CALLBACK(compose_popup_menu), compose);
7136 g_signal_connect(G_OBJECT(subject_entry), "changed",
7137 G_CALLBACK(compose_changed_cb), compose);
7140 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7141 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7142 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7143 g_signal_connect(G_OBJECT(text), "drag_data_received",
7144 G_CALLBACK(compose_insert_drag_received_cb),
7146 g_signal_connect(G_OBJECT(text), "drag-drop",
7147 G_CALLBACK(compose_drag_drop),
7149 gtk_widget_show_all(vbox);
7151 /* pane between attach clist and text */
7152 paned = gtk_vpaned_new();
7153 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7155 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7156 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7158 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7160 gtk_paned_add1(GTK_PANED(paned), notebook);
7161 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7162 gtk_widget_show_all(paned);
7165 if (prefs_common.textfont) {
7166 PangoFontDescription *font_desc;
7168 font_desc = pango_font_description_from_string
7169 (prefs_common.textfont);
7171 gtk_widget_modify_font(text, font_desc);
7172 pango_font_description_free(font_desc);
7176 gtk_action_group_add_actions(action_group, compose_popup_entries,
7177 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7179 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7185 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7187 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7188 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7189 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7191 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7193 undostruct = undo_init(text);
7194 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7197 address_completion_start(window);
7199 compose->window = window;
7200 compose->vbox = vbox;
7201 compose->menubar = menubar;
7202 compose->handlebox = handlebox;
7204 compose->vbox2 = vbox2;
7206 compose->paned = paned;
7208 compose->attach_label = attach_lab2;
7210 compose->notebook = notebook;
7211 compose->edit_vbox = edit_vbox;
7212 compose->ruler_hbox = ruler_hbox;
7213 compose->ruler = ruler;
7214 compose->scrolledwin = scrolledwin;
7215 compose->text = text;
7217 compose->focused_editable = NULL;
7219 compose->popupmenu = popupmenu;
7221 compose->tmpl_menu = tmpl_menu;
7223 compose->mode = mode;
7224 compose->rmode = mode;
7226 compose->targetinfo = NULL;
7227 compose->replyinfo = NULL;
7228 compose->fwdinfo = NULL;
7230 compose->replyto = NULL;
7232 compose->bcc = NULL;
7233 compose->followup_to = NULL;
7235 compose->ml_post = NULL;
7237 compose->inreplyto = NULL;
7238 compose->references = NULL;
7239 compose->msgid = NULL;
7240 compose->boundary = NULL;
7242 compose->autowrap = prefs_common.autowrap;
7243 compose->autoindent = prefs_common.auto_indent;
7244 compose->use_signing = FALSE;
7245 compose->use_encryption = FALSE;
7246 compose->privacy_system = NULL;
7248 compose->modified = FALSE;
7250 compose->return_receipt = FALSE;
7252 compose->to_list = NULL;
7253 compose->newsgroup_list = NULL;
7255 compose->undostruct = undostruct;
7257 compose->sig_str = NULL;
7259 compose->exteditor_file = NULL;
7260 compose->exteditor_pid = -1;
7261 compose->exteditor_tag = -1;
7262 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7266 if (mode != COMPOSE_REDIRECT) {
7267 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7268 strcmp(prefs_common.dictionary, "")) {
7269 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7270 prefs_common.alt_dictionary,
7271 conv_get_locale_charset_str(),
7272 prefs_common.misspelled_col,
7273 prefs_common.check_while_typing,
7274 prefs_common.recheck_when_changing_dict,
7275 prefs_common.use_alternate,
7276 prefs_common.use_both_dicts,
7277 GTK_TEXT_VIEW(text),
7278 GTK_WINDOW(compose->window),
7279 compose_spell_menu_changed,
7282 alertpanel_error(_("Spell checker could not "
7284 gtkaspell_checkers_strerror());
7285 gtkaspell_checkers_reset_error();
7287 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7291 compose->gtkaspell = gtkaspell;
7292 compose_spell_menu_changed(compose);
7295 compose_select_account(compose, account, TRUE);
7297 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7298 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7300 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7301 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7303 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7304 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7306 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7307 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7309 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7310 if (account->protocol != A_NNTP)
7311 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7312 prefs_common_translated_header_name("To:"));
7314 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7315 prefs_common_translated_header_name("Newsgroups:"));
7317 addressbook_set_target_compose(compose);
7319 if (mode != COMPOSE_REDIRECT)
7320 compose_set_template_menu(compose);
7322 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7325 compose_list = g_list_append(compose_list, compose);
7327 if (!prefs_common.show_ruler)
7328 gtk_widget_hide(ruler_hbox);
7330 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7333 compose->priority = PRIORITY_NORMAL;
7334 compose_update_priority_menu_item(compose);
7336 compose_set_out_encoding(compose);
7339 compose_update_actions_menu(compose);
7341 /* Privacy Systems menu */
7342 compose_update_privacy_systems_menu(compose);
7344 activate_privacy_system(compose, account, TRUE);
7345 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7347 gtk_widget_realize(window);
7349 gtk_widget_show(window);
7351 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7352 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7359 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7364 GtkWidget *optmenubox;
7367 GtkWidget *from_name = NULL;
7368 #if !(GTK_CHECK_VERSION(2,12,0))
7369 GtkTooltips *tips = compose->tooltips;
7372 gint num = 0, def_menu = 0;
7374 accounts = account_get_list();
7375 g_return_val_if_fail(accounts != NULL, NULL);
7377 optmenubox = gtk_event_box_new();
7378 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7379 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7381 hbox = gtk_hbox_new(FALSE, 6);
7382 from_name = gtk_entry_new();
7384 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7385 G_CALLBACK(compose_grab_focus_cb), compose);
7387 for (; accounts != NULL; accounts = accounts->next, num++) {
7388 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7389 gchar *name, *from = NULL;
7391 if (ac == compose->account) def_menu = num;
7393 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7396 if (ac == compose->account) {
7397 if (ac->name && *ac->name) {
7399 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7400 from = g_strdup_printf("%s <%s>",
7402 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7404 from = g_strdup_printf("%s",
7406 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7409 COMBOBOX_ADD(menu, name, ac->account_id);
7414 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7416 g_signal_connect(G_OBJECT(optmenu), "changed",
7417 G_CALLBACK(account_activated),
7419 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7420 G_CALLBACK(compose_entry_popup_extend),
7423 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7424 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7426 CLAWS_SET_TIP(optmenubox,
7427 _("Account to use for this email"));
7428 CLAWS_SET_TIP(from_name,
7429 _("Sender address to be used"));
7431 compose->from_name = from_name;
7436 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7438 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7439 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7440 Compose *compose = (Compose *) data;
7442 compose->priority = value;
7446 static void compose_reply_change_mode(Compose *compose,
7449 gboolean was_modified = compose->modified;
7451 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7453 g_return_if_fail(compose->replyinfo != NULL);
7455 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7457 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7459 if (action == COMPOSE_REPLY_TO_ALL)
7461 if (action == COMPOSE_REPLY_TO_SENDER)
7463 if (action == COMPOSE_REPLY_TO_LIST)
7466 compose_remove_header_entries(compose);
7467 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7468 if (compose->account->set_autocc && compose->account->auto_cc)
7469 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7471 if (compose->account->set_autobcc && compose->account->auto_bcc)
7472 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7474 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7475 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7476 compose_show_first_last_header(compose, TRUE);
7477 compose->modified = was_modified;
7478 compose_set_title(compose);
7481 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7483 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7484 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7485 Compose *compose = (Compose *) data;
7488 compose_reply_change_mode(compose, value);
7491 static void compose_update_priority_menu_item(Compose * compose)
7493 GtkWidget *menuitem = NULL;
7494 switch (compose->priority) {
7495 case PRIORITY_HIGHEST:
7496 menuitem = gtk_ui_manager_get_widget
7497 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7500 menuitem = gtk_ui_manager_get_widget
7501 (compose->ui_manager, "/Menu/Options/Priority/High");
7503 case PRIORITY_NORMAL:
7504 menuitem = gtk_ui_manager_get_widget
7505 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7508 menuitem = gtk_ui_manager_get_widget
7509 (compose->ui_manager, "/Menu/Options/Priority/Low");
7511 case PRIORITY_LOWEST:
7512 menuitem = gtk_ui_manager_get_widget
7513 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7516 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7519 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7521 Compose *compose = (Compose *) data;
7523 gboolean can_sign = FALSE, can_encrypt = FALSE;
7525 g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7527 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7530 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7531 g_free(compose->privacy_system);
7532 compose->privacy_system = NULL;
7533 if (systemid != NULL) {
7534 compose->privacy_system = g_strdup(systemid);
7536 can_sign = privacy_system_can_sign(systemid);
7537 can_encrypt = privacy_system_can_encrypt(systemid);
7540 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7542 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7543 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7546 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7548 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7549 GtkWidget *menuitem = NULL;
7551 gboolean can_sign = FALSE, can_encrypt = FALSE;
7552 gboolean found = FALSE;
7554 if (compose->privacy_system != NULL) {
7556 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7557 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7558 g_return_if_fail(menuitem != NULL);
7560 amenu = GTK_MENU_SHELL(menuitem)->children;
7562 while (amenu != NULL) {
7563 GList *alist = amenu->next;
7565 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7566 if (systemid != NULL) {
7567 if (strcmp(systemid, compose->privacy_system) == 0 &&
7568 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7569 menuitem = GTK_WIDGET(amenu->data);
7571 can_sign = privacy_system_can_sign(systemid);
7572 can_encrypt = privacy_system_can_encrypt(systemid);
7576 } else if (strlen(compose->privacy_system) == 0 &&
7577 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7578 menuitem = GTK_WIDGET(amenu->data);
7581 can_encrypt = FALSE;
7588 if (menuitem != NULL)
7589 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7591 if (warn && !found && strlen(compose->privacy_system)) {
7592 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7593 "will not be able to sign or encrypt this message."),
7594 compose->privacy_system);
7598 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7599 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7602 static void compose_set_out_encoding(Compose *compose)
7604 CharSet out_encoding;
7605 const gchar *branch = NULL;
7606 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7608 switch(out_encoding) {
7609 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7610 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7611 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7612 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7613 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7614 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7615 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7616 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7617 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7618 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7619 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7620 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7621 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7622 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7623 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7624 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7625 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7626 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7627 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7628 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7629 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7630 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7631 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7632 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7633 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7634 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7635 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7636 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7637 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7638 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7639 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7640 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7642 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7645 static void compose_set_template_menu(Compose *compose)
7647 GSList *tmpl_list, *cur;
7651 tmpl_list = template_get_config();
7653 menu = gtk_menu_new();
7655 gtk_menu_set_accel_group (GTK_MENU (menu),
7656 gtk_ui_manager_get_accel_group(compose->ui_manager));
7657 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7658 Template *tmpl = (Template *)cur->data;
7659 gchar *accel_path = NULL;
7660 item = gtk_menu_item_new_with_label(tmpl->name);
7661 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7662 g_signal_connect(G_OBJECT(item), "activate",
7663 G_CALLBACK(compose_template_activate_cb),
7665 g_object_set_data(G_OBJECT(item), "template", tmpl);
7666 gtk_widget_show(item);
7667 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7668 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7672 gtk_widget_show(menu);
7673 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7676 void compose_update_actions_menu(Compose *compose)
7678 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7681 static void compose_update_privacy_systems_menu(Compose *compose)
7683 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7684 GSList *systems, *cur;
7686 GtkWidget *system_none;
7688 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7689 GtkWidget *privacy_menu = gtk_menu_new();
7691 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7692 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7694 g_signal_connect(G_OBJECT(system_none), "activate",
7695 G_CALLBACK(compose_set_privacy_system_cb), compose);
7697 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7698 gtk_widget_show(system_none);
7700 systems = privacy_get_system_ids();
7701 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7702 gchar *systemid = cur->data;
7704 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7705 widget = gtk_radio_menu_item_new_with_label(group,
7706 privacy_system_get_name(systemid));
7707 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7708 g_strdup(systemid), g_free);
7709 g_signal_connect(G_OBJECT(widget), "activate",
7710 G_CALLBACK(compose_set_privacy_system_cb), compose);
7712 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7713 gtk_widget_show(widget);
7716 g_slist_free(systems);
7717 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7718 gtk_widget_show_all(privacy_menu);
7719 gtk_widget_show_all(privacy_menuitem);
7722 void compose_reflect_prefs_all(void)
7727 for (cur = compose_list; cur != NULL; cur = cur->next) {
7728 compose = (Compose *)cur->data;
7729 compose_set_template_menu(compose);
7733 void compose_reflect_prefs_pixmap_theme(void)
7738 for (cur = compose_list; cur != NULL; cur = cur->next) {
7739 compose = (Compose *)cur->data;
7740 toolbar_update(TOOLBAR_COMPOSE, compose);
7744 static const gchar *compose_quote_char_from_context(Compose *compose)
7746 const gchar *qmark = NULL;
7748 g_return_val_if_fail(compose != NULL, NULL);
7750 switch (compose->mode) {
7751 /* use forward-specific quote char */
7752 case COMPOSE_FORWARD:
7753 case COMPOSE_FORWARD_AS_ATTACH:
7754 case COMPOSE_FORWARD_INLINE:
7755 if (compose->folder && compose->folder->prefs &&
7756 compose->folder->prefs->forward_with_format)
7757 qmark = compose->folder->prefs->forward_quotemark;
7758 else if (compose->account->forward_with_format)
7759 qmark = compose->account->forward_quotemark;
7761 qmark = prefs_common.fw_quotemark;
7764 /* use reply-specific quote char in all other modes */
7766 if (compose->folder && compose->folder->prefs &&
7767 compose->folder->prefs->reply_with_format)
7768 qmark = compose->folder->prefs->reply_quotemark;
7769 else if (compose->account->reply_with_format)
7770 qmark = compose->account->reply_quotemark;
7772 qmark = prefs_common.quotemark;
7776 if (qmark == NULL || *qmark == '\0')
7782 static void compose_template_apply(Compose *compose, Template *tmpl,
7786 GtkTextBuffer *buffer;
7790 gchar *parsed_str = NULL;
7791 gint cursor_pos = 0;
7792 const gchar *err_msg = _("Template body format error at line %d.");
7795 /* process the body */
7797 text = GTK_TEXT_VIEW(compose->text);
7798 buffer = gtk_text_view_get_buffer(text);
7801 qmark = compose_quote_char_from_context(compose);
7803 if (compose->replyinfo != NULL) {
7806 gtk_text_buffer_set_text(buffer, "", -1);
7807 mark = gtk_text_buffer_get_insert(buffer);
7808 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7810 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7811 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7813 } else if (compose->fwdinfo != NULL) {
7816 gtk_text_buffer_set_text(buffer, "", -1);
7817 mark = gtk_text_buffer_get_insert(buffer);
7818 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7820 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7821 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7824 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7826 GtkTextIter start, end;
7829 gtk_text_buffer_get_start_iter(buffer, &start);
7830 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7831 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7833 /* clear the buffer now */
7835 gtk_text_buffer_set_text(buffer, "", -1);
7837 parsed_str = compose_quote_fmt(compose, dummyinfo,
7838 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7839 procmsg_msginfo_free( dummyinfo );
7845 gtk_text_buffer_set_text(buffer, "", -1);
7846 mark = gtk_text_buffer_get_insert(buffer);
7847 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7850 if (replace && parsed_str && compose->account->auto_sig)
7851 compose_insert_sig(compose, FALSE);
7853 if (replace && parsed_str) {
7854 gtk_text_buffer_get_start_iter(buffer, &iter);
7855 gtk_text_buffer_place_cursor(buffer, &iter);
7859 cursor_pos = quote_fmt_get_cursor_pos();
7860 compose->set_cursor_pos = cursor_pos;
7861 if (cursor_pos == -1)
7863 gtk_text_buffer_get_start_iter(buffer, &iter);
7864 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7865 gtk_text_buffer_place_cursor(buffer, &iter);
7868 /* process the other fields */
7870 compose_template_apply_fields(compose, tmpl);
7871 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7872 quote_fmt_reset_vartable();
7873 compose_changed_cb(NULL, compose);
7876 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7878 MsgInfo* dummyinfo = NULL;
7879 MsgInfo *msginfo = NULL;
7882 if (compose->replyinfo != NULL)
7883 msginfo = compose->replyinfo;
7884 else if (compose->fwdinfo != NULL)
7885 msginfo = compose->fwdinfo;
7887 dummyinfo = compose_msginfo_new_from_compose(compose);
7888 msginfo = dummyinfo;
7891 if (tmpl->from && *tmpl->from != '\0') {
7893 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7894 compose->gtkaspell);
7896 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7898 quote_fmt_scan_string(tmpl->from);
7901 buf = quote_fmt_get_buffer();
7903 alertpanel_error(_("Template From format error."));
7905 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7909 if (tmpl->to && *tmpl->to != '\0') {
7911 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7912 compose->gtkaspell);
7914 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7916 quote_fmt_scan_string(tmpl->to);
7919 buf = quote_fmt_get_buffer();
7921 alertpanel_error(_("Template To format error."));
7923 compose_entry_append(compose, buf, COMPOSE_TO);
7927 if (tmpl->cc && *tmpl->cc != '\0') {
7929 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7930 compose->gtkaspell);
7932 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7934 quote_fmt_scan_string(tmpl->cc);
7937 buf = quote_fmt_get_buffer();
7939 alertpanel_error(_("Template Cc format error."));
7941 compose_entry_append(compose, buf, COMPOSE_CC);
7945 if (tmpl->bcc && *tmpl->bcc != '\0') {
7947 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7948 compose->gtkaspell);
7950 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7952 quote_fmt_scan_string(tmpl->bcc);
7955 buf = quote_fmt_get_buffer();
7957 alertpanel_error(_("Template Bcc format error."));
7959 compose_entry_append(compose, buf, COMPOSE_BCC);
7963 /* process the subject */
7964 if (tmpl->subject && *tmpl->subject != '\0') {
7966 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7967 compose->gtkaspell);
7969 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7971 quote_fmt_scan_string(tmpl->subject);
7974 buf = quote_fmt_get_buffer();
7976 alertpanel_error(_("Template subject format error."));
7978 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7982 procmsg_msginfo_free( dummyinfo );
7985 static void compose_destroy(Compose *compose)
7987 GtkTextBuffer *buffer;
7988 GtkClipboard *clipboard;
7990 compose_list = g_list_remove(compose_list, compose);
7992 if (compose->updating) {
7993 debug_print("danger, not destroying anything now\n");
7994 compose->deferred_destroy = TRUE;
7997 /* NOTE: address_completion_end() does nothing with the window
7998 * however this may change. */
7999 address_completion_end(compose->window);
8001 slist_free_strings(compose->to_list);
8002 g_slist_free(compose->to_list);
8003 slist_free_strings(compose->newsgroup_list);
8004 g_slist_free(compose->newsgroup_list);
8005 slist_free_strings(compose->header_list);
8006 g_slist_free(compose->header_list);
8008 procmsg_msginfo_free(compose->targetinfo);
8009 procmsg_msginfo_free(compose->replyinfo);
8010 procmsg_msginfo_free(compose->fwdinfo);
8012 g_free(compose->replyto);
8013 g_free(compose->cc);
8014 g_free(compose->bcc);
8015 g_free(compose->newsgroups);
8016 g_free(compose->followup_to);
8018 g_free(compose->ml_post);
8020 g_free(compose->inreplyto);
8021 g_free(compose->references);
8022 g_free(compose->msgid);
8023 g_free(compose->boundary);
8025 g_free(compose->redirect_filename);
8026 if (compose->undostruct)
8027 undo_destroy(compose->undostruct);
8029 g_free(compose->sig_str);
8031 g_free(compose->exteditor_file);
8033 g_free(compose->orig_charset);
8035 g_free(compose->privacy_system);
8037 if (addressbook_get_target_compose() == compose)
8038 addressbook_set_target_compose(NULL);
8041 if (compose->gtkaspell) {
8042 gtkaspell_delete(compose->gtkaspell);
8043 compose->gtkaspell = NULL;
8047 if (!compose->batch) {
8048 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8049 prefs_common.compose_height = compose->window->allocation.height;
8052 if (!gtk_widget_get_parent(compose->paned))
8053 gtk_widget_destroy(compose->paned);
8054 gtk_widget_destroy(compose->popupmenu);
8056 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8057 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8058 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8060 gtk_widget_destroy(compose->window);
8061 toolbar_destroy(compose->toolbar);
8062 g_free(compose->toolbar);
8063 g_mutex_free(compose->mutex);
8067 static void compose_attach_info_free(AttachInfo *ainfo)
8069 g_free(ainfo->file);
8070 g_free(ainfo->content_type);
8071 g_free(ainfo->name);
8075 static void compose_attach_update_label(Compose *compose)
8080 GtkTreeModel *model;
8085 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8086 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8087 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8091 while(gtk_tree_model_iter_next(model, &iter))
8094 text = g_strdup_printf("(%d)", i);
8095 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8099 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8101 Compose *compose = (Compose *)data;
8102 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8103 GtkTreeSelection *selection;
8105 GtkTreeModel *model;
8107 selection = gtk_tree_view_get_selection(tree_view);
8108 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8113 for (cur = sel; cur != NULL; cur = cur->next) {
8114 GtkTreePath *path = cur->data;
8115 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8118 gtk_tree_path_free(path);
8121 for (cur = sel; cur != NULL; cur = cur->next) {
8122 GtkTreeRowReference *ref = cur->data;
8123 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8126 if (gtk_tree_model_get_iter(model, &iter, path))
8127 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8129 gtk_tree_path_free(path);
8130 gtk_tree_row_reference_free(ref);
8134 compose_attach_update_label(compose);
8137 static struct _AttachProperty
8140 GtkWidget *mimetype_entry;
8141 GtkWidget *encoding_optmenu;
8142 GtkWidget *path_entry;
8143 GtkWidget *filename_entry;
8145 GtkWidget *cancel_btn;
8148 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8150 gtk_tree_path_free((GtkTreePath *)ptr);
8153 static void compose_attach_property(GtkAction *action, gpointer data)
8155 Compose *compose = (Compose *)data;
8156 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8158 GtkComboBox *optmenu;
8159 GtkTreeSelection *selection;
8161 GtkTreeModel *model;
8164 static gboolean cancelled;
8166 /* only if one selected */
8167 selection = gtk_tree_view_get_selection(tree_view);
8168 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8171 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8175 path = (GtkTreePath *) sel->data;
8176 gtk_tree_model_get_iter(model, &iter, path);
8177 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8180 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8186 if (!attach_prop.window)
8187 compose_attach_property_create(&cancelled);
8188 gtk_widget_grab_focus(attach_prop.ok_btn);
8189 gtk_widget_show(attach_prop.window);
8190 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8192 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8193 if (ainfo->encoding == ENC_UNKNOWN)
8194 combobox_select_by_data(optmenu, ENC_BASE64);
8196 combobox_select_by_data(optmenu, ainfo->encoding);
8198 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8199 ainfo->content_type ? ainfo->content_type : "");
8200 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8201 ainfo->file ? ainfo->file : "");
8202 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8203 ainfo->name ? ainfo->name : "");
8206 const gchar *entry_text;
8208 gchar *cnttype = NULL;
8215 gtk_widget_hide(attach_prop.window);
8220 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8221 if (*entry_text != '\0') {
8224 text = g_strstrip(g_strdup(entry_text));
8225 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8226 cnttype = g_strdup(text);
8229 alertpanel_error(_("Invalid MIME type."));
8235 ainfo->encoding = combobox_get_active_data(optmenu);
8237 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8238 if (*entry_text != '\0') {
8239 if (is_file_exist(entry_text) &&
8240 (size = get_file_size(entry_text)) > 0)
8241 file = g_strdup(entry_text);
8244 (_("File doesn't exist or is empty."));
8250 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8251 if (*entry_text != '\0') {
8252 g_free(ainfo->name);
8253 ainfo->name = g_strdup(entry_text);
8257 g_free(ainfo->content_type);
8258 ainfo->content_type = cnttype;
8261 g_free(ainfo->file);
8265 ainfo->size = (goffset)size;
8267 /* update tree store */
8268 text = to_human_readable(ainfo->size);
8269 gtk_tree_model_get_iter(model, &iter, path);
8270 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8271 COL_MIMETYPE, ainfo->content_type,
8273 COL_NAME, ainfo->name,
8279 gtk_tree_path_free(path);
8282 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8284 label = gtk_label_new(str); \
8285 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8286 GTK_FILL, 0, 0, 0); \
8287 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8289 entry = gtk_entry_new(); \
8290 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8291 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8294 static void compose_attach_property_create(gboolean *cancelled)
8300 GtkWidget *mimetype_entry;
8303 GtkListStore *optmenu_menu;
8304 GtkWidget *path_entry;
8305 GtkWidget *filename_entry;
8308 GtkWidget *cancel_btn;
8309 GList *mime_type_list, *strlist;
8312 debug_print("Creating attach_property window...\n");
8314 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8315 gtk_widget_set_size_request(window, 480, -1);
8316 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8317 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8318 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8319 gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8320 g_signal_connect(G_OBJECT(window), "delete_event",
8321 G_CALLBACK(attach_property_delete_event),
8323 g_signal_connect(G_OBJECT(window), "key_press_event",
8324 G_CALLBACK(attach_property_key_pressed),
8327 vbox = gtk_vbox_new(FALSE, 8);
8328 gtk_container_add(GTK_CONTAINER(window), vbox);
8330 table = gtk_table_new(4, 2, FALSE);
8331 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8332 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8333 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8335 label = gtk_label_new(_("MIME type"));
8336 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8338 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8339 mimetype_entry = gtk_combo_box_entry_new_text();
8340 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8341 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8343 /* stuff with list */
8344 mime_type_list = procmime_get_mime_type_list();
8346 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8347 MimeType *type = (MimeType *) mime_type_list->data;
8350 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8352 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8355 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8356 (GCompareFunc)strcmp2);
8359 for (mime_type_list = strlist; mime_type_list != NULL;
8360 mime_type_list = mime_type_list->next) {
8361 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8362 g_free(mime_type_list->data);
8364 g_list_free(strlist);
8365 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8366 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8368 label = gtk_label_new(_("Encoding"));
8369 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8371 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8373 hbox = gtk_hbox_new(FALSE, 0);
8374 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8375 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8377 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8378 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8380 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8381 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8382 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8383 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8384 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8386 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8388 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8389 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8391 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8392 &ok_btn, GTK_STOCK_OK,
8394 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8395 gtk_widget_grab_default(ok_btn);
8397 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8398 G_CALLBACK(attach_property_ok),
8400 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8401 G_CALLBACK(attach_property_cancel),
8404 gtk_widget_show_all(vbox);
8406 attach_prop.window = window;
8407 attach_prop.mimetype_entry = mimetype_entry;
8408 attach_prop.encoding_optmenu = optmenu;
8409 attach_prop.path_entry = path_entry;
8410 attach_prop.filename_entry = filename_entry;
8411 attach_prop.ok_btn = ok_btn;
8412 attach_prop.cancel_btn = cancel_btn;
8415 #undef SET_LABEL_AND_ENTRY
8417 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8423 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8429 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8430 gboolean *cancelled)
8438 static gboolean attach_property_key_pressed(GtkWidget *widget,
8440 gboolean *cancelled)
8442 if (event && event->keyval == GDK_Escape) {
8446 if (event && event->keyval == GDK_Return) {
8453 static void compose_exec_ext_editor(Compose *compose)
8460 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8461 G_DIR_SEPARATOR, compose);
8463 if (pipe(pipe_fds) < 0) {
8469 if ((pid = fork()) < 0) {
8476 /* close the write side of the pipe */
8479 compose->exteditor_file = g_strdup(tmp);
8480 compose->exteditor_pid = pid;
8482 compose_set_ext_editor_sensitive(compose, FALSE);
8485 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8487 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8489 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8493 } else { /* process-monitoring process */
8499 /* close the read side of the pipe */
8502 if (compose_write_body_to_file(compose, tmp) < 0) {
8503 fd_write_all(pipe_fds[1], "2\n", 2);
8507 pid_ed = compose_exec_ext_editor_real(tmp);
8509 fd_write_all(pipe_fds[1], "1\n", 2);
8513 /* wait until editor is terminated */
8514 waitpid(pid_ed, NULL, 0);
8516 fd_write_all(pipe_fds[1], "0\n", 2);
8523 #endif /* G_OS_UNIX */
8527 static gint compose_exec_ext_editor_real(const gchar *file)
8534 g_return_val_if_fail(file != NULL, -1);
8536 if ((pid = fork()) < 0) {
8541 if (pid != 0) return pid;
8543 /* grandchild process */
8545 if (setpgid(0, getppid()))
8548 if (prefs_common_get_ext_editor_cmd() &&
8549 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8550 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8551 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8553 if (prefs_common_get_ext_editor_cmd())
8554 g_warning("External editor command-line is invalid: '%s'\n",
8555 prefs_common_get_ext_editor_cmd());
8556 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8559 cmdline = strsplit_with_quote(buf, " ", 1024);
8560 execvp(cmdline[0], cmdline);
8563 g_strfreev(cmdline);
8568 static gboolean compose_ext_editor_kill(Compose *compose)
8570 pid_t pgid = compose->exteditor_pid * -1;
8573 ret = kill(pgid, 0);
8575 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8579 msg = g_strdup_printf
8580 (_("The external editor is still working.\n"
8581 "Force terminating the process?\n"
8582 "process group id: %d"), -pgid);
8583 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8584 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8588 if (val == G_ALERTALTERNATE) {
8589 g_source_remove(compose->exteditor_tag);
8590 g_io_channel_shutdown(compose->exteditor_ch,
8592 g_io_channel_unref(compose->exteditor_ch);
8594 if (kill(pgid, SIGTERM) < 0) perror("kill");
8595 waitpid(compose->exteditor_pid, NULL, 0);
8597 g_warning("Terminated process group id: %d", -pgid);
8598 g_warning("Temporary file: %s",
8599 compose->exteditor_file);
8601 compose_set_ext_editor_sensitive(compose, TRUE);
8603 g_free(compose->exteditor_file);
8604 compose->exteditor_file = NULL;
8605 compose->exteditor_pid = -1;
8606 compose->exteditor_ch = NULL;
8607 compose->exteditor_tag = -1;
8615 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8619 Compose *compose = (Compose *)data;
8622 debug_print(_("Compose: input from monitoring process\n"));
8624 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8626 g_io_channel_shutdown(source, FALSE, NULL);
8627 g_io_channel_unref(source);
8629 waitpid(compose->exteditor_pid, NULL, 0);
8631 if (buf[0] == '0') { /* success */
8632 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8633 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8635 gtk_text_buffer_set_text(buffer, "", -1);
8636 compose_insert_file(compose, compose->exteditor_file);
8637 compose_changed_cb(NULL, compose);
8638 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8640 if (claws_unlink(compose->exteditor_file) < 0)
8641 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8642 } else if (buf[0] == '1') { /* failed */
8643 g_warning("Couldn't exec external editor\n");
8644 if (claws_unlink(compose->exteditor_file) < 0)
8645 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8646 } else if (buf[0] == '2') {
8647 g_warning("Couldn't write to file\n");
8648 } else if (buf[0] == '3') {
8649 g_warning("Pipe read failed\n");
8652 compose_set_ext_editor_sensitive(compose, TRUE);
8654 g_free(compose->exteditor_file);
8655 compose->exteditor_file = NULL;
8656 compose->exteditor_pid = -1;
8657 compose->exteditor_ch = NULL;
8658 compose->exteditor_tag = -1;
8663 static void compose_set_ext_editor_sensitive(Compose *compose,
8666 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8667 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8668 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8669 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8670 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8671 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8672 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8674 gtk_widget_set_sensitive(compose->text, sensitive);
8675 if (compose->toolbar->send_btn)
8676 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8677 if (compose->toolbar->sendl_btn)
8678 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8679 if (compose->toolbar->draft_btn)
8680 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
8681 if (compose->toolbar->insert_btn)
8682 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
8683 if (compose->toolbar->sig_btn)
8684 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
8685 if (compose->toolbar->exteditor_btn)
8686 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8687 if (compose->toolbar->linewrap_current_btn)
8688 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8689 if (compose->toolbar->linewrap_all_btn)
8690 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8692 #endif /* G_OS_UNIX */
8695 * compose_undo_state_changed:
8697 * Change the sensivity of the menuentries undo and redo
8699 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8700 gint redo_state, gpointer data)
8702 Compose *compose = (Compose *)data;
8704 switch (undo_state) {
8705 case UNDO_STATE_TRUE:
8706 if (!undostruct->undo_state) {
8707 undostruct->undo_state = TRUE;
8708 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8711 case UNDO_STATE_FALSE:
8712 if (undostruct->undo_state) {
8713 undostruct->undo_state = FALSE;
8714 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8717 case UNDO_STATE_UNCHANGED:
8719 case UNDO_STATE_REFRESH:
8720 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8723 g_warning("Undo state not recognized");
8727 switch (redo_state) {
8728 case UNDO_STATE_TRUE:
8729 if (!undostruct->redo_state) {
8730 undostruct->redo_state = TRUE;
8731 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8734 case UNDO_STATE_FALSE:
8735 if (undostruct->redo_state) {
8736 undostruct->redo_state = FALSE;
8737 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8740 case UNDO_STATE_UNCHANGED:
8742 case UNDO_STATE_REFRESH:
8743 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8746 g_warning("Redo state not recognized");
8751 /* callback functions */
8753 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8754 * includes "non-client" (windows-izm) in calculation, so this calculation
8755 * may not be accurate.
8757 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8758 GtkAllocation *allocation,
8759 GtkSHRuler *shruler)
8761 if (prefs_common.show_ruler) {
8762 gint char_width = 0, char_height = 0;
8763 gint line_width_in_chars;
8765 gtkut_get_font_size(GTK_WIDGET(widget),
8766 &char_width, &char_height);
8767 line_width_in_chars =
8768 (allocation->width - allocation->x) / char_width;
8770 /* got the maximum */
8771 gtk_ruler_set_range(GTK_RULER(shruler),
8772 0.0, line_width_in_chars, 0,
8773 /*line_width_in_chars*/ char_width);
8779 static void account_activated(GtkComboBox *optmenu, gpointer data)
8781 Compose *compose = (Compose *)data;
8784 gchar *folderidentifier;
8785 gint account_id = 0;
8789 /* Get ID of active account in the combo box */
8790 menu = gtk_combo_box_get_model(optmenu);
8791 gtk_combo_box_get_active_iter(optmenu, &iter);
8792 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8794 ac = account_find_from_id(account_id);
8795 g_return_if_fail(ac != NULL);
8797 if (ac != compose->account)
8798 compose_select_account(compose, ac, FALSE);
8800 /* Set message save folder */
8801 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8802 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8804 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8805 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8807 compose_set_save_to(compose, NULL);
8808 if (account_get_special_folder(compose->account, F_OUTBOX)) {
8809 folderidentifier = folder_item_get_identifier(account_get_special_folder
8810 (compose->account, F_OUTBOX));
8811 compose_set_save_to(compose, folderidentifier);
8812 g_free(folderidentifier);
8816 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8817 GtkTreeViewColumn *column, Compose *compose)
8819 compose_attach_property(NULL, compose);
8822 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8825 Compose *compose = (Compose *)data;
8826 GtkTreeSelection *attach_selection;
8827 gint attach_nr_selected;
8829 if (!event) return FALSE;
8831 if (event->button == 3) {
8832 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8833 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8835 if (attach_nr_selected > 0)
8837 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8838 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8840 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8841 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8844 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8845 NULL, NULL, event->button, event->time);
8852 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8855 Compose *compose = (Compose *)data;
8857 if (!event) return FALSE;
8859 switch (event->keyval) {
8861 compose_attach_remove_selected(NULL, compose);
8867 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8869 toolbar_comp_set_sensitive(compose, allow);
8870 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8871 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8873 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8875 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8876 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8877 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8879 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8883 static void compose_send_cb(GtkAction *action, gpointer data)
8885 Compose *compose = (Compose *)data;
8887 if (prefs_common.work_offline &&
8888 !inc_offline_should_override(TRUE,
8889 _("Claws Mail needs network access in order "
8890 "to send this email.")))
8893 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8894 g_source_remove(compose->draft_timeout_tag);
8895 compose->draft_timeout_tag = -1;
8898 compose_send(compose);
8901 static void compose_send_later_cb(GtkAction *action, gpointer data)
8903 Compose *compose = (Compose *)data;
8907 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8911 compose_close(compose);
8912 } else if (val == -1) {
8913 alertpanel_error(_("Could not queue message."));
8914 } else if (val == -2) {
8915 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8916 } else if (val == -3) {
8917 if (privacy_peek_error())
8918 alertpanel_error(_("Could not queue message for sending:\n\n"
8919 "Signature failed: %s"), privacy_get_error());
8920 } else if (val == -4) {
8921 alertpanel_error(_("Could not queue message for sending:\n\n"
8922 "Charset conversion failed."));
8923 } else if (val == -5) {
8924 alertpanel_error(_("Could not queue message for sending:\n\n"
8925 "Couldn't get recipient encryption key."));
8926 } else if (val == -6) {
8929 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8932 #define DRAFTED_AT_EXIT "drafted_at_exit"
8933 static void compose_register_draft(MsgInfo *info)
8935 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8936 DRAFTED_AT_EXIT, NULL);
8937 FILE *fp = g_fopen(filepath, "ab");
8940 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
8948 gboolean compose_draft (gpointer data, guint action)
8950 Compose *compose = (Compose *)data;
8954 MsgFlags flag = {0, 0};
8955 static gboolean lock = FALSE;
8956 MsgInfo *newmsginfo;
8958 gboolean target_locked = FALSE;
8959 gboolean err = FALSE;
8961 if (lock) return FALSE;
8963 if (compose->sending)
8966 draft = account_get_special_folder(compose->account, F_DRAFT);
8967 g_return_val_if_fail(draft != NULL, FALSE);
8969 if (!g_mutex_trylock(compose->mutex)) {
8970 /* we don't want to lock the mutex once it's available,
8971 * because as the only other part of compose.c locking
8972 * it is compose_close - which means once unlocked,
8973 * the compose struct will be freed */
8974 debug_print("couldn't lock mutex, probably sending\n");
8980 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8981 G_DIR_SEPARATOR, compose);
8982 if ((fp = g_fopen(tmp, "wb")) == NULL) {
8983 FILE_OP_ERROR(tmp, "fopen");
8987 /* chmod for security */
8988 if (change_file_mode_rw(fp, tmp) < 0) {
8989 FILE_OP_ERROR(tmp, "chmod");
8990 g_warning("can't change file mode\n");
8993 /* Save draft infos */
8994 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8995 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8997 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8998 gchar *savefolderid;
9000 savefolderid = compose_get_save_to(compose);
9001 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9002 g_free(savefolderid);
9004 if (compose->return_receipt) {
9005 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9007 if (compose->privacy_system) {
9008 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9009 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9010 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9013 /* Message-ID of message replying to */
9014 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9017 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9018 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9021 /* Message-ID of message forwarding to */
9022 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9025 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9026 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9030 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9031 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9033 /* end of headers */
9034 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9041 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9045 if (fclose(fp) == EOF) {
9049 if (compose->targetinfo) {
9050 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9051 flag.perm_flags = target_locked?MSG_LOCKED:0;
9053 flag.tmp_flags = MSG_DRAFT;
9055 folder_item_scan(draft);
9056 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9057 MsgInfo *tmpinfo = NULL;
9058 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9059 if (compose->msgid) {
9060 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9063 msgnum = tmpinfo->msgnum;
9064 procmsg_msginfo_free(tmpinfo);
9065 debug_print("got draft msgnum %d from scanning\n", msgnum);
9067 debug_print("didn't get draft msgnum after scanning\n");
9070 debug_print("got draft msgnum %d from adding\n", msgnum);
9076 if (action != COMPOSE_AUTO_SAVE) {
9077 if (action != COMPOSE_DRAFT_FOR_EXIT)
9078 alertpanel_error(_("Could not save draft."));
9081 gtkut_window_popup(compose->window);
9082 val = alertpanel_full(_("Could not save draft"),
9083 _("Could not save draft.\n"
9084 "Do you want to cancel exit or discard this email?"),
9085 _("_Cancel exit"), _("_Discard email"), NULL,
9086 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9087 if (val == G_ALERTALTERNATE) {
9089 g_mutex_unlock(compose->mutex); /* must be done before closing */
9090 compose_close(compose);
9094 g_mutex_unlock(compose->mutex); /* must be done before closing */
9103 if (compose->mode == COMPOSE_REEDIT) {
9104 compose_remove_reedit_target(compose, TRUE);
9107 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9110 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9112 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9114 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9115 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9116 procmsg_msginfo_set_flags(newmsginfo, 0,
9117 MSG_HAS_ATTACHMENT);
9119 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9120 compose_register_draft(newmsginfo);
9122 procmsg_msginfo_free(newmsginfo);
9125 folder_item_scan(draft);
9127 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9129 g_mutex_unlock(compose->mutex); /* must be done before closing */
9130 compose_close(compose);
9136 path = folder_item_fetch_msg(draft, msgnum);
9138 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9141 if (g_stat(path, &s) < 0) {
9142 FILE_OP_ERROR(path, "stat");
9148 procmsg_msginfo_free(compose->targetinfo);
9149 compose->targetinfo = procmsg_msginfo_new();
9150 compose->targetinfo->msgnum = msgnum;
9151 compose->targetinfo->size = (goffset)s.st_size;
9152 compose->targetinfo->mtime = s.st_mtime;
9153 compose->targetinfo->folder = draft;
9155 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9156 compose->mode = COMPOSE_REEDIT;
9158 if (action == COMPOSE_AUTO_SAVE) {
9159 compose->autosaved_draft = compose->targetinfo;
9161 compose->modified = FALSE;
9162 compose_set_title(compose);
9166 g_mutex_unlock(compose->mutex);
9170 void compose_clear_exit_drafts(void)
9172 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9173 DRAFTED_AT_EXIT, NULL);
9174 if (is_file_exist(filepath))
9175 claws_unlink(filepath);
9180 void compose_reopen_exit_drafts(void)
9182 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9183 DRAFTED_AT_EXIT, NULL);
9184 FILE *fp = g_fopen(filepath, "rb");
9188 while (fgets(buf, sizeof(buf), fp)) {
9189 gchar **parts = g_strsplit(buf, "\t", 2);
9190 const gchar *folder = parts[0];
9191 int msgnum = parts[1] ? atoi(parts[1]):-1;
9193 if (folder && *folder && msgnum > -1) {
9194 FolderItem *item = folder_find_item_from_identifier(folder);
9195 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9197 compose_reedit(info, FALSE);
9204 compose_clear_exit_drafts();
9207 static void compose_save_cb(GtkAction *action, gpointer data)
9209 Compose *compose = (Compose *)data;
9210 compose_draft(compose, COMPOSE_KEEP_EDITING);
9211 compose->rmode = COMPOSE_REEDIT;
9214 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9216 if (compose && file_list) {
9219 for ( tmp = file_list; tmp; tmp = tmp->next) {
9220 gchar *file = (gchar *) tmp->data;
9221 gchar *utf8_filename = conv_filename_to_utf8(file);
9222 compose_attach_append(compose, file, utf8_filename, NULL);
9223 compose_changed_cb(NULL, compose);
9228 g_free(utf8_filename);
9233 static void compose_attach_cb(GtkAction *action, gpointer data)
9235 Compose *compose = (Compose *)data;
9238 if (compose->redirect_filename != NULL)
9241 file_list = filesel_select_multiple_files_open(_("Select file"));
9244 compose_attach_from_list(compose, file_list, TRUE);
9245 g_list_free(file_list);
9249 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9251 Compose *compose = (Compose *)data;
9254 file_list = filesel_select_multiple_files_open(_("Select file"));
9259 for ( tmp = file_list; tmp; tmp = tmp->next) {
9260 gchar *file = (gchar *) tmp->data;
9261 gchar *filedup = g_strdup(file);
9262 gchar *shortfile = g_path_get_basename(filedup);
9263 ComposeInsertResult res;
9265 res = compose_insert_file(compose, file);
9266 if (res == COMPOSE_INSERT_READ_ERROR) {
9267 alertpanel_error(_("File '%s' could not be read."), shortfile);
9268 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9269 alertpanel_error(_("File '%s' contained invalid characters\n"
9270 "for the current encoding, insertion may be incorrect."), shortfile);
9276 g_list_free(file_list);
9280 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9282 Compose *compose = (Compose *)data;
9284 compose_insert_sig(compose, FALSE);
9287 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9291 Compose *compose = (Compose *)data;
9293 gtkut_widget_get_uposition(widget, &x, &y);
9294 if (!compose->batch) {
9295 prefs_common.compose_x = x;
9296 prefs_common.compose_y = y;
9298 if (compose->sending || compose->updating)
9300 compose_close_cb(NULL, compose);
9304 void compose_close_toolbar(Compose *compose)
9306 compose_close_cb(NULL, compose);
9309 static void compose_close_cb(GtkAction *action, gpointer data)
9311 Compose *compose = (Compose *)data;
9315 if (compose->exteditor_tag != -1) {
9316 if (!compose_ext_editor_kill(compose))
9321 if (compose->modified) {
9322 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9323 if (!g_mutex_trylock(compose->mutex)) {
9324 /* we don't want to lock the mutex once it's available,
9325 * because as the only other part of compose.c locking
9326 * it is compose_close - which means once unlocked,
9327 * the compose struct will be freed */
9328 debug_print("couldn't lock mutex, probably sending\n");
9332 val = alertpanel(_("Discard message"),
9333 _("This message has been modified. Discard it?"),
9334 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9336 val = alertpanel(_("Save changes"),
9337 _("This message has been modified. Save the latest changes?"),
9338 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9340 g_mutex_unlock(compose->mutex);
9342 case G_ALERTDEFAULT:
9343 if (prefs_common.autosave && !reedit)
9344 compose_remove_draft(compose);
9346 case G_ALERTALTERNATE:
9347 compose_draft(data, COMPOSE_QUIT_EDITING);
9354 compose_close(compose);
9357 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9359 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9360 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9361 Compose *compose = (Compose *) data;
9364 compose->out_encoding = (CharSet)value;
9367 static void compose_address_cb(GtkAction *action, gpointer data)
9369 Compose *compose = (Compose *)data;
9371 addressbook_open(compose);
9374 static void about_show_cb(GtkAction *action, gpointer data)
9379 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9381 Compose *compose = (Compose *)data;
9386 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9387 g_return_if_fail(tmpl != NULL);
9389 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9391 val = alertpanel(_("Apply template"), msg,
9392 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9395 if (val == G_ALERTDEFAULT)
9396 compose_template_apply(compose, tmpl, TRUE);
9397 else if (val == G_ALERTALTERNATE)
9398 compose_template_apply(compose, tmpl, FALSE);
9401 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9403 Compose *compose = (Compose *)data;
9405 compose_exec_ext_editor(compose);
9408 static void compose_undo_cb(GtkAction *action, gpointer data)
9410 Compose *compose = (Compose *)data;
9411 gboolean prev_autowrap = compose->autowrap;
9413 compose->autowrap = FALSE;
9414 undo_undo(compose->undostruct);
9415 compose->autowrap = prev_autowrap;
9418 static void compose_redo_cb(GtkAction *action, gpointer data)
9420 Compose *compose = (Compose *)data;
9421 gboolean prev_autowrap = compose->autowrap;
9423 compose->autowrap = FALSE;
9424 undo_redo(compose->undostruct);
9425 compose->autowrap = prev_autowrap;
9428 static void entry_cut_clipboard(GtkWidget *entry)
9430 if (GTK_IS_EDITABLE(entry))
9431 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9432 else if (GTK_IS_TEXT_VIEW(entry))
9433 gtk_text_buffer_cut_clipboard(
9434 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9435 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9439 static void entry_copy_clipboard(GtkWidget *entry)
9441 if (GTK_IS_EDITABLE(entry))
9442 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9443 else if (GTK_IS_TEXT_VIEW(entry))
9444 gtk_text_buffer_copy_clipboard(
9445 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9446 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9449 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9450 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9452 if (GTK_IS_TEXT_VIEW(entry)) {
9453 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9454 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9455 GtkTextIter start_iter, end_iter;
9457 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9459 if (contents == NULL)
9462 /* we shouldn't delete the selection when middle-click-pasting, or we
9463 * can't mid-click-paste our own selection */
9464 if (clip != GDK_SELECTION_PRIMARY) {
9465 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9466 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9469 if (insert_place == NULL) {
9470 /* if insert_place isn't specified, insert at the cursor.
9471 * used for Ctrl-V pasting */
9472 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9473 start = gtk_text_iter_get_offset(&start_iter);
9474 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9476 /* if insert_place is specified, paste here.
9477 * used for mid-click-pasting */
9478 start = gtk_text_iter_get_offset(insert_place);
9479 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9480 if (prefs_common.primary_paste_unselects)
9481 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9485 /* paste unwrapped: mark the paste so it's not wrapped later */
9486 end = start + strlen(contents);
9487 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9488 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9489 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9490 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9491 /* rewrap paragraph now (after a mid-click-paste) */
9492 mark_start = gtk_text_buffer_get_insert(buffer);
9493 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9494 gtk_text_iter_backward_char(&start_iter);
9495 compose_beautify_paragraph(compose, &start_iter, TRUE);
9497 } else if (GTK_IS_EDITABLE(entry))
9498 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9500 compose->modified = TRUE;
9503 static void entry_allsel(GtkWidget *entry)
9505 if (GTK_IS_EDITABLE(entry))
9506 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9507 else if (GTK_IS_TEXT_VIEW(entry)) {
9508 GtkTextIter startiter, enditer;
9509 GtkTextBuffer *textbuf;
9511 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9512 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9513 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9515 gtk_text_buffer_move_mark_by_name(textbuf,
9516 "selection_bound", &startiter);
9517 gtk_text_buffer_move_mark_by_name(textbuf,
9518 "insert", &enditer);
9522 static void compose_cut_cb(GtkAction *action, gpointer data)
9524 Compose *compose = (Compose *)data;
9525 if (compose->focused_editable
9526 #ifndef GENERIC_UMPC
9527 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9530 entry_cut_clipboard(compose->focused_editable);
9533 static void compose_copy_cb(GtkAction *action, gpointer data)
9535 Compose *compose = (Compose *)data;
9536 if (compose->focused_editable
9537 #ifndef GENERIC_UMPC
9538 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9541 entry_copy_clipboard(compose->focused_editable);
9544 static void compose_paste_cb(GtkAction *action, gpointer data)
9546 Compose *compose = (Compose *)data;
9548 GtkTextBuffer *buffer;
9550 if (compose->focused_editable &&
9551 GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9552 entry_paste_clipboard(compose, compose->focused_editable,
9553 prefs_common.linewrap_pastes,
9554 GDK_SELECTION_CLIPBOARD, NULL);
9558 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9560 Compose *compose = (Compose *)data;
9561 gint wrap_quote = prefs_common.linewrap_quote;
9562 if (compose->focused_editable
9563 #ifndef GENERIC_UMPC
9564 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9567 /* let text_insert() (called directly or at a later time
9568 * after the gtk_editable_paste_clipboard) know that
9569 * text is to be inserted as a quotation. implemented
9570 * by using a simple refcount... */
9571 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9572 G_OBJECT(compose->focused_editable),
9573 "paste_as_quotation"));
9574 g_object_set_data(G_OBJECT(compose->focused_editable),
9575 "paste_as_quotation",
9576 GINT_TO_POINTER(paste_as_quotation + 1));
9577 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9578 entry_paste_clipboard(compose, compose->focused_editable,
9579 prefs_common.linewrap_pastes,
9580 GDK_SELECTION_CLIPBOARD, NULL);
9581 prefs_common.linewrap_quote = wrap_quote;
9585 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9587 Compose *compose = (Compose *)data;
9589 GtkTextBuffer *buffer;
9591 if (compose->focused_editable
9592 #ifndef GENERIC_UMPC
9593 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9596 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9597 GDK_SELECTION_CLIPBOARD, NULL);
9601 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9603 Compose *compose = (Compose *)data;
9605 GtkTextBuffer *buffer;
9607 if (compose->focused_editable
9608 #ifndef GENERIC_UMPC
9609 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9612 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9613 GDK_SELECTION_CLIPBOARD, NULL);
9617 static void compose_allsel_cb(GtkAction *action, gpointer data)
9619 Compose *compose = (Compose *)data;
9620 if (compose->focused_editable
9621 #ifndef GENERIC_UMPC
9622 && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9625 entry_allsel(compose->focused_editable);
9628 static void textview_move_beginning_of_line (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 gtk_text_iter_set_line_offset(&ins, 0);
9640 gtk_text_buffer_place_cursor(buffer, &ins);
9643 static void textview_move_forward_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_forward_cursor_position(&ins))
9655 gtk_text_buffer_place_cursor(buffer, &ins);
9658 static void textview_move_backward_character (GtkTextView *text)
9660 GtkTextBuffer *buffer;
9664 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9666 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9667 mark = gtk_text_buffer_get_insert(buffer);
9668 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9669 if (gtk_text_iter_backward_cursor_position(&ins))
9670 gtk_text_buffer_place_cursor(buffer, &ins);
9673 static void textview_move_forward_word (GtkTextView *text)
9675 GtkTextBuffer *buffer;
9680 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9682 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9683 mark = gtk_text_buffer_get_insert(buffer);
9684 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9685 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9686 if (gtk_text_iter_forward_word_ends(&ins, count)) {
9687 gtk_text_iter_backward_word_start(&ins);
9688 gtk_text_buffer_place_cursor(buffer, &ins);
9692 static void textview_move_backward_word (GtkTextView *text)
9694 GtkTextBuffer *buffer;
9699 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9701 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9702 mark = gtk_text_buffer_get_insert(buffer);
9703 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9704 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9705 if (gtk_text_iter_backward_word_starts(&ins, 1))
9706 gtk_text_buffer_place_cursor(buffer, &ins);
9709 static void textview_move_end_of_line (GtkTextView *text)
9711 GtkTextBuffer *buffer;
9715 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9717 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9718 mark = gtk_text_buffer_get_insert(buffer);
9719 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9720 if (gtk_text_iter_forward_to_line_end(&ins))
9721 gtk_text_buffer_place_cursor(buffer, &ins);
9724 static void textview_move_next_line (GtkTextView *text)
9726 GtkTextBuffer *buffer;
9731 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9733 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9734 mark = gtk_text_buffer_get_insert(buffer);
9735 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9736 offset = gtk_text_iter_get_line_offset(&ins);
9737 if (gtk_text_iter_forward_line(&ins)) {
9738 gtk_text_iter_set_line_offset(&ins, offset);
9739 gtk_text_buffer_place_cursor(buffer, &ins);
9743 static void textview_move_previous_line (GtkTextView *text)
9745 GtkTextBuffer *buffer;
9750 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9752 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9753 mark = gtk_text_buffer_get_insert(buffer);
9754 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9755 offset = gtk_text_iter_get_line_offset(&ins);
9756 if (gtk_text_iter_backward_line(&ins)) {
9757 gtk_text_iter_set_line_offset(&ins, offset);
9758 gtk_text_buffer_place_cursor(buffer, &ins);
9762 static void textview_delete_forward_character (GtkTextView *text)
9764 GtkTextBuffer *buffer;
9766 GtkTextIter ins, end_iter;
9768 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9770 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9771 mark = gtk_text_buffer_get_insert(buffer);
9772 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9774 if (gtk_text_iter_forward_char(&end_iter)) {
9775 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9779 static void textview_delete_backward_character (GtkTextView *text)
9781 GtkTextBuffer *buffer;
9783 GtkTextIter ins, end_iter;
9785 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9787 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9788 mark = gtk_text_buffer_get_insert(buffer);
9789 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9791 if (gtk_text_iter_backward_char(&end_iter)) {
9792 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9796 static void textview_delete_forward_word (GtkTextView *text)
9798 GtkTextBuffer *buffer;
9800 GtkTextIter ins, end_iter;
9802 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9804 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9805 mark = gtk_text_buffer_get_insert(buffer);
9806 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9808 if (gtk_text_iter_forward_word_end(&end_iter)) {
9809 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9813 static void textview_delete_backward_word (GtkTextView *text)
9815 GtkTextBuffer *buffer;
9817 GtkTextIter ins, end_iter;
9819 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9821 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9822 mark = gtk_text_buffer_get_insert(buffer);
9823 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9825 if (gtk_text_iter_backward_word_start(&end_iter)) {
9826 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9830 static void textview_delete_line (GtkTextView *text)
9832 GtkTextBuffer *buffer;
9834 GtkTextIter ins, start_iter, end_iter;
9836 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9838 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9839 mark = gtk_text_buffer_get_insert(buffer);
9840 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9843 gtk_text_iter_set_line_offset(&start_iter, 0);
9846 if (gtk_text_iter_ends_line(&end_iter)){
9847 if (!gtk_text_iter_forward_char(&end_iter))
9848 gtk_text_iter_backward_char(&start_iter);
9851 gtk_text_iter_forward_to_line_end(&end_iter);
9852 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9855 static void textview_delete_to_line_end (GtkTextView *text)
9857 GtkTextBuffer *buffer;
9859 GtkTextIter ins, end_iter;
9861 g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9863 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9864 mark = gtk_text_buffer_get_insert(buffer);
9865 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9867 if (gtk_text_iter_ends_line(&end_iter))
9868 gtk_text_iter_forward_char(&end_iter);
9870 gtk_text_iter_forward_to_line_end(&end_iter);
9871 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9874 #define DO_ACTION(name, act) { \
9875 if(!strcmp(name, a_name)) { \
9879 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9881 const gchar *a_name = gtk_action_get_name(action);
9882 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9883 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9884 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9885 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9886 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9887 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9888 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9889 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9890 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9891 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9892 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9893 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9894 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9895 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9899 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9901 Compose *compose = (Compose *)data;
9902 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9903 ComposeCallAdvancedAction action = -1;
9905 action = compose_call_advanced_action_from_path(gaction);
9908 void (*do_action) (GtkTextView *text);
9909 } action_table[] = {
9910 {textview_move_beginning_of_line},
9911 {textview_move_forward_character},
9912 {textview_move_backward_character},
9913 {textview_move_forward_word},
9914 {textview_move_backward_word},
9915 {textview_move_end_of_line},
9916 {textview_move_next_line},
9917 {textview_move_previous_line},
9918 {textview_delete_forward_character},
9919 {textview_delete_backward_character},
9920 {textview_delete_forward_word},
9921 {textview_delete_backward_word},
9922 {textview_delete_line},
9923 {textview_delete_to_line_end}
9926 if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9928 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9929 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9930 if (action_table[action].do_action)
9931 action_table[action].do_action(text);
9933 g_warning("Not implemented yet.");
9937 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9941 if (GTK_IS_EDITABLE(widget)) {
9942 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9943 gtk_editable_set_position(GTK_EDITABLE(widget),
9946 if (widget->parent && widget->parent->parent
9947 && widget->parent->parent->parent) {
9948 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9949 gint y = widget->allocation.y;
9950 gint height = widget->allocation.height;
9951 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9952 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9954 if (y < (int)shown->value) {
9955 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9957 if (y + height > (int)shown->value + (int)shown->page_size) {
9958 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9959 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9960 y + height - (int)shown->page_size - 1);
9962 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
9963 (int)shown->upper - (int)shown->page_size - 1);
9970 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9971 compose->focused_editable = widget;
9974 if (GTK_IS_TEXT_VIEW(widget)
9975 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9976 g_object_ref(compose->notebook);
9977 g_object_ref(compose->edit_vbox);
9978 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9979 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9980 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9981 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9982 g_object_unref(compose->notebook);
9983 g_object_unref(compose->edit_vbox);
9984 g_signal_handlers_block_by_func(G_OBJECT(widget),
9985 G_CALLBACK(compose_grab_focus_cb),
9987 gtk_widget_grab_focus(widget);
9988 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9989 G_CALLBACK(compose_grab_focus_cb),
9991 } else if (!GTK_IS_TEXT_VIEW(widget)
9992 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9993 g_object_ref(compose->notebook);
9994 g_object_ref(compose->edit_vbox);
9995 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9996 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9997 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9998 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9999 g_object_unref(compose->notebook);
10000 g_object_unref(compose->edit_vbox);
10001 g_signal_handlers_block_by_func(G_OBJECT(widget),
10002 G_CALLBACK(compose_grab_focus_cb),
10004 gtk_widget_grab_focus(widget);
10005 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10006 G_CALLBACK(compose_grab_focus_cb),
10012 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10014 compose->modified = TRUE;
10015 // compose_beautify_paragraph(compose, NULL, TRUE);
10016 #ifndef GENERIC_UMPC
10017 compose_set_title(compose);
10021 static void compose_wrap_cb(GtkAction *action, gpointer data)
10023 Compose *compose = (Compose *)data;
10024 compose_beautify_paragraph(compose, NULL, TRUE);
10027 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10029 Compose *compose = (Compose *)data;
10030 compose_wrap_all_full(compose, TRUE);
10033 static void compose_find_cb(GtkAction *action, gpointer data)
10035 Compose *compose = (Compose *)data;
10037 message_search_compose(compose);
10040 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10043 Compose *compose = (Compose *)data;
10044 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10045 if (compose->autowrap)
10046 compose_wrap_all_full(compose, TRUE);
10047 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10050 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10053 Compose *compose = (Compose *)data;
10054 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10057 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10059 Compose *compose = (Compose *)data;
10061 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10064 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10066 Compose *compose = (Compose *)data;
10068 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10071 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10073 g_free(compose->privacy_system);
10075 compose->privacy_system = g_strdup(account->default_privacy_system);
10076 compose_update_privacy_system_menu_item(compose, warn);
10079 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10081 Compose *compose = (Compose *)data;
10083 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10084 gtk_widget_show(compose->ruler_hbox);
10085 prefs_common.show_ruler = TRUE;
10087 gtk_widget_hide(compose->ruler_hbox);
10088 gtk_widget_queue_resize(compose->edit_vbox);
10089 prefs_common.show_ruler = FALSE;
10093 static void compose_attach_drag_received_cb (GtkWidget *widget,
10094 GdkDragContext *context,
10097 GtkSelectionData *data,
10100 gpointer user_data)
10102 Compose *compose = (Compose *)user_data;
10105 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10107 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10109 ) && gtk_drag_get_source_widget(context) !=
10110 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10111 list = uri_list_extract_filenames((const gchar *)data->data);
10112 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10113 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10114 compose_attach_append
10115 (compose, (const gchar *)tmp->data,
10116 utf8_filename, NULL);
10117 g_free(utf8_filename);
10119 if (list) compose_changed_cb(NULL, compose);
10120 list_free_strings(list);
10122 } else if (gtk_drag_get_source_widget(context)
10123 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10124 /* comes from our summaryview */
10125 SummaryView * summaryview = NULL;
10126 GSList * list = NULL, *cur = NULL;
10128 if (mainwindow_get_mainwindow())
10129 summaryview = mainwindow_get_mainwindow()->summaryview;
10132 list = summary_get_selected_msg_list(summaryview);
10134 for (cur = list; cur; cur = cur->next) {
10135 MsgInfo *msginfo = (MsgInfo *)cur->data;
10136 gchar *file = NULL;
10138 file = procmsg_get_message_file_full(msginfo,
10141 compose_attach_append(compose, (const gchar *)file,
10142 (const gchar *)file, "message/rfc822");
10146 g_slist_free(list);
10150 static gboolean compose_drag_drop(GtkWidget *widget,
10151 GdkDragContext *drag_context,
10153 guint time, gpointer user_data)
10155 /* not handling this signal makes compose_insert_drag_received_cb
10160 static void compose_insert_drag_received_cb (GtkWidget *widget,
10161 GdkDragContext *drag_context,
10164 GtkSelectionData *data,
10167 gpointer user_data)
10169 Compose *compose = (Compose *)user_data;
10172 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10174 debug_print("drop: %s (%s)\n", gdk_atom_name(data->type)?gdk_atom_name(data->type):"nul",
10175 (gchar *)(data->data?data->data:"nul"));
10177 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10179 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10181 AlertValue val = G_ALERTDEFAULT;
10183 list = uri_list_extract_filenames((const gchar *)data->data);
10184 debug_print("list: %p (%s)\n", list,
10185 (gchar *)(data->data?data->data:"nul"));
10186 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10187 /* Assume a list of no files, and data has ://, is a remote link */
10188 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10189 gchar *tmpfile = get_tmp_file();
10190 str_write_to_file(tmpdata, tmpfile);
10192 compose_insert_file(compose, tmpfile);
10193 claws_unlink(tmpfile);
10195 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10196 compose_beautify_paragraph(compose, NULL, TRUE);
10199 switch (prefs_common.compose_dnd_mode) {
10200 case COMPOSE_DND_ASK:
10201 val = alertpanel_full(_("Insert or attach?"),
10202 _("Do you want to insert the contents of the file(s) "
10203 "into the message body, or attach it to the email?"),
10204 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10205 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10207 case COMPOSE_DND_INSERT:
10208 val = G_ALERTALTERNATE;
10210 case COMPOSE_DND_ATTACH:
10211 val = G_ALERTOTHER;
10214 /* unexpected case */
10215 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10218 if (val & G_ALERTDISABLE) {
10219 val &= ~G_ALERTDISABLE;
10220 /* remember what action to perform by default, only if we don't click Cancel */
10221 if (val == G_ALERTALTERNATE)
10222 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10223 else if (val == G_ALERTOTHER)
10224 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10227 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10228 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10229 list_free_strings(list);
10232 } else if (val == G_ALERTOTHER) {
10233 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10234 list_free_strings(list);
10239 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10240 compose_insert_file(compose, (const gchar *)tmp->data);
10242 list_free_strings(list);
10244 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10247 #if GTK_CHECK_VERSION(2, 8, 0)
10248 /* do nothing, handled by GTK */
10250 gchar *tmpfile = get_tmp_file();
10251 str_write_to_file((const gchar *)data->data, tmpfile);
10252 compose_insert_file(compose, tmpfile);
10253 claws_unlink(tmpfile);
10255 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10259 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10262 static void compose_header_drag_received_cb (GtkWidget *widget,
10263 GdkDragContext *drag_context,
10266 GtkSelectionData *data,
10269 gpointer user_data)
10271 GtkEditable *entry = (GtkEditable *)user_data;
10272 gchar *email = (gchar *)data->data;
10274 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10277 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10278 gchar *decoded=g_new(gchar, strlen(email));
10281 email += strlen("mailto:");
10282 decode_uri(decoded, email); /* will fit */
10283 gtk_editable_delete_text(entry, 0, -1);
10284 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10285 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10289 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10292 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10294 Compose *compose = (Compose *)data;
10296 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10297 compose->return_receipt = TRUE;
10299 compose->return_receipt = FALSE;
10302 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10304 Compose *compose = (Compose *)data;
10306 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10307 compose->remove_references = TRUE;
10309 compose->remove_references = FALSE;
10312 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10313 GdkEventKey *event,
10314 ComposeHeaderEntry *headerentry)
10316 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10317 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10318 !(event->state & GDK_MODIFIER_MASK) &&
10319 (event->keyval == GDK_BackSpace) &&
10320 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10321 gtk_container_remove
10322 (GTK_CONTAINER(headerentry->compose->header_table),
10323 headerentry->combo);
10324 gtk_container_remove
10325 (GTK_CONTAINER(headerentry->compose->header_table),
10326 headerentry->entry);
10327 headerentry->compose->header_list =
10328 g_slist_remove(headerentry->compose->header_list,
10330 g_free(headerentry);
10331 } else if (event->keyval == GDK_Tab) {
10332 if (headerentry->compose->header_last == headerentry) {
10333 /* Override default next focus, and give it to subject_entry
10334 * instead of notebook tabs
10336 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10337 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10344 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10345 ComposeHeaderEntry *headerentry)
10347 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10348 compose_create_header_entry(headerentry->compose);
10349 g_signal_handlers_disconnect_matched
10350 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10351 0, 0, NULL, NULL, headerentry);
10353 /* Automatically scroll down */
10354 compose_show_first_last_header(headerentry->compose, FALSE);
10360 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10362 GtkAdjustment *vadj;
10364 g_return_if_fail(compose);
10365 g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10366 g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10368 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10369 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10370 gtk_adjustment_changed(vadj);
10373 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10374 const gchar *text, gint len, Compose *compose)
10376 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10377 (G_OBJECT(compose->text), "paste_as_quotation"));
10380 g_return_if_fail(text != NULL);
10382 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10383 G_CALLBACK(text_inserted),
10385 if (paste_as_quotation) {
10387 const gchar *qmark;
10389 GtkTextIter start_iter;
10392 len = strlen(text);
10394 new_text = g_strndup(text, len);
10396 qmark = compose_quote_char_from_context(compose);
10398 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10399 gtk_text_buffer_place_cursor(buffer, iter);
10401 pos = gtk_text_iter_get_offset(iter);
10403 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10404 _("Quote format error at line %d."));
10405 quote_fmt_reset_vartable();
10407 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10408 GINT_TO_POINTER(paste_as_quotation - 1));
10410 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10411 gtk_text_buffer_place_cursor(buffer, iter);
10412 gtk_text_buffer_delete_mark(buffer, mark);
10414 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10415 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10416 compose_beautify_paragraph(compose, &start_iter, FALSE);
10417 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10418 gtk_text_buffer_delete_mark(buffer, mark);
10420 if (strcmp(text, "\n") || compose->automatic_break
10421 || gtk_text_iter_starts_line(iter)) {
10422 GtkTextIter before_ins;
10423 gtk_text_buffer_insert(buffer, iter, text, len);
10424 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10425 before_ins = *iter;
10426 gtk_text_iter_backward_chars(&before_ins, len);
10427 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10430 /* check if the preceding is just whitespace or quote */
10431 GtkTextIter start_line;
10432 gchar *tmp = NULL, *quote = NULL;
10433 gint quote_len = 0, is_normal = 0;
10434 start_line = *iter;
10435 gtk_text_iter_set_line_offset(&start_line, 0);
10436 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10439 if (*tmp == '\0') {
10442 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10450 gtk_text_buffer_insert(buffer, iter, text, len);
10452 gtk_text_buffer_insert_with_tags_by_name(buffer,
10453 iter, text, len, "no_join", NULL);
10458 if (!paste_as_quotation) {
10459 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10460 compose_beautify_paragraph(compose, iter, FALSE);
10461 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10462 gtk_text_buffer_delete_mark(buffer, mark);
10465 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10466 G_CALLBACK(text_inserted),
10468 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10470 if (prefs_common.autosave &&
10471 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10472 compose->draft_timeout_tag != -2 /* disabled while loading */)
10473 compose->draft_timeout_tag = g_timeout_add
10474 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10476 static gint compose_defer_auto_save_draft(Compose *compose)
10478 compose->draft_timeout_tag = -1;
10479 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10484 static void compose_check_all(GtkAction *action, gpointer data)
10486 Compose *compose = (Compose *)data;
10487 if (compose->gtkaspell)
10488 gtkaspell_check_all(compose->gtkaspell);
10491 static void compose_highlight_all(GtkAction *action, gpointer data)
10493 Compose *compose = (Compose *)data;
10494 if (compose->gtkaspell)
10495 gtkaspell_highlight_all(compose->gtkaspell);
10498 static void compose_check_backwards(GtkAction *action, gpointer data)
10500 Compose *compose = (Compose *)data;
10501 if (compose->gtkaspell)
10502 gtkaspell_check_backwards(compose->gtkaspell);
10504 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10508 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10510 Compose *compose = (Compose *)data;
10511 if (compose->gtkaspell)
10512 gtkaspell_check_forwards_go(compose->gtkaspell);
10514 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10520 *\brief Guess originating forward account from MsgInfo and several
10521 * "common preference" settings. Return NULL if no guess.
10523 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10525 PrefsAccount *account = NULL;
10527 g_return_val_if_fail(msginfo, NULL);
10528 g_return_val_if_fail(msginfo->folder, NULL);
10529 g_return_val_if_fail(msginfo->folder->prefs, NULL);
10531 if (msginfo->folder->prefs->enable_default_account)
10532 account = account_find_from_id(msginfo->folder->prefs->default_account);
10535 account = msginfo->folder->folder->account;
10537 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10539 Xstrdup_a(to, msginfo->to, return NULL);
10540 extract_address(to);
10541 account = account_find_from_address(to, FALSE);
10544 if (!account && prefs_common.forward_account_autosel) {
10545 gchar cc[BUFFSIZE];
10546 if (!procheader_get_header_from_msginfo
10547 (msginfo, cc,sizeof cc , "Cc:")) {
10548 gchar *buf = cc + strlen("Cc:");
10549 extract_address(buf);
10550 account = account_find_from_address(buf, FALSE);
10554 if (!account && prefs_common.forward_account_autosel) {
10555 gchar deliveredto[BUFFSIZE];
10556 if (!procheader_get_header_from_msginfo
10557 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
10558 gchar *buf = deliveredto + strlen("Delivered-To:");
10559 extract_address(buf);
10560 account = account_find_from_address(buf, FALSE);
10567 gboolean compose_close(Compose *compose)
10571 if (!g_mutex_trylock(compose->mutex)) {
10572 /* we have to wait for the (possibly deferred by auto-save)
10573 * drafting to be done, before destroying the compose under
10575 debug_print("waiting for drafting to finish...\n");
10576 compose_allow_user_actions(compose, FALSE);
10577 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10580 g_return_val_if_fail(compose, FALSE);
10581 gtkut_widget_get_uposition(compose->window, &x, &y);
10582 if (!compose->batch) {
10583 prefs_common.compose_x = x;
10584 prefs_common.compose_y = y;
10586 g_mutex_unlock(compose->mutex);
10587 compose_destroy(compose);
10592 * Add entry field for each address in list.
10593 * \param compose E-Mail composition object.
10594 * \param listAddress List of (formatted) E-Mail addresses.
10596 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10599 node = listAddress;
10601 addr = ( gchar * ) node->data;
10602 compose_entry_append( compose, addr, COMPOSE_TO );
10603 node = g_list_next( node );
10607 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
10608 guint action, gboolean opening_multiple)
10610 gchar *body = NULL;
10611 GSList *new_msglist = NULL;
10612 MsgInfo *tmp_msginfo = NULL;
10613 gboolean originally_enc = FALSE;
10614 gboolean originally_sig = FALSE;
10615 Compose *compose = NULL;
10616 gchar *s_system = NULL;
10618 g_return_if_fail(msgview != NULL);
10620 g_return_if_fail(msginfo_list != NULL);
10622 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10623 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10624 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10626 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
10627 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10628 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10629 orig_msginfo, mimeinfo);
10630 if (tmp_msginfo != NULL) {
10631 new_msglist = g_slist_append(NULL, tmp_msginfo);
10633 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10634 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
10635 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10637 tmp_msginfo->folder = orig_msginfo->folder;
10638 tmp_msginfo->msgnum = orig_msginfo->msgnum;
10639 if (orig_msginfo->tags)
10640 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10645 if (!opening_multiple)
10646 body = messageview_get_selection(msgview);
10649 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10650 procmsg_msginfo_free(tmp_msginfo);
10651 g_slist_free(new_msglist);
10653 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10655 if (compose && originally_enc) {
10656 compose_force_encryption(compose, compose->account, FALSE, s_system);
10659 if (compose && originally_sig && compose->account->default_sign_reply) {
10660 compose_force_signing(compose, compose->account, s_system);
10666 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
10669 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
10670 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10671 GSList *cur = msginfo_list;
10672 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10673 "messages. Opening the windows "
10674 "could take some time. Do you "
10675 "want to continue?"),
10676 g_slist_length(msginfo_list));
10677 if (g_slist_length(msginfo_list) > 9
10678 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10679 != G_ALERTALTERNATE) {
10684 /* We'll open multiple compose windows */
10685 /* let the WM place the next windows */
10686 compose_force_window_origin = FALSE;
10687 for (; cur; cur = cur->next) {
10689 tmplist.data = cur->data;
10690 tmplist.next = NULL;
10691 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10693 compose_force_window_origin = TRUE;
10695 /* forwarding multiple mails as attachments is done via a
10696 * single compose window */
10697 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10701 void compose_set_position(Compose *compose, gint pos)
10703 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10705 gtkut_text_view_set_position(text, pos);
10708 gboolean compose_search_string(Compose *compose,
10709 const gchar *str, gboolean case_sens)
10711 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10713 return gtkut_text_view_search_string(text, str, case_sens);
10716 gboolean compose_search_string_backward(Compose *compose,
10717 const gchar *str, gboolean case_sens)
10719 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10721 return gtkut_text_view_search_string_backward(text, str, case_sens);
10724 /* allocate a msginfo structure and populate its data from a compose data structure */
10725 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10727 MsgInfo *newmsginfo;
10729 gchar buf[BUFFSIZE];
10731 g_return_val_if_fail( compose != NULL, NULL );
10733 newmsginfo = procmsg_msginfo_new();
10736 get_rfc822_date(buf, sizeof(buf));
10737 newmsginfo->date = g_strdup(buf);
10740 if (compose->from_name) {
10741 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10742 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10746 if (compose->subject_entry)
10747 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10749 /* to, cc, reply-to, newsgroups */
10750 for (list = compose->header_list; list; list = list->next) {
10751 gchar *header = gtk_editable_get_chars(
10753 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10754 gchar *entry = gtk_editable_get_chars(
10755 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10757 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10758 if ( newmsginfo->to == NULL ) {
10759 newmsginfo->to = g_strdup(entry);
10760 } else if (entry && *entry) {
10761 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10762 g_free(newmsginfo->to);
10763 newmsginfo->to = tmp;
10766 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10767 if ( newmsginfo->cc == NULL ) {
10768 newmsginfo->cc = g_strdup(entry);
10769 } else if (entry && *entry) {
10770 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10771 g_free(newmsginfo->cc);
10772 newmsginfo->cc = tmp;
10775 if ( strcasecmp(header,
10776 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10777 if ( newmsginfo->newsgroups == NULL ) {
10778 newmsginfo->newsgroups = g_strdup(entry);
10779 } else if (entry && *entry) {
10780 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10781 g_free(newmsginfo->newsgroups);
10782 newmsginfo->newsgroups = tmp;
10790 /* other data is unset */
10796 /* update compose's dictionaries from folder dict settings */
10797 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10798 FolderItem *folder_item)
10800 g_return_if_fail(compose != NULL);
10802 if (compose->gtkaspell && folder_item && folder_item->prefs) {
10803 FolderItemPrefs *prefs = folder_item->prefs;
10805 if (prefs->enable_default_dictionary)
10806 gtkaspell_change_dict(compose->gtkaspell,
10807 prefs->default_dictionary, FALSE);
10808 if (folder_item->prefs->enable_default_alt_dictionary)
10809 gtkaspell_change_alt_dict(compose->gtkaspell,
10810 prefs->default_alt_dictionary);
10811 if (prefs->enable_default_dictionary
10812 || prefs->enable_default_alt_dictionary)
10813 compose_spell_menu_changed(compose);