2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 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/>.
22 #include "claws-features.h"
27 #ifndef PANGO_ENABLE_ENGINE
28 # define PANGO_ENABLE_ENGINE
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
36 #include <pango/pango-break.h>
41 #include <sys/types.h>
47 # include <sys/wait.h>
51 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
62 #include "mainwindow.h"
64 #ifndef USE_NEW_ADDRBOOK
65 #include "addressbook.h"
67 #include "addressbook-dbus.h"
68 #include "addressadd.h"
70 #include "folderview.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
83 #include "procheader.h"
85 #include "statusbar.h"
88 #include "quoted-printable.h"
92 #include "gtkshruler.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
101 #include "foldersel.h"
104 #include "message_search.h"
105 #include "combobox.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
123 #define N_ATTACH_COLS (N_COL_COLUMNS)
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
145 PRIORITY_HIGHEST = 1,
154 COMPOSE_INSERT_SUCCESS,
155 COMPOSE_INSERT_READ_ERROR,
156 COMPOSE_INSERT_INVALID_CHARACTER,
157 COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
162 COMPOSE_WRITE_FOR_SEND,
163 COMPOSE_WRITE_FOR_STORE
168 COMPOSE_QUOTE_FORCED,
175 SUBJECT_FIELD_PRESENT,
180 #define B64_LINE_SIZE 57
181 #define B64_BUFFSIZE 77
183 #define MAX_REFERENCES_LEN 999
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
188 static Compose *compose_generic_new (PrefsAccount *account,
192 GList *listAddress );
194 static Compose *compose_create (PrefsAccount *account,
199 static void compose_entry_mark_default_to (Compose *compose,
200 const gchar *address);
201 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
202 ComposeQuoteMode quote_mode,
206 static Compose *compose_forward_multiple (PrefsAccount *account,
207 GSList *msginfo_list);
208 static Compose *compose_reply (MsgInfo *msginfo,
209 ComposeQuoteMode quote_mode,
214 static Compose *compose_reply_mode (ComposeMode mode,
215 GSList *msginfo_list,
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
220 static GtkWidget *compose_account_option_menu_create
222 static void compose_set_out_encoding (Compose *compose);
223 static void compose_set_template_menu (Compose *compose);
224 static void compose_destroy (Compose *compose);
226 static MailField compose_entries_set (Compose *compose,
228 ComposeEntryType to_type);
229 static gint compose_parse_header (Compose *compose,
231 static gint compose_parse_manual_headers (Compose *compose,
233 HeaderEntry *entries);
234 static gchar *compose_parse_references (const gchar *ref,
237 static gchar *compose_quote_fmt (Compose *compose,
243 gboolean need_unescape,
244 const gchar *err_msg);
246 static void compose_reply_set_entry (Compose *compose,
252 followup_and_reply_to);
253 static void compose_reedit_set_entry (Compose *compose,
256 static void compose_insert_sig (Compose *compose,
258 static ComposeInsertResult compose_insert_file (Compose *compose,
261 static gboolean compose_attach_append (Compose *compose,
264 const gchar *content_type,
265 const gchar *charset);
266 static void compose_attach_parts (Compose *compose,
269 static gboolean compose_beautify_paragraph (Compose *compose,
270 GtkTextIter *par_iter,
272 static void compose_wrap_all (Compose *compose);
273 static void compose_wrap_all_full (Compose *compose,
276 static void compose_set_title (Compose *compose);
277 static void compose_select_account (Compose *compose,
278 PrefsAccount *account,
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send (Compose *compose); */
283 static gboolean compose_check_for_valid_recipient
285 static gboolean compose_check_entries (Compose *compose,
286 gboolean check_everything);
287 static gint compose_write_to_file (Compose *compose,
290 gboolean attach_parts);
291 static gint compose_write_body_to_file (Compose *compose,
293 static gint compose_remove_reedit_target (Compose *compose,
295 static void compose_remove_draft (Compose *compose);
296 static gint compose_queue_sub (Compose *compose,
300 gboolean check_subject,
301 gboolean remove_reedit_target);
302 static int compose_add_attachments (Compose *compose,
304 static gchar *compose_get_header (Compose *compose);
305 static gchar *compose_get_manual_headers_info (Compose *compose);
307 static void compose_convert_header (Compose *compose,
312 gboolean addr_field);
314 static void compose_attach_info_free (AttachInfo *ainfo);
315 static void compose_attach_remove_selected (GtkAction *action,
318 static void compose_template_apply (Compose *compose,
321 static void compose_attach_property (GtkAction *action,
323 static void compose_attach_property_create (gboolean *cancelled);
324 static void attach_property_ok (GtkWidget *widget,
325 gboolean *cancelled);
326 static void attach_property_cancel (GtkWidget *widget,
327 gboolean *cancelled);
328 static gint attach_property_delete_event (GtkWidget *widget,
330 gboolean *cancelled);
331 static gboolean attach_property_key_pressed (GtkWidget *widget,
333 gboolean *cancelled);
335 static void compose_exec_ext_editor (Compose *compose);
337 static gint compose_exec_ext_editor_real (const gchar *file);
338 static gboolean compose_ext_editor_kill (Compose *compose);
339 static gboolean compose_input_cb (GIOChannel *source,
340 GIOCondition condition,
342 static void compose_set_ext_editor_sensitive (Compose *compose,
344 #endif /* G_OS_UNIX */
346 static void compose_undo_state_changed (UndoMain *undostruct,
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry (Compose *compose, const gchar *header,
353 gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
356 static void compose_update_priority_menu_item(Compose * compose);
358 static void compose_spell_menu_changed (void *data);
359 static void compose_dict_changed (void *data);
361 static void compose_add_field_list ( Compose *compose,
362 GList *listAddress );
364 /* callback functions */
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367 GtkAllocation *allocation,
369 static gboolean compose_edit_size_alloc (GtkEditable *widget,
370 GtkAllocation *allocation,
371 GtkSHRuler *shruler);
372 static void account_activated (GtkComboBox *optmenu,
374 static void attach_selected (GtkTreeView *tree_view,
375 GtkTreePath *tree_path,
376 GtkTreeViewColumn *column,
378 static gboolean attach_button_pressed (GtkWidget *widget,
379 GdkEventButton *event,
381 static gboolean attach_key_pressed (GtkWidget *widget,
384 static void compose_send_cb (GtkAction *action, gpointer data);
385 static void compose_send_later_cb (GtkAction *action, gpointer data);
387 static void compose_save_cb (GtkAction *action,
390 static void compose_attach_cb (GtkAction *action,
392 static void compose_insert_file_cb (GtkAction *action,
394 static void compose_insert_sig_cb (GtkAction *action,
397 static void compose_close_cb (GtkAction *action,
399 static void compose_print_cb (GtkAction *action,
402 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
404 static void compose_address_cb (GtkAction *action,
406 static void about_show_cb (GtkAction *action,
408 static void compose_template_activate_cb(GtkWidget *widget,
411 static void compose_ext_editor_cb (GtkAction *action,
414 static gint compose_delete_cb (GtkWidget *widget,
418 static void compose_undo_cb (GtkAction *action,
420 static void compose_redo_cb (GtkAction *action,
422 static void compose_cut_cb (GtkAction *action,
424 static void compose_copy_cb (GtkAction *action,
426 static void compose_paste_cb (GtkAction *action,
428 static void compose_paste_as_quote_cb (GtkAction *action,
430 static void compose_paste_no_wrap_cb (GtkAction *action,
432 static void compose_paste_wrap_cb (GtkAction *action,
434 static void compose_allsel_cb (GtkAction *action,
437 static void compose_advanced_action_cb (GtkAction *action,
440 static void compose_grab_focus_cb (GtkWidget *widget,
443 static void compose_changed_cb (GtkTextBuffer *textbuf,
446 static void compose_wrap_cb (GtkAction *action,
448 static void compose_wrap_all_cb (GtkAction *action,
450 static void compose_find_cb (GtkAction *action,
452 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
454 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
457 static void compose_toggle_ruler_cb (GtkToggleAction *action,
459 static void compose_toggle_sign_cb (GtkToggleAction *action,
461 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
463 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system (Compose *compose,
466 PrefsAccount *account,
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
472 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
474 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
476 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
478 static void compose_attach_drag_received_cb (GtkWidget *widget,
479 GdkDragContext *drag_context,
482 GtkSelectionData *data,
486 static void compose_insert_drag_received_cb (GtkWidget *widget,
487 GdkDragContext *drag_context,
490 GtkSelectionData *data,
494 static void compose_header_drag_received_cb (GtkWidget *widget,
495 GdkDragContext *drag_context,
498 GtkSelectionData *data,
503 static gboolean compose_drag_drop (GtkWidget *widget,
504 GdkDragContext *drag_context,
506 guint time, gpointer user_data);
507 static gboolean completion_set_focus_to_subject
512 static void text_inserted (GtkTextBuffer *buffer,
517 static Compose *compose_generic_reply(MsgInfo *msginfo,
518 ComposeQuoteMode quote_mode,
522 gboolean followup_and_reply_to,
525 static void compose_headerentry_changed_cb (GtkWidget *entry,
526 ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
529 ComposeHeaderEntry *headerentry);
530 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
531 ComposeHeaderEntry *headerentry);
533 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
535 static void compose_allow_user_actions (Compose *compose, gboolean allow);
537 static void compose_nothing_cb (GtkAction *action, gpointer data)
543 static void compose_check_all (GtkAction *action, gpointer data);
544 static void compose_highlight_all (GtkAction *action, gpointer data);
545 static void compose_check_backwards (GtkAction *action, gpointer data);
546 static void compose_check_forwards_go (GtkAction *action, gpointer data);
549 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
551 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
554 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
555 FolderItem *folder_item);
557 static void compose_attach_update_label(Compose *compose);
558 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
559 gboolean respect_default_to);
560 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
562 static GtkActionEntry compose_popup_entries[] =
564 {"Compose", NULL, "Compose" },
565 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
566 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
567 {"Compose/---", NULL, "---", NULL, NULL, NULL },
568 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
571 static GtkActionEntry compose_entries[] =
573 {"Menu", NULL, "Menu" },
575 {"Message", NULL, N_("_Message") },
576 {"Edit", NULL, N_("_Edit") },
578 {"Spelling", NULL, N_("_Spelling") },
580 {"Options", NULL, N_("_Options") },
581 {"Tools", NULL, N_("_Tools") },
582 {"Help", NULL, N_("_Help") },
584 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
585 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
586 {"Message/---", NULL, "---" },
588 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
589 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
590 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
591 /* {"Message/---", NULL, "---" }, */
592 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
593 /* {"Message/---", NULL, "---" }, */
594 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
595 /* {"Message/---", NULL, "---" }, */
596 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
599 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
600 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
601 {"Edit/---", NULL, "---" },
603 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
604 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
605 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
607 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
608 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
609 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
610 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
612 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
614 {"Edit/Advanced", NULL, N_("A_dvanced") },
615 {"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*/
616 {"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*/
617 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
618 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"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*/
623 {"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*/
624 {"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*/
625 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
626 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
627 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
628 {"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*/
630 /* {"Edit/---", NULL, "---" }, */
631 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
633 /* {"Edit/---", NULL, "---" }, */
634 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
635 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
636 /* {"Edit/---", NULL, "---" }, */
637 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
640 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
641 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
642 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
643 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
645 {"Spelling/---", NULL, "---" },
646 {"Spelling/Options", NULL, N_("_Options") },
651 {"Options/ReplyMode", NULL, N_("Reply _mode") },
652 {"Options/---", NULL, "---" },
653 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
654 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
656 /* {"Options/---", NULL, "---" }, */
658 {"Options/Priority", NULL, N_("_Priority") },
660 {"Options/Encoding", NULL, N_("Character _encoding") },
661 {"Options/Encoding/---", NULL, "---" },
662 #define ENC_ACTION(cs_char,c_char,string) \
663 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
665 {"Options/Encoding/Western", NULL, N_("Western European") },
666 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
667 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
668 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
669 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
670 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
671 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
672 {"Options/Encoding/Korean", NULL, N_("Korean") },
673 {"Options/Encoding/Thai", NULL, N_("Thai") },
676 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
678 {"Tools/Template", NULL, N_("_Template") },
679 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680 {"Tools/Actions", NULL, N_("Actio_ns") },
681 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
684 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
687 static GtkToggleActionEntry compose_toggle_entries[] =
689 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
690 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
691 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
692 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
693 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
694 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
695 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
698 static GtkRadioActionEntry compose_radio_rm_entries[] =
700 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
701 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
702 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
703 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
706 static GtkRadioActionEntry compose_radio_prio_entries[] =
708 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
710 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
711 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
712 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
715 static GtkRadioActionEntry compose_radio_enc_entries[] =
717 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
751 static GtkTargetEntry compose_mime_types[] =
753 {"text/uri-list", 0, 0},
754 {"UTF8_STRING", 0, 0},
758 static gboolean compose_put_existing_to_front(MsgInfo *info)
760 GList *compose_list = compose_get_compose_list();
764 for (elem = compose_list; elem != NULL && elem->data != NULL;
766 Compose *c = (Compose*)elem->data;
768 if (!c->targetinfo || !c->targetinfo->msgid ||
772 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
773 gtkut_window_popup(c->window);
781 static GdkColor quote_color1 =
782 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color2 =
784 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_color3 =
786 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788 static GdkColor quote_bgcolor1 =
789 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor2 =
791 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_bgcolor3 =
793 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
795 static GdkColor signature_color = {
802 static GdkColor uri_color = {
809 static void compose_create_tags(GtkTextView *text, Compose *compose)
811 GtkTextBuffer *buffer;
812 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 #if !GTK_CHECK_VERSION(2, 24, 0)
820 buffer = gtk_text_view_get_buffer(text);
822 if (prefs_common.enable_color) {
823 /* grab the quote colors, converting from an int to a GdkColor */
824 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
826 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
828 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
830 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
832 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
834 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
836 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
838 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
841 signature_color = quote_color1 = quote_color2 = quote_color3 =
842 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
845 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
846 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847 "foreground-gdk", "e_color1,
848 "paragraph-background-gdk", "e_bgcolor1,
850 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
851 "foreground-gdk", "e_color2,
852 "paragraph-background-gdk", "e_bgcolor2,
854 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
855 "foreground-gdk", "e_color3,
856 "paragraph-background-gdk", "e_bgcolor3,
859 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860 "foreground-gdk", "e_color1,
862 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
863 "foreground-gdk", "e_color2,
865 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
866 "foreground-gdk", "e_color3,
870 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
871 "foreground-gdk", &signature_color,
874 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
875 "foreground-gdk", &uri_color,
877 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
878 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881 color[0] = quote_color1;
882 color[1] = quote_color2;
883 color[2] = quote_color3;
884 color[3] = quote_bgcolor1;
885 color[4] = quote_bgcolor2;
886 color[5] = quote_bgcolor3;
887 color[6] = signature_color;
888 color[7] = uri_color;
890 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
891 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
893 for (i = 0; i < 8; i++) {
894 if (success[i] == FALSE) {
895 g_warning("Compose: color allocation failed.\n");
896 quote_color1 = quote_color2 = quote_color3 =
897 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
898 signature_color = uri_color = black;
904 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
907 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
910 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
912 return compose_generic_new(account, mailto, item, NULL, NULL);
915 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
917 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
920 #define SCROLL_TO_CURSOR(compose) { \
921 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
922 gtk_text_view_get_buffer( \
923 GTK_TEXT_VIEW(compose->text))); \
924 gtk_text_view_scroll_mark_onscreen( \
925 GTK_TEXT_VIEW(compose->text), \
929 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
932 if (folderidentifier) {
933 #if !GTK_CHECK_VERSION(2, 24, 0)
934 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
936 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
938 prefs_common.compose_save_to_history = add_history(
939 prefs_common.compose_save_to_history, folderidentifier);
940 #if !GTK_CHECK_VERSION(2, 24, 0)
941 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
942 prefs_common.compose_save_to_history);
944 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
945 prefs_common.compose_save_to_history);
949 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
950 if (folderidentifier)
951 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
953 gtk_entry_set_text(GTK_ENTRY(entry), "");
956 static gchar *compose_get_save_to(Compose *compose)
959 gchar *result = NULL;
960 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
961 result = gtk_editable_get_chars(entry, 0, -1);
964 #if !GTK_CHECK_VERSION(2, 24, 0)
965 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
967 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
969 prefs_common.compose_save_to_history = add_history(
970 prefs_common.compose_save_to_history, result);
971 #if !GTK_CHECK_VERSION(2, 24, 0)
972 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
973 prefs_common.compose_save_to_history);
975 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
976 prefs_common.compose_save_to_history);
982 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
983 GList *attach_files, GList *listAddress )
986 GtkTextView *textview;
987 GtkTextBuffer *textbuf;
989 const gchar *subject_format = NULL;
990 const gchar *body_format = NULL;
991 gchar *mailto_from = NULL;
992 PrefsAccount *mailto_account = NULL;
993 MsgInfo* dummyinfo = NULL;
994 gint cursor_pos = -1;
995 MailField mfield = NO_FIELD_PRESENT;
999 /* check if mailto defines a from */
1000 if (mailto && *mailto != '\0') {
1001 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1002 /* mailto defines a from, check if we can get account prefs from it,
1003 if not, the account prefs will be guessed using other ways, but we'll keep
1006 mailto_account = account_find_from_address(mailto_from, TRUE);
1007 if (mailto_account == NULL) {
1009 Xstrdup_a(tmp_from, mailto_from, return NULL);
1010 extract_address(tmp_from);
1011 mailto_account = account_find_from_address(tmp_from, TRUE);
1015 account = mailto_account;
1018 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1019 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1020 account = account_find_from_id(item->prefs->default_account);
1022 /* if no account prefs set, fallback to the current one */
1023 if (!account) account = cur_account;
1024 cm_return_val_if_fail(account != NULL, NULL);
1026 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1028 /* override from name if mailto asked for it */
1030 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1031 g_free(mailto_from);
1033 /* override from name according to folder properties */
1034 if (item && item->prefs &&
1035 item->prefs->compose_with_format &&
1036 item->prefs->compose_override_from_format &&
1037 *item->prefs->compose_override_from_format != '\0') {
1042 dummyinfo = compose_msginfo_new_from_compose(compose);
1044 /* decode \-escape sequences in the internal representation of the quote format */
1045 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1046 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1049 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1050 compose->gtkaspell);
1052 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1054 quote_fmt_scan_string(tmp);
1057 buf = quote_fmt_get_buffer();
1059 alertpanel_error(_("New message From format error."));
1061 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1062 quote_fmt_reset_vartable();
1067 compose->replyinfo = NULL;
1068 compose->fwdinfo = NULL;
1070 textview = GTK_TEXT_VIEW(compose->text);
1071 textbuf = gtk_text_view_get_buffer(textview);
1072 compose_create_tags(textview, compose);
1074 undo_block(compose->undostruct);
1076 compose_set_dictionaries_from_folder_prefs(compose, item);
1079 if (account->auto_sig)
1080 compose_insert_sig(compose, FALSE);
1081 gtk_text_buffer_get_start_iter(textbuf, &iter);
1082 gtk_text_buffer_place_cursor(textbuf, &iter);
1084 if (account->protocol != A_NNTP) {
1085 if (mailto && *mailto != '\0') {
1086 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1089 compose_set_folder_prefs(compose, item, TRUE);
1091 if (item && item->ret_rcpt) {
1092 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1095 if (mailto && *mailto != '\0') {
1096 if (!strchr(mailto, '@'))
1097 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1099 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1100 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1101 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1102 mfield = TO_FIELD_PRESENT;
1105 * CLAWS: just don't allow return receipt request, even if the user
1106 * may want to send an email. simple but foolproof.
1108 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1110 compose_add_field_list( compose, listAddress );
1112 if (item && item->prefs && item->prefs->compose_with_format) {
1113 subject_format = item->prefs->compose_subject_format;
1114 body_format = item->prefs->compose_body_format;
1115 } else if (account->compose_with_format) {
1116 subject_format = account->compose_subject_format;
1117 body_format = account->compose_body_format;
1118 } else if (prefs_common.compose_with_format) {
1119 subject_format = prefs_common.compose_subject_format;
1120 body_format = prefs_common.compose_body_format;
1123 if (subject_format || body_format) {
1126 && *subject_format != '\0' )
1128 gchar *subject = NULL;
1133 dummyinfo = compose_msginfo_new_from_compose(compose);
1135 /* decode \-escape sequences in the internal representation of the quote format */
1136 tmp = g_malloc(strlen(subject_format)+1);
1137 pref_get_unescaped_pref(tmp, subject_format);
1139 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1141 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1142 compose->gtkaspell);
1144 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1146 quote_fmt_scan_string(tmp);
1149 buf = quote_fmt_get_buffer();
1151 alertpanel_error(_("New message subject format error."));
1153 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1154 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1155 quote_fmt_reset_vartable();
1159 mfield = SUBJECT_FIELD_PRESENT;
1163 && *body_format != '\0' )
1166 GtkTextBuffer *buffer;
1167 GtkTextIter start, end;
1171 dummyinfo = compose_msginfo_new_from_compose(compose);
1173 text = GTK_TEXT_VIEW(compose->text);
1174 buffer = gtk_text_view_get_buffer(text);
1175 gtk_text_buffer_get_start_iter(buffer, &start);
1176 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1177 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1179 compose_quote_fmt(compose, dummyinfo,
1181 NULL, tmp, FALSE, TRUE,
1182 _("The body of the \"New message\" template has an error at line %d."));
1183 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1184 quote_fmt_reset_vartable();
1188 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1189 gtkaspell_highlight_all(compose->gtkaspell);
1191 mfield = BODY_FIELD_PRESENT;
1195 procmsg_msginfo_free( dummyinfo );
1201 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1202 ainfo = (AttachInfo *) curr->data;
1203 compose_attach_append(compose, ainfo->file, ainfo->name,
1204 ainfo->content_type, ainfo->charset);
1208 compose_show_first_last_header(compose, TRUE);
1210 /* Set save folder */
1211 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1212 gchar *folderidentifier;
1214 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1215 folderidentifier = folder_item_get_identifier(item);
1216 compose_set_save_to(compose, folderidentifier);
1217 g_free(folderidentifier);
1220 /* Place cursor according to provided input (mfield) */
1222 case NO_FIELD_PRESENT:
1223 if (compose->header_last)
1224 gtk_widget_grab_focus(compose->header_last->entry);
1226 case TO_FIELD_PRESENT:
1227 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1229 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1232 gtk_widget_grab_focus(compose->subject_entry);
1234 case SUBJECT_FIELD_PRESENT:
1235 textview = GTK_TEXT_VIEW(compose->text);
1238 textbuf = gtk_text_view_get_buffer(textview);
1241 mark = gtk_text_buffer_get_insert(textbuf);
1242 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1243 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1245 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1246 * only defers where it comes to the variable body
1247 * is not null. If no body is present compose->text
1248 * will be null in which case you cannot place the
1249 * cursor inside the component so. An empty component
1250 * is therefore created before placing the cursor
1252 case BODY_FIELD_PRESENT:
1253 cursor_pos = quote_fmt_get_cursor_pos();
1254 if (cursor_pos == -1)
1255 gtk_widget_grab_focus(compose->header_last->entry);
1257 gtk_widget_grab_focus(compose->text);
1261 undo_unblock(compose->undostruct);
1263 if (prefs_common.auto_exteditor)
1264 compose_exec_ext_editor(compose);
1266 compose->draft_timeout_tag = -1;
1267 SCROLL_TO_CURSOR(compose);
1269 compose->modified = FALSE;
1270 compose_set_title(compose);
1272 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1277 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1278 gboolean override_pref, const gchar *system)
1280 const gchar *privacy = NULL;
1282 cm_return_if_fail(compose != NULL);
1283 cm_return_if_fail(account != NULL);
1285 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1290 else if (account->default_privacy_system
1291 && strlen(account->default_privacy_system)) {
1292 privacy = account->default_privacy_system;
1294 GSList *privacy_avail = privacy_get_system_ids();
1295 if (privacy_avail && g_slist_length(privacy_avail)) {
1296 privacy = (gchar *)(privacy_avail->data);
1299 if (privacy != NULL) {
1301 g_free(compose->privacy_system);
1302 compose->privacy_system = NULL;
1304 if (compose->privacy_system == NULL)
1305 compose->privacy_system = g_strdup(privacy);
1306 else if (*(compose->privacy_system) == '\0') {
1307 g_free(compose->privacy_system);
1308 compose->privacy_system = g_strdup(privacy);
1310 compose_update_privacy_system_menu_item(compose, FALSE);
1311 compose_use_encryption(compose, TRUE);
1315 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1317 const gchar *privacy = NULL;
1321 else if (account->default_privacy_system
1322 && strlen(account->default_privacy_system)) {
1323 privacy = account->default_privacy_system;
1325 GSList *privacy_avail = privacy_get_system_ids();
1326 if (privacy_avail && g_slist_length(privacy_avail)) {
1327 privacy = (gchar *)(privacy_avail->data);
1331 if (privacy != NULL) {
1333 g_free(compose->privacy_system);
1334 compose->privacy_system = NULL;
1336 if (compose->privacy_system == NULL)
1337 compose->privacy_system = g_strdup(privacy);
1338 compose_update_privacy_system_menu_item(compose, FALSE);
1339 compose_use_signing(compose, TRUE);
1343 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1347 Compose *compose = NULL;
1349 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1351 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1352 cm_return_val_if_fail(msginfo != NULL, NULL);
1354 list_len = g_slist_length(msginfo_list);
1358 case COMPOSE_REPLY_TO_ADDRESS:
1359 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1360 FALSE, prefs_common.default_reply_list, FALSE, body);
1362 case COMPOSE_REPLY_WITH_QUOTE:
1363 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1364 FALSE, prefs_common.default_reply_list, FALSE, body);
1366 case COMPOSE_REPLY_WITHOUT_QUOTE:
1367 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1368 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1370 case COMPOSE_REPLY_TO_SENDER:
1371 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1372 FALSE, FALSE, TRUE, body);
1374 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1375 compose = compose_followup_and_reply_to(msginfo,
1376 COMPOSE_QUOTE_CHECK,
1377 FALSE, FALSE, body);
1379 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1380 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1381 FALSE, FALSE, TRUE, body);
1383 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1384 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1385 FALSE, FALSE, TRUE, NULL);
1387 case COMPOSE_REPLY_TO_ALL:
1388 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1389 TRUE, FALSE, FALSE, body);
1391 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1392 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1393 TRUE, FALSE, FALSE, body);
1395 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1396 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1397 TRUE, FALSE, FALSE, NULL);
1399 case COMPOSE_REPLY_TO_LIST:
1400 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1401 FALSE, TRUE, FALSE, body);
1403 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1404 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1405 FALSE, TRUE, FALSE, body);
1407 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1408 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1409 FALSE, TRUE, FALSE, NULL);
1411 case COMPOSE_FORWARD:
1412 if (prefs_common.forward_as_attachment) {
1413 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1416 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1420 case COMPOSE_FORWARD_INLINE:
1421 /* check if we reply to more than one Message */
1422 if (list_len == 1) {
1423 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1426 /* more messages FALL THROUGH */
1427 case COMPOSE_FORWARD_AS_ATTACH:
1428 compose = compose_forward_multiple(NULL, msginfo_list);
1430 case COMPOSE_REDIRECT:
1431 compose = compose_redirect(NULL, msginfo, FALSE);
1434 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1437 if (compose == NULL) {
1438 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1442 compose->rmode = mode;
1443 switch (compose->rmode) {
1445 case COMPOSE_REPLY_WITH_QUOTE:
1446 case COMPOSE_REPLY_WITHOUT_QUOTE:
1447 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1448 debug_print("reply mode Normal\n");
1449 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1450 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1452 case COMPOSE_REPLY_TO_SENDER:
1453 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1454 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1455 debug_print("reply mode Sender\n");
1456 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1458 case COMPOSE_REPLY_TO_ALL:
1459 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1460 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1461 debug_print("reply mode All\n");
1462 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1464 case COMPOSE_REPLY_TO_LIST:
1465 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1466 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1467 debug_print("reply mode List\n");
1468 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1470 case COMPOSE_REPLY_TO_ADDRESS:
1471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1479 static Compose *compose_reply(MsgInfo *msginfo,
1480 ComposeQuoteMode quote_mode,
1486 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1487 to_sender, FALSE, body);
1490 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1491 ComposeQuoteMode quote_mode,
1496 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1497 to_sender, TRUE, body);
1500 static void compose_extract_original_charset(Compose *compose)
1502 MsgInfo *info = NULL;
1503 if (compose->replyinfo) {
1504 info = compose->replyinfo;
1505 } else if (compose->fwdinfo) {
1506 info = compose->fwdinfo;
1507 } else if (compose->targetinfo) {
1508 info = compose->targetinfo;
1511 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1512 MimeInfo *partinfo = mimeinfo;
1513 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1514 partinfo = procmime_mimeinfo_next(partinfo);
1516 compose->orig_charset =
1517 g_strdup(procmime_mimeinfo_get_parameter(
1518 partinfo, "charset"));
1520 procmime_mimeinfo_free_all(mimeinfo);
1524 #define SIGNAL_BLOCK(buffer) { \
1525 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1526 G_CALLBACK(compose_changed_cb), \
1528 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1529 G_CALLBACK(text_inserted), \
1533 #define SIGNAL_UNBLOCK(buffer) { \
1534 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1535 G_CALLBACK(compose_changed_cb), \
1537 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1538 G_CALLBACK(text_inserted), \
1542 static Compose *compose_generic_reply(MsgInfo *msginfo,
1543 ComposeQuoteMode quote_mode,
1544 gboolean to_all, gboolean to_ml,
1546 gboolean followup_and_reply_to,
1550 PrefsAccount *account = NULL;
1551 GtkTextView *textview;
1552 GtkTextBuffer *textbuf;
1553 gboolean quote = FALSE;
1554 const gchar *qmark = NULL;
1555 const gchar *body_fmt = NULL;
1556 gchar *s_system = NULL;
1558 cm_return_val_if_fail(msginfo != NULL, NULL);
1559 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1561 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1563 cm_return_val_if_fail(account != NULL, NULL);
1565 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1567 compose->updating = TRUE;
1569 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1570 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1572 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1573 if (!compose->replyinfo)
1574 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1576 compose_extract_original_charset(compose);
1578 if (msginfo->folder && msginfo->folder->ret_rcpt)
1579 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1581 /* Set save folder */
1582 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1583 gchar *folderidentifier;
1585 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1586 folderidentifier = folder_item_get_identifier(msginfo->folder);
1587 compose_set_save_to(compose, folderidentifier);
1588 g_free(folderidentifier);
1591 if (compose_parse_header(compose, msginfo) < 0) {
1592 compose->updating = FALSE;
1593 compose_destroy(compose);
1597 /* override from name according to folder properties */
1598 if (msginfo->folder && msginfo->folder->prefs &&
1599 msginfo->folder->prefs->reply_with_format &&
1600 msginfo->folder->prefs->reply_override_from_format &&
1601 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1606 /* decode \-escape sequences in the internal representation of the quote format */
1607 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1608 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1611 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1612 compose->gtkaspell);
1614 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1616 quote_fmt_scan_string(tmp);
1619 buf = quote_fmt_get_buffer();
1621 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1623 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1624 quote_fmt_reset_vartable();
1629 textview = (GTK_TEXT_VIEW(compose->text));
1630 textbuf = gtk_text_view_get_buffer(textview);
1631 compose_create_tags(textview, compose);
1633 undo_block(compose->undostruct);
1635 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1636 gtkaspell_block_check(compose->gtkaspell);
1639 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1640 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1641 /* use the reply format of folder (if enabled), or the account's one
1642 (if enabled) or fallback to the global reply format, which is always
1643 enabled (even if empty), and use the relevant quotemark */
1645 if (msginfo->folder && msginfo->folder->prefs &&
1646 msginfo->folder->prefs->reply_with_format) {
1647 qmark = msginfo->folder->prefs->reply_quotemark;
1648 body_fmt = msginfo->folder->prefs->reply_body_format;
1650 } else if (account->reply_with_format) {
1651 qmark = account->reply_quotemark;
1652 body_fmt = account->reply_body_format;
1655 qmark = prefs_common.quotemark;
1656 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1657 body_fmt = gettext(prefs_common.quotefmt);
1664 /* empty quotemark is not allowed */
1665 if (qmark == NULL || *qmark == '\0')
1667 compose_quote_fmt(compose, compose->replyinfo,
1668 body_fmt, qmark, body, FALSE, TRUE,
1669 _("The body of the \"Reply\" template has an error at line %d."));
1670 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1671 quote_fmt_reset_vartable();
1674 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1675 compose_force_encryption(compose, account, FALSE, s_system);
1678 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1679 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1680 compose_force_signing(compose, account, s_system);
1684 SIGNAL_BLOCK(textbuf);
1686 if (account->auto_sig)
1687 compose_insert_sig(compose, FALSE);
1689 compose_wrap_all(compose);
1692 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1693 gtkaspell_highlight_all(compose->gtkaspell);
1694 gtkaspell_unblock_check(compose->gtkaspell);
1696 SIGNAL_UNBLOCK(textbuf);
1698 gtk_widget_grab_focus(compose->text);
1700 undo_unblock(compose->undostruct);
1702 if (prefs_common.auto_exteditor)
1703 compose_exec_ext_editor(compose);
1705 compose->modified = FALSE;
1706 compose_set_title(compose);
1708 compose->updating = FALSE;
1709 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1710 SCROLL_TO_CURSOR(compose);
1712 if (compose->deferred_destroy) {
1713 compose_destroy(compose);
1721 #define INSERT_FW_HEADER(var, hdr) \
1722 if (msginfo->var && *msginfo->var) { \
1723 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1724 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1725 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1728 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1729 gboolean as_attach, const gchar *body,
1730 gboolean no_extedit,
1734 GtkTextView *textview;
1735 GtkTextBuffer *textbuf;
1736 gint cursor_pos = -1;
1739 cm_return_val_if_fail(msginfo != NULL, NULL);
1740 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1743 !(account = compose_guess_forward_account_from_msginfo
1745 account = cur_account;
1747 if (!prefs_common.forward_as_attachment)
1748 mode = COMPOSE_FORWARD_INLINE;
1750 mode = COMPOSE_FORWARD;
1751 compose = compose_create(account, msginfo->folder, mode, batch);
1753 compose->updating = TRUE;
1754 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1755 if (!compose->fwdinfo)
1756 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1758 compose_extract_original_charset(compose);
1760 if (msginfo->subject && *msginfo->subject) {
1761 gchar *buf, *buf2, *p;
1763 buf = p = g_strdup(msginfo->subject);
1764 p += subject_get_prefix_length(p);
1765 memmove(buf, p, strlen(p) + 1);
1767 buf2 = g_strdup_printf("Fw: %s", buf);
1768 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1774 /* override from name according to folder properties */
1775 if (msginfo->folder && msginfo->folder->prefs &&
1776 msginfo->folder->prefs->forward_with_format &&
1777 msginfo->folder->prefs->forward_override_from_format &&
1778 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1782 MsgInfo *full_msginfo = NULL;
1785 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1787 full_msginfo = procmsg_msginfo_copy(msginfo);
1789 /* decode \-escape sequences in the internal representation of the quote format */
1790 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1791 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1794 gtkaspell_block_check(compose->gtkaspell);
1795 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1796 compose->gtkaspell);
1798 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1800 quote_fmt_scan_string(tmp);
1803 buf = quote_fmt_get_buffer();
1805 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1807 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1808 quote_fmt_reset_vartable();
1811 procmsg_msginfo_free(full_msginfo);
1814 textview = GTK_TEXT_VIEW(compose->text);
1815 textbuf = gtk_text_view_get_buffer(textview);
1816 compose_create_tags(textview, compose);
1818 undo_block(compose->undostruct);
1822 msgfile = procmsg_get_message_file(msginfo);
1823 if (!is_file_exist(msgfile))
1824 g_warning("%s: file not exist\n", msgfile);
1826 compose_attach_append(compose, msgfile, msgfile,
1827 "message/rfc822", NULL);
1831 const gchar *qmark = NULL;
1832 const gchar *body_fmt = NULL;
1833 MsgInfo *full_msginfo;
1835 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1837 full_msginfo = procmsg_msginfo_copy(msginfo);
1839 /* use the forward format of folder (if enabled), or the account's one
1840 (if enabled) or fallback to the global forward format, which is always
1841 enabled (even if empty), and use the relevant quotemark */
1842 if (msginfo->folder && msginfo->folder->prefs &&
1843 msginfo->folder->prefs->forward_with_format) {
1844 qmark = msginfo->folder->prefs->forward_quotemark;
1845 body_fmt = msginfo->folder->prefs->forward_body_format;
1847 } else if (account->forward_with_format) {
1848 qmark = account->forward_quotemark;
1849 body_fmt = account->forward_body_format;
1852 qmark = prefs_common.fw_quotemark;
1853 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1854 body_fmt = gettext(prefs_common.fw_quotefmt);
1859 /* empty quotemark is not allowed */
1860 if (qmark == NULL || *qmark == '\0')
1863 compose_quote_fmt(compose, full_msginfo,
1864 body_fmt, qmark, body, FALSE, TRUE,
1865 _("The body of the \"Forward\" template has an error at line %d."));
1866 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1867 quote_fmt_reset_vartable();
1868 compose_attach_parts(compose, msginfo);
1870 procmsg_msginfo_free(full_msginfo);
1873 SIGNAL_BLOCK(textbuf);
1875 if (account->auto_sig)
1876 compose_insert_sig(compose, FALSE);
1878 compose_wrap_all(compose);
1881 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1882 gtkaspell_highlight_all(compose->gtkaspell);
1883 gtkaspell_unblock_check(compose->gtkaspell);
1885 SIGNAL_UNBLOCK(textbuf);
1887 cursor_pos = quote_fmt_get_cursor_pos();
1888 if (cursor_pos == -1)
1889 gtk_widget_grab_focus(compose->header_last->entry);
1891 gtk_widget_grab_focus(compose->text);
1893 if (!no_extedit && prefs_common.auto_exteditor)
1894 compose_exec_ext_editor(compose);
1897 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1898 gchar *folderidentifier;
1900 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1901 folderidentifier = folder_item_get_identifier(msginfo->folder);
1902 compose_set_save_to(compose, folderidentifier);
1903 g_free(folderidentifier);
1906 undo_unblock(compose->undostruct);
1908 compose->modified = FALSE;
1909 compose_set_title(compose);
1911 compose->updating = FALSE;
1912 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1913 SCROLL_TO_CURSOR(compose);
1915 if (compose->deferred_destroy) {
1916 compose_destroy(compose);
1920 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1925 #undef INSERT_FW_HEADER
1927 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1930 GtkTextView *textview;
1931 GtkTextBuffer *textbuf;
1935 gboolean single_mail = TRUE;
1937 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1939 if (g_slist_length(msginfo_list) > 1)
1940 single_mail = FALSE;
1942 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1943 if (((MsgInfo *)msginfo->data)->folder == NULL)
1946 /* guess account from first selected message */
1948 !(account = compose_guess_forward_account_from_msginfo
1949 (msginfo_list->data)))
1950 account = cur_account;
1952 cm_return_val_if_fail(account != NULL, NULL);
1954 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1955 if (msginfo->data) {
1956 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1957 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1961 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1962 g_warning("no msginfo_list");
1966 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1968 compose->updating = TRUE;
1970 /* override from name according to folder properties */
1971 if (msginfo_list->data) {
1972 MsgInfo *msginfo = msginfo_list->data;
1974 if (msginfo->folder && msginfo->folder->prefs &&
1975 msginfo->folder->prefs->forward_with_format &&
1976 msginfo->folder->prefs->forward_override_from_format &&
1977 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1982 /* decode \-escape sequences in the internal representation of the quote format */
1983 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1984 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1987 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1988 compose->gtkaspell);
1990 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1992 quote_fmt_scan_string(tmp);
1995 buf = quote_fmt_get_buffer();
1997 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1999 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2000 quote_fmt_reset_vartable();
2006 textview = GTK_TEXT_VIEW(compose->text);
2007 textbuf = gtk_text_view_get_buffer(textview);
2008 compose_create_tags(textview, compose);
2010 undo_block(compose->undostruct);
2011 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2012 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2014 if (!is_file_exist(msgfile))
2015 g_warning("%s: file not exist\n", msgfile);
2017 compose_attach_append(compose, msgfile, msgfile,
2018 "message/rfc822", NULL);
2023 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2024 if (info->subject && *info->subject) {
2025 gchar *buf, *buf2, *p;
2027 buf = p = g_strdup(info->subject);
2028 p += subject_get_prefix_length(p);
2029 memmove(buf, p, strlen(p) + 1);
2031 buf2 = g_strdup_printf("Fw: %s", buf);
2032 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2038 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2039 _("Fw: multiple emails"));
2042 SIGNAL_BLOCK(textbuf);
2044 if (account->auto_sig)
2045 compose_insert_sig(compose, FALSE);
2047 compose_wrap_all(compose);
2049 SIGNAL_UNBLOCK(textbuf);
2051 gtk_text_buffer_get_start_iter(textbuf, &iter);
2052 gtk_text_buffer_place_cursor(textbuf, &iter);
2054 gtk_widget_grab_focus(compose->header_last->entry);
2055 undo_unblock(compose->undostruct);
2056 compose->modified = FALSE;
2057 compose_set_title(compose);
2059 compose->updating = FALSE;
2060 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2061 SCROLL_TO_CURSOR(compose);
2063 if (compose->deferred_destroy) {
2064 compose_destroy(compose);
2068 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2073 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2075 GtkTextIter start = *iter;
2076 GtkTextIter end_iter;
2077 int start_pos = gtk_text_iter_get_offset(&start);
2079 if (!compose->account->sig_sep)
2082 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2083 start_pos+strlen(compose->account->sig_sep));
2085 /* check sig separator */
2086 str = gtk_text_iter_get_text(&start, &end_iter);
2087 if (!strcmp(str, compose->account->sig_sep)) {
2089 /* check end of line (\n) */
2090 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2091 start_pos+strlen(compose->account->sig_sep));
2092 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2093 start_pos+strlen(compose->account->sig_sep)+1);
2094 tmp = gtk_text_iter_get_text(&start, &end_iter);
2095 if (!strcmp(tmp,"\n")) {
2107 static void compose_colorize_signature(Compose *compose)
2109 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2111 GtkTextIter end_iter;
2112 gtk_text_buffer_get_start_iter(buffer, &iter);
2113 while (gtk_text_iter_forward_line(&iter))
2114 if (compose_is_sig_separator(compose, buffer, &iter)) {
2115 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2116 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2120 #define BLOCK_WRAP() { \
2121 prev_autowrap = compose->autowrap; \
2122 buffer = gtk_text_view_get_buffer( \
2123 GTK_TEXT_VIEW(compose->text)); \
2124 compose->autowrap = FALSE; \
2126 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2127 G_CALLBACK(compose_changed_cb), \
2129 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2130 G_CALLBACK(text_inserted), \
2133 #define UNBLOCK_WRAP() { \
2134 compose->autowrap = prev_autowrap; \
2135 if (compose->autowrap) { \
2136 gint old = compose->draft_timeout_tag; \
2137 compose->draft_timeout_tag = -2; \
2138 compose_wrap_all(compose); \
2139 compose->draft_timeout_tag = old; \
2142 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2143 G_CALLBACK(compose_changed_cb), \
2145 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2146 G_CALLBACK(text_inserted), \
2150 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2152 Compose *compose = NULL;
2153 PrefsAccount *account = NULL;
2154 GtkTextView *textview;
2155 GtkTextBuffer *textbuf;
2159 gchar buf[BUFFSIZE];
2160 gboolean use_signing = FALSE;
2161 gboolean use_encryption = FALSE;
2162 gchar *privacy_system = NULL;
2163 int priority = PRIORITY_NORMAL;
2164 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2165 gboolean autowrap = prefs_common.autowrap;
2166 gboolean autoindent = prefs_common.auto_indent;
2167 HeaderEntry *manual_headers = NULL;
2169 cm_return_val_if_fail(msginfo != NULL, NULL);
2170 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2172 if (compose_put_existing_to_front(msginfo)) {
2176 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2177 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2178 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2179 gchar queueheader_buf[BUFFSIZE];
2182 /* Select Account from queue headers */
2183 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2184 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2185 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2186 account = account_find_from_id(id);
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2190 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2191 account = account_find_from_id(id);
2193 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2194 sizeof(queueheader_buf), "NAID:")) {
2195 id = atoi(&queueheader_buf[strlen("NAID:")]);
2196 account = account_find_from_id(id);
2198 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "MAID:")) {
2200 id = atoi(&queueheader_buf[strlen("MAID:")]);
2201 account = account_find_from_id(id);
2203 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "S:")) {
2205 account = account_find_from_address(queueheader_buf, FALSE);
2207 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2208 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2209 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2210 use_signing = param;
2213 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2214 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2215 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2216 use_signing = param;
2219 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2220 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2221 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2222 use_encryption = param;
2224 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2225 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2226 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2227 use_encryption = param;
2229 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2230 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2231 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2234 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2235 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2236 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2239 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2240 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2241 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2243 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2244 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2245 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2247 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2248 sizeof(queueheader_buf), "X-Priority: ")) {
2249 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2252 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2253 sizeof(queueheader_buf), "RMID:")) {
2254 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2255 if (tokens[0] && tokens[1] && tokens[2]) {
2256 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2257 if (orig_item != NULL) {
2258 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2263 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2264 sizeof(queueheader_buf), "FMID:")) {
2265 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2266 if (tokens[0] && tokens[1] && tokens[2]) {
2267 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2268 if (orig_item != NULL) {
2269 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2274 /* Get manual headers */
2275 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2276 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2277 if (*listmh != '\0') {
2278 debug_print("Got manual headers: %s\n", listmh);
2279 manual_headers = procheader_entries_from_str(listmh);
2284 account = msginfo->folder->folder->account;
2287 if (!account && prefs_common.reedit_account_autosel) {
2288 gchar from[BUFFSIZE];
2289 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2290 extract_address(from);
2291 account = account_find_from_address(from, FALSE);
2295 account = cur_account;
2297 cm_return_val_if_fail(account != NULL, NULL);
2299 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2301 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2302 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2303 compose->autowrap = autowrap;
2304 compose->replyinfo = replyinfo;
2305 compose->fwdinfo = fwdinfo;
2307 compose->updating = TRUE;
2308 compose->priority = priority;
2310 if (privacy_system != NULL) {
2311 compose->privacy_system = privacy_system;
2312 compose_use_signing(compose, use_signing);
2313 compose_use_encryption(compose, use_encryption);
2314 compose_update_privacy_system_menu_item(compose, FALSE);
2316 activate_privacy_system(compose, account, FALSE);
2319 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2321 compose_extract_original_charset(compose);
2323 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2324 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2325 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2326 gchar queueheader_buf[BUFFSIZE];
2328 /* Set message save folder */
2329 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2330 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2331 compose_set_save_to(compose, &queueheader_buf[4]);
2333 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2334 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2336 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2341 if (compose_parse_header(compose, msginfo) < 0) {
2342 compose->updating = FALSE;
2343 compose_destroy(compose);
2346 compose_reedit_set_entry(compose, msginfo);
2348 textview = GTK_TEXT_VIEW(compose->text);
2349 textbuf = gtk_text_view_get_buffer(textview);
2350 compose_create_tags(textview, compose);
2352 mark = gtk_text_buffer_get_insert(textbuf);
2353 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2355 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2356 G_CALLBACK(compose_changed_cb),
2359 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2360 fp = procmime_get_first_encrypted_text_content(msginfo);
2362 compose_force_encryption(compose, account, TRUE, NULL);
2365 fp = procmime_get_first_text_content(msginfo);
2368 g_warning("Can't get text part\n");
2372 gboolean prev_autowrap;
2373 GtkTextBuffer *buffer;
2375 while (fgets(buf, sizeof(buf), fp) != NULL) {
2377 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2383 compose_attach_parts(compose, msginfo);
2385 compose_colorize_signature(compose);
2387 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2388 G_CALLBACK(compose_changed_cb),
2391 if (manual_headers != NULL) {
2392 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2393 procheader_entries_free(manual_headers);
2394 compose->updating = FALSE;
2395 compose_destroy(compose);
2398 procheader_entries_free(manual_headers);
2401 gtk_widget_grab_focus(compose->text);
2403 if (prefs_common.auto_exteditor) {
2404 compose_exec_ext_editor(compose);
2406 compose->modified = FALSE;
2407 compose_set_title(compose);
2409 compose->updating = FALSE;
2410 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2411 SCROLL_TO_CURSOR(compose);
2413 if (compose->deferred_destroy) {
2414 compose_destroy(compose);
2418 compose->sig_str = account_get_signature_str(compose->account);
2420 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2425 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2432 cm_return_val_if_fail(msginfo != NULL, NULL);
2435 account = account_get_reply_account(msginfo,
2436 prefs_common.reply_account_autosel);
2437 cm_return_val_if_fail(account != NULL, NULL);
2439 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2441 compose->updating = TRUE;
2443 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2444 compose->replyinfo = NULL;
2445 compose->fwdinfo = NULL;
2447 compose_show_first_last_header(compose, TRUE);
2449 gtk_widget_grab_focus(compose->header_last->entry);
2451 filename = procmsg_get_message_file(msginfo);
2453 if (filename == NULL) {
2454 compose->updating = FALSE;
2455 compose_destroy(compose);
2460 compose->redirect_filename = filename;
2462 /* Set save folder */
2463 item = msginfo->folder;
2464 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2465 gchar *folderidentifier;
2467 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2468 folderidentifier = folder_item_get_identifier(item);
2469 compose_set_save_to(compose, folderidentifier);
2470 g_free(folderidentifier);
2473 compose_attach_parts(compose, msginfo);
2475 if (msginfo->subject)
2476 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2478 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2480 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2481 _("The body of the \"Redirect\" template has an error at line %d."));
2482 quote_fmt_reset_vartable();
2483 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2485 compose_colorize_signature(compose);
2488 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2489 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2490 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2492 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2493 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2494 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2495 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2496 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2497 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2498 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2499 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2501 if (compose->toolbar->draft_btn)
2502 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2503 if (compose->toolbar->insert_btn)
2504 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2505 if (compose->toolbar->attach_btn)
2506 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2507 if (compose->toolbar->sig_btn)
2508 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2509 if (compose->toolbar->exteditor_btn)
2510 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2511 if (compose->toolbar->linewrap_current_btn)
2512 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2513 if (compose->toolbar->linewrap_all_btn)
2514 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2516 compose->modified = FALSE;
2517 compose_set_title(compose);
2518 compose->updating = FALSE;
2519 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2520 SCROLL_TO_CURSOR(compose);
2522 if (compose->deferred_destroy) {
2523 compose_destroy(compose);
2527 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2532 GList *compose_get_compose_list(void)
2534 return compose_list;
2537 void compose_entry_append(Compose *compose, const gchar *address,
2538 ComposeEntryType type, ComposePrefType pref_type)
2540 const gchar *header;
2542 gboolean in_quote = FALSE;
2543 if (!address || *address == '\0') return;
2550 header = N_("Bcc:");
2552 case COMPOSE_REPLYTO:
2553 header = N_("Reply-To:");
2555 case COMPOSE_NEWSGROUPS:
2556 header = N_("Newsgroups:");
2558 case COMPOSE_FOLLOWUPTO:
2559 header = N_( "Followup-To:");
2561 case COMPOSE_INREPLYTO:
2562 header = N_( "In-Reply-To:");
2569 header = prefs_common_translated_header_name(header);
2571 cur = begin = (gchar *)address;
2573 /* we separate the line by commas, but not if we're inside a quoted
2575 while (*cur != '\0') {
2577 in_quote = !in_quote;
2578 if (*cur == ',' && !in_quote) {
2579 gchar *tmp = g_strdup(begin);
2581 tmp[cur-begin]='\0';
2584 while (*tmp == ' ' || *tmp == '\t')
2586 compose_add_header_entry(compose, header, tmp, pref_type);
2593 gchar *tmp = g_strdup(begin);
2595 tmp[cur-begin]='\0';
2596 while (*tmp == ' ' || *tmp == '\t')
2598 compose_add_header_entry(compose, header, tmp, pref_type);
2603 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2605 #if !GTK_CHECK_VERSION(3, 0, 0)
2606 static GdkColor yellow;
2607 static GdkColor black;
2608 static gboolean yellow_initialised = FALSE;
2610 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2611 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2616 #if !GTK_CHECK_VERSION(3, 0, 0)
2617 if (!yellow_initialised) {
2618 gdk_color_parse("#f5f6be", &yellow);
2619 gdk_color_parse("#000000", &black);
2620 yellow_initialised = gdk_colormap_alloc_color(
2621 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2622 yellow_initialised &= gdk_colormap_alloc_color(
2623 gdk_colormap_get_system(), &black, FALSE, TRUE);
2627 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2628 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2629 if (gtk_entry_get_text(entry) &&
2630 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2631 #if !GTK_CHECK_VERSION(3, 0, 0)
2632 if (yellow_initialised) {
2634 gtk_widget_modify_base(
2635 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2636 GTK_STATE_NORMAL, &yellow);
2637 gtk_widget_modify_text(
2638 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2639 GTK_STATE_NORMAL, &black);
2640 #if !GTK_CHECK_VERSION(3, 0, 0)
2647 void compose_toolbar_cb(gint action, gpointer data)
2649 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2650 Compose *compose = (Compose*)toolbar_item->parent;
2652 cm_return_if_fail(compose != NULL);
2656 compose_send_cb(NULL, compose);
2659 compose_send_later_cb(NULL, compose);
2662 compose_draft(compose, COMPOSE_QUIT_EDITING);
2665 compose_insert_file_cb(NULL, compose);
2668 compose_attach_cb(NULL, compose);
2671 compose_insert_sig(compose, FALSE);
2674 compose_ext_editor_cb(NULL, compose);
2676 case A_LINEWRAP_CURRENT:
2677 compose_beautify_paragraph(compose, NULL, TRUE);
2679 case A_LINEWRAP_ALL:
2680 compose_wrap_all_full(compose, TRUE);
2683 compose_address_cb(NULL, compose);
2686 case A_CHECK_SPELLING:
2687 compose_check_all(NULL, compose);
2695 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2700 gchar *subject = NULL;
2704 gchar **attach = NULL;
2705 gchar *inreplyto = NULL;
2706 MailField mfield = NO_FIELD_PRESENT;
2708 /* get mailto parts but skip from */
2709 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2712 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2713 mfield = TO_FIELD_PRESENT;
2716 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2718 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2720 if (!g_utf8_validate (subject, -1, NULL)) {
2721 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2722 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2725 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2727 mfield = SUBJECT_FIELD_PRESENT;
2730 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2731 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2734 gboolean prev_autowrap = compose->autowrap;
2736 compose->autowrap = FALSE;
2738 mark = gtk_text_buffer_get_insert(buffer);
2739 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2741 if (!g_utf8_validate (body, -1, NULL)) {
2742 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2743 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2746 gtk_text_buffer_insert(buffer, &iter, body, -1);
2748 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2750 compose->autowrap = prev_autowrap;
2751 if (compose->autowrap)
2752 compose_wrap_all(compose);
2753 mfield = BODY_FIELD_PRESENT;
2757 gint i = 0, att = 0;
2758 gchar *warn_files = NULL;
2759 while (attach[i] != NULL) {
2760 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2761 if (utf8_filename) {
2762 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2763 gchar *tmp = g_strdup_printf("%s%s\n",
2764 warn_files?warn_files:"",
2770 g_free(utf8_filename);
2772 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2777 alertpanel_notice(ngettext(
2778 "The following file has been attached: \n%s",
2779 "The following files have been attached: \n%s", att), warn_files);
2784 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2797 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2799 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2800 {"Cc:", NULL, TRUE},
2801 {"References:", NULL, FALSE},
2802 {"Bcc:", NULL, TRUE},
2803 {"Newsgroups:", NULL, TRUE},
2804 {"Followup-To:", NULL, TRUE},
2805 {"List-Post:", NULL, FALSE},
2806 {"X-Priority:", NULL, FALSE},
2807 {NULL, NULL, FALSE}};
2823 cm_return_val_if_fail(msginfo != NULL, -1);
2825 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2826 procheader_get_header_fields(fp, hentry);
2829 if (hentry[H_REPLY_TO].body != NULL) {
2830 if (hentry[H_REPLY_TO].body[0] != '\0') {
2832 conv_unmime_header(hentry[H_REPLY_TO].body,
2835 g_free(hentry[H_REPLY_TO].body);
2836 hentry[H_REPLY_TO].body = NULL;
2838 if (hentry[H_CC].body != NULL) {
2839 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2840 g_free(hentry[H_CC].body);
2841 hentry[H_CC].body = NULL;
2843 if (hentry[H_REFERENCES].body != NULL) {
2844 if (compose->mode == COMPOSE_REEDIT)
2845 compose->references = hentry[H_REFERENCES].body;
2847 compose->references = compose_parse_references
2848 (hentry[H_REFERENCES].body, msginfo->msgid);
2849 g_free(hentry[H_REFERENCES].body);
2851 hentry[H_REFERENCES].body = NULL;
2853 if (hentry[H_BCC].body != NULL) {
2854 if (compose->mode == COMPOSE_REEDIT)
2856 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2857 g_free(hentry[H_BCC].body);
2858 hentry[H_BCC].body = NULL;
2860 if (hentry[H_NEWSGROUPS].body != NULL) {
2861 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2862 hentry[H_NEWSGROUPS].body = NULL;
2864 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2865 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2866 compose->followup_to =
2867 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2870 g_free(hentry[H_FOLLOWUP_TO].body);
2871 hentry[H_FOLLOWUP_TO].body = NULL;
2873 if (hentry[H_LIST_POST].body != NULL) {
2874 gchar *to = NULL, *start = NULL;
2876 extract_address(hentry[H_LIST_POST].body);
2877 if (hentry[H_LIST_POST].body[0] != '\0') {
2878 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2880 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2881 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2884 g_free(compose->ml_post);
2885 compose->ml_post = to;
2888 g_free(hentry[H_LIST_POST].body);
2889 hentry[H_LIST_POST].body = NULL;
2892 /* CLAWS - X-Priority */
2893 if (compose->mode == COMPOSE_REEDIT)
2894 if (hentry[H_X_PRIORITY].body != NULL) {
2897 priority = atoi(hentry[H_X_PRIORITY].body);
2898 g_free(hentry[H_X_PRIORITY].body);
2900 hentry[H_X_PRIORITY].body = NULL;
2902 if (priority < PRIORITY_HIGHEST ||
2903 priority > PRIORITY_LOWEST)
2904 priority = PRIORITY_NORMAL;
2906 compose->priority = priority;
2909 if (compose->mode == COMPOSE_REEDIT) {
2910 if (msginfo->inreplyto && *msginfo->inreplyto)
2911 compose->inreplyto = g_strdup(msginfo->inreplyto);
2915 if (msginfo->msgid && *msginfo->msgid)
2916 compose->inreplyto = g_strdup(msginfo->msgid);
2918 if (!compose->references) {
2919 if (msginfo->msgid && *msginfo->msgid) {
2920 if (msginfo->inreplyto && *msginfo->inreplyto)
2921 compose->references =
2922 g_strdup_printf("<%s>\n\t<%s>",
2926 compose->references =
2927 g_strconcat("<", msginfo->msgid, ">",
2929 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2930 compose->references =
2931 g_strconcat("<", msginfo->inreplyto, ">",
2939 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2944 cm_return_val_if_fail(msginfo != NULL, -1);
2946 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2947 procheader_get_header_fields(fp, entries);
2951 while (he != NULL && he->name != NULL) {
2953 GtkListStore *model = NULL;
2955 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2956 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2957 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2958 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2959 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2966 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2968 GSList *ref_id_list, *cur;
2972 ref_id_list = references_list_append(NULL, ref);
2973 if (!ref_id_list) return NULL;
2974 if (msgid && *msgid)
2975 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2980 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2981 /* "<" + Message-ID + ">" + CR+LF+TAB */
2982 len += strlen((gchar *)cur->data) + 5;
2984 if (len > MAX_REFERENCES_LEN) {
2985 /* remove second message-ID */
2986 if (ref_id_list && ref_id_list->next &&
2987 ref_id_list->next->next) {
2988 g_free(ref_id_list->next->data);
2989 ref_id_list = g_slist_remove
2990 (ref_id_list, ref_id_list->next->data);
2992 slist_free_strings_full(ref_id_list);
2999 new_ref = g_string_new("");
3000 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3001 if (new_ref->len > 0)
3002 g_string_append(new_ref, "\n\t");
3003 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3006 slist_free_strings_full(ref_id_list);
3008 new_ref_str = new_ref->str;
3009 g_string_free(new_ref, FALSE);
3014 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3015 const gchar *fmt, const gchar *qmark,
3016 const gchar *body, gboolean rewrap,
3017 gboolean need_unescape,
3018 const gchar *err_msg)
3020 MsgInfo* dummyinfo = NULL;
3021 gchar *quote_str = NULL;
3023 gboolean prev_autowrap;
3024 const gchar *trimmed_body = body;
3025 gint cursor_pos = -1;
3026 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3027 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3032 SIGNAL_BLOCK(buffer);
3035 dummyinfo = compose_msginfo_new_from_compose(compose);
3036 msginfo = dummyinfo;
3039 if (qmark != NULL) {
3041 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3042 compose->gtkaspell);
3044 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3046 quote_fmt_scan_string(qmark);
3049 buf = quote_fmt_get_buffer();
3051 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3053 Xstrdup_a(quote_str, buf, goto error)
3056 if (fmt && *fmt != '\0') {
3059 while (*trimmed_body == '\n')
3063 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3064 compose->gtkaspell);
3066 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3068 if (need_unescape) {
3071 /* decode \-escape sequences in the internal representation of the quote format */
3072 tmp = g_malloc(strlen(fmt)+1);
3073 pref_get_unescaped_pref(tmp, fmt);
3074 quote_fmt_scan_string(tmp);
3078 quote_fmt_scan_string(fmt);
3082 buf = quote_fmt_get_buffer();
3084 gint line = quote_fmt_get_line();
3085 alertpanel_error(err_msg, line);
3091 prev_autowrap = compose->autowrap;
3092 compose->autowrap = FALSE;
3094 mark = gtk_text_buffer_get_insert(buffer);
3095 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3096 if (g_utf8_validate(buf, -1, NULL)) {
3097 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3099 gchar *tmpout = NULL;
3100 tmpout = conv_codeset_strdup
3101 (buf, conv_get_locale_charset_str_no_utf8(),
3103 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3105 tmpout = g_malloc(strlen(buf)*2+1);
3106 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3108 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3112 cursor_pos = quote_fmt_get_cursor_pos();
3113 if (cursor_pos == -1)
3114 cursor_pos = gtk_text_iter_get_offset(&iter);
3115 compose->set_cursor_pos = cursor_pos;
3117 gtk_text_buffer_get_start_iter(buffer, &iter);
3118 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3119 gtk_text_buffer_place_cursor(buffer, &iter);
3121 compose->autowrap = prev_autowrap;
3122 if (compose->autowrap && rewrap)
3123 compose_wrap_all(compose);
3130 SIGNAL_UNBLOCK(buffer);
3132 procmsg_msginfo_free( dummyinfo );
3137 /* if ml_post is of type addr@host and from is of type
3138 * addr-anything@host, return TRUE
3140 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3142 gchar *left_ml = NULL;
3143 gchar *right_ml = NULL;
3144 gchar *left_from = NULL;
3145 gchar *right_from = NULL;
3146 gboolean result = FALSE;
3148 if (!ml_post || !from)
3151 left_ml = g_strdup(ml_post);
3152 if (strstr(left_ml, "@")) {
3153 right_ml = strstr(left_ml, "@")+1;
3154 *(strstr(left_ml, "@")) = '\0';
3157 left_from = g_strdup(from);
3158 if (strstr(left_from, "@")) {
3159 right_from = strstr(left_from, "@")+1;
3160 *(strstr(left_from, "@")) = '\0';
3163 if (left_ml && left_from && right_ml && right_from
3164 && !strncmp(left_from, left_ml, strlen(left_ml))
3165 && !strcmp(right_from, right_ml)) {
3174 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3175 gboolean respect_default_to)
3179 if (!folder || !folder->prefs)
3182 if (respect_default_to && folder->prefs->enable_default_to) {
3183 compose_entry_append(compose, folder->prefs->default_to,
3184 COMPOSE_TO, PREF_FOLDER);
3185 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3187 if (folder->prefs->enable_default_cc)
3188 compose_entry_append(compose, folder->prefs->default_cc,
3189 COMPOSE_CC, PREF_FOLDER);
3190 if (folder->prefs->enable_default_bcc)
3191 compose_entry_append(compose, folder->prefs->default_bcc,
3192 COMPOSE_BCC, PREF_FOLDER);
3193 if (folder->prefs->enable_default_replyto)
3194 compose_entry_append(compose, folder->prefs->default_replyto,
3195 COMPOSE_REPLYTO, PREF_FOLDER);
3198 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3203 if (!compose || !msginfo)
3206 if (msginfo->subject && *msginfo->subject) {
3207 buf = p = g_strdup(msginfo->subject);
3208 p += subject_get_prefix_length(p);
3209 memmove(buf, p, strlen(p) + 1);
3211 buf2 = g_strdup_printf("Re: %s", buf);
3212 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3217 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3220 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3221 gboolean to_all, gboolean to_ml,
3223 gboolean followup_and_reply_to)
3225 GSList *cc_list = NULL;
3228 gchar *replyto = NULL;
3229 gchar *ac_email = NULL;
3231 gboolean reply_to_ml = FALSE;
3232 gboolean default_reply_to = FALSE;
3234 cm_return_if_fail(compose->account != NULL);
3235 cm_return_if_fail(msginfo != NULL);
3237 reply_to_ml = to_ml && compose->ml_post;
3239 default_reply_to = msginfo->folder &&
3240 msginfo->folder->prefs->enable_default_reply_to;
3242 if (compose->account->protocol != A_NNTP) {
3243 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3245 if (reply_to_ml && !default_reply_to) {
3247 gboolean is_subscr = is_subscription(compose->ml_post,
3250 /* normal answer to ml post with a reply-to */
3251 compose_entry_append(compose,
3253 COMPOSE_TO, PREF_ML);
3254 if (compose->replyto)
3255 compose_entry_append(compose,
3257 COMPOSE_CC, PREF_ML);
3259 /* answer to subscription confirmation */
3260 if (compose->replyto)
3261 compose_entry_append(compose,
3263 COMPOSE_TO, PREF_ML);
3264 else if (msginfo->from)
3265 compose_entry_append(compose,
3267 COMPOSE_TO, PREF_ML);
3270 else if (!(to_all || to_sender) && default_reply_to) {
3271 compose_entry_append(compose,
3272 msginfo->folder->prefs->default_reply_to,
3273 COMPOSE_TO, PREF_FOLDER);
3274 compose_entry_mark_default_to(compose,
3275 msginfo->folder->prefs->default_reply_to);
3280 Xstrdup_a(tmp1, msginfo->from, return);
3281 extract_address(tmp1);
3282 if (to_all || to_sender ||
3283 !account_find_from_address(tmp1, FALSE))
3284 compose_entry_append(compose,
3285 (compose->replyto && !to_sender)
3286 ? compose->replyto :
3287 msginfo->from ? msginfo->from : "",
3288 COMPOSE_TO, PREF_NONE);
3289 else if (!to_all && !to_sender) {
3290 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3291 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3292 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3293 if (compose->replyto) {
3294 compose_entry_append(compose,
3296 COMPOSE_TO, PREF_NONE);
3298 compose_entry_append(compose,
3299 msginfo->from ? msginfo->from : "",
3300 COMPOSE_TO, PREF_NONE);
3303 /* replying to own mail, use original recp */
3304 compose_entry_append(compose,
3305 msginfo->to ? msginfo->to : "",
3306 COMPOSE_TO, PREF_NONE);
3307 compose_entry_append(compose,
3308 msginfo->cc ? msginfo->cc : "",
3309 COMPOSE_CC, PREF_NONE);
3314 if (to_sender || (compose->followup_to &&
3315 !strncmp(compose->followup_to, "poster", 6)))
3316 compose_entry_append
3318 (compose->replyto ? compose->replyto :
3319 msginfo->from ? msginfo->from : ""),
3320 COMPOSE_TO, PREF_NONE);
3322 else if (followup_and_reply_to || to_all) {
3323 compose_entry_append
3325 (compose->replyto ? compose->replyto :
3326 msginfo->from ? msginfo->from : ""),
3327 COMPOSE_TO, PREF_NONE);
3329 compose_entry_append
3331 compose->followup_to ? compose->followup_to :
3332 compose->newsgroups ? compose->newsgroups : "",
3333 COMPOSE_NEWSGROUPS, PREF_NONE);
3336 compose_entry_append
3338 compose->followup_to ? compose->followup_to :
3339 compose->newsgroups ? compose->newsgroups : "",
3340 COMPOSE_NEWSGROUPS, PREF_NONE);
3342 compose_reply_set_subject(compose, msginfo);
3344 if (to_ml && compose->ml_post) return;
3345 if (!to_all || compose->account->protocol == A_NNTP) return;
3347 if (compose->replyto) {
3348 Xstrdup_a(replyto, compose->replyto, return);
3349 extract_address(replyto);
3351 if (msginfo->from) {
3352 Xstrdup_a(from, msginfo->from, return);
3353 extract_address(from);
3356 if (replyto && from)
3357 cc_list = address_list_append_with_comments(cc_list, from);
3358 if (to_all && msginfo->folder &&
3359 msginfo->folder->prefs->enable_default_reply_to)
3360 cc_list = address_list_append_with_comments(cc_list,
3361 msginfo->folder->prefs->default_reply_to);
3362 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3363 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3365 ac_email = g_utf8_strdown(compose->account->address, -1);
3368 for (cur = cc_list; cur != NULL; cur = cur->next) {
3369 gchar *addr = g_utf8_strdown(cur->data, -1);
3370 extract_address(addr);
3372 if (strcmp(ac_email, addr))
3373 compose_entry_append(compose, (gchar *)cur->data,
3374 COMPOSE_CC, PREF_NONE);
3376 debug_print("Cc address same as compose account's, ignoring\n");
3381 slist_free_strings_full(cc_list);
3387 #define SET_ENTRY(entry, str) \
3390 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3393 #define SET_ADDRESS(type, str) \
3396 compose_entry_append(compose, str, type, PREF_NONE); \
3399 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3401 cm_return_if_fail(msginfo != NULL);
3403 SET_ENTRY(subject_entry, msginfo->subject);
3404 SET_ENTRY(from_name, msginfo->from);
3405 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3406 SET_ADDRESS(COMPOSE_CC, compose->cc);
3407 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3408 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3409 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3410 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3412 compose_update_priority_menu_item(compose);
3413 compose_update_privacy_system_menu_item(compose, FALSE);
3414 compose_show_first_last_header(compose, TRUE);
3420 static void compose_insert_sig(Compose *compose, gboolean replace)
3422 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3423 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3425 GtkTextIter iter, iter_end;
3426 gint cur_pos, ins_pos;
3427 gboolean prev_autowrap;
3428 gboolean found = FALSE;
3429 gboolean exists = FALSE;
3431 cm_return_if_fail(compose->account != NULL);
3435 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3436 G_CALLBACK(compose_changed_cb),
3439 mark = gtk_text_buffer_get_insert(buffer);
3440 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3441 cur_pos = gtk_text_iter_get_offset (&iter);
3444 gtk_text_buffer_get_end_iter(buffer, &iter);
3446 exists = (compose->sig_str != NULL);
3449 GtkTextIter first_iter, start_iter, end_iter;
3451 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3453 if (!exists || compose->sig_str[0] == '\0')
3456 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3457 compose->signature_tag);
3460 /* include previous \n\n */
3461 gtk_text_iter_backward_chars(&first_iter, 1);
3462 start_iter = first_iter;
3463 end_iter = first_iter;
3465 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3466 compose->signature_tag);
3467 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3468 compose->signature_tag);
3470 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3476 g_free(compose->sig_str);
3477 compose->sig_str = account_get_signature_str(compose->account);
3479 cur_pos = gtk_text_iter_get_offset(&iter);
3481 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3482 g_free(compose->sig_str);
3483 compose->sig_str = NULL;
3485 if (compose->sig_inserted == FALSE)
3486 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3487 compose->sig_inserted = TRUE;
3489 cur_pos = gtk_text_iter_get_offset(&iter);
3490 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3492 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3493 gtk_text_iter_forward_chars(&iter, 1);
3494 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3495 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3497 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3498 cur_pos = gtk_text_buffer_get_char_count (buffer);
3501 /* put the cursor where it should be
3502 * either where the quote_fmt says, either where it was */
3503 if (compose->set_cursor_pos < 0)
3504 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3506 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3507 compose->set_cursor_pos);
3509 compose->set_cursor_pos = -1;
3510 gtk_text_buffer_place_cursor(buffer, &iter);
3511 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3512 G_CALLBACK(compose_changed_cb),
3518 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3521 GtkTextBuffer *buffer;
3524 const gchar *cur_encoding;
3525 gchar buf[BUFFSIZE];
3528 gboolean prev_autowrap;
3529 gboolean badtxt = FALSE;
3530 struct stat file_stat;
3532 GString *file_contents = NULL;
3534 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3536 /* get the size of the file we are about to insert */
3537 ret = g_stat(file, &file_stat);
3539 gchar *shortfile = g_path_get_basename(file);
3540 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3542 return COMPOSE_INSERT_NO_FILE;
3543 } else if (prefs_common.warn_large_insert == TRUE) {
3545 /* ask user for confirmation if the file is large */
3546 if (prefs_common.warn_large_insert_size < 0 ||
3547 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3551 msg = g_strdup_printf(_("You are about to insert a file of %s "
3552 "in the message body. Are you sure you want to do that?"),
3553 to_human_readable(file_stat.st_size));
3554 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3555 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3558 /* do we ask for confirmation next time? */
3559 if (aval & G_ALERTDISABLE) {
3560 /* no confirmation next time, disable feature in preferences */
3561 aval &= ~G_ALERTDISABLE;
3562 prefs_common.warn_large_insert = FALSE;
3565 /* abort file insertion if user canceled action */
3566 if (aval != G_ALERTALTERNATE) {
3567 return COMPOSE_INSERT_NO_FILE;
3573 if ((fp = g_fopen(file, "rb")) == NULL) {
3574 FILE_OP_ERROR(file, "fopen");
3575 return COMPOSE_INSERT_READ_ERROR;
3578 prev_autowrap = compose->autowrap;
3579 compose->autowrap = FALSE;
3581 text = GTK_TEXT_VIEW(compose->text);
3582 buffer = gtk_text_view_get_buffer(text);
3583 mark = gtk_text_buffer_get_insert(buffer);
3584 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3586 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3587 G_CALLBACK(text_inserted),
3590 cur_encoding = conv_get_locale_charset_str_no_utf8();
3592 file_contents = g_string_new("");
3593 while (fgets(buf, sizeof(buf), fp) != NULL) {
3596 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3597 str = g_strdup(buf);
3599 str = conv_codeset_strdup
3600 (buf, cur_encoding, CS_INTERNAL);
3603 /* strip <CR> if DOS/Windows file,
3604 replace <CR> with <LF> if Macintosh file. */
3607 if (len > 0 && str[len - 1] != '\n') {
3609 if (str[len] == '\r') str[len] = '\n';
3612 file_contents = g_string_append(file_contents, str);
3616 gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3617 g_string_free(file_contents, TRUE);
3619 compose_changed_cb(NULL, compose);
3620 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3621 G_CALLBACK(text_inserted),
3623 compose->autowrap = prev_autowrap;
3624 if (compose->autowrap)
3625 compose_wrap_all(compose);
3630 return COMPOSE_INSERT_INVALID_CHARACTER;
3632 return COMPOSE_INSERT_SUCCESS;
3635 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3636 const gchar *filename,
3637 const gchar *content_type,
3638 const gchar *charset)
3646 GtkListStore *store;
3648 gboolean has_binary = FALSE;
3650 if (!is_file_exist(file)) {
3651 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3652 gboolean result = FALSE;
3653 if (file_from_uri && is_file_exist(file_from_uri)) {
3654 result = compose_attach_append(
3655 compose, file_from_uri,
3656 filename, content_type,
3659 g_free(file_from_uri);
3662 alertpanel_error("File %s doesn't exist\n", filename);
3665 if ((size = get_file_size(file)) < 0) {
3666 alertpanel_error("Can't get file size of %s\n", filename);
3670 alertpanel_error(_("File %s is empty."), filename);
3673 if ((fp = g_fopen(file, "rb")) == NULL) {
3674 alertpanel_error(_("Can't read %s."), filename);
3679 ainfo = g_new0(AttachInfo, 1);
3680 auto_ainfo = g_auto_pointer_new_with_free
3681 (ainfo, (GFreeFunc) compose_attach_info_free);
3682 ainfo->file = g_strdup(file);
3685 ainfo->content_type = g_strdup(content_type);
3686 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3688 MsgFlags flags = {0, 0};
3690 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3691 ainfo->encoding = ENC_7BIT;
3693 ainfo->encoding = ENC_8BIT;
3695 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3696 if (msginfo && msginfo->subject)
3697 name = g_strdup(msginfo->subject);
3699 name = g_path_get_basename(filename ? filename : file);
3701 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3703 procmsg_msginfo_free(msginfo);
3705 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3706 ainfo->charset = g_strdup(charset);
3707 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3709 ainfo->encoding = ENC_BASE64;
3711 name = g_path_get_basename(filename ? filename : file);
3712 ainfo->name = g_strdup(name);
3716 ainfo->content_type = procmime_get_mime_type(file);
3717 if (!ainfo->content_type) {
3718 ainfo->content_type =
3719 g_strdup("application/octet-stream");
3720 ainfo->encoding = ENC_BASE64;
3721 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3723 procmime_get_encoding_for_text_file(file, &has_binary);
3725 ainfo->encoding = ENC_BASE64;
3726 name = g_path_get_basename(filename ? filename : file);
3727 ainfo->name = g_strdup(name);
3731 if (ainfo->name != NULL
3732 && !strcmp(ainfo->name, ".")) {
3733 g_free(ainfo->name);
3737 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3738 g_free(ainfo->content_type);
3739 ainfo->content_type = g_strdup("application/octet-stream");
3740 g_free(ainfo->charset);
3741 ainfo->charset = NULL;
3744 ainfo->size = (goffset)size;
3745 size_text = to_human_readable((goffset)size);
3747 store = GTK_LIST_STORE(gtk_tree_view_get_model
3748 (GTK_TREE_VIEW(compose->attach_clist)));
3750 gtk_list_store_append(store, &iter);
3751 gtk_list_store_set(store, &iter,
3752 COL_MIMETYPE, ainfo->content_type,
3753 COL_SIZE, size_text,
3754 COL_NAME, ainfo->name,
3755 COL_CHARSET, ainfo->charset,
3757 COL_AUTODATA, auto_ainfo,
3760 g_auto_pointer_free(auto_ainfo);
3761 compose_attach_update_label(compose);
3765 static void compose_use_signing(Compose *compose, gboolean use_signing)
3767 compose->use_signing = use_signing;
3768 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3771 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3773 compose->use_encryption = use_encryption;
3774 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3777 #define NEXT_PART_NOT_CHILD(info) \
3779 node = info->node; \
3780 while (node->children) \
3781 node = g_node_last_child(node); \
3782 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3785 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3789 MimeInfo *firsttext = NULL;
3790 MimeInfo *encrypted = NULL;
3793 const gchar *partname = NULL;
3795 mimeinfo = procmime_scan_message(msginfo);
3796 if (!mimeinfo) return;
3798 if (mimeinfo->node->children == NULL) {
3799 procmime_mimeinfo_free_all(mimeinfo);
3803 /* find first content part */
3804 child = (MimeInfo *) mimeinfo->node->children->data;
3805 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3806 child = (MimeInfo *)child->node->children->data;
3809 if (child->type == MIMETYPE_TEXT) {
3811 debug_print("First text part found\n");
3812 } else if (compose->mode == COMPOSE_REEDIT &&
3813 child->type == MIMETYPE_APPLICATION &&
3814 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3815 encrypted = (MimeInfo *)child->node->parent->data;
3818 child = (MimeInfo *) mimeinfo->node->children->data;
3819 while (child != NULL) {
3822 if (child == encrypted) {
3823 /* skip this part of tree */
3824 NEXT_PART_NOT_CHILD(child);
3828 if (child->type == MIMETYPE_MULTIPART) {
3829 /* get the actual content */
3830 child = procmime_mimeinfo_next(child);
3834 if (child == firsttext) {
3835 child = procmime_mimeinfo_next(child);
3839 outfile = procmime_get_tmp_file_name(child);
3840 if ((err = procmime_get_part(outfile, child)) < 0)
3841 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3843 gchar *content_type;
3845 content_type = procmime_get_content_type_str(child->type, child->subtype);
3847 /* if we meet a pgp signature, we don't attach it, but
3848 * we force signing. */
3849 if ((strcmp(content_type, "application/pgp-signature") &&
3850 strcmp(content_type, "application/pkcs7-signature") &&
3851 strcmp(content_type, "application/x-pkcs7-signature"))
3852 || compose->mode == COMPOSE_REDIRECT) {
3853 partname = procmime_mimeinfo_get_parameter(child, "filename");
3854 if (partname == NULL)
3855 partname = procmime_mimeinfo_get_parameter(child, "name");
3856 if (partname == NULL)
3858 compose_attach_append(compose, outfile,
3859 partname, content_type,
3860 procmime_mimeinfo_get_parameter(child, "charset"));
3862 compose_force_signing(compose, compose->account, NULL);
3864 g_free(content_type);
3867 NEXT_PART_NOT_CHILD(child);
3869 procmime_mimeinfo_free_all(mimeinfo);
3872 #undef NEXT_PART_NOT_CHILD
3877 WAIT_FOR_INDENT_CHAR,
3878 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3881 /* return indent length, we allow:
3882 indent characters followed by indent characters or spaces/tabs,
3883 alphabets and numbers immediately followed by indent characters,
3884 and the repeating sequences of the above
3885 If quote ends with multiple spaces, only the first one is included. */
3886 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3887 const GtkTextIter *start, gint *len)
3889 GtkTextIter iter = *start;
3893 IndentState state = WAIT_FOR_INDENT_CHAR;
3896 gint alnum_count = 0;
3897 gint space_count = 0;
3900 if (prefs_common.quote_chars == NULL) {
3904 while (!gtk_text_iter_ends_line(&iter)) {
3905 wc = gtk_text_iter_get_char(&iter);
3906 if (g_unichar_iswide(wc))
3908 clen = g_unichar_to_utf8(wc, ch);
3912 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3913 is_space = g_unichar_isspace(wc);
3915 if (state == WAIT_FOR_INDENT_CHAR) {
3916 if (!is_indent && !g_unichar_isalnum(wc))
3919 quote_len += alnum_count + space_count + 1;
3920 alnum_count = space_count = 0;
3921 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3924 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3925 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3929 else if (is_indent) {
3930 quote_len += alnum_count + space_count + 1;
3931 alnum_count = space_count = 0;
3934 state = WAIT_FOR_INDENT_CHAR;
3938 gtk_text_iter_forward_char(&iter);
3941 if (quote_len > 0 && space_count > 0)
3947 if (quote_len > 0) {
3949 gtk_text_iter_forward_chars(&iter, quote_len);
3950 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3956 /* return >0 if the line is itemized */
3957 static int compose_itemized_length(GtkTextBuffer *buffer,
3958 const GtkTextIter *start)
3960 GtkTextIter iter = *start;
3965 if (gtk_text_iter_ends_line(&iter))
3970 wc = gtk_text_iter_get_char(&iter);
3971 if (!g_unichar_isspace(wc))
3973 gtk_text_iter_forward_char(&iter);
3974 if (gtk_text_iter_ends_line(&iter))
3978 clen = g_unichar_to_utf8(wc, ch);
3982 if (!strchr("*-+", ch[0]))
3985 gtk_text_iter_forward_char(&iter);
3986 if (gtk_text_iter_ends_line(&iter))
3988 wc = gtk_text_iter_get_char(&iter);
3989 if (g_unichar_isspace(wc)) {
3995 /* return the string at the start of the itemization */
3996 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3997 const GtkTextIter *start)
3999 GtkTextIter iter = *start;
4002 GString *item_chars = g_string_new("");
4005 if (gtk_text_iter_ends_line(&iter))
4010 wc = gtk_text_iter_get_char(&iter);
4011 if (!g_unichar_isspace(wc))
4013 gtk_text_iter_forward_char(&iter);
4014 if (gtk_text_iter_ends_line(&iter))
4016 g_string_append_unichar(item_chars, wc);
4019 str = item_chars->str;
4020 g_string_free(item_chars, FALSE);
4024 /* return the number of spaces at a line's start */
4025 static int compose_left_offset_length(GtkTextBuffer *buffer,
4026 const GtkTextIter *start)
4028 GtkTextIter iter = *start;
4031 if (gtk_text_iter_ends_line(&iter))
4035 wc = gtk_text_iter_get_char(&iter);
4036 if (!g_unichar_isspace(wc))
4039 gtk_text_iter_forward_char(&iter);
4040 if (gtk_text_iter_ends_line(&iter))
4044 gtk_text_iter_forward_char(&iter);
4045 if (gtk_text_iter_ends_line(&iter))
4050 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4051 const GtkTextIter *start,
4052 GtkTextIter *break_pos,
4056 GtkTextIter iter = *start, line_end = *start;
4057 PangoLogAttr *attrs;
4064 gboolean can_break = FALSE;
4065 gboolean do_break = FALSE;
4066 gboolean was_white = FALSE;
4067 gboolean prev_dont_break = FALSE;
4069 gtk_text_iter_forward_to_line_end(&line_end);
4070 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4071 len = g_utf8_strlen(str, -1);
4075 g_warning("compose_get_line_break_pos: len = 0!\n");
4079 /* g_print("breaking line: %d: %s (len = %d)\n",
4080 gtk_text_iter_get_line(&iter), str, len); */
4082 attrs = g_new(PangoLogAttr, len + 1);
4084 pango_default_break(str, -1, NULL, attrs, len + 1);
4088 /* skip quote and leading spaces */
4089 for (i = 0; *p != '\0' && i < len; i++) {
4092 wc = g_utf8_get_char(p);
4093 if (i >= quote_len && !g_unichar_isspace(wc))
4095 if (g_unichar_iswide(wc))
4097 else if (*p == '\t')
4101 p = g_utf8_next_char(p);
4104 for (; *p != '\0' && i < len; i++) {
4105 PangoLogAttr *attr = attrs + i;
4109 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4112 was_white = attr->is_white;
4114 /* don't wrap URI */
4115 if ((uri_len = get_uri_len(p)) > 0) {
4117 if (pos > 0 && col > max_col) {
4127 wc = g_utf8_get_char(p);
4128 if (g_unichar_iswide(wc)) {
4130 if (prev_dont_break && can_break && attr->is_line_break)
4132 } else if (*p == '\t')
4136 if (pos > 0 && col > max_col) {
4141 if (*p == '-' || *p == '/')
4142 prev_dont_break = TRUE;
4144 prev_dont_break = FALSE;
4146 p = g_utf8_next_char(p);
4150 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4155 *break_pos = *start;
4156 gtk_text_iter_set_line_offset(break_pos, pos);
4161 static gboolean compose_join_next_line(Compose *compose,
4162 GtkTextBuffer *buffer,
4164 const gchar *quote_str)
4166 GtkTextIter iter_ = *iter, cur, prev, next, end;
4167 PangoLogAttr attrs[3];
4169 gchar *next_quote_str;
4172 gboolean keep_cursor = FALSE;
4174 if (!gtk_text_iter_forward_line(&iter_) ||
4175 gtk_text_iter_ends_line(&iter_)) {
4178 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4180 if ((quote_str || next_quote_str) &&
4181 strcmp2(quote_str, next_quote_str) != 0) {
4182 g_free(next_quote_str);
4185 g_free(next_quote_str);
4188 if (quote_len > 0) {
4189 gtk_text_iter_forward_chars(&end, quote_len);
4190 if (gtk_text_iter_ends_line(&end)) {
4195 /* don't join itemized lines */
4196 if (compose_itemized_length(buffer, &end) > 0) {
4200 /* don't join signature separator */
4201 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4204 /* delete quote str */
4206 gtk_text_buffer_delete(buffer, &iter_, &end);
4208 /* don't join line breaks put by the user */
4210 gtk_text_iter_backward_char(&cur);
4211 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4212 gtk_text_iter_forward_char(&cur);
4216 gtk_text_iter_forward_char(&cur);
4217 /* delete linebreak and extra spaces */
4218 while (gtk_text_iter_backward_char(&cur)) {
4219 wc1 = gtk_text_iter_get_char(&cur);
4220 if (!g_unichar_isspace(wc1))
4225 while (!gtk_text_iter_ends_line(&cur)) {
4226 wc1 = gtk_text_iter_get_char(&cur);
4227 if (!g_unichar_isspace(wc1))
4229 gtk_text_iter_forward_char(&cur);
4232 if (!gtk_text_iter_equal(&prev, &next)) {
4235 mark = gtk_text_buffer_get_insert(buffer);
4236 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4237 if (gtk_text_iter_equal(&prev, &cur))
4239 gtk_text_buffer_delete(buffer, &prev, &next);
4243 /* insert space if required */
4244 gtk_text_iter_backward_char(&prev);
4245 wc1 = gtk_text_iter_get_char(&prev);
4246 wc2 = gtk_text_iter_get_char(&next);
4247 gtk_text_iter_forward_char(&next);
4248 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4249 pango_default_break(str, -1, NULL, attrs, 3);
4250 if (!attrs[1].is_line_break ||
4251 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4252 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4254 gtk_text_iter_backward_char(&iter_);
4255 gtk_text_buffer_place_cursor(buffer, &iter_);
4264 #define ADD_TXT_POS(bp_, ep_, pti_) \
4265 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4266 last = last->next; \
4267 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4268 last->next = NULL; \
4270 g_warning("alloc error scanning URIs\n"); \
4273 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4275 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4276 GtkTextBuffer *buffer;
4277 GtkTextIter iter, break_pos, end_of_line;
4278 gchar *quote_str = NULL;
4280 gboolean wrap_quote = prefs_common.linewrap_quote;
4281 gboolean prev_autowrap = compose->autowrap;
4282 gint startq_offset = -1, noq_offset = -1;
4283 gint uri_start = -1, uri_stop = -1;
4284 gint nouri_start = -1, nouri_stop = -1;
4285 gint num_blocks = 0;
4286 gint quotelevel = -1;
4287 gboolean modified = force;
4288 gboolean removed = FALSE;
4289 gboolean modified_before_remove = FALSE;
4291 gboolean start = TRUE;
4292 gint itemized_len = 0, rem_item_len = 0;
4293 gchar *itemized_chars = NULL;
4294 gboolean item_continuation = FALSE;
4299 if (compose->draft_timeout_tag == -2) {
4303 compose->autowrap = FALSE;
4305 buffer = gtk_text_view_get_buffer(text);
4306 undo_wrapping(compose->undostruct, TRUE);
4311 mark = gtk_text_buffer_get_insert(buffer);
4312 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4316 if (compose->draft_timeout_tag == -2) {
4317 if (gtk_text_iter_ends_line(&iter)) {
4318 while (gtk_text_iter_ends_line(&iter) &&
4319 gtk_text_iter_forward_line(&iter))
4322 while (gtk_text_iter_backward_line(&iter)) {
4323 if (gtk_text_iter_ends_line(&iter)) {
4324 gtk_text_iter_forward_line(&iter);
4330 /* move to line start */
4331 gtk_text_iter_set_line_offset(&iter, 0);
4334 itemized_len = compose_itemized_length(buffer, &iter);
4336 if (!itemized_len) {
4337 itemized_len = compose_left_offset_length(buffer, &iter);
4338 item_continuation = TRUE;
4342 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4344 /* go until paragraph end (empty line) */
4345 while (start || !gtk_text_iter_ends_line(&iter)) {
4346 gchar *scanpos = NULL;
4347 /* parse table - in order of priority */
4349 const gchar *needle; /* token */
4351 /* token search function */
4352 gchar *(*search) (const gchar *haystack,
4353 const gchar *needle);
4354 /* part parsing function */
4355 gboolean (*parse) (const gchar *start,
4356 const gchar *scanpos,
4360 /* part to URI function */
4361 gchar *(*build_uri) (const gchar *bp,
4365 static struct table parser[] = {
4366 {"http://", strcasestr, get_uri_part, make_uri_string},
4367 {"https://", strcasestr, get_uri_part, make_uri_string},
4368 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4369 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4370 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4371 {"www.", strcasestr, get_uri_part, make_http_string},
4372 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4373 {"@", strcasestr, get_email_part, make_email_string}
4375 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4376 gint last_index = PARSE_ELEMS;
4378 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4382 if (!prev_autowrap && num_blocks == 0) {
4384 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4385 G_CALLBACK(text_inserted),
4388 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4391 uri_start = uri_stop = -1;
4393 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4396 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4397 if (startq_offset == -1)
4398 startq_offset = gtk_text_iter_get_offset(&iter);
4399 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4400 if (quotelevel > 2) {
4401 /* recycle colors */
4402 if (prefs_common.recycle_quote_colors)
4411 if (startq_offset == -1)
4412 noq_offset = gtk_text_iter_get_offset(&iter);
4416 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4419 if (gtk_text_iter_ends_line(&iter)) {
4421 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4422 prefs_common.linewrap_len,
4424 GtkTextIter prev, next, cur;
4425 if (prev_autowrap != FALSE || force) {
4426 compose->automatic_break = TRUE;
4428 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4429 compose->automatic_break = FALSE;
4430 if (itemized_len && compose->autoindent) {
4431 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4432 if (!item_continuation)
4433 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4435 } else if (quote_str && wrap_quote) {
4436 compose->automatic_break = TRUE;
4438 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4439 compose->automatic_break = FALSE;
4440 if (itemized_len && compose->autoindent) {
4441 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4442 if (!item_continuation)
4443 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4447 /* remove trailing spaces */
4449 rem_item_len = itemized_len;
4450 while (compose->autoindent && rem_item_len-- > 0)
4451 gtk_text_iter_backward_char(&cur);
4452 gtk_text_iter_backward_char(&cur);
4455 while (!gtk_text_iter_starts_line(&cur)) {
4458 gtk_text_iter_backward_char(&cur);
4459 wc = gtk_text_iter_get_char(&cur);
4460 if (!g_unichar_isspace(wc))
4464 if (!gtk_text_iter_equal(&prev, &next)) {
4465 gtk_text_buffer_delete(buffer, &prev, &next);
4467 gtk_text_iter_forward_char(&break_pos);
4471 gtk_text_buffer_insert(buffer, &break_pos,
4475 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4477 /* move iter to current line start */
4478 gtk_text_iter_set_line_offset(&iter, 0);
4485 /* move iter to next line start */
4491 if (!prev_autowrap && num_blocks > 0) {
4493 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4494 G_CALLBACK(text_inserted),
4498 while (!gtk_text_iter_ends_line(&end_of_line)) {
4499 gtk_text_iter_forward_char(&end_of_line);
4501 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4503 nouri_start = gtk_text_iter_get_offset(&iter);
4504 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4506 walk_pos = gtk_text_iter_get_offset(&iter);
4507 /* FIXME: this looks phony. scanning for anything in the parse table */
4508 for (n = 0; n < PARSE_ELEMS; n++) {
4511 tmp = parser[n].search(walk, parser[n].needle);
4513 if (scanpos == NULL || tmp < scanpos) {
4522 /* check if URI can be parsed */
4523 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4524 (const gchar **)&ep, FALSE)
4525 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4529 strlen(parser[last_index].needle);
4532 uri_start = walk_pos + (bp - o_walk);
4533 uri_stop = walk_pos + (ep - o_walk);
4537 gtk_text_iter_forward_line(&iter);
4540 if (startq_offset != -1) {
4541 GtkTextIter startquote, endquote;
4542 gtk_text_buffer_get_iter_at_offset(
4543 buffer, &startquote, startq_offset);
4546 switch (quotelevel) {
4548 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4549 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4550 gtk_text_buffer_apply_tag_by_name(
4551 buffer, "quote0", &startquote, &endquote);
4552 gtk_text_buffer_remove_tag_by_name(
4553 buffer, "quote1", &startquote, &endquote);
4554 gtk_text_buffer_remove_tag_by_name(
4555 buffer, "quote2", &startquote, &endquote);
4560 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4561 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4562 gtk_text_buffer_apply_tag_by_name(
4563 buffer, "quote1", &startquote, &endquote);
4564 gtk_text_buffer_remove_tag_by_name(
4565 buffer, "quote0", &startquote, &endquote);
4566 gtk_text_buffer_remove_tag_by_name(
4567 buffer, "quote2", &startquote, &endquote);
4572 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4573 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4574 gtk_text_buffer_apply_tag_by_name(
4575 buffer, "quote2", &startquote, &endquote);
4576 gtk_text_buffer_remove_tag_by_name(
4577 buffer, "quote0", &startquote, &endquote);
4578 gtk_text_buffer_remove_tag_by_name(
4579 buffer, "quote1", &startquote, &endquote);
4585 } else if (noq_offset != -1) {
4586 GtkTextIter startnoquote, endnoquote;
4587 gtk_text_buffer_get_iter_at_offset(
4588 buffer, &startnoquote, noq_offset);
4591 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4592 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4593 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4594 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4595 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4596 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4597 gtk_text_buffer_remove_tag_by_name(
4598 buffer, "quote0", &startnoquote, &endnoquote);
4599 gtk_text_buffer_remove_tag_by_name(
4600 buffer, "quote1", &startnoquote, &endnoquote);
4601 gtk_text_buffer_remove_tag_by_name(
4602 buffer, "quote2", &startnoquote, &endnoquote);
4608 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4609 GtkTextIter nouri_start_iter, nouri_end_iter;
4610 gtk_text_buffer_get_iter_at_offset(
4611 buffer, &nouri_start_iter, nouri_start);
4612 gtk_text_buffer_get_iter_at_offset(
4613 buffer, &nouri_end_iter, nouri_stop);
4614 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4615 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4616 gtk_text_buffer_remove_tag_by_name(
4617 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4618 modified_before_remove = modified;
4623 if (uri_start >= 0 && uri_stop > 0) {
4624 GtkTextIter uri_start_iter, uri_end_iter, back;
4625 gtk_text_buffer_get_iter_at_offset(
4626 buffer, &uri_start_iter, uri_start);
4627 gtk_text_buffer_get_iter_at_offset(
4628 buffer, &uri_end_iter, uri_stop);
4629 back = uri_end_iter;
4630 gtk_text_iter_backward_char(&back);
4631 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4632 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4633 gtk_text_buffer_apply_tag_by_name(
4634 buffer, "link", &uri_start_iter, &uri_end_iter);
4636 if (removed && !modified_before_remove) {
4642 // debug_print("not modified, out after %d lines\n", lines);
4646 // debug_print("modified, out after %d lines\n", lines);
4648 g_free(itemized_chars);
4651 undo_wrapping(compose->undostruct, FALSE);
4652 compose->autowrap = prev_autowrap;
4657 void compose_action_cb(void *data)
4659 Compose *compose = (Compose *)data;
4660 compose_wrap_all(compose);
4663 static void compose_wrap_all(Compose *compose)
4665 compose_wrap_all_full(compose, FALSE);
4668 static void compose_wrap_all_full(Compose *compose, gboolean force)
4670 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4671 GtkTextBuffer *buffer;
4673 gboolean modified = TRUE;
4675 buffer = gtk_text_view_get_buffer(text);
4677 gtk_text_buffer_get_start_iter(buffer, &iter);
4678 while (!gtk_text_iter_is_end(&iter) && modified)
4679 modified = compose_beautify_paragraph(compose, &iter, force);
4683 static void compose_set_title(Compose *compose)
4689 edited = compose->modified ? _(" [Edited]") : "";
4691 subject = gtk_editable_get_chars(
4692 GTK_EDITABLE(compose->subject_entry), 0, -1);
4694 #ifndef GENERIC_UMPC
4695 if (subject && strlen(subject))
4696 str = g_strdup_printf(_("%s - Compose message%s"),
4699 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4701 str = g_strdup(_("Compose message"));
4704 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4710 * compose_current_mail_account:
4712 * Find a current mail account (the currently selected account, or the
4713 * default account, if a news account is currently selected). If a
4714 * mail account cannot be found, display an error message.
4716 * Return value: Mail account, or NULL if not found.
4718 static PrefsAccount *
4719 compose_current_mail_account(void)
4723 if (cur_account && cur_account->protocol != A_NNTP)
4726 ac = account_get_default();
4727 if (!ac || ac->protocol == A_NNTP) {
4728 alertpanel_error(_("Account for sending mail is not specified.\n"
4729 "Please select a mail account before sending."));
4736 #define QUOTE_IF_REQUIRED(out, str) \
4738 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4742 len = strlen(str) + 3; \
4743 if ((__tmp = alloca(len)) == NULL) { \
4744 g_warning("can't allocate memory\n"); \
4745 g_string_free(header, TRUE); \
4748 g_snprintf(__tmp, len, "\"%s\"", str); \
4753 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4754 g_warning("can't allocate memory\n"); \
4755 g_string_free(header, TRUE); \
4758 strcpy(__tmp, str); \
4764 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4766 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4770 len = strlen(str) + 3; \
4771 if ((__tmp = alloca(len)) == NULL) { \
4772 g_warning("can't allocate memory\n"); \
4775 g_snprintf(__tmp, len, "\"%s\"", str); \
4780 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4781 g_warning("can't allocate memory\n"); \
4784 strcpy(__tmp, str); \
4790 static void compose_select_account(Compose *compose, PrefsAccount *account,
4793 gchar *from = NULL, *header = NULL;
4794 ComposeHeaderEntry *header_entry;
4795 #if GTK_CHECK_VERSION(2, 24, 0)
4799 cm_return_if_fail(account != NULL);
4801 compose->account = account;
4802 if (account->name && *account->name) {
4804 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4805 from = g_strdup_printf("%s <%s>",
4806 buf, account->address);
4807 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4809 from = g_strdup_printf("<%s>",
4811 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4816 compose_set_title(compose);
4818 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4819 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4821 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4822 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4823 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4825 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4827 activate_privacy_system(compose, account, FALSE);
4829 if (!init && compose->mode != COMPOSE_REDIRECT) {
4830 undo_block(compose->undostruct);
4831 compose_insert_sig(compose, TRUE);
4832 undo_unblock(compose->undostruct);
4835 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4836 #if !GTK_CHECK_VERSION(2, 24, 0)
4837 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4839 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry->combo), &iter))
4840 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4841 header_entry->combo)), &iter, COMBOBOX_TEXT, &header, -1);
4844 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4845 if (account->protocol == A_NNTP) {
4846 if (!strcmp(header, _("To:")))
4847 combobox_select_by_text(
4848 GTK_COMBO_BOX(header_entry->combo),
4851 if (!strcmp(header, _("Newsgroups:")))
4852 combobox_select_by_text(
4853 GTK_COMBO_BOX(header_entry->combo),
4861 /* use account's dict info if set */
4862 if (compose->gtkaspell) {
4863 if (account->enable_default_dictionary)
4864 gtkaspell_change_dict(compose->gtkaspell,
4865 account->default_dictionary, FALSE);
4866 if (account->enable_default_alt_dictionary)
4867 gtkaspell_change_alt_dict(compose->gtkaspell,
4868 account->default_alt_dictionary);
4869 if (account->enable_default_dictionary
4870 || account->enable_default_alt_dictionary)
4871 compose_spell_menu_changed(compose);
4876 gboolean compose_check_for_valid_recipient(Compose *compose) {
4877 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4878 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4879 gboolean recipient_found = FALSE;
4883 /* free to and newsgroup list */
4884 slist_free_strings_full(compose->to_list);
4885 compose->to_list = NULL;
4887 slist_free_strings_full(compose->newsgroup_list);
4888 compose->newsgroup_list = NULL;
4890 /* search header entries for to and newsgroup entries */
4891 for (list = compose->header_list; list; list = list->next) {
4894 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4895 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4898 if (entry[0] != '\0') {
4899 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4900 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4901 compose->to_list = address_list_append(compose->to_list, entry);
4902 recipient_found = TRUE;
4905 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4906 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4907 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4908 recipient_found = TRUE;
4915 return recipient_found;
4918 static gboolean compose_check_for_set_recipients(Compose *compose)
4920 if (compose->account->set_autocc && compose->account->auto_cc) {
4921 gboolean found_other = FALSE;
4923 /* search header entries for to and newsgroup entries */
4924 for (list = compose->header_list; list; list = list->next) {
4927 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4928 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4931 if (strcmp(entry, compose->account->auto_cc)
4932 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4942 if (compose->batch) {
4943 gtk_widget_show_all(compose->window);
4945 aval = alertpanel(_("Send"),
4946 _("The only recipient is the default CC address. Send anyway?"),
4947 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4948 if (aval != G_ALERTALTERNATE)
4952 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4953 gboolean found_other = FALSE;
4955 /* search header entries for to and newsgroup entries */
4956 for (list = compose->header_list; list; list = list->next) {
4959 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4960 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4963 if (strcmp(entry, compose->account->auto_bcc)
4964 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4974 if (compose->batch) {
4975 gtk_widget_show_all(compose->window);
4977 aval = alertpanel(_("Send"),
4978 _("The only recipient is the default BCC address. Send anyway?"),
4979 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4980 if (aval != G_ALERTALTERNATE)
4987 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4991 if (compose_check_for_valid_recipient(compose) == FALSE) {
4992 if (compose->batch) {
4993 gtk_widget_show_all(compose->window);
4995 alertpanel_error(_("Recipient is not specified."));
4999 if (compose_check_for_set_recipients(compose) == FALSE) {
5003 if (!compose->batch) {
5004 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5005 if (*str == '\0' && check_everything == TRUE &&
5006 compose->mode != COMPOSE_REDIRECT) {
5008 gchar *button_label;
5011 if (compose->sending)
5012 button_label = _("+_Send");
5014 button_label = _("+_Queue");
5015 message = g_strdup_printf(_("Subject is empty. %s"),
5016 compose->sending?_("Send it anyway?"):
5017 _("Queue it anyway?"));
5019 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
5020 GTK_STOCK_CANCEL, button_label, NULL);
5022 if (aval != G_ALERTALTERNATE)
5027 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5033 gint compose_send(Compose *compose)
5036 FolderItem *folder = NULL;
5038 gchar *msgpath = NULL;
5039 gboolean discard_window = FALSE;
5040 gchar *errstr = NULL;
5041 gchar *tmsgid = NULL;
5042 MainWindow *mainwin = mainwindow_get_mainwindow();
5043 gboolean queued_removed = FALSE;
5045 if (prefs_common.send_dialog_invisible
5046 || compose->batch == TRUE)
5047 discard_window = TRUE;
5049 compose_allow_user_actions (compose, FALSE);
5050 compose->sending = TRUE;
5052 if (compose_check_entries(compose, TRUE) == FALSE) {
5053 if (compose->batch) {
5054 gtk_widget_show_all(compose->window);
5060 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5063 if (compose->batch) {
5064 gtk_widget_show_all(compose->window);
5067 alertpanel_error(_("Could not queue message for sending:\n\n"
5068 "Charset conversion failed."));
5069 } else if (val == -5) {
5070 alertpanel_error(_("Could not queue message for sending:\n\n"
5071 "Couldn't get recipient encryption key."));
5072 } else if (val == -6) {
5074 } else if (val == -3) {
5075 if (privacy_peek_error())
5076 alertpanel_error(_("Could not queue message for sending:\n\n"
5077 "Signature failed: %s"), privacy_get_error());
5078 } else if (val == -2 && errno != 0) {
5079 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5081 alertpanel_error(_("Could not queue message for sending."));
5086 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5087 if (discard_window) {
5088 compose->sending = FALSE;
5089 compose_close(compose);
5090 /* No more compose access in the normal codepath
5091 * after this point! */
5096 alertpanel_error(_("The message was queued but could not be "
5097 "sent.\nUse \"Send queued messages\" from "
5098 "the main window to retry."));
5099 if (!discard_window) {
5106 if (msgpath == NULL) {
5107 msgpath = folder_item_fetch_msg(folder, msgnum);
5108 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5111 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5112 claws_unlink(msgpath);
5115 if (!discard_window) {
5117 if (!queued_removed)
5118 folder_item_remove_msg(folder, msgnum);
5119 folder_item_scan(folder);
5121 /* make sure we delete that */
5122 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5124 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5125 folder_item_remove_msg(folder, tmp->msgnum);
5126 procmsg_msginfo_free(tmp);
5133 if (!queued_removed)
5134 folder_item_remove_msg(folder, msgnum);
5135 folder_item_scan(folder);
5137 /* make sure we delete that */
5138 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5140 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5141 folder_item_remove_msg(folder, tmp->msgnum);
5142 procmsg_msginfo_free(tmp);
5145 if (!discard_window) {
5146 compose->sending = FALSE;
5147 compose_allow_user_actions (compose, TRUE);
5148 compose_close(compose);
5152 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5153 "the main window to retry."), errstr);
5156 alertpanel_error_log(_("The message was queued but could not be "
5157 "sent.\nUse \"Send queued messages\" from "
5158 "the main window to retry."));
5160 if (!discard_window) {
5169 toolbar_main_set_sensitive(mainwin);
5170 main_window_set_menu_sensitive(mainwin);
5176 compose_allow_user_actions (compose, TRUE);
5177 compose->sending = FALSE;
5178 compose->modified = TRUE;
5179 toolbar_main_set_sensitive(mainwin);
5180 main_window_set_menu_sensitive(mainwin);
5185 static gboolean compose_use_attach(Compose *compose)
5187 GtkTreeModel *model = gtk_tree_view_get_model
5188 (GTK_TREE_VIEW(compose->attach_clist));
5189 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5192 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5195 gchar buf[BUFFSIZE];
5197 gboolean first_to_address;
5198 gboolean first_cc_address;
5200 ComposeHeaderEntry *headerentry;
5201 const gchar *headerentryname;
5202 const gchar *cc_hdr;
5203 const gchar *to_hdr;
5204 gboolean err = FALSE;
5206 debug_print("Writing redirect header\n");
5208 cc_hdr = prefs_common_translated_header_name("Cc:");
5209 to_hdr = prefs_common_translated_header_name("To:");
5211 first_to_address = TRUE;
5212 for (list = compose->header_list; list; list = list->next) {
5213 headerentry = ((ComposeHeaderEntry *)list->data);
5214 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5216 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5217 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5218 Xstrdup_a(str, entstr, return -1);
5220 if (str[0] != '\0') {
5221 compose_convert_header
5222 (compose, buf, sizeof(buf), str,
5223 strlen("Resent-To") + 2, TRUE);
5225 if (first_to_address) {
5226 err |= (fprintf(fp, "Resent-To: ") < 0);
5227 first_to_address = FALSE;
5229 err |= (fprintf(fp, ",") < 0);
5231 err |= (fprintf(fp, "%s", buf) < 0);
5235 if (!first_to_address) {
5236 err |= (fprintf(fp, "\n") < 0);
5239 first_cc_address = TRUE;
5240 for (list = compose->header_list; list; list = list->next) {
5241 headerentry = ((ComposeHeaderEntry *)list->data);
5242 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5244 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5245 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5246 Xstrdup_a(str, strg, return -1);
5248 if (str[0] != '\0') {
5249 compose_convert_header
5250 (compose, buf, sizeof(buf), str,
5251 strlen("Resent-Cc") + 2, TRUE);
5253 if (first_cc_address) {
5254 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5255 first_cc_address = FALSE;
5257 err |= (fprintf(fp, ",") < 0);
5259 err |= (fprintf(fp, "%s", buf) < 0);
5263 if (!first_cc_address) {
5264 err |= (fprintf(fp, "\n") < 0);
5267 return (err ? -1:0);
5270 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5272 gchar buf[BUFFSIZE];
5274 const gchar *entstr;
5275 /* struct utsname utsbuf; */
5276 gboolean err = FALSE;
5278 cm_return_val_if_fail(fp != NULL, -1);
5279 cm_return_val_if_fail(compose->account != NULL, -1);
5280 cm_return_val_if_fail(compose->account->address != NULL, -1);
5283 get_rfc822_date(buf, sizeof(buf));
5284 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5287 if (compose->account->name && *compose->account->name) {
5288 compose_convert_header
5289 (compose, buf, sizeof(buf), compose->account->name,
5290 strlen("From: "), TRUE);
5291 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5292 buf, compose->account->address) < 0);
5294 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5297 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5298 if (*entstr != '\0') {
5299 Xstrdup_a(str, entstr, return -1);
5302 compose_convert_header(compose, buf, sizeof(buf), str,
5303 strlen("Subject: "), FALSE);
5304 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5308 /* Resent-Message-ID */
5309 if (compose->account->set_domain && compose->account->domain) {
5310 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5311 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5312 g_snprintf(buf, sizeof(buf), "%s",
5313 strchr(compose->account->address, '@') ?
5314 strchr(compose->account->address, '@')+1 :
5315 compose->account->address);
5317 g_snprintf(buf, sizeof(buf), "%s", "");
5320 if (compose->account->gen_msgid) {
5322 if (compose->account->msgid_with_addr) {
5323 addr = compose->account->address;
5325 generate_msgid(buf, sizeof(buf), addr);
5326 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5327 compose->msgid = g_strdup(buf);
5329 compose->msgid = NULL;
5332 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5335 /* separator between header and body */
5336 err |= (fputs("\n", fp) == EOF);
5338 return (err ? -1:0);
5341 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5345 gchar buf[BUFFSIZE];
5347 gboolean skip = FALSE;
5348 gboolean err = FALSE;
5349 gchar *not_included[]={
5350 "Return-Path:", "Delivered-To:", "Received:",
5351 "Subject:", "X-UIDL:", "AF:",
5352 "NF:", "PS:", "SRH:",
5353 "SFN:", "DSR:", "MID:",
5354 "CFG:", "PT:", "S:",
5355 "RQ:", "SSV:", "NSV:",
5356 "SSH:", "R:", "MAID:",
5357 "NAID:", "RMID:", "FMID:",
5358 "SCF:", "RRCPT:", "NG:",
5359 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5360 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5361 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5362 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5363 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5366 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5367 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5371 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5373 for (i = 0; not_included[i] != NULL; i++) {
5374 if (g_ascii_strncasecmp(buf, not_included[i],
5375 strlen(not_included[i])) == 0) {
5382 if (fputs(buf, fdest) == -1)
5385 if (!prefs_common.redirect_keep_from) {
5386 if (g_ascii_strncasecmp(buf, "From:",
5387 strlen("From:")) == 0) {
5388 err |= (fputs(" (by way of ", fdest) == EOF);
5389 if (compose->account->name
5390 && *compose->account->name) {
5391 compose_convert_header
5392 (compose, buf, sizeof(buf),
5393 compose->account->name,
5396 err |= (fprintf(fdest, "%s <%s>",
5398 compose->account->address) < 0);
5400 err |= (fprintf(fdest, "%s",
5401 compose->account->address) < 0);
5402 err |= (fputs(")", fdest) == EOF);
5406 if (fputs("\n", fdest) == -1)
5413 if (compose_redirect_write_headers(compose, fdest))
5416 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5417 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5430 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5432 GtkTextBuffer *buffer;
5433 GtkTextIter start, end;
5436 const gchar *out_codeset;
5437 EncodingType encoding = ENC_UNKNOWN;
5438 MimeInfo *mimemsg, *mimetext;
5440 const gchar *src_codeset = CS_INTERNAL;
5441 gchar *from_addr = NULL;
5442 gchar *from_name = NULL;
5444 if (action == COMPOSE_WRITE_FOR_SEND)
5445 attach_parts = TRUE;
5447 /* create message MimeInfo */
5448 mimemsg = procmime_mimeinfo_new();
5449 mimemsg->type = MIMETYPE_MESSAGE;
5450 mimemsg->subtype = g_strdup("rfc822");
5451 mimemsg->content = MIMECONTENT_MEM;
5452 mimemsg->tmp = TRUE; /* must free content later */
5453 mimemsg->data.mem = compose_get_header(compose);
5455 /* Create text part MimeInfo */
5456 /* get all composed text */
5457 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5458 gtk_text_buffer_get_start_iter(buffer, &start);
5459 gtk_text_buffer_get_end_iter(buffer, &end);
5460 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5462 out_codeset = conv_get_charset_str(compose->out_encoding);
5464 if (!out_codeset && is_ascii_str(chars)) {
5465 out_codeset = CS_US_ASCII;
5466 } else if (prefs_common.outgoing_fallback_to_ascii &&
5467 is_ascii_str(chars)) {
5468 out_codeset = CS_US_ASCII;
5469 encoding = ENC_7BIT;
5473 gchar *test_conv_global_out = NULL;
5474 gchar *test_conv_reply = NULL;
5476 /* automatic mode. be automatic. */
5477 codeconv_set_strict(TRUE);
5479 out_codeset = conv_get_outgoing_charset_str();
5481 debug_print("trying to convert to %s\n", out_codeset);
5482 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5485 if (!test_conv_global_out && compose->orig_charset
5486 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5487 out_codeset = compose->orig_charset;
5488 debug_print("failure; trying to convert to %s\n", out_codeset);
5489 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5492 if (!test_conv_global_out && !test_conv_reply) {
5494 out_codeset = CS_INTERNAL;
5495 debug_print("failure; finally using %s\n", out_codeset);
5497 g_free(test_conv_global_out);
5498 g_free(test_conv_reply);
5499 codeconv_set_strict(FALSE);
5502 if (encoding == ENC_UNKNOWN) {
5503 if (prefs_common.encoding_method == CTE_BASE64)
5504 encoding = ENC_BASE64;
5505 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5506 encoding = ENC_QUOTED_PRINTABLE;
5507 else if (prefs_common.encoding_method == CTE_8BIT)
5508 encoding = ENC_8BIT;
5510 encoding = procmime_get_encoding_for_charset(out_codeset);
5513 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5514 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5516 if (action == COMPOSE_WRITE_FOR_SEND) {
5517 codeconv_set_strict(TRUE);
5518 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5519 codeconv_set_strict(FALSE);
5525 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5526 "to the specified %s charset.\n"
5527 "Send it as %s?"), out_codeset, src_codeset);
5528 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5529 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5532 if (aval != G_ALERTALTERNATE) {
5537 out_codeset = src_codeset;
5543 out_codeset = src_codeset;
5548 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5549 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5550 strstr(buf, "\nFrom ") != NULL) {
5551 encoding = ENC_QUOTED_PRINTABLE;
5555 mimetext = procmime_mimeinfo_new();
5556 mimetext->content = MIMECONTENT_MEM;
5557 mimetext->tmp = TRUE; /* must free content later */
5558 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5559 * and free the data, which we need later. */
5560 mimetext->data.mem = g_strdup(buf);
5561 mimetext->type = MIMETYPE_TEXT;
5562 mimetext->subtype = g_strdup("plain");
5563 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5564 g_strdup(out_codeset));
5566 /* protect trailing spaces when signing message */
5567 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5568 privacy_system_can_sign(compose->privacy_system)) {
5569 encoding = ENC_QUOTED_PRINTABLE;
5572 debug_print("main text: %zd bytes encoded as %s in %d\n",
5573 strlen(buf), out_codeset, encoding);
5575 /* check for line length limit */
5576 if (action == COMPOSE_WRITE_FOR_SEND &&
5577 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5578 check_line_length(buf, 1000, &line) < 0) {
5582 msg = g_strdup_printf
5583 (_("Line %d exceeds the line length limit (998 bytes).\n"
5584 "The contents of the message might be broken on the way to the delivery.\n"
5586 "Send it anyway?"), line + 1);
5587 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5589 if (aval != G_ALERTALTERNATE) {
5595 if (encoding != ENC_UNKNOWN)
5596 procmime_encode_content(mimetext, encoding);
5598 /* append attachment parts */
5599 if (compose_use_attach(compose) && attach_parts) {
5600 MimeInfo *mimempart;
5601 gchar *boundary = NULL;
5602 mimempart = procmime_mimeinfo_new();
5603 mimempart->content = MIMECONTENT_EMPTY;
5604 mimempart->type = MIMETYPE_MULTIPART;
5605 mimempart->subtype = g_strdup("mixed");
5609 boundary = generate_mime_boundary(NULL);
5610 } while (strstr(buf, boundary) != NULL);
5612 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5615 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5617 g_node_append(mimempart->node, mimetext->node);
5618 g_node_append(mimemsg->node, mimempart->node);
5620 if (compose_add_attachments(compose, mimempart) < 0)
5623 g_node_append(mimemsg->node, mimetext->node);
5627 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5628 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5629 /* extract name and address */
5630 if (strstr(spec, " <") && strstr(spec, ">")) {
5631 from_addr = g_strdup(strrchr(spec, '<')+1);
5632 *(strrchr(from_addr, '>')) = '\0';
5633 from_name = g_strdup(spec);
5634 *(strrchr(from_name, '<')) = '\0';
5641 /* sign message if sending */
5642 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5643 privacy_system_can_sign(compose->privacy_system))
5644 if (!privacy_sign(compose->privacy_system, mimemsg,
5645 compose->account, from_addr)) {
5652 procmime_write_mimeinfo(mimemsg, fp);
5654 procmime_mimeinfo_free_all(mimemsg);
5659 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5661 GtkTextBuffer *buffer;
5662 GtkTextIter start, end;
5667 if ((fp = g_fopen(file, "wb")) == NULL) {
5668 FILE_OP_ERROR(file, "fopen");
5672 /* chmod for security */
5673 if (change_file_mode_rw(fp, file) < 0) {
5674 FILE_OP_ERROR(file, "chmod");
5675 g_warning("can't change file mode\n");
5678 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5679 gtk_text_buffer_get_start_iter(buffer, &start);
5680 gtk_text_buffer_get_end_iter(buffer, &end);
5681 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5683 chars = conv_codeset_strdup
5684 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5687 if (!chars) return -1;
5690 len = strlen(chars);
5691 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5692 FILE_OP_ERROR(file, "fwrite");
5701 if (fclose(fp) == EOF) {
5702 FILE_OP_ERROR(file, "fclose");
5709 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5712 MsgInfo *msginfo = compose->targetinfo;
5714 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5715 if (!msginfo) return -1;
5717 if (!force && MSG_IS_LOCKED(msginfo->flags))
5720 item = msginfo->folder;
5721 cm_return_val_if_fail(item != NULL, -1);
5723 if (procmsg_msg_exist(msginfo) &&
5724 (folder_has_parent_of_type(item, F_QUEUE) ||
5725 folder_has_parent_of_type(item, F_DRAFT)
5726 || msginfo == compose->autosaved_draft)) {
5727 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5728 g_warning("can't remove the old message\n");
5731 debug_print("removed reedit target %d\n", msginfo->msgnum);
5738 static void compose_remove_draft(Compose *compose)
5741 MsgInfo *msginfo = compose->targetinfo;
5742 drafts = account_get_special_folder(compose->account, F_DRAFT);
5744 if (procmsg_msg_exist(msginfo)) {
5745 folder_item_remove_msg(drafts, msginfo->msgnum);
5750 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5751 gboolean remove_reedit_target)
5753 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5756 static gboolean compose_warn_encryption(Compose *compose)
5758 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5759 AlertValue val = G_ALERTALTERNATE;
5761 if (warning == NULL)
5764 val = alertpanel_full(_("Encryption warning"), warning,
5765 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5766 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5767 if (val & G_ALERTDISABLE) {
5768 val &= ~G_ALERTDISABLE;
5769 if (val == G_ALERTALTERNATE)
5770 privacy_inhibit_encrypt_warning(compose->privacy_system,
5774 if (val == G_ALERTALTERNATE) {
5781 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5782 gchar **msgpath, gboolean check_subject,
5783 gboolean remove_reedit_target)
5790 PrefsAccount *mailac = NULL, *newsac = NULL;
5791 gboolean err = FALSE;
5793 debug_print("queueing message...\n");
5794 cm_return_val_if_fail(compose->account != NULL, -1);
5796 if (compose_check_entries(compose, check_subject) == FALSE) {
5797 if (compose->batch) {
5798 gtk_widget_show_all(compose->window);
5803 if (!compose->to_list && !compose->newsgroup_list) {
5804 g_warning("can't get recipient list.");
5808 if (compose->to_list) {
5809 if (compose->account->protocol != A_NNTP)
5810 mailac = compose->account;
5811 else if (cur_account && cur_account->protocol != A_NNTP)
5812 mailac = cur_account;
5813 else if (!(mailac = compose_current_mail_account())) {
5814 alertpanel_error(_("No account for sending mails available!"));
5819 if (compose->newsgroup_list) {
5820 if (compose->account->protocol == A_NNTP)
5821 newsac = compose->account;
5823 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5828 /* write queue header */
5829 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5830 G_DIR_SEPARATOR, compose, (guint) rand());
5831 debug_print("queuing to %s\n", tmp);
5832 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5833 FILE_OP_ERROR(tmp, "fopen");
5838 if (change_file_mode_rw(fp, tmp) < 0) {
5839 FILE_OP_ERROR(tmp, "chmod");
5840 g_warning("can't change file mode\n");
5843 /* queueing variables */
5844 err |= (fprintf(fp, "AF:\n") < 0);
5845 err |= (fprintf(fp, "NF:0\n") < 0);
5846 err |= (fprintf(fp, "PS:10\n") < 0);
5847 err |= (fprintf(fp, "SRH:1\n") < 0);
5848 err |= (fprintf(fp, "SFN:\n") < 0);
5849 err |= (fprintf(fp, "DSR:\n") < 0);
5851 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5853 err |= (fprintf(fp, "MID:\n") < 0);
5854 err |= (fprintf(fp, "CFG:\n") < 0);
5855 err |= (fprintf(fp, "PT:0\n") < 0);
5856 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5857 err |= (fprintf(fp, "RQ:\n") < 0);
5859 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5861 err |= (fprintf(fp, "SSV:\n") < 0);
5863 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5865 err |= (fprintf(fp, "NSV:\n") < 0);
5866 err |= (fprintf(fp, "SSH:\n") < 0);
5867 /* write recepient list */
5868 if (compose->to_list) {
5869 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5870 for (cur = compose->to_list->next; cur != NULL;
5872 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5873 err |= (fprintf(fp, "\n") < 0);
5875 /* write newsgroup list */
5876 if (compose->newsgroup_list) {
5877 err |= (fprintf(fp, "NG:") < 0);
5878 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5879 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5880 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5881 err |= (fprintf(fp, "\n") < 0);
5883 /* Sylpheed account IDs */
5885 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5887 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5890 if (compose->privacy_system != NULL) {
5891 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5892 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5893 if (compose->use_encryption) {
5895 if (!compose_warn_encryption(compose)) {
5901 if (mailac && mailac->encrypt_to_self) {
5902 GSList *tmp_list = g_slist_copy(compose->to_list);
5903 tmp_list = g_slist_append(tmp_list, compose->account->address);
5904 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5905 g_slist_free(tmp_list);
5907 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5909 if (encdata != NULL) {
5910 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5911 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5912 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5914 } /* else we finally dont want to encrypt */
5916 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5917 /* and if encdata was null, it means there's been a problem in
5920 g_warning("failed to write queue message");
5930 /* Save copy folder */
5931 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5932 gchar *savefolderid;
5934 savefolderid = compose_get_save_to(compose);
5935 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5936 g_free(savefolderid);
5938 /* Save copy folder */
5939 if (compose->return_receipt) {
5940 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5942 /* Message-ID of message replying to */
5943 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5946 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5947 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5950 /* Message-ID of message forwarding to */
5951 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5954 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5955 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5959 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5960 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5962 /* end of headers */
5963 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5965 if (compose->redirect_filename != NULL) {
5966 if (compose_redirect_write_to_file(compose, fp) < 0) {
5974 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5978 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5982 g_warning("failed to write queue message\n");
5988 if (fclose(fp) == EOF) {
5989 FILE_OP_ERROR(tmp, "fclose");
5995 if (item && *item) {
5998 queue = account_get_special_folder(compose->account, F_QUEUE);
6001 g_warning("can't find queue folder\n");
6006 folder_item_scan(queue);
6007 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6008 g_warning("can't queue the message\n");
6014 if (msgpath == NULL) {
6020 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6021 compose_remove_reedit_target(compose, FALSE);
6024 if ((msgnum != NULL) && (item != NULL)) {
6032 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6035 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6037 struct stat statbuf;
6038 gchar *type, *subtype;
6039 GtkTreeModel *model;
6042 model = gtk_tree_view_get_model(tree_view);
6044 if (!gtk_tree_model_get_iter_first(model, &iter))
6047 gtk_tree_model_get(model, &iter,
6051 if (!is_file_exist(ainfo->file)) {
6052 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6053 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6054 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6056 if (val == G_ALERTDEFAULT) {
6061 mimepart = procmime_mimeinfo_new();
6062 mimepart->content = MIMECONTENT_FILE;
6063 mimepart->data.filename = g_strdup(ainfo->file);
6064 mimepart->tmp = FALSE; /* or we destroy our attachment */
6065 mimepart->offset = 0;
6067 g_stat(ainfo->file, &statbuf);
6068 mimepart->length = statbuf.st_size;
6070 type = g_strdup(ainfo->content_type);
6072 if (!strchr(type, '/')) {
6074 type = g_strdup("application/octet-stream");
6077 subtype = strchr(type, '/') + 1;
6078 *(subtype - 1) = '\0';
6079 mimepart->type = procmime_get_media_type(type);
6080 mimepart->subtype = g_strdup(subtype);
6083 if (mimepart->type == MIMETYPE_MESSAGE &&
6084 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6085 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6086 } else if (mimepart->type == MIMETYPE_TEXT) {
6087 if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6088 /* Text parts with no name come from multipart/alternative
6089 * forwards. Make sure the recipient won't look at the
6090 * original HTML part by mistake. */
6091 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6092 ainfo->name = g_strdup_printf(_("Original %s part"),
6096 g_hash_table_insert(mimepart->typeparameters,
6097 g_strdup("charset"), g_strdup(ainfo->charset));
6099 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6100 if (mimepart->type == MIMETYPE_APPLICATION &&
6101 !strcmp2(mimepart->subtype, "octet-stream"))
6102 g_hash_table_insert(mimepart->typeparameters,
6103 g_strdup("name"), g_strdup(ainfo->name));
6104 g_hash_table_insert(mimepart->dispositionparameters,
6105 g_strdup("filename"), g_strdup(ainfo->name));
6106 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6109 if (mimepart->type == MIMETYPE_MESSAGE
6110 || mimepart->type == MIMETYPE_MULTIPART)
6111 ainfo->encoding = ENC_BINARY;
6112 else if (compose->use_signing) {
6113 if (ainfo->encoding == ENC_7BIT)
6114 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6115 else if (ainfo->encoding == ENC_8BIT)
6116 ainfo->encoding = ENC_BASE64;
6121 procmime_encode_content(mimepart, ainfo->encoding);
6123 g_node_append(parent->node, mimepart->node);
6124 } while (gtk_tree_model_iter_next(model, &iter));
6129 #define IS_IN_CUSTOM_HEADER(header) \
6130 (compose->account->add_customhdr && \
6131 custom_header_find(compose->account->customhdr_list, header) != NULL)
6133 static void compose_add_headerfield_from_headerlist(Compose *compose,
6135 const gchar *fieldname,
6136 const gchar *seperator)
6138 gchar *str, *fieldname_w_colon;
6139 gboolean add_field = FALSE;
6141 ComposeHeaderEntry *headerentry;
6142 const gchar *headerentryname;
6143 const gchar *trans_fieldname;
6146 if (IS_IN_CUSTOM_HEADER(fieldname))
6149 debug_print("Adding %s-fields\n", fieldname);
6151 fieldstr = g_string_sized_new(64);
6153 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6154 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6156 for (list = compose->header_list; list; list = list->next) {
6157 headerentry = ((ComposeHeaderEntry *)list->data);
6158 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6160 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6161 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6163 if (str[0] != '\0') {
6165 g_string_append(fieldstr, seperator);
6166 g_string_append(fieldstr, str);
6175 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6176 compose_convert_header
6177 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6178 strlen(fieldname) + 2, TRUE);
6179 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6183 g_free(fieldname_w_colon);
6184 g_string_free(fieldstr, TRUE);
6189 static gchar *compose_get_manual_headers_info(Compose *compose)
6191 GString *sh_header = g_string_new(" ");
6193 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6195 for (list = compose->header_list; list; list = list->next) {
6196 ComposeHeaderEntry *headerentry;
6199 gchar *headername_wcolon;
6200 const gchar *headername_trans;
6202 gboolean standard_header = FALSE;
6204 headerentry = ((ComposeHeaderEntry *)list->data);
6206 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6208 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6213 if (!strstr(tmp, ":")) {
6214 headername_wcolon = g_strconcat(tmp, ":", NULL);
6215 headername = g_strdup(tmp);
6217 headername_wcolon = g_strdup(tmp);
6218 headername = g_strdup(strtok(tmp, ":"));
6222 string = std_headers;
6223 while (*string != NULL) {
6224 headername_trans = prefs_common_translated_header_name(*string);
6225 if (!strcmp(headername_trans, headername_wcolon))
6226 standard_header = TRUE;
6229 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6230 g_string_append_printf(sh_header, "%s ", headername);
6232 g_free(headername_wcolon);
6234 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6235 return g_string_free(sh_header, FALSE);
6238 static gchar *compose_get_header(Compose *compose)
6240 gchar buf[BUFFSIZE];
6241 const gchar *entry_str;
6245 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6247 gchar *from_name = NULL, *from_address = NULL;
6250 cm_return_val_if_fail(compose->account != NULL, NULL);
6251 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6253 header = g_string_sized_new(64);
6256 get_rfc822_date(buf, sizeof(buf));
6257 g_string_append_printf(header, "Date: %s\n", buf);
6261 if (compose->account->name && *compose->account->name) {
6263 QUOTE_IF_REQUIRED(buf, compose->account->name);
6264 tmp = g_strdup_printf("%s <%s>",
6265 buf, compose->account->address);
6267 tmp = g_strdup_printf("%s",
6268 compose->account->address);
6270 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6271 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6273 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6274 from_address = g_strdup(compose->account->address);
6276 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6277 /* extract name and address */
6278 if (strstr(spec, " <") && strstr(spec, ">")) {
6279 from_address = g_strdup(strrchr(spec, '<')+1);
6280 *(strrchr(from_address, '>')) = '\0';
6281 from_name = g_strdup(spec);
6282 *(strrchr(from_name, '<')) = '\0';
6285 from_address = g_strdup(spec);
6292 if (from_name && *from_name) {
6293 compose_convert_header
6294 (compose, buf, sizeof(buf), from_name,
6295 strlen("From: "), TRUE);
6296 QUOTE_IF_REQUIRED(name, buf);
6298 g_string_append_printf(header, "From: %s <%s>\n",
6299 name, from_address);
6301 g_string_append_printf(header, "From: %s\n", from_address);
6304 g_free(from_address);
6307 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6310 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6313 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6317 * If this account is a NNTP account remove Bcc header from
6318 * message body since it otherwise will be publicly shown
6320 if (compose->account->protocol != A_NNTP)
6321 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6324 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6326 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6329 compose_convert_header(compose, buf, sizeof(buf), str,
6330 strlen("Subject: "), FALSE);
6331 g_string_append_printf(header, "Subject: %s\n", buf);
6337 if (compose->account->set_domain && compose->account->domain) {
6338 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6339 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6340 g_snprintf(buf, sizeof(buf), "%s",
6341 strchr(compose->account->address, '@') ?
6342 strchr(compose->account->address, '@')+1 :
6343 compose->account->address);
6345 g_snprintf(buf, sizeof(buf), "%s", "");
6348 if (compose->account->gen_msgid) {
6350 if (compose->account->msgid_with_addr) {
6351 addr = compose->account->address;
6353 generate_msgid(buf, sizeof(buf), addr);
6354 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6355 compose->msgid = g_strdup(buf);
6357 compose->msgid = NULL;
6360 if (compose->remove_references == FALSE) {
6362 if (compose->inreplyto && compose->to_list)
6363 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6366 if (compose->references)
6367 g_string_append_printf(header, "References: %s\n", compose->references);
6371 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6374 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6377 if (compose->account->organization &&
6378 strlen(compose->account->organization) &&
6379 !IS_IN_CUSTOM_HEADER("Organization")) {
6380 compose_convert_header(compose, buf, sizeof(buf),
6381 compose->account->organization,
6382 strlen("Organization: "), FALSE);
6383 g_string_append_printf(header, "Organization: %s\n", buf);
6386 /* Program version and system info */
6387 if (compose->account->gen_xmailer &&
6388 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6389 !compose->newsgroup_list) {
6390 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6392 gtk_major_version, gtk_minor_version, gtk_micro_version,
6395 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6396 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6398 gtk_major_version, gtk_minor_version, gtk_micro_version,
6402 /* custom headers */
6403 if (compose->account->add_customhdr) {
6406 for (cur = compose->account->customhdr_list; cur != NULL;
6408 CustomHeader *chdr = (CustomHeader *)cur->data;
6410 if (custom_header_is_allowed(chdr->name)
6411 && chdr->value != NULL
6412 && *(chdr->value) != '\0') {
6413 compose_convert_header
6414 (compose, buf, sizeof(buf),
6416 strlen(chdr->name) + 2, FALSE);
6417 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6422 /* Automatic Faces and X-Faces */
6423 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6424 g_string_append_printf(header, "X-Face: %s\n", buf);
6426 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6427 g_string_append_printf(header, "X-Face: %s\n", buf);
6429 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6430 g_string_append_printf(header, "Face: %s\n", buf);
6432 else if (get_default_face (buf, sizeof(buf)) == 0) {
6433 g_string_append_printf(header, "Face: %s\n", buf);
6437 switch (compose->priority) {
6438 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6439 "X-Priority: 1 (Highest)\n");
6441 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6442 "X-Priority: 2 (High)\n");
6444 case PRIORITY_NORMAL: break;
6445 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6446 "X-Priority: 4 (Low)\n");
6448 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6449 "X-Priority: 5 (Lowest)\n");
6451 default: debug_print("compose: priority unknown : %d\n",
6455 /* Request Return Receipt */
6456 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6457 if (compose->return_receipt) {
6458 if (compose->account->name
6459 && *compose->account->name) {
6460 compose_convert_header(compose, buf, sizeof(buf),
6461 compose->account->name,
6462 strlen("Disposition-Notification-To: "),
6464 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6466 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6470 /* get special headers */
6471 for (list = compose->header_list; list; list = list->next) {
6472 ComposeHeaderEntry *headerentry;
6475 gchar *headername_wcolon;
6476 const gchar *headername_trans;
6479 gboolean standard_header = FALSE;
6481 headerentry = ((ComposeHeaderEntry *)list->data);
6483 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6485 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6490 if (!strstr(tmp, ":")) {
6491 headername_wcolon = g_strconcat(tmp, ":", NULL);
6492 headername = g_strdup(tmp);
6494 headername_wcolon = g_strdup(tmp);
6495 headername = g_strdup(strtok(tmp, ":"));
6499 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6500 Xstrdup_a(headervalue, entry_str, return NULL);
6501 subst_char(headervalue, '\r', ' ');
6502 subst_char(headervalue, '\n', ' ');
6503 string = std_headers;
6504 while (*string != NULL) {
6505 headername_trans = prefs_common_translated_header_name(*string);
6506 if (!strcmp(headername_trans, headername_wcolon))
6507 standard_header = TRUE;
6510 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6511 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6514 g_free(headername_wcolon);
6518 g_string_free(header, FALSE);
6523 #undef IS_IN_CUSTOM_HEADER
6525 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6526 gint header_len, gboolean addr_field)
6528 gchar *tmpstr = NULL;
6529 const gchar *out_codeset = NULL;
6531 cm_return_if_fail(src != NULL);
6532 cm_return_if_fail(dest != NULL);
6534 if (len < 1) return;
6536 tmpstr = g_strdup(src);
6538 subst_char(tmpstr, '\n', ' ');
6539 subst_char(tmpstr, '\r', ' ');
6542 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6543 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6544 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6549 codeconv_set_strict(TRUE);
6550 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6551 conv_get_charset_str(compose->out_encoding));
6552 codeconv_set_strict(FALSE);
6554 if (!dest || *dest == '\0') {
6555 gchar *test_conv_global_out = NULL;
6556 gchar *test_conv_reply = NULL;
6558 /* automatic mode. be automatic. */
6559 codeconv_set_strict(TRUE);
6561 out_codeset = conv_get_outgoing_charset_str();
6563 debug_print("trying to convert to %s\n", out_codeset);
6564 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6567 if (!test_conv_global_out && compose->orig_charset
6568 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6569 out_codeset = compose->orig_charset;
6570 debug_print("failure; trying to convert to %s\n", out_codeset);
6571 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6574 if (!test_conv_global_out && !test_conv_reply) {
6576 out_codeset = CS_INTERNAL;
6577 debug_print("finally using %s\n", out_codeset);
6579 g_free(test_conv_global_out);
6580 g_free(test_conv_reply);
6581 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6583 codeconv_set_strict(FALSE);
6588 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6592 cm_return_if_fail(user_data != NULL);
6594 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6595 g_strstrip(address);
6596 if (*address != '\0') {
6597 gchar *name = procheader_get_fromname(address);
6598 extract_address(address);
6599 #ifndef USE_NEW_ADDRBOOK
6600 addressbook_add_contact(name, address, NULL, NULL);
6602 debug_print("%s: %s\n", name, address);
6603 if (addressadd_selection(name, address, NULL, NULL)) {
6604 debug_print( "addressbook_add_contact - added\n" );
6611 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6613 GtkWidget *menuitem;
6616 cm_return_if_fail(menu != NULL);
6617 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6619 menuitem = gtk_separator_menu_item_new();
6620 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6621 gtk_widget_show(menuitem);
6623 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6624 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6626 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6627 g_strstrip(address);
6628 if (*address == '\0') {
6629 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6632 g_signal_connect(G_OBJECT(menuitem), "activate",
6633 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6634 gtk_widget_show(menuitem);
6637 void compose_add_extra_header(gchar *header, GtkListStore *model)
6640 if (strcmp(header, "")) {
6641 COMBOBOX_ADD(model, header, COMPOSE_TO);
6645 void compose_add_extra_header_entries(GtkListStore *model)
6649 gchar buf[BUFFSIZE];
6652 if (extra_headers == NULL) {
6653 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6654 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6655 debug_print("extra headers file not found\n");
6656 goto extra_headers_done;
6658 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6659 lastc = strlen(buf) - 1; /* remove trailing control chars */
6660 while (lastc >= 0 && buf[lastc] != ':')
6661 buf[lastc--] = '\0';
6662 if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6663 buf[lastc] = '\0'; /* remove trailing : for comparison */
6664 if (custom_header_is_allowed(buf)) {
6666 extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6669 g_message("disallowed extra header line: %s\n", buf);
6673 g_message("invalid extra header line: %s\n", buf);
6679 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6680 extra_headers = g_slist_reverse(extra_headers);
6682 g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6685 static void compose_create_header_entry(Compose *compose)
6687 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6694 const gchar *header = NULL;
6695 ComposeHeaderEntry *headerentry;
6696 gboolean standard_header = FALSE;
6697 GtkListStore *model;
6699 #if !(GTK_CHECK_VERSION(2,12,0))
6700 GtkTooltips *tips = compose->tooltips;
6703 headerentry = g_new0(ComposeHeaderEntry, 1);
6705 /* Combo box model */
6706 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6707 #if !GTK_CHECK_VERSION(2, 24, 0)
6708 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6710 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6712 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6714 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6716 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6717 COMPOSE_NEWSGROUPS);
6718 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6720 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6721 COMPOSE_FOLLOWUPTO);
6722 compose_add_extra_header_entries(model);
6725 #if GTK_CHECK_VERSION(2, 24, 0)
6726 combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6727 GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6728 gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6729 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6730 gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6732 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6733 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6734 G_CALLBACK(compose_grab_focus_cb), compose);
6735 gtk_widget_show(combo);
6738 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6739 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6742 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6743 compose->header_nextrow, compose->header_nextrow+1,
6744 GTK_SHRINK, GTK_FILL, 0, 0);
6745 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6746 const gchar *last_header_entry = gtk_entry_get_text(
6747 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6749 while (*string != NULL) {
6750 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6751 standard_header = TRUE;
6754 if (standard_header)
6755 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6757 if (!compose->header_last || !standard_header) {
6758 switch(compose->account->protocol) {
6760 header = prefs_common_translated_header_name("Newsgroups:");
6763 header = prefs_common_translated_header_name("To:");
6768 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6770 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6771 G_CALLBACK(compose_grab_focus_cb), compose);
6773 /* Entry field with cleanup button */
6774 button = gtk_button_new();
6775 gtk_button_set_image(GTK_BUTTON(button),
6776 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6777 gtk_widget_show(button);
6778 CLAWS_SET_TIP(button,
6779 _("Delete entry contents"));
6780 entry = gtk_entry_new();
6781 gtk_widget_show(entry);
6782 CLAWS_SET_TIP(entry,
6783 _("Use <tab> to autocomplete from addressbook"));
6784 hbox = gtk_hbox_new (FALSE, 0);
6785 gtk_widget_show(hbox);
6786 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6787 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6788 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6789 compose->header_nextrow, compose->header_nextrow+1,
6790 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6792 g_signal_connect(G_OBJECT(entry), "key-press-event",
6793 G_CALLBACK(compose_headerentry_key_press_event_cb),
6795 g_signal_connect(G_OBJECT(entry), "changed",
6796 G_CALLBACK(compose_headerentry_changed_cb),
6798 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6799 G_CALLBACK(compose_grab_focus_cb), compose);
6801 g_signal_connect(G_OBJECT(button), "clicked",
6802 G_CALLBACK(compose_headerentry_button_clicked_cb),
6806 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6807 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6808 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6809 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6810 G_CALLBACK(compose_header_drag_received_cb),
6812 g_signal_connect(G_OBJECT(entry), "drag-drop",
6813 G_CALLBACK(compose_drag_drop),
6815 g_signal_connect(G_OBJECT(entry), "populate-popup",
6816 G_CALLBACK(compose_entry_popup_extend),
6819 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6821 headerentry->compose = compose;
6822 headerentry->combo = combo;
6823 headerentry->entry = entry;
6824 headerentry->button = button;
6825 headerentry->hbox = hbox;
6826 headerentry->headernum = compose->header_nextrow;
6827 headerentry->type = PREF_NONE;
6829 compose->header_nextrow++;
6830 compose->header_last = headerentry;
6831 compose->header_list =
6832 g_slist_append(compose->header_list,
6836 static void compose_add_header_entry(Compose *compose, const gchar *header,
6837 gchar *text, ComposePrefType pref_type)
6839 ComposeHeaderEntry *last_header = compose->header_last;
6840 gchar *tmp = g_strdup(text), *email;
6841 gboolean replyto_hdr;
6843 replyto_hdr = (!strcasecmp(header,
6844 prefs_common_translated_header_name("Reply-To:")) ||
6846 prefs_common_translated_header_name("Followup-To:")) ||
6848 prefs_common_translated_header_name("In-Reply-To:")));
6850 extract_address(tmp);
6851 email = g_utf8_strdown(tmp, -1);
6853 if (replyto_hdr == FALSE &&
6854 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6856 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6857 header, text, (gint) pref_type);
6863 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6864 gtk_entry_set_text(GTK_ENTRY(
6865 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6867 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6868 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6869 last_header->type = pref_type;
6871 if (replyto_hdr == FALSE)
6872 g_hash_table_insert(compose->email_hashtable, email,
6873 GUINT_TO_POINTER(1));
6880 static void compose_destroy_headerentry(Compose *compose,
6881 ComposeHeaderEntry *headerentry)
6883 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6886 extract_address(text);
6887 email = g_utf8_strdown(text, -1);
6888 g_hash_table_remove(compose->email_hashtable, email);
6892 gtk_widget_destroy(headerentry->combo);
6893 gtk_widget_destroy(headerentry->entry);
6894 gtk_widget_destroy(headerentry->button);
6895 gtk_widget_destroy(headerentry->hbox);
6896 g_free(headerentry);
6899 static void compose_remove_header_entries(Compose *compose)
6902 for (list = compose->header_list; list; list = list->next)
6903 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6905 compose->header_last = NULL;
6906 g_slist_free(compose->header_list);
6907 compose->header_list = NULL;
6908 compose->header_nextrow = 1;
6909 compose_create_header_entry(compose);
6912 static GtkWidget *compose_create_header(Compose *compose)
6914 GtkWidget *from_optmenu_hbox;
6915 GtkWidget *header_scrolledwin_main;
6916 GtkWidget *header_table_main;
6917 GtkWidget *header_scrolledwin;
6918 GtkWidget *header_table;
6920 /* parent with account selection and from header */
6921 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6922 gtk_widget_show(header_scrolledwin_main);
6923 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6925 header_table_main = gtk_table_new(2, 2, FALSE);
6926 gtk_widget_show(header_table_main);
6927 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6928 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6929 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6931 from_optmenu_hbox = compose_account_option_menu_create(compose);
6932 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6933 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6935 /* child with header labels and entries */
6936 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6937 gtk_widget_show(header_scrolledwin);
6938 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6940 header_table = gtk_table_new(2, 2, FALSE);
6941 gtk_widget_show(header_table);
6942 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6943 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6944 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6946 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6947 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6949 compose->header_table = header_table;
6950 compose->header_list = NULL;
6951 compose->header_nextrow = 0;
6953 compose_create_header_entry(compose);
6955 compose->table = NULL;
6957 return header_scrolledwin_main;
6960 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6962 Compose *compose = (Compose *)data;
6963 GdkEventButton event;
6966 event.time = gtk_get_current_event_time();
6968 return attach_button_pressed(compose->attach_clist, &event, compose);
6971 static GtkWidget *compose_create_attach(Compose *compose)
6973 GtkWidget *attach_scrwin;
6974 GtkWidget *attach_clist;
6976 GtkListStore *store;
6977 GtkCellRenderer *renderer;
6978 GtkTreeViewColumn *column;
6979 GtkTreeSelection *selection;
6981 /* attachment list */
6982 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6983 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6984 GTK_POLICY_AUTOMATIC,
6985 GTK_POLICY_AUTOMATIC);
6986 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6988 store = gtk_list_store_new(N_ATTACH_COLS,
6994 G_TYPE_AUTO_POINTER,
6996 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6997 (GTK_TREE_MODEL(store)));
6998 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6999 g_object_unref(store);
7001 renderer = gtk_cell_renderer_text_new();
7002 column = gtk_tree_view_column_new_with_attributes
7003 (_("Mime type"), renderer, "text",
7004 COL_MIMETYPE, NULL);
7005 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7007 renderer = gtk_cell_renderer_text_new();
7008 column = gtk_tree_view_column_new_with_attributes
7009 (_("Size"), renderer, "text",
7011 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7013 renderer = gtk_cell_renderer_text_new();
7014 column = gtk_tree_view_column_new_with_attributes
7015 (_("Name"), renderer, "text",
7017 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7019 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7020 prefs_common.use_stripes_everywhere);
7021 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7022 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7024 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7025 G_CALLBACK(attach_selected), compose);
7026 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7027 G_CALLBACK(attach_button_pressed), compose);
7029 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7030 G_CALLBACK(popup_attach_button_pressed), compose);
7032 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
7033 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7034 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
7035 G_CALLBACK(popup_attach_button_pressed), compose);
7037 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7038 G_CALLBACK(attach_key_pressed), compose);
7041 gtk_drag_dest_set(attach_clist,
7042 GTK_DEST_DEFAULT_ALL, compose_mime_types,
7043 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7044 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7045 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7046 G_CALLBACK(compose_attach_drag_received_cb),
7048 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7049 G_CALLBACK(compose_drag_drop),
7052 compose->attach_scrwin = attach_scrwin;
7053 compose->attach_clist = attach_clist;
7055 return attach_scrwin;
7058 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7059 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7061 static GtkWidget *compose_create_others(Compose *compose)
7064 GtkWidget *savemsg_checkbtn;
7065 GtkWidget *savemsg_combo;
7066 GtkWidget *savemsg_select;
7069 gchar *folderidentifier;
7071 /* Table for settings */
7072 table = gtk_table_new(3, 1, FALSE);
7073 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7074 gtk_widget_show(table);
7075 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7078 /* Save Message to folder */
7079 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7080 gtk_widget_show(savemsg_checkbtn);
7081 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7082 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7083 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7085 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7086 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7088 #if !GTK_CHECK_VERSION(2, 24, 0)
7089 savemsg_combo = gtk_combo_box_entry_new_text();
7091 savemsg_combo = gtk_combo_box_text_new_with_entry();
7093 compose->savemsg_checkbtn = savemsg_checkbtn;
7094 compose->savemsg_combo = savemsg_combo;
7095 gtk_widget_show(savemsg_combo);
7097 if (prefs_common.compose_save_to_history)
7098 #if !GTK_CHECK_VERSION(2, 24, 0)
7099 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7100 prefs_common.compose_save_to_history);
7102 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7103 prefs_common.compose_save_to_history);
7105 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7106 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7107 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7108 G_CALLBACK(compose_grab_focus_cb), compose);
7109 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7110 folderidentifier = folder_item_get_identifier(account_get_special_folder
7111 (compose->account, F_OUTBOX));
7112 compose_set_save_to(compose, folderidentifier);
7113 g_free(folderidentifier);
7116 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7117 gtk_widget_show(savemsg_select);
7118 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7119 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7120 G_CALLBACK(compose_savemsg_select_cb),
7126 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7128 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7129 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7132 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7137 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7140 path = folder_item_get_identifier(dest);
7142 compose_set_save_to(compose, path);
7146 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7147 GdkAtom clip, GtkTextIter *insert_place);
7150 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7154 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7156 if (event->button == 3) {
7158 GtkTextIter sel_start, sel_end;
7159 gboolean stuff_selected;
7161 /* move the cursor to allow GtkAspell to check the word
7162 * under the mouse */
7163 if (event->x && event->y) {
7164 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7165 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7167 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7170 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7171 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7174 stuff_selected = gtk_text_buffer_get_selection_bounds(
7176 &sel_start, &sel_end);
7178 gtk_text_buffer_place_cursor (buffer, &iter);
7179 /* reselect stuff */
7181 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7182 gtk_text_buffer_select_range(buffer,
7183 &sel_start, &sel_end);
7185 return FALSE; /* pass the event so that the right-click goes through */
7188 if (event->button == 2) {
7193 /* get the middle-click position to paste at the correct place */
7194 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7195 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7197 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7200 entry_paste_clipboard(compose, text,
7201 prefs_common.linewrap_pastes,
7202 GDK_SELECTION_PRIMARY, &iter);
7210 static void compose_spell_menu_changed(void *data)
7212 Compose *compose = (Compose *)data;
7214 GtkWidget *menuitem;
7215 GtkWidget *parent_item;
7216 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7219 if (compose->gtkaspell == NULL)
7222 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7223 "/Menu/Spelling/Options");
7225 /* setting the submenu removes /Spelling/Options from the factory
7226 * so we need to save it */
7228 if (parent_item == NULL) {
7229 parent_item = compose->aspell_options_menu;
7230 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7232 compose->aspell_options_menu = parent_item;
7234 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7236 spell_menu = g_slist_reverse(spell_menu);
7237 for (items = spell_menu;
7238 items; items = items->next) {
7239 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7240 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7241 gtk_widget_show(GTK_WIDGET(menuitem));
7243 g_slist_free(spell_menu);
7245 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7246 gtk_widget_show(parent_item);
7249 static void compose_dict_changed(void *data)
7251 Compose *compose = (Compose *) data;
7253 if(compose->gtkaspell &&
7254 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7257 gtkaspell_highlight_all(compose->gtkaspell);
7258 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7262 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7264 Compose *compose = (Compose *)data;
7265 GdkEventButton event;
7268 event.time = gtk_get_current_event_time();
7272 return text_clicked(compose->text, &event, compose);
7275 static gboolean compose_force_window_origin = TRUE;
7276 static Compose *compose_create(PrefsAccount *account,
7285 GtkWidget *handlebox;
7287 GtkWidget *notebook;
7289 GtkWidget *attach_hbox;
7290 GtkWidget *attach_lab1;
7291 GtkWidget *attach_lab2;
7296 GtkWidget *subject_hbox;
7297 GtkWidget *subject_frame;
7298 GtkWidget *subject_entry;
7302 GtkWidget *edit_vbox;
7303 GtkWidget *ruler_hbox;
7305 GtkWidget *scrolledwin;
7307 GtkTextBuffer *buffer;
7308 GtkClipboard *clipboard;
7310 UndoMain *undostruct;
7312 GtkWidget *popupmenu;
7313 GtkWidget *tmpl_menu;
7314 GtkActionGroup *action_group = NULL;
7317 GtkAspell * gtkaspell = NULL;
7320 static GdkGeometry geometry;
7322 cm_return_val_if_fail(account != NULL, NULL);
7324 debug_print("Creating compose window...\n");
7325 compose = g_new0(Compose, 1);
7327 compose->batch = batch;
7328 compose->account = account;
7329 compose->folder = folder;
7331 compose->mutex = cm_mutex_new();
7332 compose->set_cursor_pos = -1;
7334 #if !(GTK_CHECK_VERSION(2,12,0))
7335 compose->tooltips = tips;
7338 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7340 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7341 gtk_widget_set_size_request(window, prefs_common.compose_width,
7342 prefs_common.compose_height);
7344 if (!geometry.max_width) {
7345 geometry.max_width = gdk_screen_width();
7346 geometry.max_height = gdk_screen_height();
7349 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7350 &geometry, GDK_HINT_MAX_SIZE);
7351 if (!geometry.min_width) {
7352 geometry.min_width = 600;
7353 geometry.min_height = 440;
7355 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7356 &geometry, GDK_HINT_MIN_SIZE);
7358 #ifndef GENERIC_UMPC
7359 if (compose_force_window_origin)
7360 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7361 prefs_common.compose_y);
7363 g_signal_connect(G_OBJECT(window), "delete_event",
7364 G_CALLBACK(compose_delete_cb), compose);
7365 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7366 gtk_widget_realize(window);
7368 gtkut_widget_set_composer_icon(window);
7370 vbox = gtk_vbox_new(FALSE, 0);
7371 gtk_container_add(GTK_CONTAINER(window), vbox);
7373 compose->ui_manager = gtk_ui_manager_new();
7374 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7375 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7376 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7377 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7378 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7379 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7380 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7381 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7382 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7383 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7391 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7407 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7408 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7412 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7420 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7430 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7431 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7435 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7436 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7437 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7439 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7441 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7443 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7444 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7449 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7450 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7451 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7454 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7460 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7461 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7462 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7463 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7464 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7465 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7469 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7470 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7471 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7472 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7473 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7475 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7476 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7477 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7478 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7479 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7484 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7485 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7486 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7487 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7488 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7490 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7491 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7492 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7493 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7494 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7496 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7498 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7499 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7500 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7501 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7502 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7504 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7505 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)
7506 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)
7507 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7509 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7511 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7512 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)
7513 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)
7515 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7517 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7518 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)
7519 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7521 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7522 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)
7523 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7525 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7527 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7528 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)
7529 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7530 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7531 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7533 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7534 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)
7535 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)
7536 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7537 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7539 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7540 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7541 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7542 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7543 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7544 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7546 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7547 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7548 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)
7550 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7551 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7552 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7556 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7557 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7558 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7559 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7560 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7561 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7564 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7566 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7567 gtk_widget_show_all(menubar);
7569 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7571 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7573 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7576 if (prefs_common.toolbar_detachable) {
7577 handlebox = gtk_handle_box_new();
7579 handlebox = gtk_hbox_new(FALSE, 0);
7581 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7583 gtk_widget_realize(handlebox);
7585 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7588 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7592 vbox2 = gtk_vbox_new(FALSE, 2);
7593 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7594 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7597 notebook = gtk_notebook_new();
7598 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7599 gtk_widget_show(notebook);
7601 /* header labels and entries */
7602 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7603 compose_create_header(compose),
7604 gtk_label_new_with_mnemonic(_("Hea_der")));
7605 /* attachment list */
7606 attach_hbox = gtk_hbox_new(FALSE, 0);
7607 gtk_widget_show(attach_hbox);
7609 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7610 gtk_widget_show(attach_lab1);
7611 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7613 attach_lab2 = gtk_label_new("");
7614 gtk_widget_show(attach_lab2);
7615 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7617 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7618 compose_create_attach(compose),
7621 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7622 compose_create_others(compose),
7623 gtk_label_new_with_mnemonic(_("Othe_rs")));
7626 subject_hbox = gtk_hbox_new(FALSE, 0);
7627 gtk_widget_show(subject_hbox);
7629 subject_frame = gtk_frame_new(NULL);
7630 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7631 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7632 gtk_widget_show(subject_frame);
7634 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7635 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7636 gtk_widget_show(subject);
7638 label = gtk_label_new(_("Subject:"));
7639 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7640 gtk_widget_show(label);
7643 subject_entry = claws_spell_entry_new();
7645 subject_entry = gtk_entry_new();
7647 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7648 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7649 G_CALLBACK(compose_grab_focus_cb), compose);
7650 gtk_widget_show(subject_entry);
7651 compose->subject_entry = subject_entry;
7652 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7654 edit_vbox = gtk_vbox_new(FALSE, 0);
7656 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7659 ruler_hbox = gtk_hbox_new(FALSE, 0);
7660 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7662 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7663 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7664 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7668 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7669 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7670 GTK_POLICY_AUTOMATIC,
7671 GTK_POLICY_AUTOMATIC);
7672 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7674 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7676 text = gtk_text_view_new();
7677 if (prefs_common.show_compose_margin) {
7678 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7679 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7681 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7682 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7683 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7684 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7685 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7687 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7688 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7689 G_CALLBACK(compose_notebook_size_alloc), compose);
7690 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7691 G_CALLBACK(compose_edit_size_alloc),
7693 g_signal_connect(G_OBJECT(buffer), "changed",
7694 G_CALLBACK(compose_changed_cb), compose);
7695 g_signal_connect(G_OBJECT(text), "grab_focus",
7696 G_CALLBACK(compose_grab_focus_cb), compose);
7697 g_signal_connect(G_OBJECT(buffer), "insert_text",
7698 G_CALLBACK(text_inserted), compose);
7699 g_signal_connect(G_OBJECT(text), "button_press_event",
7700 G_CALLBACK(text_clicked), compose);
7702 g_signal_connect(G_OBJECT(text), "popup-menu",
7703 G_CALLBACK(compose_popup_menu), compose);
7705 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7706 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7707 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7708 G_CALLBACK(compose_popup_menu), compose);
7710 g_signal_connect(G_OBJECT(subject_entry), "changed",
7711 G_CALLBACK(compose_changed_cb), compose);
7712 g_signal_connect(G_OBJECT(subject_entry), "activate",
7713 G_CALLBACK(compose_subject_entry_activated), compose);
7716 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7717 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7718 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7719 g_signal_connect(G_OBJECT(text), "drag_data_received",
7720 G_CALLBACK(compose_insert_drag_received_cb),
7722 g_signal_connect(G_OBJECT(text), "drag-drop",
7723 G_CALLBACK(compose_drag_drop),
7725 g_signal_connect(G_OBJECT(text), "key-press-event",
7726 G_CALLBACK(completion_set_focus_to_subject),
7728 gtk_widget_show_all(vbox);
7730 /* pane between attach clist and text */
7731 paned = gtk_vpaned_new();
7732 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7734 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7735 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7737 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7739 gtk_paned_add1(GTK_PANED(paned), notebook);
7740 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7741 gtk_widget_show_all(paned);
7744 if (prefs_common.textfont) {
7745 PangoFontDescription *font_desc;
7747 font_desc = pango_font_description_from_string
7748 (prefs_common.textfont);
7750 gtk_widget_modify_font(text, font_desc);
7751 pango_font_description_free(font_desc);
7755 gtk_action_group_add_actions(action_group, compose_popup_entries,
7756 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7757 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7758 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7759 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7760 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7761 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7762 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7764 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7766 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7767 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7768 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7770 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7772 undostruct = undo_init(text);
7773 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7776 address_completion_start(window);
7778 compose->window = window;
7779 compose->vbox = vbox;
7780 compose->menubar = menubar;
7781 compose->handlebox = handlebox;
7783 compose->vbox2 = vbox2;
7785 compose->paned = paned;
7787 compose->attach_label = attach_lab2;
7789 compose->notebook = notebook;
7790 compose->edit_vbox = edit_vbox;
7791 compose->ruler_hbox = ruler_hbox;
7792 compose->ruler = ruler;
7793 compose->scrolledwin = scrolledwin;
7794 compose->text = text;
7796 compose->focused_editable = NULL;
7798 compose->popupmenu = popupmenu;
7800 compose->tmpl_menu = tmpl_menu;
7802 compose->mode = mode;
7803 compose->rmode = mode;
7805 compose->targetinfo = NULL;
7806 compose->replyinfo = NULL;
7807 compose->fwdinfo = NULL;
7809 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7810 g_str_equal, (GDestroyNotify) g_free, NULL);
7812 compose->replyto = NULL;
7814 compose->bcc = NULL;
7815 compose->followup_to = NULL;
7817 compose->ml_post = NULL;
7819 compose->inreplyto = NULL;
7820 compose->references = NULL;
7821 compose->msgid = NULL;
7822 compose->boundary = NULL;
7824 compose->autowrap = prefs_common.autowrap;
7825 compose->autoindent = prefs_common.auto_indent;
7826 compose->use_signing = FALSE;
7827 compose->use_encryption = FALSE;
7828 compose->privacy_system = NULL;
7830 compose->modified = FALSE;
7832 compose->return_receipt = FALSE;
7834 compose->to_list = NULL;
7835 compose->newsgroup_list = NULL;
7837 compose->undostruct = undostruct;
7839 compose->sig_str = NULL;
7841 compose->exteditor_file = NULL;
7842 compose->exteditor_pid = -1;
7843 compose->exteditor_tag = -1;
7844 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7847 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7848 if (mode != COMPOSE_REDIRECT) {
7849 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7850 strcmp(prefs_common.dictionary, "")) {
7851 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7852 prefs_common.alt_dictionary,
7853 conv_get_locale_charset_str(),
7854 prefs_common.misspelled_col,
7855 prefs_common.check_while_typing,
7856 prefs_common.recheck_when_changing_dict,
7857 prefs_common.use_alternate,
7858 prefs_common.use_both_dicts,
7859 GTK_TEXT_VIEW(text),
7860 GTK_WINDOW(compose->window),
7861 compose_dict_changed,
7862 compose_spell_menu_changed,
7865 alertpanel_error(_("Spell checker could not "
7867 gtkaspell_checkers_strerror());
7868 gtkaspell_checkers_reset_error();
7870 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7874 compose->gtkaspell = gtkaspell;
7875 compose_spell_menu_changed(compose);
7876 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7879 compose_select_account(compose, account, TRUE);
7881 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7882 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7884 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7885 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7887 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7888 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7890 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7891 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7893 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7894 if (account->protocol != A_NNTP)
7895 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7896 prefs_common_translated_header_name("To:"));
7898 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7899 prefs_common_translated_header_name("Newsgroups:"));
7901 #ifndef USE_NEW_ADDRBOOK
7902 addressbook_set_target_compose(compose);
7904 if (mode != COMPOSE_REDIRECT)
7905 compose_set_template_menu(compose);
7907 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7910 compose_list = g_list_append(compose_list, compose);
7912 if (!prefs_common.show_ruler)
7913 gtk_widget_hide(ruler_hbox);
7915 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7918 compose->priority = PRIORITY_NORMAL;
7919 compose_update_priority_menu_item(compose);
7921 compose_set_out_encoding(compose);
7924 compose_update_actions_menu(compose);
7926 /* Privacy Systems menu */
7927 compose_update_privacy_systems_menu(compose);
7929 activate_privacy_system(compose, account, TRUE);
7930 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7932 gtk_widget_realize(window);
7934 gtk_widget_show(window);
7936 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7937 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7944 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7949 GtkWidget *optmenubox;
7952 GtkWidget *from_name = NULL;
7953 #if !(GTK_CHECK_VERSION(2,12,0))
7954 GtkTooltips *tips = compose->tooltips;
7957 gint num = 0, def_menu = 0;
7959 accounts = account_get_list();
7960 cm_return_val_if_fail(accounts != NULL, NULL);
7962 optmenubox = gtk_event_box_new();
7963 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7964 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7966 hbox = gtk_hbox_new(FALSE, 6);
7967 from_name = gtk_entry_new();
7969 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7970 G_CALLBACK(compose_grab_focus_cb), compose);
7972 for (; accounts != NULL; accounts = accounts->next, num++) {
7973 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7974 gchar *name, *from = NULL;
7976 if (ac == compose->account) def_menu = num;
7978 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7981 if (ac == compose->account) {
7982 if (ac->name && *ac->name) {
7984 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7985 from = g_strdup_printf("%s <%s>",
7987 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7989 from = g_strdup_printf("%s",
7991 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7994 COMBOBOX_ADD(menu, name, ac->account_id);
7999 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8001 g_signal_connect(G_OBJECT(optmenu), "changed",
8002 G_CALLBACK(account_activated),
8004 g_signal_connect(G_OBJECT(from_name), "populate-popup",
8005 G_CALLBACK(compose_entry_popup_extend),
8008 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8009 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8011 CLAWS_SET_TIP(optmenubox,
8012 _("Account to use for this email"));
8013 CLAWS_SET_TIP(from_name,
8014 _("Sender address to be used"));
8016 compose->account_combo = optmenu;
8017 compose->from_name = from_name;
8022 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8024 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8025 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8026 Compose *compose = (Compose *) data;
8028 compose->priority = value;
8032 static void compose_reply_change_mode(Compose *compose,
8035 gboolean was_modified = compose->modified;
8037 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8039 cm_return_if_fail(compose->replyinfo != NULL);
8041 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8043 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8045 if (action == COMPOSE_REPLY_TO_ALL)
8047 if (action == COMPOSE_REPLY_TO_SENDER)
8049 if (action == COMPOSE_REPLY_TO_LIST)
8052 compose_remove_header_entries(compose);
8053 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8054 if (compose->account->set_autocc && compose->account->auto_cc)
8055 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8057 if (compose->account->set_autobcc && compose->account->auto_bcc)
8058 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8060 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8061 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8062 compose_show_first_last_header(compose, TRUE);
8063 compose->modified = was_modified;
8064 compose_set_title(compose);
8067 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8069 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8070 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8071 Compose *compose = (Compose *) data;
8074 compose_reply_change_mode(compose, value);
8077 static void compose_update_priority_menu_item(Compose * compose)
8079 GtkWidget *menuitem = NULL;
8080 switch (compose->priority) {
8081 case PRIORITY_HIGHEST:
8082 menuitem = gtk_ui_manager_get_widget
8083 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8086 menuitem = gtk_ui_manager_get_widget
8087 (compose->ui_manager, "/Menu/Options/Priority/High");
8089 case PRIORITY_NORMAL:
8090 menuitem = gtk_ui_manager_get_widget
8091 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8094 menuitem = gtk_ui_manager_get_widget
8095 (compose->ui_manager, "/Menu/Options/Priority/Low");
8097 case PRIORITY_LOWEST:
8098 menuitem = gtk_ui_manager_get_widget
8099 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8102 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8105 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8107 Compose *compose = (Compose *) data;
8109 gboolean can_sign = FALSE, can_encrypt = FALSE;
8111 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8113 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8116 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8117 g_free(compose->privacy_system);
8118 compose->privacy_system = NULL;
8119 if (systemid != NULL) {
8120 compose->privacy_system = g_strdup(systemid);
8122 can_sign = privacy_system_can_sign(systemid);
8123 can_encrypt = privacy_system_can_encrypt(systemid);
8126 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8128 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8129 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8132 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8134 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8135 GtkWidget *menuitem = NULL;
8136 GList *children, *amenu;
8137 gboolean can_sign = FALSE, can_encrypt = FALSE;
8138 gboolean found = FALSE;
8140 if (compose->privacy_system != NULL) {
8142 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8143 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8144 cm_return_if_fail(menuitem != NULL);
8146 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8149 while (amenu != NULL) {
8150 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8151 if (systemid != NULL) {
8152 if (strcmp(systemid, compose->privacy_system) == 0 &&
8153 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8154 menuitem = GTK_WIDGET(amenu->data);
8156 can_sign = privacy_system_can_sign(systemid);
8157 can_encrypt = privacy_system_can_encrypt(systemid);
8161 } else if (strlen(compose->privacy_system) == 0 &&
8162 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8163 menuitem = GTK_WIDGET(amenu->data);
8166 can_encrypt = FALSE;
8171 amenu = amenu->next;
8173 g_list_free(children);
8174 if (menuitem != NULL)
8175 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8177 if (warn && !found && strlen(compose->privacy_system)) {
8178 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8179 "will not be able to sign or encrypt this message."),
8180 compose->privacy_system);
8184 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8185 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8188 static void compose_set_out_encoding(Compose *compose)
8190 CharSet out_encoding;
8191 const gchar *branch = NULL;
8192 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8194 switch(out_encoding) {
8195 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8196 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8197 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8198 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8199 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8200 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8201 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8202 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8203 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8204 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8205 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8206 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8207 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8208 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8209 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8210 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8211 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8212 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8213 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8214 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8215 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8216 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8217 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8218 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8219 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8220 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8221 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8222 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8223 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8224 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8225 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8226 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8227 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8229 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8232 static void compose_set_template_menu(Compose *compose)
8234 GSList *tmpl_list, *cur;
8238 tmpl_list = template_get_config();
8240 menu = gtk_menu_new();
8242 gtk_menu_set_accel_group (GTK_MENU (menu),
8243 gtk_ui_manager_get_accel_group(compose->ui_manager));
8244 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8245 Template *tmpl = (Template *)cur->data;
8246 gchar *accel_path = NULL;
8247 item = gtk_menu_item_new_with_label(tmpl->name);
8248 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8249 g_signal_connect(G_OBJECT(item), "activate",
8250 G_CALLBACK(compose_template_activate_cb),
8252 g_object_set_data(G_OBJECT(item), "template", tmpl);
8253 gtk_widget_show(item);
8254 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8255 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8259 gtk_widget_show(menu);
8260 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8263 void compose_update_actions_menu(Compose *compose)
8265 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8268 static void compose_update_privacy_systems_menu(Compose *compose)
8270 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8271 GSList *systems, *cur;
8273 GtkWidget *system_none;
8275 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8276 GtkWidget *privacy_menu = gtk_menu_new();
8278 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8279 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8281 g_signal_connect(G_OBJECT(system_none), "activate",
8282 G_CALLBACK(compose_set_privacy_system_cb), compose);
8284 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8285 gtk_widget_show(system_none);
8287 systems = privacy_get_system_ids();
8288 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8289 gchar *systemid = cur->data;
8291 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8292 widget = gtk_radio_menu_item_new_with_label(group,
8293 privacy_system_get_name(systemid));
8294 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8295 g_strdup(systemid), g_free);
8296 g_signal_connect(G_OBJECT(widget), "activate",
8297 G_CALLBACK(compose_set_privacy_system_cb), compose);
8299 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8300 gtk_widget_show(widget);
8303 g_slist_free(systems);
8304 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8305 gtk_widget_show_all(privacy_menu);
8306 gtk_widget_show_all(privacy_menuitem);
8309 void compose_reflect_prefs_all(void)
8314 for (cur = compose_list; cur != NULL; cur = cur->next) {
8315 compose = (Compose *)cur->data;
8316 compose_set_template_menu(compose);
8320 void compose_reflect_prefs_pixmap_theme(void)
8325 for (cur = compose_list; cur != NULL; cur = cur->next) {
8326 compose = (Compose *)cur->data;
8327 toolbar_update(TOOLBAR_COMPOSE, compose);
8331 static const gchar *compose_quote_char_from_context(Compose *compose)
8333 const gchar *qmark = NULL;
8335 cm_return_val_if_fail(compose != NULL, NULL);
8337 switch (compose->mode) {
8338 /* use forward-specific quote char */
8339 case COMPOSE_FORWARD:
8340 case COMPOSE_FORWARD_AS_ATTACH:
8341 case COMPOSE_FORWARD_INLINE:
8342 if (compose->folder && compose->folder->prefs &&
8343 compose->folder->prefs->forward_with_format)
8344 qmark = compose->folder->prefs->forward_quotemark;
8345 else if (compose->account->forward_with_format)
8346 qmark = compose->account->forward_quotemark;
8348 qmark = prefs_common.fw_quotemark;
8351 /* use reply-specific quote char in all other modes */
8353 if (compose->folder && compose->folder->prefs &&
8354 compose->folder->prefs->reply_with_format)
8355 qmark = compose->folder->prefs->reply_quotemark;
8356 else if (compose->account->reply_with_format)
8357 qmark = compose->account->reply_quotemark;
8359 qmark = prefs_common.quotemark;
8363 if (qmark == NULL || *qmark == '\0')
8369 static void compose_template_apply(Compose *compose, Template *tmpl,
8373 GtkTextBuffer *buffer;
8377 gchar *parsed_str = NULL;
8378 gint cursor_pos = 0;
8379 const gchar *err_msg = _("The body of the template has an error at line %d.");
8382 /* process the body */
8384 text = GTK_TEXT_VIEW(compose->text);
8385 buffer = gtk_text_view_get_buffer(text);
8388 qmark = compose_quote_char_from_context(compose);
8390 if (compose->replyinfo != NULL) {
8393 gtk_text_buffer_set_text(buffer, "", -1);
8394 mark = gtk_text_buffer_get_insert(buffer);
8395 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8397 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8398 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8400 } else if (compose->fwdinfo != NULL) {
8403 gtk_text_buffer_set_text(buffer, "", -1);
8404 mark = gtk_text_buffer_get_insert(buffer);
8405 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8407 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8408 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8411 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8413 GtkTextIter start, end;
8416 gtk_text_buffer_get_start_iter(buffer, &start);
8417 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8418 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8420 /* clear the buffer now */
8422 gtk_text_buffer_set_text(buffer, "", -1);
8424 parsed_str = compose_quote_fmt(compose, dummyinfo,
8425 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8426 procmsg_msginfo_free( dummyinfo );
8432 gtk_text_buffer_set_text(buffer, "", -1);
8433 mark = gtk_text_buffer_get_insert(buffer);
8434 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8437 if (replace && parsed_str && compose->account->auto_sig)
8438 compose_insert_sig(compose, FALSE);
8440 if (replace && parsed_str) {
8441 gtk_text_buffer_get_start_iter(buffer, &iter);
8442 gtk_text_buffer_place_cursor(buffer, &iter);
8446 cursor_pos = quote_fmt_get_cursor_pos();
8447 compose->set_cursor_pos = cursor_pos;
8448 if (cursor_pos == -1)
8450 gtk_text_buffer_get_start_iter(buffer, &iter);
8451 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8452 gtk_text_buffer_place_cursor(buffer, &iter);
8455 /* process the other fields */
8457 compose_template_apply_fields(compose, tmpl);
8458 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8459 quote_fmt_reset_vartable();
8460 compose_changed_cb(NULL, compose);
8463 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8464 gtkaspell_highlight_all(compose->gtkaspell);
8468 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8470 MsgInfo* dummyinfo = NULL;
8471 MsgInfo *msginfo = NULL;
8474 if (compose->replyinfo != NULL)
8475 msginfo = compose->replyinfo;
8476 else if (compose->fwdinfo != NULL)
8477 msginfo = compose->fwdinfo;
8479 dummyinfo = compose_msginfo_new_from_compose(compose);
8480 msginfo = dummyinfo;
8483 if (tmpl->from && *tmpl->from != '\0') {
8485 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8486 compose->gtkaspell);
8488 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8490 quote_fmt_scan_string(tmpl->from);
8493 buf = quote_fmt_get_buffer();
8495 alertpanel_error(_("Template From format error."));
8497 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8501 if (tmpl->to && *tmpl->to != '\0') {
8503 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8504 compose->gtkaspell);
8506 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8508 quote_fmt_scan_string(tmpl->to);
8511 buf = quote_fmt_get_buffer();
8513 alertpanel_error(_("Template To format error."));
8515 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8519 if (tmpl->cc && *tmpl->cc != '\0') {
8521 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8522 compose->gtkaspell);
8524 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8526 quote_fmt_scan_string(tmpl->cc);
8529 buf = quote_fmt_get_buffer();
8531 alertpanel_error(_("Template Cc format error."));
8533 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8537 if (tmpl->bcc && *tmpl->bcc != '\0') {
8539 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8540 compose->gtkaspell);
8542 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8544 quote_fmt_scan_string(tmpl->bcc);
8547 buf = quote_fmt_get_buffer();
8549 alertpanel_error(_("Template Bcc format error."));
8551 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8555 /* process the subject */
8556 if (tmpl->subject && *tmpl->subject != '\0') {
8558 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8559 compose->gtkaspell);
8561 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8563 quote_fmt_scan_string(tmpl->subject);
8566 buf = quote_fmt_get_buffer();
8568 alertpanel_error(_("Template subject format error."));
8570 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8574 procmsg_msginfo_free( dummyinfo );
8577 static void compose_destroy(Compose *compose)
8579 GtkAllocation allocation;
8580 GtkTextBuffer *buffer;
8581 GtkClipboard *clipboard;
8583 compose_list = g_list_remove(compose_list, compose);
8585 if (compose->updating) {
8586 debug_print("danger, not destroying anything now\n");
8587 compose->deferred_destroy = TRUE;
8590 /* NOTE: address_completion_end() does nothing with the window
8591 * however this may change. */
8592 address_completion_end(compose->window);
8594 slist_free_strings_full(compose->to_list);
8595 slist_free_strings_full(compose->newsgroup_list);
8596 slist_free_strings_full(compose->header_list);
8598 slist_free_strings_full(extra_headers);
8599 extra_headers = NULL;
8601 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8603 g_hash_table_destroy(compose->email_hashtable);
8605 procmsg_msginfo_free(compose->targetinfo);
8606 procmsg_msginfo_free(compose->replyinfo);
8607 procmsg_msginfo_free(compose->fwdinfo);
8609 g_free(compose->replyto);
8610 g_free(compose->cc);
8611 g_free(compose->bcc);
8612 g_free(compose->newsgroups);
8613 g_free(compose->followup_to);
8615 g_free(compose->ml_post);
8617 g_free(compose->inreplyto);
8618 g_free(compose->references);
8619 g_free(compose->msgid);
8620 g_free(compose->boundary);
8622 g_free(compose->redirect_filename);
8623 if (compose->undostruct)
8624 undo_destroy(compose->undostruct);
8626 g_free(compose->sig_str);
8628 g_free(compose->exteditor_file);
8630 g_free(compose->orig_charset);
8632 g_free(compose->privacy_system);
8634 #ifndef USE_NEW_ADDRBOOK
8635 if (addressbook_get_target_compose() == compose)
8636 addressbook_set_target_compose(NULL);
8639 if (compose->gtkaspell) {
8640 gtkaspell_delete(compose->gtkaspell);
8641 compose->gtkaspell = NULL;
8645 if (!compose->batch) {
8646 gtk_widget_get_allocation(compose->window, &allocation);
8647 prefs_common.compose_width = allocation.width;
8648 prefs_common.compose_height = allocation.height;
8651 if (!gtk_widget_get_parent(compose->paned))
8652 gtk_widget_destroy(compose->paned);
8653 gtk_widget_destroy(compose->popupmenu);
8655 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8656 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8657 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8659 gtk_widget_destroy(compose->window);
8660 toolbar_destroy(compose->toolbar);
8661 g_free(compose->toolbar);
8662 cm_mutex_free(compose->mutex);
8666 static void compose_attach_info_free(AttachInfo *ainfo)
8668 g_free(ainfo->file);
8669 g_free(ainfo->content_type);
8670 g_free(ainfo->name);
8671 g_free(ainfo->charset);
8675 static void compose_attach_update_label(Compose *compose)
8680 GtkTreeModel *model;
8685 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8686 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8687 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8691 while(gtk_tree_model_iter_next(model, &iter))
8694 text = g_strdup_printf("(%d)", i);
8695 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8699 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8701 Compose *compose = (Compose *)data;
8702 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8703 GtkTreeSelection *selection;
8705 GtkTreeModel *model;
8707 selection = gtk_tree_view_get_selection(tree_view);
8708 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8713 for (cur = sel; cur != NULL; cur = cur->next) {
8714 GtkTreePath *path = cur->data;
8715 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8718 gtk_tree_path_free(path);
8721 for (cur = sel; cur != NULL; cur = cur->next) {
8722 GtkTreeRowReference *ref = cur->data;
8723 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8726 if (gtk_tree_model_get_iter(model, &iter, path))
8727 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8729 gtk_tree_path_free(path);
8730 gtk_tree_row_reference_free(ref);
8734 compose_attach_update_label(compose);
8737 static struct _AttachProperty
8740 GtkWidget *mimetype_entry;
8741 GtkWidget *encoding_optmenu;
8742 GtkWidget *path_entry;
8743 GtkWidget *filename_entry;
8745 GtkWidget *cancel_btn;
8748 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8750 gtk_tree_path_free((GtkTreePath *)ptr);
8753 static void compose_attach_property(GtkAction *action, gpointer data)
8755 Compose *compose = (Compose *)data;
8756 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8758 GtkComboBox *optmenu;
8759 GtkTreeSelection *selection;
8761 GtkTreeModel *model;
8764 static gboolean cancelled;
8766 /* only if one selected */
8767 selection = gtk_tree_view_get_selection(tree_view);
8768 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8771 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8775 path = (GtkTreePath *) sel->data;
8776 gtk_tree_model_get_iter(model, &iter, path);
8777 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8780 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8786 if (!attach_prop.window)
8787 compose_attach_property_create(&cancelled);
8788 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8789 gtk_widget_grab_focus(attach_prop.ok_btn);
8790 gtk_widget_show(attach_prop.window);
8791 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8792 GTK_WINDOW(compose->window));
8794 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8795 if (ainfo->encoding == ENC_UNKNOWN)
8796 combobox_select_by_data(optmenu, ENC_BASE64);
8798 combobox_select_by_data(optmenu, ainfo->encoding);
8800 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8801 ainfo->content_type ? ainfo->content_type : "");
8802 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8803 ainfo->file ? ainfo->file : "");
8804 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8805 ainfo->name ? ainfo->name : "");
8808 const gchar *entry_text;
8810 gchar *cnttype = NULL;
8817 gtk_widget_hide(attach_prop.window);
8818 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8823 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8824 if (*entry_text != '\0') {
8827 text = g_strstrip(g_strdup(entry_text));
8828 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8829 cnttype = g_strdup(text);
8832 alertpanel_error(_("Invalid MIME type."));
8838 ainfo->encoding = combobox_get_active_data(optmenu);
8840 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8841 if (*entry_text != '\0') {
8842 if (is_file_exist(entry_text) &&
8843 (size = get_file_size(entry_text)) > 0)
8844 file = g_strdup(entry_text);
8847 (_("File doesn't exist or is empty."));
8853 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8854 if (*entry_text != '\0') {
8855 g_free(ainfo->name);
8856 ainfo->name = g_strdup(entry_text);
8860 g_free(ainfo->content_type);
8861 ainfo->content_type = cnttype;
8864 g_free(ainfo->file);
8868 ainfo->size = (goffset)size;
8870 /* update tree store */
8871 text = to_human_readable(ainfo->size);
8872 gtk_tree_model_get_iter(model, &iter, path);
8873 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8874 COL_MIMETYPE, ainfo->content_type,
8876 COL_NAME, ainfo->name,
8877 COL_CHARSET, ainfo->charset,
8883 gtk_tree_path_free(path);
8886 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8888 label = gtk_label_new(str); \
8889 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8890 GTK_FILL, 0, 0, 0); \
8891 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8893 entry = gtk_entry_new(); \
8894 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8895 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8898 static void compose_attach_property_create(gboolean *cancelled)
8904 GtkWidget *mimetype_entry;
8907 GtkListStore *optmenu_menu;
8908 GtkWidget *path_entry;
8909 GtkWidget *filename_entry;
8912 GtkWidget *cancel_btn;
8913 GList *mime_type_list, *strlist;
8916 debug_print("Creating attach_property window...\n");
8918 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8919 gtk_widget_set_size_request(window, 480, -1);
8920 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8921 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8922 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8923 g_signal_connect(G_OBJECT(window), "delete_event",
8924 G_CALLBACK(attach_property_delete_event),
8926 g_signal_connect(G_OBJECT(window), "key_press_event",
8927 G_CALLBACK(attach_property_key_pressed),
8930 vbox = gtk_vbox_new(FALSE, 8);
8931 gtk_container_add(GTK_CONTAINER(window), vbox);
8933 table = gtk_table_new(4, 2, FALSE);
8934 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8935 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8936 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8938 label = gtk_label_new(_("MIME type"));
8939 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8941 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8942 #if !GTK_CHECK_VERSION(2, 24, 0)
8943 mimetype_entry = gtk_combo_box_entry_new_text();
8945 mimetype_entry = gtk_combo_box_text_new_with_entry();
8947 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8948 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8950 /* stuff with list */
8951 mime_type_list = procmime_get_mime_type_list();
8953 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8954 MimeType *type = (MimeType *) mime_type_list->data;
8957 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8959 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8962 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8963 (GCompareFunc)strcmp2);
8966 for (mime_type_list = strlist; mime_type_list != NULL;
8967 mime_type_list = mime_type_list->next) {
8968 #if !GTK_CHECK_VERSION(2, 24, 0)
8969 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8971 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
8973 g_free(mime_type_list->data);
8975 g_list_free(strlist);
8976 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8977 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8979 label = gtk_label_new(_("Encoding"));
8980 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8982 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8984 hbox = gtk_hbox_new(FALSE, 0);
8985 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8986 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8988 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8989 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8991 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8992 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8993 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8994 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8995 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8997 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8999 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
9000 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9002 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9003 &ok_btn, GTK_STOCK_OK,
9005 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9006 gtk_widget_grab_default(ok_btn);
9008 g_signal_connect(G_OBJECT(ok_btn), "clicked",
9009 G_CALLBACK(attach_property_ok),
9011 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9012 G_CALLBACK(attach_property_cancel),
9015 gtk_widget_show_all(vbox);
9017 attach_prop.window = window;
9018 attach_prop.mimetype_entry = mimetype_entry;
9019 attach_prop.encoding_optmenu = optmenu;
9020 attach_prop.path_entry = path_entry;
9021 attach_prop.filename_entry = filename_entry;
9022 attach_prop.ok_btn = ok_btn;
9023 attach_prop.cancel_btn = cancel_btn;
9026 #undef SET_LABEL_AND_ENTRY
9028 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9034 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9040 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9041 gboolean *cancelled)
9049 static gboolean attach_property_key_pressed(GtkWidget *widget,
9051 gboolean *cancelled)
9053 if (event && event->keyval == GDK_KEY_Escape) {
9057 if (event && event->keyval == GDK_KEY_Return) {
9065 static void compose_exec_ext_editor(Compose *compose)
9072 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9073 G_DIR_SEPARATOR, compose);
9075 if (pipe(pipe_fds) < 0) {
9081 if ((pid = fork()) < 0) {
9088 /* close the write side of the pipe */
9091 compose->exteditor_file = g_strdup(tmp);
9092 compose->exteditor_pid = pid;
9094 compose_set_ext_editor_sensitive(compose, FALSE);
9097 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9099 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9101 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9105 } else { /* process-monitoring process */
9111 /* close the read side of the pipe */
9114 if (compose_write_body_to_file(compose, tmp) < 0) {
9115 fd_write_all(pipe_fds[1], "2\n", 2);
9119 pid_ed = compose_exec_ext_editor_real(tmp);
9121 fd_write_all(pipe_fds[1], "1\n", 2);
9125 /* wait until editor is terminated */
9126 waitpid(pid_ed, NULL, 0);
9128 fd_write_all(pipe_fds[1], "0\n", 2);
9135 #endif /* G_OS_UNIX */
9139 static gint compose_exec_ext_editor_real(const gchar *file)
9146 cm_return_val_if_fail(file != NULL, -1);
9148 if ((pid = fork()) < 0) {
9153 if (pid != 0) return pid;
9155 /* grandchild process */
9157 if (setpgid(0, getppid()))
9160 if (prefs_common_get_ext_editor_cmd() &&
9161 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9162 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9163 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9165 if (prefs_common_get_ext_editor_cmd())
9166 g_warning("External editor command-line is invalid: '%s'\n",
9167 prefs_common_get_ext_editor_cmd());
9168 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9171 cmdline = strsplit_with_quote(buf, " ", 1024);
9172 execvp(cmdline[0], cmdline);
9175 g_strfreev(cmdline);
9180 static gboolean compose_ext_editor_kill(Compose *compose)
9182 pid_t pgid = compose->exteditor_pid * -1;
9185 ret = kill(pgid, 0);
9187 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9191 msg = g_strdup_printf
9192 (_("The external editor is still working.\n"
9193 "Force terminating the process?\n"
9194 "process group id: %d"), -pgid);
9195 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9196 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9200 if (val == G_ALERTALTERNATE) {
9201 g_source_remove(compose->exteditor_tag);
9202 g_io_channel_shutdown(compose->exteditor_ch,
9204 g_io_channel_unref(compose->exteditor_ch);
9206 if (kill(pgid, SIGTERM) < 0) perror("kill");
9207 waitpid(compose->exteditor_pid, NULL, 0);
9209 g_warning("Terminated process group id: %d", -pgid);
9210 g_warning("Temporary file: %s",
9211 compose->exteditor_file);
9213 compose_set_ext_editor_sensitive(compose, TRUE);
9215 g_free(compose->exteditor_file);
9216 compose->exteditor_file = NULL;
9217 compose->exteditor_pid = -1;
9218 compose->exteditor_ch = NULL;
9219 compose->exteditor_tag = -1;
9227 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9231 Compose *compose = (Compose *)data;
9234 debug_print("Compose: input from monitoring process\n");
9236 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9238 g_io_channel_shutdown(source, FALSE, NULL);
9239 g_io_channel_unref(source);
9241 waitpid(compose->exteditor_pid, NULL, 0);
9243 if (buf[0] == '0') { /* success */
9244 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9245 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9247 gtk_text_buffer_set_text(buffer, "", -1);
9248 compose_insert_file(compose, compose->exteditor_file);
9249 compose_changed_cb(NULL, compose);
9250 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9252 if (claws_unlink(compose->exteditor_file) < 0)
9253 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9254 } else if (buf[0] == '1') { /* failed */
9255 g_warning("Couldn't exec external editor\n");
9256 if (claws_unlink(compose->exteditor_file) < 0)
9257 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9258 } else if (buf[0] == '2') {
9259 g_warning("Couldn't write to file\n");
9260 } else if (buf[0] == '3') {
9261 g_warning("Pipe read failed\n");
9264 compose_set_ext_editor_sensitive(compose, TRUE);
9266 g_free(compose->exteditor_file);
9267 compose->exteditor_file = NULL;
9268 compose->exteditor_pid = -1;
9269 compose->exteditor_ch = NULL;
9270 compose->exteditor_tag = -1;
9275 static void compose_set_ext_editor_sensitive(Compose *compose,
9278 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9279 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9281 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9282 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9283 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9284 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9286 gtk_widget_set_sensitive(compose->text, sensitive);
9287 if (compose->toolbar->send_btn)
9288 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9289 if (compose->toolbar->sendl_btn)
9290 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9291 if (compose->toolbar->draft_btn)
9292 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9293 if (compose->toolbar->insert_btn)
9294 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9295 if (compose->toolbar->sig_btn)
9296 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9297 if (compose->toolbar->exteditor_btn)
9298 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9299 if (compose->toolbar->linewrap_current_btn)
9300 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9301 if (compose->toolbar->linewrap_all_btn)
9302 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9304 #endif /* G_OS_UNIX */
9307 * compose_undo_state_changed:
9309 * Change the sensivity of the menuentries undo and redo
9311 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9312 gint redo_state, gpointer data)
9314 Compose *compose = (Compose *)data;
9316 switch (undo_state) {
9317 case UNDO_STATE_TRUE:
9318 if (!undostruct->undo_state) {
9319 undostruct->undo_state = TRUE;
9320 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9323 case UNDO_STATE_FALSE:
9324 if (undostruct->undo_state) {
9325 undostruct->undo_state = FALSE;
9326 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9329 case UNDO_STATE_UNCHANGED:
9331 case UNDO_STATE_REFRESH:
9332 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9335 g_warning("Undo state not recognized");
9339 switch (redo_state) {
9340 case UNDO_STATE_TRUE:
9341 if (!undostruct->redo_state) {
9342 undostruct->redo_state = TRUE;
9343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9346 case UNDO_STATE_FALSE:
9347 if (undostruct->redo_state) {
9348 undostruct->redo_state = FALSE;
9349 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9352 case UNDO_STATE_UNCHANGED:
9354 case UNDO_STATE_REFRESH:
9355 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9358 g_warning("Redo state not recognized");
9363 /* callback functions */
9365 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9366 GtkAllocation *allocation,
9369 prefs_common.compose_notebook_height = allocation->height;
9372 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9373 * includes "non-client" (windows-izm) in calculation, so this calculation
9374 * may not be accurate.
9376 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9377 GtkAllocation *allocation,
9378 GtkSHRuler *shruler)
9380 if (prefs_common.show_ruler) {
9381 gint char_width = 0, char_height = 0;
9382 gint line_width_in_chars;
9384 gtkut_get_font_size(GTK_WIDGET(widget),
9385 &char_width, &char_height);
9386 line_width_in_chars =
9387 (allocation->width - allocation->x) / char_width;
9389 /* got the maximum */
9390 gtk_shruler_set_range(GTK_SHRULER(shruler),
9391 0.0, line_width_in_chars, 0);
9400 ComposePrefType type;
9401 gboolean entry_marked;
9404 static void account_activated(GtkComboBox *optmenu, gpointer data)
9406 Compose *compose = (Compose *)data;
9409 gchar *folderidentifier;
9410 gint account_id = 0;
9413 GSList *list, *saved_list = NULL;
9414 HeaderEntryState *state;
9415 GtkRcStyle *style = NULL;
9416 #if !GTK_CHECK_VERSION(3, 0, 0)
9417 static GdkColor yellow;
9418 static gboolean color_set = FALSE;
9420 static GdkColor yellow = { (guint32)0, (guint32)0xf5, (guint32)0xf6, (guint32)0xbe };
9423 /* Get ID of active account in the combo box */
9424 menu = gtk_combo_box_get_model(optmenu);
9425 gtk_combo_box_get_active_iter(optmenu, &iter);
9426 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9428 ac = account_find_from_id(account_id);
9429 cm_return_if_fail(ac != NULL);
9431 if (ac != compose->account) {
9432 compose_select_account(compose, ac, FALSE);
9434 for (list = compose->header_list; list; list = list->next) {
9435 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9437 if (hentry->type == PREF_ACCOUNT || !list->next) {
9438 compose_destroy_headerentry(compose, hentry);
9442 state = g_malloc0(sizeof(HeaderEntryState));
9443 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9444 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9445 state->entry = gtk_editable_get_chars(
9446 GTK_EDITABLE(hentry->entry), 0, -1);
9447 state->type = hentry->type;
9449 #if !GTK_CHECK_VERSION(3, 0, 0)
9451 gdk_color_parse("#f5f6be", &yellow);
9452 color_set = gdk_colormap_alloc_color(
9453 gdk_colormap_get_system(),
9454 &yellow, FALSE, TRUE);
9458 style = gtk_widget_get_modifier_style(hentry->entry);
9459 state->entry_marked = gdk_color_equal(&yellow,
9460 &style->base[GTK_STATE_NORMAL]);
9462 saved_list = g_slist_append(saved_list, state);
9463 compose_destroy_headerentry(compose, hentry);
9466 compose->header_last = NULL;
9467 g_slist_free(compose->header_list);
9468 compose->header_list = NULL;
9469 compose->header_nextrow = 1;
9470 compose_create_header_entry(compose);
9472 if (ac->set_autocc && ac->auto_cc)
9473 compose_entry_append(compose, ac->auto_cc,
9474 COMPOSE_CC, PREF_ACCOUNT);
9476 if (ac->set_autobcc && ac->auto_bcc)
9477 compose_entry_append(compose, ac->auto_bcc,
9478 COMPOSE_BCC, PREF_ACCOUNT);
9480 if (ac->set_autoreplyto && ac->auto_replyto)
9481 compose_entry_append(compose, ac->auto_replyto,
9482 COMPOSE_REPLYTO, PREF_ACCOUNT);
9484 for (list = saved_list; list; list = list->next) {
9485 state = (HeaderEntryState *) list->data;
9487 compose_add_header_entry(compose, state->header,
9488 state->entry, state->type);
9489 if (state->entry_marked)
9490 compose_entry_mark_default_to(compose, state->entry);
9492 g_free(state->header);
9493 g_free(state->entry);
9496 g_slist_free(saved_list);
9498 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9499 (ac->protocol == A_NNTP) ?
9500 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9503 /* Set message save folder */
9504 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9505 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9507 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9508 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9510 compose_set_save_to(compose, NULL);
9511 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9512 folderidentifier = folder_item_get_identifier(account_get_special_folder
9513 (compose->account, F_OUTBOX));
9514 compose_set_save_to(compose, folderidentifier);
9515 g_free(folderidentifier);
9519 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9520 GtkTreeViewColumn *column, Compose *compose)
9522 compose_attach_property(NULL, compose);
9525 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9528 Compose *compose = (Compose *)data;
9529 GtkTreeSelection *attach_selection;
9530 gint attach_nr_selected;
9532 if (!event) return FALSE;
9534 if (event->button == 3) {
9535 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9536 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9538 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9539 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9541 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9542 NULL, NULL, event->button, event->time);
9549 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9552 Compose *compose = (Compose *)data;
9554 if (!event) return FALSE;
9556 switch (event->keyval) {
9557 case GDK_KEY_Delete:
9558 compose_attach_remove_selected(NULL, compose);
9564 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9566 toolbar_comp_set_sensitive(compose, allow);
9567 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9570 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9572 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9573 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9574 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9576 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9580 static void compose_send_cb(GtkAction *action, gpointer data)
9582 Compose *compose = (Compose *)data;
9584 if (prefs_common.work_offline &&
9585 !inc_offline_should_override(TRUE,
9586 _("Claws Mail needs network access in order "
9587 "to send this email.")))
9590 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9591 g_source_remove(compose->draft_timeout_tag);
9592 compose->draft_timeout_tag = -1;
9595 compose_send(compose);
9598 static void compose_send_later_cb(GtkAction *action, gpointer data)
9600 Compose *compose = (Compose *)data;
9604 compose_allow_user_actions(compose, FALSE);
9605 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9606 compose_allow_user_actions(compose, TRUE);
9610 compose_close(compose);
9611 } else if (val == -1) {
9612 alertpanel_error(_("Could not queue message."));
9613 } else if (val == -2) {
9614 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9615 } else if (val == -3) {
9616 if (privacy_peek_error())
9617 alertpanel_error(_("Could not queue message for sending:\n\n"
9618 "Signature failed: %s"), privacy_get_error());
9619 } else if (val == -4) {
9620 alertpanel_error(_("Could not queue message for sending:\n\n"
9621 "Charset conversion failed."));
9622 } else if (val == -5) {
9623 alertpanel_error(_("Could not queue message for sending:\n\n"
9624 "Couldn't get recipient encryption key."));
9625 } else if (val == -6) {
9628 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9631 #define DRAFTED_AT_EXIT "drafted_at_exit"
9632 static void compose_register_draft(MsgInfo *info)
9634 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9635 DRAFTED_AT_EXIT, NULL);
9636 FILE *fp = g_fopen(filepath, "ab");
9639 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9647 gboolean compose_draft (gpointer data, guint action)
9649 Compose *compose = (Compose *)data;
9654 MsgFlags flag = {0, 0};
9655 static gboolean lock = FALSE;
9656 MsgInfo *newmsginfo;
9658 gboolean target_locked = FALSE;
9659 gboolean err = FALSE;
9661 if (lock) return FALSE;
9663 if (compose->sending)
9666 draft = account_get_special_folder(compose->account, F_DRAFT);
9667 cm_return_val_if_fail(draft != NULL, FALSE);
9669 if (!g_mutex_trylock(compose->mutex)) {
9670 /* we don't want to lock the mutex once it's available,
9671 * because as the only other part of compose.c locking
9672 * it is compose_close - which means once unlocked,
9673 * the compose struct will be freed */
9674 debug_print("couldn't lock mutex, probably sending\n");
9680 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9681 G_DIR_SEPARATOR, compose);
9682 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9683 FILE_OP_ERROR(tmp, "fopen");
9687 /* chmod for security */
9688 if (change_file_mode_rw(fp, tmp) < 0) {
9689 FILE_OP_ERROR(tmp, "chmod");
9690 g_warning("can't change file mode\n");
9693 /* Save draft infos */
9694 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9695 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9697 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9698 gchar *savefolderid;
9700 savefolderid = compose_get_save_to(compose);
9701 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9702 g_free(savefolderid);
9704 if (compose->return_receipt) {
9705 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9707 if (compose->privacy_system) {
9708 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9709 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9710 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9713 /* Message-ID of message replying to */
9714 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9717 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9718 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9721 /* Message-ID of message forwarding to */
9722 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9725 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9726 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9730 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9731 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9733 sheaders = compose_get_manual_headers_info(compose);
9734 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9737 /* end of headers */
9738 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9745 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9749 if (fclose(fp) == EOF) {
9753 if (compose->targetinfo) {
9754 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9755 flag.perm_flags = target_locked?MSG_LOCKED:0;
9757 flag.tmp_flags = MSG_DRAFT;
9759 folder_item_scan(draft);
9760 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9761 MsgInfo *tmpinfo = NULL;
9762 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9763 if (compose->msgid) {
9764 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9767 msgnum = tmpinfo->msgnum;
9768 procmsg_msginfo_free(tmpinfo);
9769 debug_print("got draft msgnum %d from scanning\n", msgnum);
9771 debug_print("didn't get draft msgnum after scanning\n");
9774 debug_print("got draft msgnum %d from adding\n", msgnum);
9780 if (action != COMPOSE_AUTO_SAVE) {
9781 if (action != COMPOSE_DRAFT_FOR_EXIT)
9782 alertpanel_error(_("Could not save draft."));
9785 gtkut_window_popup(compose->window);
9786 val = alertpanel_full(_("Could not save draft"),
9787 _("Could not save draft.\n"
9788 "Do you want to cancel exit or discard this email?"),
9789 _("_Cancel exit"), _("_Discard email"), NULL,
9790 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9791 if (val == G_ALERTALTERNATE) {
9793 g_mutex_unlock(compose->mutex); /* must be done before closing */
9794 compose_close(compose);
9798 g_mutex_unlock(compose->mutex); /* must be done before closing */
9807 if (compose->mode == COMPOSE_REEDIT) {
9808 compose_remove_reedit_target(compose, TRUE);
9811 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9814 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9816 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9818 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9819 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9820 procmsg_msginfo_set_flags(newmsginfo, 0,
9821 MSG_HAS_ATTACHMENT);
9823 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9824 compose_register_draft(newmsginfo);
9826 procmsg_msginfo_free(newmsginfo);
9829 folder_item_scan(draft);
9831 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9833 g_mutex_unlock(compose->mutex); /* must be done before closing */
9834 compose_close(compose);
9840 path = folder_item_fetch_msg(draft, msgnum);
9842 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9845 if (g_stat(path, &s) < 0) {
9846 FILE_OP_ERROR(path, "stat");
9852 procmsg_msginfo_free(compose->targetinfo);
9853 compose->targetinfo = procmsg_msginfo_new();
9854 compose->targetinfo->msgnum = msgnum;
9855 compose->targetinfo->size = (goffset)s.st_size;
9856 compose->targetinfo->mtime = s.st_mtime;
9857 compose->targetinfo->folder = draft;
9859 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9860 compose->mode = COMPOSE_REEDIT;
9862 if (action == COMPOSE_AUTO_SAVE) {
9863 compose->autosaved_draft = compose->targetinfo;
9865 compose->modified = FALSE;
9866 compose_set_title(compose);
9870 g_mutex_unlock(compose->mutex);
9874 void compose_clear_exit_drafts(void)
9876 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9877 DRAFTED_AT_EXIT, NULL);
9878 if (is_file_exist(filepath))
9879 claws_unlink(filepath);
9884 void compose_reopen_exit_drafts(void)
9886 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9887 DRAFTED_AT_EXIT, NULL);
9888 FILE *fp = g_fopen(filepath, "rb");
9892 while (fgets(buf, sizeof(buf), fp)) {
9893 gchar **parts = g_strsplit(buf, "\t", 2);
9894 const gchar *folder = parts[0];
9895 int msgnum = parts[1] ? atoi(parts[1]):-1;
9897 if (folder && *folder && msgnum > -1) {
9898 FolderItem *item = folder_find_item_from_identifier(folder);
9899 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9901 compose_reedit(info, FALSE);
9908 compose_clear_exit_drafts();
9911 static void compose_save_cb(GtkAction *action, gpointer data)
9913 Compose *compose = (Compose *)data;
9914 compose_draft(compose, COMPOSE_KEEP_EDITING);
9915 compose->rmode = COMPOSE_REEDIT;
9918 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9920 if (compose && file_list) {
9923 for ( tmp = file_list; tmp; tmp = tmp->next) {
9924 gchar *file = (gchar *) tmp->data;
9925 gchar *utf8_filename = conv_filename_to_utf8(file);
9926 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9927 compose_changed_cb(NULL, compose);
9932 g_free(utf8_filename);
9937 static void compose_attach_cb(GtkAction *action, gpointer data)
9939 Compose *compose = (Compose *)data;
9942 if (compose->redirect_filename != NULL)
9945 /* Set focus_window properly, in case we were called via popup menu,
9946 * which unsets it (via focus_out_event callback on compose window). */
9947 manage_window_focus_in(compose->window, NULL, NULL);
9949 file_list = filesel_select_multiple_files_open(_("Select file"));
9952 compose_attach_from_list(compose, file_list, TRUE);
9953 g_list_free(file_list);
9957 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9959 Compose *compose = (Compose *)data;
9961 gint files_inserted = 0;
9963 file_list = filesel_select_multiple_files_open(_("Select file"));
9968 for ( tmp = file_list; tmp; tmp = tmp->next) {
9969 gchar *file = (gchar *) tmp->data;
9970 gchar *filedup = g_strdup(file);
9971 gchar *shortfile = g_path_get_basename(filedup);
9972 ComposeInsertResult res;
9973 /* insert the file if the file is short or if the user confirmed that
9974 he/she wants to insert the large file */
9975 res = compose_insert_file(compose, file);
9976 if (res == COMPOSE_INSERT_READ_ERROR) {
9977 alertpanel_error(_("File '%s' could not be read."), shortfile);
9978 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9979 alertpanel_error(_("File '%s' contained invalid characters\n"
9980 "for the current encoding, insertion may be incorrect."),
9982 } else if (res == COMPOSE_INSERT_SUCCESS)
9989 g_list_free(file_list);
9993 if (files_inserted > 0 && compose->gtkaspell &&
9994 compose->gtkaspell->check_while_typing)
9995 gtkaspell_highlight_all(compose->gtkaspell);
9999 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10001 Compose *compose = (Compose *)data;
10003 compose_insert_sig(compose, FALSE);
10006 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10010 Compose *compose = (Compose *)data;
10012 gtkut_widget_get_uposition(widget, &x, &y);
10013 if (!compose->batch) {
10014 prefs_common.compose_x = x;
10015 prefs_common.compose_y = y;
10017 if (compose->sending || compose->updating)
10019 compose_close_cb(NULL, compose);
10023 void compose_close_toolbar(Compose *compose)
10025 compose_close_cb(NULL, compose);
10028 static void compose_close_cb(GtkAction *action, gpointer data)
10030 Compose *compose = (Compose *)data;
10034 if (compose->exteditor_tag != -1) {
10035 if (!compose_ext_editor_kill(compose))
10040 if (compose->modified) {
10041 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10042 if (!g_mutex_trylock(compose->mutex)) {
10043 /* we don't want to lock the mutex once it's available,
10044 * because as the only other part of compose.c locking
10045 * it is compose_close - which means once unlocked,
10046 * the compose struct will be freed */
10047 debug_print("couldn't lock mutex, probably sending\n");
10051 val = alertpanel(_("Discard message"),
10052 _("This message has been modified. Discard it?"),
10053 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10055 val = alertpanel(_("Save changes"),
10056 _("This message has been modified. Save the latest changes?"),
10057 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
10059 g_mutex_unlock(compose->mutex);
10061 case G_ALERTDEFAULT:
10062 if (prefs_common.autosave && !reedit)
10063 compose_remove_draft(compose);
10065 case G_ALERTALTERNATE:
10066 compose_draft(data, COMPOSE_QUIT_EDITING);
10073 compose_close(compose);
10076 static void compose_print_cb(GtkAction *action, gpointer data)
10078 Compose *compose = (Compose *) data;
10080 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10081 if (compose->targetinfo)
10082 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10085 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10087 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10088 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10089 Compose *compose = (Compose *) data;
10092 compose->out_encoding = (CharSet)value;
10095 static void compose_address_cb(GtkAction *action, gpointer data)
10097 Compose *compose = (Compose *)data;
10099 #ifndef USE_NEW_ADDRBOOK
10100 addressbook_open(compose);
10102 GError* error = NULL;
10103 addressbook_connect_signals(compose);
10104 addressbook_dbus_open(TRUE, &error);
10106 g_warning("%s", error->message);
10107 g_error_free(error);
10112 static void about_show_cb(GtkAction *action, gpointer data)
10117 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10119 Compose *compose = (Compose *)data;
10124 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10125 cm_return_if_fail(tmpl != NULL);
10127 msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10129 val = alertpanel(_("Apply template"), msg,
10130 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10133 if (val == G_ALERTDEFAULT)
10134 compose_template_apply(compose, tmpl, TRUE);
10135 else if (val == G_ALERTALTERNATE)
10136 compose_template_apply(compose, tmpl, FALSE);
10139 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10141 Compose *compose = (Compose *)data;
10143 compose_exec_ext_editor(compose);
10146 static void compose_undo_cb(GtkAction *action, gpointer data)
10148 Compose *compose = (Compose *)data;
10149 gboolean prev_autowrap = compose->autowrap;
10151 compose->autowrap = FALSE;
10152 undo_undo(compose->undostruct);
10153 compose->autowrap = prev_autowrap;
10156 static void compose_redo_cb(GtkAction *action, gpointer data)
10158 Compose *compose = (Compose *)data;
10159 gboolean prev_autowrap = compose->autowrap;
10161 compose->autowrap = FALSE;
10162 undo_redo(compose->undostruct);
10163 compose->autowrap = prev_autowrap;
10166 static void entry_cut_clipboard(GtkWidget *entry)
10168 if (GTK_IS_EDITABLE(entry))
10169 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10170 else if (GTK_IS_TEXT_VIEW(entry))
10171 gtk_text_buffer_cut_clipboard(
10172 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10173 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10177 static void entry_copy_clipboard(GtkWidget *entry)
10179 if (GTK_IS_EDITABLE(entry))
10180 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10181 else if (GTK_IS_TEXT_VIEW(entry))
10182 gtk_text_buffer_copy_clipboard(
10183 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10184 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10187 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10188 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10190 if (GTK_IS_TEXT_VIEW(entry)) {
10191 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10192 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10193 GtkTextIter start_iter, end_iter;
10195 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10197 if (contents == NULL)
10200 /* we shouldn't delete the selection when middle-click-pasting, or we
10201 * can't mid-click-paste our own selection */
10202 if (clip != GDK_SELECTION_PRIMARY) {
10203 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10204 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10207 if (insert_place == NULL) {
10208 /* if insert_place isn't specified, insert at the cursor.
10209 * used for Ctrl-V pasting */
10210 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10211 start = gtk_text_iter_get_offset(&start_iter);
10212 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10214 /* if insert_place is specified, paste here.
10215 * used for mid-click-pasting */
10216 start = gtk_text_iter_get_offset(insert_place);
10217 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10218 if (prefs_common.primary_paste_unselects)
10219 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10223 /* paste unwrapped: mark the paste so it's not wrapped later */
10224 end = start + strlen(contents);
10225 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10226 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10227 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10228 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10229 /* rewrap paragraph now (after a mid-click-paste) */
10230 mark_start = gtk_text_buffer_get_insert(buffer);
10231 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10232 gtk_text_iter_backward_char(&start_iter);
10233 compose_beautify_paragraph(compose, &start_iter, TRUE);
10235 } else if (GTK_IS_EDITABLE(entry))
10236 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10238 compose->modified = TRUE;
10241 static void entry_allsel(GtkWidget *entry)
10243 if (GTK_IS_EDITABLE(entry))
10244 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10245 else if (GTK_IS_TEXT_VIEW(entry)) {
10246 GtkTextIter startiter, enditer;
10247 GtkTextBuffer *textbuf;
10249 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10250 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10251 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10253 gtk_text_buffer_move_mark_by_name(textbuf,
10254 "selection_bound", &startiter);
10255 gtk_text_buffer_move_mark_by_name(textbuf,
10256 "insert", &enditer);
10260 static void compose_cut_cb(GtkAction *action, gpointer data)
10262 Compose *compose = (Compose *)data;
10263 if (compose->focused_editable
10264 #ifndef GENERIC_UMPC
10265 && gtk_widget_has_focus(compose->focused_editable)
10268 entry_cut_clipboard(compose->focused_editable);
10271 static void compose_copy_cb(GtkAction *action, gpointer data)
10273 Compose *compose = (Compose *)data;
10274 if (compose->focused_editable
10275 #ifndef GENERIC_UMPC
10276 && gtk_widget_has_focus(compose->focused_editable)
10279 entry_copy_clipboard(compose->focused_editable);
10282 static void compose_paste_cb(GtkAction *action, gpointer data)
10284 Compose *compose = (Compose *)data;
10285 gint prev_autowrap;
10286 GtkTextBuffer *buffer;
10288 if (compose->focused_editable &&
10289 #ifndef GENERIC_UMPC
10290 gtk_widget_has_focus(compose->focused_editable)
10293 entry_paste_clipboard(compose, compose->focused_editable,
10294 prefs_common.linewrap_pastes,
10295 GDK_SELECTION_CLIPBOARD, NULL);
10300 #ifndef GENERIC_UMPC
10301 gtk_widget_has_focus(compose->text) &&
10303 compose->gtkaspell &&
10304 compose->gtkaspell->check_while_typing)
10305 gtkaspell_highlight_all(compose->gtkaspell);
10309 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10311 Compose *compose = (Compose *)data;
10312 gint wrap_quote = prefs_common.linewrap_quote;
10313 if (compose->focused_editable
10314 #ifndef GENERIC_UMPC
10315 && gtk_widget_has_focus(compose->focused_editable)
10318 /* let text_insert() (called directly or at a later time
10319 * after the gtk_editable_paste_clipboard) know that
10320 * text is to be inserted as a quotation. implemented
10321 * by using a simple refcount... */
10322 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10323 G_OBJECT(compose->focused_editable),
10324 "paste_as_quotation"));
10325 g_object_set_data(G_OBJECT(compose->focused_editable),
10326 "paste_as_quotation",
10327 GINT_TO_POINTER(paste_as_quotation + 1));
10328 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10329 entry_paste_clipboard(compose, compose->focused_editable,
10330 prefs_common.linewrap_pastes,
10331 GDK_SELECTION_CLIPBOARD, NULL);
10332 prefs_common.linewrap_quote = wrap_quote;
10336 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10338 Compose *compose = (Compose *)data;
10339 gint prev_autowrap;
10340 GtkTextBuffer *buffer;
10342 if (compose->focused_editable
10343 #ifndef GENERIC_UMPC
10344 && gtk_widget_has_focus(compose->focused_editable)
10347 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10348 GDK_SELECTION_CLIPBOARD, NULL);
10353 #ifndef GENERIC_UMPC
10354 gtk_widget_has_focus(compose->text) &&
10356 compose->gtkaspell &&
10357 compose->gtkaspell->check_while_typing)
10358 gtkaspell_highlight_all(compose->gtkaspell);
10362 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10364 Compose *compose = (Compose *)data;
10365 gint prev_autowrap;
10366 GtkTextBuffer *buffer;
10368 if (compose->focused_editable
10369 #ifndef GENERIC_UMPC
10370 && gtk_widget_has_focus(compose->focused_editable)
10373 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10374 GDK_SELECTION_CLIPBOARD, NULL);
10379 #ifndef GENERIC_UMPC
10380 gtk_widget_has_focus(compose->text) &&
10382 compose->gtkaspell &&
10383 compose->gtkaspell->check_while_typing)
10384 gtkaspell_highlight_all(compose->gtkaspell);
10388 static void compose_allsel_cb(GtkAction *action, gpointer data)
10390 Compose *compose = (Compose *)data;
10391 if (compose->focused_editable
10392 #ifndef GENERIC_UMPC
10393 && gtk_widget_has_focus(compose->focused_editable)
10396 entry_allsel(compose->focused_editable);
10399 static void textview_move_beginning_of_line (GtkTextView *text)
10401 GtkTextBuffer *buffer;
10405 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10407 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10408 mark = gtk_text_buffer_get_insert(buffer);
10409 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10410 gtk_text_iter_set_line_offset(&ins, 0);
10411 gtk_text_buffer_place_cursor(buffer, &ins);
10414 static void textview_move_forward_character (GtkTextView *text)
10416 GtkTextBuffer *buffer;
10420 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10422 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10423 mark = gtk_text_buffer_get_insert(buffer);
10424 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10425 if (gtk_text_iter_forward_cursor_position(&ins))
10426 gtk_text_buffer_place_cursor(buffer, &ins);
10429 static void textview_move_backward_character (GtkTextView *text)
10431 GtkTextBuffer *buffer;
10435 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10437 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10438 mark = gtk_text_buffer_get_insert(buffer);
10439 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10440 if (gtk_text_iter_backward_cursor_position(&ins))
10441 gtk_text_buffer_place_cursor(buffer, &ins);
10444 static void textview_move_forward_word (GtkTextView *text)
10446 GtkTextBuffer *buffer;
10451 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10453 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10454 mark = gtk_text_buffer_get_insert(buffer);
10455 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10456 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10457 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10458 gtk_text_iter_backward_word_start(&ins);
10459 gtk_text_buffer_place_cursor(buffer, &ins);
10463 static void textview_move_backward_word (GtkTextView *text)
10465 GtkTextBuffer *buffer;
10469 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10471 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10472 mark = gtk_text_buffer_get_insert(buffer);
10473 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10474 if (gtk_text_iter_backward_word_starts(&ins, 1))
10475 gtk_text_buffer_place_cursor(buffer, &ins);
10478 static void textview_move_end_of_line (GtkTextView *text)
10480 GtkTextBuffer *buffer;
10484 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10486 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10487 mark = gtk_text_buffer_get_insert(buffer);
10488 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10489 if (gtk_text_iter_forward_to_line_end(&ins))
10490 gtk_text_buffer_place_cursor(buffer, &ins);
10493 static void textview_move_next_line (GtkTextView *text)
10495 GtkTextBuffer *buffer;
10500 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10502 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10503 mark = gtk_text_buffer_get_insert(buffer);
10504 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10505 offset = gtk_text_iter_get_line_offset(&ins);
10506 if (gtk_text_iter_forward_line(&ins)) {
10507 gtk_text_iter_set_line_offset(&ins, offset);
10508 gtk_text_buffer_place_cursor(buffer, &ins);
10512 static void textview_move_previous_line (GtkTextView *text)
10514 GtkTextBuffer *buffer;
10519 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10521 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10522 mark = gtk_text_buffer_get_insert(buffer);
10523 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10524 offset = gtk_text_iter_get_line_offset(&ins);
10525 if (gtk_text_iter_backward_line(&ins)) {
10526 gtk_text_iter_set_line_offset(&ins, offset);
10527 gtk_text_buffer_place_cursor(buffer, &ins);
10531 static void textview_delete_forward_character (GtkTextView *text)
10533 GtkTextBuffer *buffer;
10535 GtkTextIter ins, end_iter;
10537 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10539 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10540 mark = gtk_text_buffer_get_insert(buffer);
10541 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10543 if (gtk_text_iter_forward_char(&end_iter)) {
10544 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10548 static void textview_delete_backward_character (GtkTextView *text)
10550 GtkTextBuffer *buffer;
10552 GtkTextIter ins, end_iter;
10554 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10556 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10557 mark = gtk_text_buffer_get_insert(buffer);
10558 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10560 if (gtk_text_iter_backward_char(&end_iter)) {
10561 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10565 static void textview_delete_forward_word (GtkTextView *text)
10567 GtkTextBuffer *buffer;
10569 GtkTextIter ins, end_iter;
10571 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10573 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10574 mark = gtk_text_buffer_get_insert(buffer);
10575 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10577 if (gtk_text_iter_forward_word_end(&end_iter)) {
10578 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10582 static void textview_delete_backward_word (GtkTextView *text)
10584 GtkTextBuffer *buffer;
10586 GtkTextIter ins, end_iter;
10588 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10590 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10591 mark = gtk_text_buffer_get_insert(buffer);
10592 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10594 if (gtk_text_iter_backward_word_start(&end_iter)) {
10595 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10599 static void textview_delete_line (GtkTextView *text)
10601 GtkTextBuffer *buffer;
10603 GtkTextIter ins, start_iter, end_iter;
10605 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10607 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10608 mark = gtk_text_buffer_get_insert(buffer);
10609 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10612 gtk_text_iter_set_line_offset(&start_iter, 0);
10615 if (gtk_text_iter_ends_line(&end_iter)){
10616 if (!gtk_text_iter_forward_char(&end_iter))
10617 gtk_text_iter_backward_char(&start_iter);
10620 gtk_text_iter_forward_to_line_end(&end_iter);
10621 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10624 static void textview_delete_to_line_end (GtkTextView *text)
10626 GtkTextBuffer *buffer;
10628 GtkTextIter ins, end_iter;
10630 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10632 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10633 mark = gtk_text_buffer_get_insert(buffer);
10634 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10636 if (gtk_text_iter_ends_line(&end_iter))
10637 gtk_text_iter_forward_char(&end_iter);
10639 gtk_text_iter_forward_to_line_end(&end_iter);
10640 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10643 #define DO_ACTION(name, act) { \
10644 if(!strcmp(name, a_name)) { \
10648 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10650 const gchar *a_name = gtk_action_get_name(action);
10651 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10652 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10653 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10654 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10655 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10656 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10657 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10658 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10659 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10660 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10661 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10662 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10663 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10664 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10668 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10670 Compose *compose = (Compose *)data;
10671 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10672 ComposeCallAdvancedAction action = -1;
10674 action = compose_call_advanced_action_from_path(gaction);
10677 void (*do_action) (GtkTextView *text);
10678 } action_table[] = {
10679 {textview_move_beginning_of_line},
10680 {textview_move_forward_character},
10681 {textview_move_backward_character},
10682 {textview_move_forward_word},
10683 {textview_move_backward_word},
10684 {textview_move_end_of_line},
10685 {textview_move_next_line},
10686 {textview_move_previous_line},
10687 {textview_delete_forward_character},
10688 {textview_delete_backward_character},
10689 {textview_delete_forward_word},
10690 {textview_delete_backward_word},
10691 {textview_delete_line},
10692 {textview_delete_to_line_end}
10695 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10697 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10698 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10699 if (action_table[action].do_action)
10700 action_table[action].do_action(text);
10702 g_warning("Not implemented yet.");
10706 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10708 GtkAllocation allocation;
10712 if (GTK_IS_EDITABLE(widget)) {
10713 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10714 gtk_editable_set_position(GTK_EDITABLE(widget),
10717 if ((parent = gtk_widget_get_parent(widget))
10718 && (parent = gtk_widget_get_parent(parent))
10719 && (parent = gtk_widget_get_parent(parent))) {
10720 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10721 gtk_widget_get_allocation(widget, &allocation);
10722 gint y = allocation.y;
10723 gint height = allocation.height;
10724 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10725 (GTK_SCROLLED_WINDOW(parent));
10727 gfloat value = gtk_adjustment_get_value(shown);
10728 gfloat upper = gtk_adjustment_get_upper(shown);
10729 gfloat page_size = gtk_adjustment_get_page_size(shown);
10730 if (y < (int)value) {
10731 gtk_adjustment_set_value(shown, y - 1);
10733 if ((y + height) > ((int)value + (int)page_size)) {
10734 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10735 gtk_adjustment_set_value(shown,
10736 y + height - (int)page_size - 1);
10738 gtk_adjustment_set_value(shown,
10739 (int)upper - (int)page_size - 1);
10746 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10747 compose->focused_editable = widget;
10749 #ifdef GENERIC_UMPC
10750 if (GTK_IS_TEXT_VIEW(widget)
10751 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10752 g_object_ref(compose->notebook);
10753 g_object_ref(compose->edit_vbox);
10754 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10755 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10756 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10757 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10758 g_object_unref(compose->notebook);
10759 g_object_unref(compose->edit_vbox);
10760 g_signal_handlers_block_by_func(G_OBJECT(widget),
10761 G_CALLBACK(compose_grab_focus_cb),
10763 gtk_widget_grab_focus(widget);
10764 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10765 G_CALLBACK(compose_grab_focus_cb),
10767 } else if (!GTK_IS_TEXT_VIEW(widget)
10768 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10769 g_object_ref(compose->notebook);
10770 g_object_ref(compose->edit_vbox);
10771 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10772 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10773 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10774 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10775 g_object_unref(compose->notebook);
10776 g_object_unref(compose->edit_vbox);
10777 g_signal_handlers_block_by_func(G_OBJECT(widget),
10778 G_CALLBACK(compose_grab_focus_cb),
10780 gtk_widget_grab_focus(widget);
10781 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10782 G_CALLBACK(compose_grab_focus_cb),
10788 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10790 compose->modified = TRUE;
10791 // compose_beautify_paragraph(compose, NULL, TRUE);
10792 #ifndef GENERIC_UMPC
10793 compose_set_title(compose);
10797 static void compose_wrap_cb(GtkAction *action, gpointer data)
10799 Compose *compose = (Compose *)data;
10800 compose_beautify_paragraph(compose, NULL, TRUE);
10803 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10805 Compose *compose = (Compose *)data;
10806 compose_wrap_all_full(compose, TRUE);
10809 static void compose_find_cb(GtkAction *action, gpointer data)
10811 Compose *compose = (Compose *)data;
10813 message_search_compose(compose);
10816 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10819 Compose *compose = (Compose *)data;
10820 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10821 if (compose->autowrap)
10822 compose_wrap_all_full(compose, TRUE);
10823 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10826 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10829 Compose *compose = (Compose *)data;
10830 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10833 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10835 Compose *compose = (Compose *)data;
10837 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10840 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10842 Compose *compose = (Compose *)data;
10844 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10847 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10849 g_free(compose->privacy_system);
10851 compose->privacy_system = g_strdup(account->default_privacy_system);
10852 compose_update_privacy_system_menu_item(compose, warn);
10855 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10857 Compose *compose = (Compose *)data;
10859 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10860 gtk_widget_show(compose->ruler_hbox);
10861 prefs_common.show_ruler = TRUE;
10863 gtk_widget_hide(compose->ruler_hbox);
10864 gtk_widget_queue_resize(compose->edit_vbox);
10865 prefs_common.show_ruler = FALSE;
10869 static void compose_attach_drag_received_cb (GtkWidget *widget,
10870 GdkDragContext *context,
10873 GtkSelectionData *data,
10876 gpointer user_data)
10878 Compose *compose = (Compose *)user_data;
10882 type = gtk_selection_data_get_data_type(data);
10883 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10885 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10887 ) && gtk_drag_get_source_widget(context) !=
10888 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10889 list = uri_list_extract_filenames(
10890 (const gchar *)gtk_selection_data_get_data(data));
10891 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10892 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10893 compose_attach_append
10894 (compose, (const gchar *)tmp->data,
10895 utf8_filename, NULL, NULL);
10896 g_free(utf8_filename);
10898 if (list) compose_changed_cb(NULL, compose);
10899 list_free_strings(list);
10901 } else if (gtk_drag_get_source_widget(context)
10902 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10903 /* comes from our summaryview */
10904 SummaryView * summaryview = NULL;
10905 GSList * list = NULL, *cur = NULL;
10907 if (mainwindow_get_mainwindow())
10908 summaryview = mainwindow_get_mainwindow()->summaryview;
10911 list = summary_get_selected_msg_list(summaryview);
10913 for (cur = list; cur; cur = cur->next) {
10914 MsgInfo *msginfo = (MsgInfo *)cur->data;
10915 gchar *file = NULL;
10917 file = procmsg_get_message_file_full(msginfo,
10920 compose_attach_append(compose, (const gchar *)file,
10921 (const gchar *)file, "message/rfc822", NULL);
10925 g_slist_free(list);
10929 static gboolean compose_drag_drop(GtkWidget *widget,
10930 GdkDragContext *drag_context,
10932 guint time, gpointer user_data)
10934 /* not handling this signal makes compose_insert_drag_received_cb
10939 static gboolean completion_set_focus_to_subject
10940 (GtkWidget *widget,
10941 GdkEventKey *event,
10944 cm_return_val_if_fail(compose != NULL, FALSE);
10946 /* make backtab move to subject field */
10947 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10948 gtk_widget_grab_focus(compose->subject_entry);
10954 static void compose_insert_drag_received_cb (GtkWidget *widget,
10955 GdkDragContext *drag_context,
10958 GtkSelectionData *data,
10961 gpointer user_data)
10963 Compose *compose = (Compose *)user_data;
10967 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10969 type = gtk_selection_data_get_data_type(data);
10971 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10973 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10975 AlertValue val = G_ALERTDEFAULT;
10976 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10978 list = uri_list_extract_filenames(ddata);
10979 if (list == NULL && strstr(ddata, "://")) {
10980 /* Assume a list of no files, and data has ://, is a remote link */
10981 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10982 gchar *tmpfile = get_tmp_file();
10983 str_write_to_file(tmpdata, tmpfile);
10985 compose_insert_file(compose, tmpfile);
10986 claws_unlink(tmpfile);
10988 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10989 compose_beautify_paragraph(compose, NULL, TRUE);
10992 switch (prefs_common.compose_dnd_mode) {
10993 case COMPOSE_DND_ASK:
10994 val = alertpanel_full(_("Insert or attach?"),
10995 _("Do you want to insert the contents of the file(s) "
10996 "into the message body, or attach it to the email?"),
10997 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10998 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11000 case COMPOSE_DND_INSERT:
11001 val = G_ALERTALTERNATE;
11003 case COMPOSE_DND_ATTACH:
11004 val = G_ALERTOTHER;
11007 /* unexpected case */
11008 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11011 if (val & G_ALERTDISABLE) {
11012 val &= ~G_ALERTDISABLE;
11013 /* remember what action to perform by default, only if we don't click Cancel */
11014 if (val == G_ALERTALTERNATE)
11015 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11016 else if (val == G_ALERTOTHER)
11017 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11020 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11021 gtk_drag_finish(drag_context, FALSE, FALSE, time);
11022 list_free_strings(list);
11025 } else if (val == G_ALERTOTHER) {
11026 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11027 list_free_strings(list);
11032 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11033 compose_insert_file(compose, (const gchar *)tmp->data);
11035 list_free_strings(list);
11037 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11042 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11045 static void compose_header_drag_received_cb (GtkWidget *widget,
11046 GdkDragContext *drag_context,
11049 GtkSelectionData *data,
11052 gpointer user_data)
11054 GtkEditable *entry = (GtkEditable *)user_data;
11055 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11057 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11060 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11061 gchar *decoded=g_new(gchar, strlen(email));
11064 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11065 gtk_editable_delete_text(entry, 0, -1);
11066 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11067 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11071 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11074 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11076 Compose *compose = (Compose *)data;
11078 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11079 compose->return_receipt = TRUE;
11081 compose->return_receipt = FALSE;
11084 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11086 Compose *compose = (Compose *)data;
11088 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11089 compose->remove_references = TRUE;
11091 compose->remove_references = FALSE;
11094 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11095 ComposeHeaderEntry *headerentry)
11097 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11101 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11102 GdkEventKey *event,
11103 ComposeHeaderEntry *headerentry)
11105 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11106 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11107 !(event->state & GDK_MODIFIER_MASK) &&
11108 (event->keyval == GDK_KEY_BackSpace) &&
11109 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11110 gtk_container_remove
11111 (GTK_CONTAINER(headerentry->compose->header_table),
11112 headerentry->combo);
11113 gtk_container_remove
11114 (GTK_CONTAINER(headerentry->compose->header_table),
11115 headerentry->entry);
11116 headerentry->compose->header_list =
11117 g_slist_remove(headerentry->compose->header_list,
11119 g_free(headerentry);
11120 } else if (event->keyval == GDK_KEY_Tab) {
11121 if (headerentry->compose->header_last == headerentry) {
11122 /* Override default next focus, and give it to subject_entry
11123 * instead of notebook tabs
11125 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11126 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11133 static gboolean scroll_postpone(gpointer data)
11135 Compose *compose = (Compose *)data;
11137 cm_return_val_if_fail(!compose->batch, FALSE);
11139 GTK_EVENTS_FLUSH();
11140 compose_show_first_last_header(compose, FALSE);
11144 static void compose_headerentry_changed_cb(GtkWidget *entry,
11145 ComposeHeaderEntry *headerentry)
11147 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11148 compose_create_header_entry(headerentry->compose);
11149 g_signal_handlers_disconnect_matched
11150 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11151 0, 0, NULL, NULL, headerentry);
11153 if (!headerentry->compose->batch)
11154 g_timeout_add(0, scroll_postpone, headerentry->compose);
11158 static gboolean compose_defer_auto_save_draft(Compose *compose)
11160 compose->draft_timeout_tag = -1;
11161 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11165 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11167 GtkAdjustment *vadj;
11169 cm_return_if_fail(compose);
11170 cm_return_if_fail(!compose->batch);
11171 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11172 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11173 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11174 gtk_widget_get_parent(compose->header_table)));
11175 gtk_adjustment_set_value(vadj, (show_first ?
11176 gtk_adjustment_get_lower(vadj) :
11177 (gtk_adjustment_get_upper(vadj) -
11178 gtk_adjustment_get_page_size(vadj))));
11179 gtk_adjustment_changed(vadj);
11182 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11183 const gchar *text, gint len, Compose *compose)
11185 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11186 (G_OBJECT(compose->text), "paste_as_quotation"));
11189 cm_return_if_fail(text != NULL);
11191 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11192 G_CALLBACK(text_inserted),
11194 if (paste_as_quotation) {
11196 const gchar *qmark;
11198 GtkTextIter start_iter;
11201 len = strlen(text);
11203 new_text = g_strndup(text, len);
11205 qmark = compose_quote_char_from_context(compose);
11207 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11208 gtk_text_buffer_place_cursor(buffer, iter);
11210 pos = gtk_text_iter_get_offset(iter);
11212 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11213 _("Quote format error at line %d."));
11214 quote_fmt_reset_vartable();
11216 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11217 GINT_TO_POINTER(paste_as_quotation - 1));
11219 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11220 gtk_text_buffer_place_cursor(buffer, iter);
11221 gtk_text_buffer_delete_mark(buffer, mark);
11223 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11224 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11225 compose_beautify_paragraph(compose, &start_iter, FALSE);
11226 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11227 gtk_text_buffer_delete_mark(buffer, mark);
11229 if (strcmp(text, "\n") || compose->automatic_break
11230 || gtk_text_iter_starts_line(iter)) {
11231 GtkTextIter before_ins;
11232 gtk_text_buffer_insert(buffer, iter, text, len);
11233 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11234 before_ins = *iter;
11235 gtk_text_iter_backward_chars(&before_ins, len);
11236 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11239 /* check if the preceding is just whitespace or quote */
11240 GtkTextIter start_line;
11241 gchar *tmp = NULL, *quote = NULL;
11242 gint quote_len = 0, is_normal = 0;
11243 start_line = *iter;
11244 gtk_text_iter_set_line_offset(&start_line, 0);
11245 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11248 if (*tmp == '\0') {
11251 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11259 gtk_text_buffer_insert(buffer, iter, text, len);
11261 gtk_text_buffer_insert_with_tags_by_name(buffer,
11262 iter, text, len, "no_join", NULL);
11267 if (!paste_as_quotation) {
11268 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11269 compose_beautify_paragraph(compose, iter, FALSE);
11270 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11271 gtk_text_buffer_delete_mark(buffer, mark);
11274 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11275 G_CALLBACK(text_inserted),
11277 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11279 if (prefs_common.autosave &&
11280 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11281 compose->draft_timeout_tag != -2 /* disabled while loading */)
11282 compose->draft_timeout_tag = g_timeout_add
11283 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11287 static void compose_check_all(GtkAction *action, gpointer data)
11289 Compose *compose = (Compose *)data;
11290 if (!compose->gtkaspell)
11293 if (gtk_widget_has_focus(compose->subject_entry))
11294 claws_spell_entry_check_all(
11295 CLAWS_SPELL_ENTRY(compose->subject_entry));
11297 gtkaspell_check_all(compose->gtkaspell);
11300 static void compose_highlight_all(GtkAction *action, gpointer data)
11302 Compose *compose = (Compose *)data;
11303 if (compose->gtkaspell) {
11304 claws_spell_entry_recheck_all(
11305 CLAWS_SPELL_ENTRY(compose->subject_entry));
11306 gtkaspell_highlight_all(compose->gtkaspell);
11310 static void compose_check_backwards(GtkAction *action, gpointer data)
11312 Compose *compose = (Compose *)data;
11313 if (!compose->gtkaspell) {
11314 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11318 if (gtk_widget_has_focus(compose->subject_entry))
11319 claws_spell_entry_check_backwards(
11320 CLAWS_SPELL_ENTRY(compose->subject_entry));
11322 gtkaspell_check_backwards(compose->gtkaspell);
11325 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11327 Compose *compose = (Compose *)data;
11328 if (!compose->gtkaspell) {
11329 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11333 if (gtk_widget_has_focus(compose->subject_entry))
11334 claws_spell_entry_check_forwards_go(
11335 CLAWS_SPELL_ENTRY(compose->subject_entry));
11337 gtkaspell_check_forwards_go(compose->gtkaspell);
11342 *\brief Guess originating forward account from MsgInfo and several
11343 * "common preference" settings. Return NULL if no guess.
11345 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11347 PrefsAccount *account = NULL;
11349 cm_return_val_if_fail(msginfo, NULL);
11350 cm_return_val_if_fail(msginfo->folder, NULL);
11351 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11353 if (msginfo->folder->prefs->enable_default_account)
11354 account = account_find_from_id(msginfo->folder->prefs->default_account);
11357 account = msginfo->folder->folder->account;
11359 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11361 Xstrdup_a(to, msginfo->to, return NULL);
11362 extract_address(to);
11363 account = account_find_from_address(to, FALSE);
11366 if (!account && prefs_common.forward_account_autosel) {
11367 gchar cc[BUFFSIZE];
11368 if (!procheader_get_header_from_msginfo
11369 (msginfo, cc,sizeof cc , "Cc:")) {
11370 gchar *buf = cc + strlen("Cc:");
11371 extract_address(buf);
11372 account = account_find_from_address(buf, FALSE);
11376 if (!account && prefs_common.forward_account_autosel) {
11377 gchar deliveredto[BUFFSIZE];
11378 if (!procheader_get_header_from_msginfo
11379 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11380 gchar *buf = deliveredto + strlen("Delivered-To:");
11381 extract_address(buf);
11382 account = account_find_from_address(buf, FALSE);
11389 gboolean compose_close(Compose *compose)
11393 if (!g_mutex_trylock(compose->mutex)) {
11394 /* we have to wait for the (possibly deferred by auto-save)
11395 * drafting to be done, before destroying the compose under
11397 debug_print("waiting for drafting to finish...\n");
11398 compose_allow_user_actions(compose, FALSE);
11399 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11402 cm_return_val_if_fail(compose, FALSE);
11403 gtkut_widget_get_uposition(compose->window, &x, &y);
11404 if (!compose->batch) {
11405 prefs_common.compose_x = x;
11406 prefs_common.compose_y = y;
11408 g_mutex_unlock(compose->mutex);
11409 compose_destroy(compose);
11414 * Add entry field for each address in list.
11415 * \param compose E-Mail composition object.
11416 * \param listAddress List of (formatted) E-Mail addresses.
11418 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11421 node = listAddress;
11423 addr = ( gchar * ) node->data;
11424 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11425 node = g_list_next( node );
11429 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11430 guint action, gboolean opening_multiple)
11432 gchar *body = NULL;
11433 GSList *new_msglist = NULL;
11434 MsgInfo *tmp_msginfo = NULL;
11435 gboolean originally_enc = FALSE;
11436 gboolean originally_sig = FALSE;
11437 Compose *compose = NULL;
11438 gchar *s_system = NULL;
11440 cm_return_if_fail(msgview != NULL);
11442 cm_return_if_fail(msginfo_list != NULL);
11444 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11445 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11446 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11448 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11449 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11450 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11451 orig_msginfo, mimeinfo);
11452 if (tmp_msginfo != NULL) {
11453 new_msglist = g_slist_append(NULL, tmp_msginfo);
11455 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11456 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11457 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11459 tmp_msginfo->folder = orig_msginfo->folder;
11460 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11461 if (orig_msginfo->tags) {
11462 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11463 tmp_msginfo->folder->tags_dirty = TRUE;
11469 if (!opening_multiple)
11470 body = messageview_get_selection(msgview);
11473 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11474 procmsg_msginfo_free(tmp_msginfo);
11475 g_slist_free(new_msglist);
11477 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11479 if (compose && originally_enc) {
11480 compose_force_encryption(compose, compose->account, FALSE, s_system);
11483 if (compose && originally_sig && compose->account->default_sign_reply) {
11484 compose_force_signing(compose, compose->account, s_system);
11488 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11491 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11494 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11495 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11496 GSList *cur = msginfo_list;
11497 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11498 "messages. Opening the windows "
11499 "could take some time. Do you "
11500 "want to continue?"),
11501 g_slist_length(msginfo_list));
11502 if (g_slist_length(msginfo_list) > 9
11503 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11504 != G_ALERTALTERNATE) {
11509 /* We'll open multiple compose windows */
11510 /* let the WM place the next windows */
11511 compose_force_window_origin = FALSE;
11512 for (; cur; cur = cur->next) {
11514 tmplist.data = cur->data;
11515 tmplist.next = NULL;
11516 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11518 compose_force_window_origin = TRUE;
11520 /* forwarding multiple mails as attachments is done via a
11521 * single compose window */
11522 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11526 void compose_check_for_email_account(Compose *compose)
11528 PrefsAccount *ac = NULL, *curr = NULL;
11534 if (compose->account && compose->account->protocol == A_NNTP) {
11535 ac = account_get_cur_account();
11536 if (ac->protocol == A_NNTP) {
11537 list = account_get_list();
11539 for( ; list != NULL ; list = g_list_next(list)) {
11540 curr = (PrefsAccount *) list->data;
11541 if (curr->protocol != A_NNTP) {
11547 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11552 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11553 const gchar *address)
11555 GSList *msginfo_list = NULL;
11556 gchar *body = messageview_get_selection(msgview);
11559 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11561 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11562 compose_check_for_email_account(compose);
11563 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11564 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11565 compose_reply_set_subject(compose, msginfo);
11568 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11571 void compose_set_position(Compose *compose, gint pos)
11573 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11575 gtkut_text_view_set_position(text, pos);
11578 gboolean compose_search_string(Compose *compose,
11579 const gchar *str, gboolean case_sens)
11581 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11583 return gtkut_text_view_search_string(text, str, case_sens);
11586 gboolean compose_search_string_backward(Compose *compose,
11587 const gchar *str, gboolean case_sens)
11589 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11591 return gtkut_text_view_search_string_backward(text, str, case_sens);
11594 /* allocate a msginfo structure and populate its data from a compose data structure */
11595 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11597 MsgInfo *newmsginfo;
11599 gchar buf[BUFFSIZE];
11601 cm_return_val_if_fail( compose != NULL, NULL );
11603 newmsginfo = procmsg_msginfo_new();
11606 get_rfc822_date(buf, sizeof(buf));
11607 newmsginfo->date = g_strdup(buf);
11610 if (compose->from_name) {
11611 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11612 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11616 if (compose->subject_entry)
11617 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11619 /* to, cc, reply-to, newsgroups */
11620 for (list = compose->header_list; list; list = list->next) {
11621 gchar *header = gtk_editable_get_chars(
11623 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11624 gchar *entry = gtk_editable_get_chars(
11625 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11627 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11628 if ( newmsginfo->to == NULL ) {
11629 newmsginfo->to = g_strdup(entry);
11630 } else if (entry && *entry) {
11631 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11632 g_free(newmsginfo->to);
11633 newmsginfo->to = tmp;
11636 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11637 if ( newmsginfo->cc == NULL ) {
11638 newmsginfo->cc = g_strdup(entry);
11639 } else if (entry && *entry) {
11640 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11641 g_free(newmsginfo->cc);
11642 newmsginfo->cc = tmp;
11645 if ( strcasecmp(header,
11646 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11647 if ( newmsginfo->newsgroups == NULL ) {
11648 newmsginfo->newsgroups = g_strdup(entry);
11649 } else if (entry && *entry) {
11650 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11651 g_free(newmsginfo->newsgroups);
11652 newmsginfo->newsgroups = tmp;
11660 /* other data is unset */
11666 /* update compose's dictionaries from folder dict settings */
11667 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11668 FolderItem *folder_item)
11670 cm_return_if_fail(compose != NULL);
11672 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11673 FolderItemPrefs *prefs = folder_item->prefs;
11675 if (prefs->enable_default_dictionary)
11676 gtkaspell_change_dict(compose->gtkaspell,
11677 prefs->default_dictionary, FALSE);
11678 if (folder_item->prefs->enable_default_alt_dictionary)
11679 gtkaspell_change_alt_dict(compose->gtkaspell,
11680 prefs->default_alt_dictionary);
11681 if (prefs->enable_default_dictionary
11682 || prefs->enable_default_alt_dictionary)
11683 compose_spell_menu_changed(compose);
11688 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
11690 Compose *compose = (Compose *)data;
11692 cm_return_if_fail(compose != NULL);
11694 gtk_widget_grab_focus(compose->text);