2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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);
561 static GtkActionEntry compose_popup_entries[] =
563 {"Compose", NULL, "Compose" },
564 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
565 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
566 {"Compose/---", NULL, "---", NULL, NULL, NULL },
567 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
570 static GtkActionEntry compose_entries[] =
572 {"Menu", NULL, "Menu" },
574 {"Message", NULL, N_("_Message") },
575 {"Edit", NULL, N_("_Edit") },
577 {"Spelling", NULL, N_("_Spelling") },
579 {"Options", NULL, N_("_Options") },
580 {"Tools", NULL, N_("_Tools") },
581 {"Help", NULL, N_("_Help") },
583 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
584 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
585 {"Message/---", NULL, "---" },
587 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
588 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
589 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
590 /* {"Message/---", NULL, "---" }, */
591 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
592 /* {"Message/---", NULL, "---" }, */
593 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
594 /* {"Message/---", NULL, "---" }, */
595 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
598 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
599 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
600 {"Edit/---", NULL, "---" },
602 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
603 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
604 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
606 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
607 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
608 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
609 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
611 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
613 {"Edit/Advanced", NULL, N_("A_dvanced") },
614 {"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*/
615 {"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*/
616 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
617 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
618 {"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*/
619 {"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*/
620 {"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*/
621 {"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*/
622 {"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*/
623 {"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*/
624 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
625 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
626 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
627 {"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*/
629 /* {"Edit/---", NULL, "---" }, */
630 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
632 /* {"Edit/---", NULL, "---" }, */
633 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
634 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
635 /* {"Edit/---", NULL, "---" }, */
636 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
639 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
640 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
641 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
642 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
644 {"Spelling/---", NULL, "---" },
645 {"Spelling/Options", NULL, N_("_Options") },
650 {"Options/ReplyMode", NULL, N_("Reply _mode") },
651 {"Options/---", NULL, "---" },
652 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
653 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
655 /* {"Options/---", NULL, "---" }, */
657 {"Options/Priority", NULL, N_("_Priority") },
659 {"Options/Encoding", NULL, N_("Character _encoding") },
660 {"Options/Encoding/---", NULL, "---" },
661 #define ENC_ACTION(cs_char,c_char,string) \
662 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
664 {"Options/Encoding/Western", NULL, N_("Western European") },
665 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
666 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
667 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
668 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
669 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
670 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
671 {"Options/Encoding/Korean", NULL, N_("Korean") },
672 {"Options/Encoding/Thai", NULL, N_("Thai") },
675 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
677 {"Tools/Template", NULL, N_("_Template") },
678 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
679 {"Tools/Actions", NULL, N_("Actio_ns") },
680 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
683 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
686 static GtkToggleActionEntry compose_toggle_entries[] =
688 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
689 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
690 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
691 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
692 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
693 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
694 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
697 static GtkRadioActionEntry compose_radio_rm_entries[] =
699 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
700 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
701 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
702 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
705 static GtkRadioActionEntry compose_radio_prio_entries[] =
707 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
708 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
709 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
710 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
711 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
714 static GtkRadioActionEntry compose_radio_enc_entries[] =
716 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
724 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
725 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
726 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
727 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
728 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
729 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
730 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
731 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
732 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
733 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
734 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
735 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
736 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
737 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
738 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
739 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
740 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
741 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
742 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
750 static GtkTargetEntry compose_mime_types[] =
752 {"text/uri-list", 0, 0},
753 {"UTF8_STRING", 0, 0},
757 static gboolean compose_put_existing_to_front(MsgInfo *info)
759 GList *compose_list = compose_get_compose_list();
763 for (elem = compose_list; elem != NULL && elem->data != NULL;
765 Compose *c = (Compose*)elem->data;
767 if (!c->targetinfo || !c->targetinfo->msgid ||
771 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
772 gtkut_window_popup(c->window);
780 static GdkColor quote_color1 =
781 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
782 static GdkColor quote_color2 =
783 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 static GdkColor quote_color3 =
785 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787 static GdkColor quote_bgcolor1 =
788 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
789 static GdkColor quote_bgcolor2 =
790 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
791 static GdkColor quote_bgcolor3 =
792 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
794 static GdkColor signature_color = {
801 static GdkColor uri_color = {
808 static void compose_create_tags(GtkTextView *text, Compose *compose)
810 GtkTextBuffer *buffer;
811 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
812 #if !GTK_CHECK_VERSION(2, 24, 0)
819 buffer = gtk_text_view_get_buffer(text);
821 if (prefs_common.enable_color) {
822 /* grab the quote colors, converting from an int to a GdkColor */
823 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
825 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
827 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
829 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
831 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
833 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
835 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
837 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
840 signature_color = quote_color1 = quote_color2 = quote_color3 =
841 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
844 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
845 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
846 "foreground-gdk", "e_color1,
847 "paragraph-background-gdk", "e_bgcolor1,
849 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
850 "foreground-gdk", "e_color2,
851 "paragraph-background-gdk", "e_bgcolor2,
853 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
854 "foreground-gdk", "e_color3,
855 "paragraph-background-gdk", "e_bgcolor3,
858 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
859 "foreground-gdk", "e_color1,
861 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
862 "foreground-gdk", "e_color2,
864 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
865 "foreground-gdk", "e_color3,
869 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
870 "foreground-gdk", &signature_color,
873 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
874 "foreground-gdk", &uri_color,
876 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
877 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
879 #if !GTK_CHECK_VERSION(2, 24, 0)
880 color[0] = quote_color1;
881 color[1] = quote_color2;
882 color[2] = quote_color3;
883 color[3] = quote_bgcolor1;
884 color[4] = quote_bgcolor2;
885 color[5] = quote_bgcolor3;
886 color[6] = signature_color;
887 color[7] = uri_color;
889 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
890 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
892 for (i = 0; i < 8; i++) {
893 if (success[i] == FALSE) {
894 g_warning("Compose: color allocation failed.\n");
895 quote_color1 = quote_color2 = quote_color3 =
896 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
897 signature_color = uri_color = black;
903 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
906 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
909 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
911 return compose_generic_new(account, mailto, item, NULL, NULL);
914 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
916 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
919 #define SCROLL_TO_CURSOR(compose) { \
920 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
921 gtk_text_view_get_buffer( \
922 GTK_TEXT_VIEW(compose->text))); \
923 gtk_text_view_scroll_mark_onscreen( \
924 GTK_TEXT_VIEW(compose->text), \
928 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
931 if (folderidentifier) {
932 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
933 prefs_common.compose_save_to_history = add_history(
934 prefs_common.compose_save_to_history, folderidentifier);
935 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
936 prefs_common.compose_save_to_history);
939 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
940 if (folderidentifier)
941 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
943 gtk_entry_set_text(GTK_ENTRY(entry), "");
946 static gchar *compose_get_save_to(Compose *compose)
949 gchar *result = NULL;
950 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
951 result = gtk_editable_get_chars(entry, 0, -1);
954 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
955 prefs_common.compose_save_to_history = add_history(
956 prefs_common.compose_save_to_history, result);
957 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
958 prefs_common.compose_save_to_history);
963 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
964 GList *attach_files, GList *listAddress )
967 GtkTextView *textview;
968 GtkTextBuffer *textbuf;
970 const gchar *subject_format = NULL;
971 const gchar *body_format = NULL;
972 gchar *mailto_from = NULL;
973 PrefsAccount *mailto_account = NULL;
974 MsgInfo* dummyinfo = NULL;
975 gint cursor_pos = -1;
976 MailField mfield = NO_FIELD_PRESENT;
980 /* check if mailto defines a from */
981 if (mailto && *mailto != '\0') {
982 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
983 /* mailto defines a from, check if we can get account prefs from it,
984 if not, the account prefs will be guessed using other ways, but we'll keep
987 mailto_account = account_find_from_address(mailto_from, TRUE);
989 account = mailto_account;
992 /* if no account prefs set from mailto, set if from folder prefs (if any) */
993 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
994 account = account_find_from_id(item->prefs->default_account);
996 /* if no account prefs set, fallback to the current one */
997 if (!account) account = cur_account;
998 cm_return_val_if_fail(account != NULL, NULL);
1000 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1002 /* override from name if mailto asked for it */
1004 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1005 g_free(mailto_from);
1007 /* override from name according to folder properties */
1008 if (item && item->prefs &&
1009 item->prefs->compose_with_format &&
1010 item->prefs->compose_override_from_format &&
1011 *item->prefs->compose_override_from_format != '\0') {
1016 dummyinfo = compose_msginfo_new_from_compose(compose);
1018 /* decode \-escape sequences in the internal representation of the quote format */
1019 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1020 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1023 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1024 compose->gtkaspell);
1026 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1028 quote_fmt_scan_string(tmp);
1031 buf = quote_fmt_get_buffer();
1033 alertpanel_error(_("New message From format error."));
1035 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1036 quote_fmt_reset_vartable();
1041 compose->replyinfo = NULL;
1042 compose->fwdinfo = NULL;
1044 textview = GTK_TEXT_VIEW(compose->text);
1045 textbuf = gtk_text_view_get_buffer(textview);
1046 compose_create_tags(textview, compose);
1048 undo_block(compose->undostruct);
1050 compose_set_dictionaries_from_folder_prefs(compose, item);
1053 if (account->auto_sig)
1054 compose_insert_sig(compose, FALSE);
1055 gtk_text_buffer_get_start_iter(textbuf, &iter);
1056 gtk_text_buffer_place_cursor(textbuf, &iter);
1058 if (account->protocol != A_NNTP) {
1059 if (mailto && *mailto != '\0') {
1060 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1063 compose_set_folder_prefs(compose, item, TRUE);
1065 if (item && item->ret_rcpt) {
1066 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1069 if (mailto && *mailto != '\0') {
1070 if (!strchr(mailto, '@'))
1071 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1073 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1074 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1075 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1076 mfield = TO_FIELD_PRESENT;
1079 * CLAWS: just don't allow return receipt request, even if the user
1080 * may want to send an email. simple but foolproof.
1082 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1084 compose_add_field_list( compose, listAddress );
1086 if (item && item->prefs && item->prefs->compose_with_format) {
1087 subject_format = item->prefs->compose_subject_format;
1088 body_format = item->prefs->compose_body_format;
1089 } else if (account->compose_with_format) {
1090 subject_format = account->compose_subject_format;
1091 body_format = account->compose_body_format;
1092 } else if (prefs_common.compose_with_format) {
1093 subject_format = prefs_common.compose_subject_format;
1094 body_format = prefs_common.compose_body_format;
1097 if (subject_format || body_format) {
1100 && *subject_format != '\0' )
1102 gchar *subject = NULL;
1107 dummyinfo = compose_msginfo_new_from_compose(compose);
1109 /* decode \-escape sequences in the internal representation of the quote format */
1110 tmp = g_malloc(strlen(subject_format)+1);
1111 pref_get_unescaped_pref(tmp, subject_format);
1113 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1115 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1116 compose->gtkaspell);
1118 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1120 quote_fmt_scan_string(tmp);
1123 buf = quote_fmt_get_buffer();
1125 alertpanel_error(_("New message subject format error."));
1127 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1128 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1129 quote_fmt_reset_vartable();
1133 mfield = SUBJECT_FIELD_PRESENT;
1137 && *body_format != '\0' )
1140 GtkTextBuffer *buffer;
1141 GtkTextIter start, end;
1145 dummyinfo = compose_msginfo_new_from_compose(compose);
1147 text = GTK_TEXT_VIEW(compose->text);
1148 buffer = gtk_text_view_get_buffer(text);
1149 gtk_text_buffer_get_start_iter(buffer, &start);
1150 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1151 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1153 compose_quote_fmt(compose, dummyinfo,
1155 NULL, tmp, FALSE, TRUE,
1156 _("The body of the \"New message\" template has an error at line %d."));
1157 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1158 quote_fmt_reset_vartable();
1162 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1163 gtkaspell_highlight_all(compose->gtkaspell);
1165 mfield = BODY_FIELD_PRESENT;
1169 procmsg_msginfo_free( dummyinfo );
1175 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1176 ainfo = (AttachInfo *) curr->data;
1177 compose_attach_append(compose, ainfo->file, ainfo->name,
1178 ainfo->content_type, ainfo->charset);
1182 compose_show_first_last_header(compose, TRUE);
1184 /* Set save folder */
1185 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1186 gchar *folderidentifier;
1188 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1189 folderidentifier = folder_item_get_identifier(item);
1190 compose_set_save_to(compose, folderidentifier);
1191 g_free(folderidentifier);
1194 /* Place cursor according to provided input (mfield) */
1196 case NO_FIELD_PRESENT:
1197 if (compose->header_last)
1198 gtk_widget_grab_focus(compose->header_last->entry);
1200 case TO_FIELD_PRESENT:
1201 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1203 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1206 gtk_widget_grab_focus(compose->subject_entry);
1208 case SUBJECT_FIELD_PRESENT:
1209 textview = GTK_TEXT_VIEW(compose->text);
1212 textbuf = gtk_text_view_get_buffer(textview);
1215 mark = gtk_text_buffer_get_insert(textbuf);
1216 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1217 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1219 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1220 * only defers where it comes to the variable body
1221 * is not null. If no body is present compose->text
1222 * will be null in which case you cannot place the
1223 * cursor inside the component so. An empty component
1224 * is therefore created before placing the cursor
1226 case BODY_FIELD_PRESENT:
1227 cursor_pos = quote_fmt_get_cursor_pos();
1228 if (cursor_pos == -1)
1229 gtk_widget_grab_focus(compose->header_last->entry);
1231 gtk_widget_grab_focus(compose->text);
1235 undo_unblock(compose->undostruct);
1237 if (prefs_common.auto_exteditor)
1238 compose_exec_ext_editor(compose);
1240 compose->draft_timeout_tag = -1;
1241 SCROLL_TO_CURSOR(compose);
1243 compose->modified = FALSE;
1244 compose_set_title(compose);
1246 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1251 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1252 gboolean override_pref, const gchar *system)
1254 const gchar *privacy = NULL;
1256 cm_return_if_fail(compose != NULL);
1257 cm_return_if_fail(account != NULL);
1259 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1264 else if (account->default_privacy_system
1265 && strlen(account->default_privacy_system)) {
1266 privacy = account->default_privacy_system;
1268 GSList *privacy_avail = privacy_get_system_ids();
1269 if (privacy_avail && g_slist_length(privacy_avail)) {
1270 privacy = (gchar *)(privacy_avail->data);
1273 if (privacy != NULL) {
1275 g_free(compose->privacy_system);
1276 compose->privacy_system = NULL;
1278 if (compose->privacy_system == NULL)
1279 compose->privacy_system = g_strdup(privacy);
1280 else if (*(compose->privacy_system) == '\0') {
1281 g_free(compose->privacy_system);
1282 compose->privacy_system = g_strdup(privacy);
1284 compose_update_privacy_system_menu_item(compose, FALSE);
1285 compose_use_encryption(compose, TRUE);
1289 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1291 const gchar *privacy = NULL;
1295 else if (account->default_privacy_system
1296 && strlen(account->default_privacy_system)) {
1297 privacy = account->default_privacy_system;
1299 GSList *privacy_avail = privacy_get_system_ids();
1300 if (privacy_avail && g_slist_length(privacy_avail)) {
1301 privacy = (gchar *)(privacy_avail->data);
1305 if (privacy != NULL) {
1307 g_free(compose->privacy_system);
1308 compose->privacy_system = NULL;
1310 if (compose->privacy_system == NULL)
1311 compose->privacy_system = g_strdup(privacy);
1312 compose_update_privacy_system_menu_item(compose, FALSE);
1313 compose_use_signing(compose, TRUE);
1317 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1321 Compose *compose = NULL;
1323 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1325 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1326 cm_return_val_if_fail(msginfo != NULL, NULL);
1328 list_len = g_slist_length(msginfo_list);
1332 case COMPOSE_REPLY_TO_ADDRESS:
1333 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1334 FALSE, prefs_common.default_reply_list, FALSE, body);
1336 case COMPOSE_REPLY_WITH_QUOTE:
1337 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1338 FALSE, prefs_common.default_reply_list, FALSE, body);
1340 case COMPOSE_REPLY_WITHOUT_QUOTE:
1341 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1342 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1344 case COMPOSE_REPLY_TO_SENDER:
1345 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1346 FALSE, FALSE, TRUE, body);
1348 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1349 compose = compose_followup_and_reply_to(msginfo,
1350 COMPOSE_QUOTE_CHECK,
1351 FALSE, FALSE, body);
1353 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1354 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1355 FALSE, FALSE, TRUE, body);
1357 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1358 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1359 FALSE, FALSE, TRUE, NULL);
1361 case COMPOSE_REPLY_TO_ALL:
1362 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1363 TRUE, FALSE, FALSE, body);
1365 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1366 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1367 TRUE, FALSE, FALSE, body);
1369 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1370 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1371 TRUE, FALSE, FALSE, NULL);
1373 case COMPOSE_REPLY_TO_LIST:
1374 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1375 FALSE, TRUE, FALSE, body);
1377 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1378 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1379 FALSE, TRUE, FALSE, body);
1381 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1382 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1383 FALSE, TRUE, FALSE, NULL);
1385 case COMPOSE_FORWARD:
1386 if (prefs_common.forward_as_attachment) {
1387 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1390 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1394 case COMPOSE_FORWARD_INLINE:
1395 /* check if we reply to more than one Message */
1396 if (list_len == 1) {
1397 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1400 /* more messages FALL THROUGH */
1401 case COMPOSE_FORWARD_AS_ATTACH:
1402 compose = compose_forward_multiple(NULL, msginfo_list);
1404 case COMPOSE_REDIRECT:
1405 compose = compose_redirect(NULL, msginfo, FALSE);
1408 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1411 if (compose == NULL) {
1412 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1416 compose->rmode = mode;
1417 switch (compose->rmode) {
1419 case COMPOSE_REPLY_WITH_QUOTE:
1420 case COMPOSE_REPLY_WITHOUT_QUOTE:
1421 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1422 debug_print("reply mode Normal\n");
1423 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1424 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1426 case COMPOSE_REPLY_TO_SENDER:
1427 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1428 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1429 debug_print("reply mode Sender\n");
1430 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1432 case COMPOSE_REPLY_TO_ALL:
1433 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1434 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1435 debug_print("reply mode All\n");
1436 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1438 case COMPOSE_REPLY_TO_LIST:
1439 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1440 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1441 debug_print("reply mode List\n");
1442 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1444 case COMPOSE_REPLY_TO_ADDRESS:
1445 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1453 static Compose *compose_reply(MsgInfo *msginfo,
1454 ComposeQuoteMode quote_mode,
1460 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1461 to_sender, FALSE, body);
1464 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1465 ComposeQuoteMode quote_mode,
1470 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1471 to_sender, TRUE, body);
1474 static void compose_extract_original_charset(Compose *compose)
1476 MsgInfo *info = NULL;
1477 if (compose->replyinfo) {
1478 info = compose->replyinfo;
1479 } else if (compose->fwdinfo) {
1480 info = compose->fwdinfo;
1481 } else if (compose->targetinfo) {
1482 info = compose->targetinfo;
1485 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1486 MimeInfo *partinfo = mimeinfo;
1487 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1488 partinfo = procmime_mimeinfo_next(partinfo);
1490 compose->orig_charset =
1491 g_strdup(procmime_mimeinfo_get_parameter(
1492 partinfo, "charset"));
1494 procmime_mimeinfo_free_all(mimeinfo);
1498 #define SIGNAL_BLOCK(buffer) { \
1499 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1500 G_CALLBACK(compose_changed_cb), \
1502 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1503 G_CALLBACK(text_inserted), \
1507 #define SIGNAL_UNBLOCK(buffer) { \
1508 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1509 G_CALLBACK(compose_changed_cb), \
1511 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1512 G_CALLBACK(text_inserted), \
1516 static Compose *compose_generic_reply(MsgInfo *msginfo,
1517 ComposeQuoteMode quote_mode,
1518 gboolean to_all, gboolean to_ml,
1520 gboolean followup_and_reply_to,
1524 PrefsAccount *account = NULL;
1525 GtkTextView *textview;
1526 GtkTextBuffer *textbuf;
1527 gboolean quote = FALSE;
1528 const gchar *qmark = NULL;
1529 const gchar *body_fmt = NULL;
1530 gchar *s_system = NULL;
1532 cm_return_val_if_fail(msginfo != NULL, NULL);
1533 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1535 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1537 cm_return_val_if_fail(account != NULL, NULL);
1539 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1541 compose->updating = TRUE;
1543 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1544 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1546 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1547 if (!compose->replyinfo)
1548 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1550 compose_extract_original_charset(compose);
1552 if (msginfo->folder && msginfo->folder->ret_rcpt)
1553 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1555 /* Set save folder */
1556 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1557 gchar *folderidentifier;
1559 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1560 folderidentifier = folder_item_get_identifier(msginfo->folder);
1561 compose_set_save_to(compose, folderidentifier);
1562 g_free(folderidentifier);
1565 if (compose_parse_header(compose, msginfo) < 0) {
1566 compose->updating = FALSE;
1567 compose_destroy(compose);
1571 /* override from name according to folder properties */
1572 if (msginfo->folder && msginfo->folder->prefs &&
1573 msginfo->folder->prefs->reply_with_format &&
1574 msginfo->folder->prefs->reply_override_from_format &&
1575 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1580 /* decode \-escape sequences in the internal representation of the quote format */
1581 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1582 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1585 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1586 compose->gtkaspell);
1588 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1590 quote_fmt_scan_string(tmp);
1593 buf = quote_fmt_get_buffer();
1595 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1597 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1598 quote_fmt_reset_vartable();
1603 textview = (GTK_TEXT_VIEW(compose->text));
1604 textbuf = gtk_text_view_get_buffer(textview);
1605 compose_create_tags(textview, compose);
1607 undo_block(compose->undostruct);
1609 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1610 gtkaspell_block_check(compose->gtkaspell);
1613 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1614 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1615 /* use the reply format of folder (if enabled), or the account's one
1616 (if enabled) or fallback to the global reply format, which is always
1617 enabled (even if empty), and use the relevant quotemark */
1619 if (msginfo->folder && msginfo->folder->prefs &&
1620 msginfo->folder->prefs->reply_with_format) {
1621 qmark = msginfo->folder->prefs->reply_quotemark;
1622 body_fmt = msginfo->folder->prefs->reply_body_format;
1624 } else if (account->reply_with_format) {
1625 qmark = account->reply_quotemark;
1626 body_fmt = account->reply_body_format;
1629 qmark = prefs_common.quotemark;
1630 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1631 body_fmt = gettext(prefs_common.quotefmt);
1638 /* empty quotemark is not allowed */
1639 if (qmark == NULL || *qmark == '\0')
1641 compose_quote_fmt(compose, compose->replyinfo,
1642 body_fmt, qmark, body, FALSE, TRUE,
1643 _("The body of the \"Reply\" template has an error at line %d."));
1644 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1645 quote_fmt_reset_vartable();
1648 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1649 compose_force_encryption(compose, account, FALSE, s_system);
1652 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1653 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1654 compose_force_signing(compose, account, s_system);
1658 SIGNAL_BLOCK(textbuf);
1660 if (account->auto_sig)
1661 compose_insert_sig(compose, FALSE);
1663 compose_wrap_all(compose);
1666 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1667 gtkaspell_highlight_all(compose->gtkaspell);
1668 gtkaspell_unblock_check(compose->gtkaspell);
1670 SIGNAL_UNBLOCK(textbuf);
1672 gtk_widget_grab_focus(compose->text);
1674 undo_unblock(compose->undostruct);
1676 if (prefs_common.auto_exteditor)
1677 compose_exec_ext_editor(compose);
1679 compose->modified = FALSE;
1680 compose_set_title(compose);
1682 compose->updating = FALSE;
1683 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1684 SCROLL_TO_CURSOR(compose);
1686 if (compose->deferred_destroy) {
1687 compose_destroy(compose);
1695 #define INSERT_FW_HEADER(var, hdr) \
1696 if (msginfo->var && *msginfo->var) { \
1697 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1698 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1699 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1702 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1703 gboolean as_attach, const gchar *body,
1704 gboolean no_extedit,
1708 GtkTextView *textview;
1709 GtkTextBuffer *textbuf;
1710 gint cursor_pos = -1;
1713 cm_return_val_if_fail(msginfo != NULL, NULL);
1714 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1717 !(account = compose_guess_forward_account_from_msginfo
1719 account = cur_account;
1721 if (!prefs_common.forward_as_attachment)
1722 mode = COMPOSE_FORWARD_INLINE;
1724 mode = COMPOSE_FORWARD;
1725 compose = compose_create(account, msginfo->folder, mode, batch);
1727 compose->updating = TRUE;
1728 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1729 if (!compose->fwdinfo)
1730 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1732 compose_extract_original_charset(compose);
1734 if (msginfo->subject && *msginfo->subject) {
1735 gchar *buf, *buf2, *p;
1737 buf = p = g_strdup(msginfo->subject);
1738 p += subject_get_prefix_length(p);
1739 memmove(buf, p, strlen(p) + 1);
1741 buf2 = g_strdup_printf("Fw: %s", buf);
1742 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1748 /* override from name according to folder properties */
1749 if (msginfo->folder && msginfo->folder->prefs &&
1750 msginfo->folder->prefs->forward_with_format &&
1751 msginfo->folder->prefs->forward_override_from_format &&
1752 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1756 MsgInfo *full_msginfo = NULL;
1759 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1761 full_msginfo = procmsg_msginfo_copy(msginfo);
1763 /* decode \-escape sequences in the internal representation of the quote format */
1764 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1765 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1768 gtkaspell_block_check(compose->gtkaspell);
1769 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1770 compose->gtkaspell);
1772 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1774 quote_fmt_scan_string(tmp);
1777 buf = quote_fmt_get_buffer();
1779 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1781 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1782 quote_fmt_reset_vartable();
1785 procmsg_msginfo_free(full_msginfo);
1788 textview = GTK_TEXT_VIEW(compose->text);
1789 textbuf = gtk_text_view_get_buffer(textview);
1790 compose_create_tags(textview, compose);
1792 undo_block(compose->undostruct);
1796 msgfile = procmsg_get_message_file(msginfo);
1797 if (!is_file_exist(msgfile))
1798 g_warning("%s: file not exist\n", msgfile);
1800 compose_attach_append(compose, msgfile, msgfile,
1801 "message/rfc822", NULL);
1805 const gchar *qmark = NULL;
1806 const gchar *body_fmt = NULL;
1807 MsgInfo *full_msginfo;
1809 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1811 full_msginfo = procmsg_msginfo_copy(msginfo);
1813 /* use the forward format of folder (if enabled), or the account's one
1814 (if enabled) or fallback to the global forward format, which is always
1815 enabled (even if empty), and use the relevant quotemark */
1816 if (msginfo->folder && msginfo->folder->prefs &&
1817 msginfo->folder->prefs->forward_with_format) {
1818 qmark = msginfo->folder->prefs->forward_quotemark;
1819 body_fmt = msginfo->folder->prefs->forward_body_format;
1821 } else if (account->forward_with_format) {
1822 qmark = account->forward_quotemark;
1823 body_fmt = account->forward_body_format;
1826 qmark = prefs_common.fw_quotemark;
1827 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1828 body_fmt = gettext(prefs_common.fw_quotefmt);
1833 /* empty quotemark is not allowed */
1834 if (qmark == NULL || *qmark == '\0')
1837 compose_quote_fmt(compose, full_msginfo,
1838 body_fmt, qmark, body, FALSE, TRUE,
1839 _("The body of the \"Forward\" template has an error at line %d."));
1840 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1841 quote_fmt_reset_vartable();
1842 compose_attach_parts(compose, msginfo);
1844 procmsg_msginfo_free(full_msginfo);
1847 SIGNAL_BLOCK(textbuf);
1849 if (account->auto_sig)
1850 compose_insert_sig(compose, FALSE);
1852 compose_wrap_all(compose);
1855 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1856 gtkaspell_highlight_all(compose->gtkaspell);
1857 gtkaspell_unblock_check(compose->gtkaspell);
1859 SIGNAL_UNBLOCK(textbuf);
1861 cursor_pos = quote_fmt_get_cursor_pos();
1862 if (cursor_pos == -1)
1863 gtk_widget_grab_focus(compose->header_last->entry);
1865 gtk_widget_grab_focus(compose->text);
1867 if (!no_extedit && prefs_common.auto_exteditor)
1868 compose_exec_ext_editor(compose);
1871 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1872 gchar *folderidentifier;
1874 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1875 folderidentifier = folder_item_get_identifier(msginfo->folder);
1876 compose_set_save_to(compose, folderidentifier);
1877 g_free(folderidentifier);
1880 undo_unblock(compose->undostruct);
1882 compose->modified = FALSE;
1883 compose_set_title(compose);
1885 compose->updating = FALSE;
1886 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1887 SCROLL_TO_CURSOR(compose);
1889 if (compose->deferred_destroy) {
1890 compose_destroy(compose);
1894 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1899 #undef INSERT_FW_HEADER
1901 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1904 GtkTextView *textview;
1905 GtkTextBuffer *textbuf;
1909 gboolean single_mail = TRUE;
1911 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1913 if (g_slist_length(msginfo_list) > 1)
1914 single_mail = FALSE;
1916 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1917 if (((MsgInfo *)msginfo->data)->folder == NULL)
1920 /* guess account from first selected message */
1922 !(account = compose_guess_forward_account_from_msginfo
1923 (msginfo_list->data)))
1924 account = cur_account;
1926 cm_return_val_if_fail(account != NULL, NULL);
1928 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1929 if (msginfo->data) {
1930 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1931 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1935 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1936 g_warning("no msginfo_list");
1940 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1942 compose->updating = TRUE;
1944 /* override from name according to folder properties */
1945 if (msginfo_list->data) {
1946 MsgInfo *msginfo = msginfo_list->data;
1948 if (msginfo->folder && msginfo->folder->prefs &&
1949 msginfo->folder->prefs->forward_with_format &&
1950 msginfo->folder->prefs->forward_override_from_format &&
1951 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1956 /* decode \-escape sequences in the internal representation of the quote format */
1957 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1958 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1961 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1962 compose->gtkaspell);
1964 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1966 quote_fmt_scan_string(tmp);
1969 buf = quote_fmt_get_buffer();
1971 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1973 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1974 quote_fmt_reset_vartable();
1980 textview = GTK_TEXT_VIEW(compose->text);
1981 textbuf = gtk_text_view_get_buffer(textview);
1982 compose_create_tags(textview, compose);
1984 undo_block(compose->undostruct);
1985 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1988 if (!is_file_exist(msgfile))
1989 g_warning("%s: file not exist\n", msgfile);
1991 compose_attach_append(compose, msgfile, msgfile,
1992 "message/rfc822", NULL);
1997 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1998 if (info->subject && *info->subject) {
1999 gchar *buf, *buf2, *p;
2001 buf = p = g_strdup(info->subject);
2002 p += subject_get_prefix_length(p);
2003 memmove(buf, p, strlen(p) + 1);
2005 buf2 = g_strdup_printf("Fw: %s", buf);
2006 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2012 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2013 _("Fw: multiple emails"));
2016 SIGNAL_BLOCK(textbuf);
2018 if (account->auto_sig)
2019 compose_insert_sig(compose, FALSE);
2021 compose_wrap_all(compose);
2023 SIGNAL_UNBLOCK(textbuf);
2025 gtk_text_buffer_get_start_iter(textbuf, &iter);
2026 gtk_text_buffer_place_cursor(textbuf, &iter);
2028 gtk_widget_grab_focus(compose->header_last->entry);
2029 undo_unblock(compose->undostruct);
2030 compose->modified = FALSE;
2031 compose_set_title(compose);
2033 compose->updating = FALSE;
2034 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2035 SCROLL_TO_CURSOR(compose);
2037 if (compose->deferred_destroy) {
2038 compose_destroy(compose);
2042 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2047 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2049 GtkTextIter start = *iter;
2050 GtkTextIter end_iter;
2051 int start_pos = gtk_text_iter_get_offset(&start);
2053 if (!compose->account->sig_sep)
2056 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2057 start_pos+strlen(compose->account->sig_sep));
2059 /* check sig separator */
2060 str = gtk_text_iter_get_text(&start, &end_iter);
2061 if (!strcmp(str, compose->account->sig_sep)) {
2063 /* check end of line (\n) */
2064 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2065 start_pos+strlen(compose->account->sig_sep));
2066 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2067 start_pos+strlen(compose->account->sig_sep)+1);
2068 tmp = gtk_text_iter_get_text(&start, &end_iter);
2069 if (!strcmp(tmp,"\n")) {
2081 static void compose_colorize_signature(Compose *compose)
2083 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2085 GtkTextIter end_iter;
2086 gtk_text_buffer_get_start_iter(buffer, &iter);
2087 while (gtk_text_iter_forward_line(&iter))
2088 if (compose_is_sig_separator(compose, buffer, &iter)) {
2089 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2090 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2094 #define BLOCK_WRAP() { \
2095 prev_autowrap = compose->autowrap; \
2096 buffer = gtk_text_view_get_buffer( \
2097 GTK_TEXT_VIEW(compose->text)); \
2098 compose->autowrap = FALSE; \
2100 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2101 G_CALLBACK(compose_changed_cb), \
2103 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2104 G_CALLBACK(text_inserted), \
2107 #define UNBLOCK_WRAP() { \
2108 compose->autowrap = prev_autowrap; \
2109 if (compose->autowrap) { \
2110 gint old = compose->draft_timeout_tag; \
2111 compose->draft_timeout_tag = -2; \
2112 compose_wrap_all(compose); \
2113 compose->draft_timeout_tag = old; \
2116 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2117 G_CALLBACK(compose_changed_cb), \
2119 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2120 G_CALLBACK(text_inserted), \
2124 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2126 Compose *compose = NULL;
2127 PrefsAccount *account = NULL;
2128 GtkTextView *textview;
2129 GtkTextBuffer *textbuf;
2133 gchar buf[BUFFSIZE];
2134 gboolean use_signing = FALSE;
2135 gboolean use_encryption = FALSE;
2136 gchar *privacy_system = NULL;
2137 int priority = PRIORITY_NORMAL;
2138 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2139 gboolean autowrap = prefs_common.autowrap;
2140 gboolean autoindent = prefs_common.auto_indent;
2141 HeaderEntry *manual_headers = NULL;
2143 cm_return_val_if_fail(msginfo != NULL, NULL);
2144 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2146 if (compose_put_existing_to_front(msginfo)) {
2150 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2151 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2152 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2153 gchar queueheader_buf[BUFFSIZE];
2156 /* Select Account from queue headers */
2157 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2158 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2159 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2160 account = account_find_from_id(id);
2162 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2163 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2164 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2165 account = account_find_from_id(id);
2167 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2168 sizeof(queueheader_buf), "NAID:")) {
2169 id = atoi(&queueheader_buf[strlen("NAID:")]);
2170 account = account_find_from_id(id);
2172 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2173 sizeof(queueheader_buf), "MAID:")) {
2174 id = atoi(&queueheader_buf[strlen("MAID:")]);
2175 account = account_find_from_id(id);
2177 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2178 sizeof(queueheader_buf), "S:")) {
2179 account = account_find_from_address(queueheader_buf, FALSE);
2181 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2182 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2183 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2184 use_signing = param;
2187 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2188 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2189 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2190 use_signing = param;
2193 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2194 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2195 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2196 use_encryption = param;
2198 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2199 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2200 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2201 use_encryption = param;
2203 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2204 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2205 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2208 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2209 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2210 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2213 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2214 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2215 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2217 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2218 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2219 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2221 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2222 sizeof(queueheader_buf), "X-Priority: ")) {
2223 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2226 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2227 sizeof(queueheader_buf), "RMID:")) {
2228 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2229 if (tokens[0] && tokens[1] && tokens[2]) {
2230 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2231 if (orig_item != NULL) {
2232 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2237 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2238 sizeof(queueheader_buf), "FMID:")) {
2239 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2240 if (tokens[0] && tokens[1] && tokens[2]) {
2241 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2242 if (orig_item != NULL) {
2243 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2248 /* Get manual headers */
2249 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2250 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2251 if (*listmh != '\0') {
2252 debug_print("Got manual headers: %s\n", listmh);
2253 manual_headers = procheader_entries_from_str(listmh);
2258 account = msginfo->folder->folder->account;
2261 if (!account && prefs_common.reedit_account_autosel) {
2262 gchar from[BUFFSIZE];
2263 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2264 extract_address(from);
2265 account = account_find_from_address(from, FALSE);
2269 account = cur_account;
2271 cm_return_val_if_fail(account != NULL, NULL);
2273 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2275 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2276 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2277 compose->autowrap = autowrap;
2278 compose->replyinfo = replyinfo;
2279 compose->fwdinfo = fwdinfo;
2281 compose->updating = TRUE;
2282 compose->priority = priority;
2284 if (privacy_system != NULL) {
2285 compose->privacy_system = privacy_system;
2286 compose_use_signing(compose, use_signing);
2287 compose_use_encryption(compose, use_encryption);
2288 compose_update_privacy_system_menu_item(compose, FALSE);
2290 activate_privacy_system(compose, account, FALSE);
2293 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2295 compose_extract_original_charset(compose);
2297 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2298 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2299 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2300 gchar queueheader_buf[BUFFSIZE];
2302 /* Set message save folder */
2303 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2304 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2305 compose_set_save_to(compose, &queueheader_buf[4]);
2307 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2308 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2310 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2315 if (compose_parse_header(compose, msginfo) < 0) {
2316 compose->updating = FALSE;
2317 compose_destroy(compose);
2320 compose_reedit_set_entry(compose, msginfo);
2322 textview = GTK_TEXT_VIEW(compose->text);
2323 textbuf = gtk_text_view_get_buffer(textview);
2324 compose_create_tags(textview, compose);
2326 mark = gtk_text_buffer_get_insert(textbuf);
2327 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2329 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2330 G_CALLBACK(compose_changed_cb),
2333 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2334 fp = procmime_get_first_encrypted_text_content(msginfo);
2336 compose_force_encryption(compose, account, TRUE, NULL);
2339 fp = procmime_get_first_text_content(msginfo);
2342 g_warning("Can't get text part\n");
2346 gboolean prev_autowrap;
2347 GtkTextBuffer *buffer;
2349 while (fgets(buf, sizeof(buf), fp) != NULL) {
2351 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2357 compose_attach_parts(compose, msginfo);
2359 compose_colorize_signature(compose);
2361 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2362 G_CALLBACK(compose_changed_cb),
2365 if (manual_headers != NULL) {
2366 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2367 procheader_entries_free(manual_headers);
2368 compose->updating = FALSE;
2369 compose_destroy(compose);
2372 procheader_entries_free(manual_headers);
2375 gtk_widget_grab_focus(compose->text);
2377 if (prefs_common.auto_exteditor) {
2378 compose_exec_ext_editor(compose);
2380 compose->modified = FALSE;
2381 compose_set_title(compose);
2383 compose->updating = FALSE;
2384 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2385 SCROLL_TO_CURSOR(compose);
2387 if (compose->deferred_destroy) {
2388 compose_destroy(compose);
2392 compose->sig_str = account_get_signature_str(compose->account);
2394 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2399 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2406 cm_return_val_if_fail(msginfo != NULL, NULL);
2409 account = account_get_reply_account(msginfo,
2410 prefs_common.reply_account_autosel);
2411 cm_return_val_if_fail(account != NULL, NULL);
2413 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2415 compose->updating = TRUE;
2417 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2418 compose->replyinfo = NULL;
2419 compose->fwdinfo = NULL;
2421 compose_show_first_last_header(compose, TRUE);
2423 gtk_widget_grab_focus(compose->header_last->entry);
2425 filename = procmsg_get_message_file(msginfo);
2427 if (filename == NULL) {
2428 compose->updating = FALSE;
2429 compose_destroy(compose);
2434 compose->redirect_filename = filename;
2436 /* Set save folder */
2437 item = msginfo->folder;
2438 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2439 gchar *folderidentifier;
2441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2442 folderidentifier = folder_item_get_identifier(item);
2443 compose_set_save_to(compose, folderidentifier);
2444 g_free(folderidentifier);
2447 compose_attach_parts(compose, msginfo);
2449 if (msginfo->subject)
2450 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2452 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2454 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2455 _("The body of the \"Redirect\" template has an error at line %d."));
2456 quote_fmt_reset_vartable();
2457 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2459 compose_colorize_signature(compose);
2462 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2463 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2464 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2466 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2467 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2468 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2469 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2470 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2471 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2472 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2473 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2475 if (compose->toolbar->draft_btn)
2476 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2477 if (compose->toolbar->insert_btn)
2478 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2479 if (compose->toolbar->attach_btn)
2480 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2481 if (compose->toolbar->sig_btn)
2482 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2483 if (compose->toolbar->exteditor_btn)
2484 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2485 if (compose->toolbar->linewrap_current_btn)
2486 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2487 if (compose->toolbar->linewrap_all_btn)
2488 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2490 compose->modified = FALSE;
2491 compose_set_title(compose);
2492 compose->updating = FALSE;
2493 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2494 SCROLL_TO_CURSOR(compose);
2496 if (compose->deferred_destroy) {
2497 compose_destroy(compose);
2501 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2506 GList *compose_get_compose_list(void)
2508 return compose_list;
2511 void compose_entry_append(Compose *compose, const gchar *address,
2512 ComposeEntryType type, ComposePrefType pref_type)
2514 const gchar *header;
2516 gboolean in_quote = FALSE;
2517 if (!address || *address == '\0') return;
2524 header = N_("Bcc:");
2526 case COMPOSE_REPLYTO:
2527 header = N_("Reply-To:");
2529 case COMPOSE_NEWSGROUPS:
2530 header = N_("Newsgroups:");
2532 case COMPOSE_FOLLOWUPTO:
2533 header = N_( "Followup-To:");
2535 case COMPOSE_INREPLYTO:
2536 header = N_( "In-Reply-To:");
2543 header = prefs_common_translated_header_name(header);
2545 cur = begin = (gchar *)address;
2547 /* we separate the line by commas, but not if we're inside a quoted
2549 while (*cur != '\0') {
2551 in_quote = !in_quote;
2552 if (*cur == ',' && !in_quote) {
2553 gchar *tmp = g_strdup(begin);
2555 tmp[cur-begin]='\0';
2558 while (*tmp == ' ' || *tmp == '\t')
2560 compose_add_header_entry(compose, header, tmp, pref_type);
2567 gchar *tmp = g_strdup(begin);
2569 tmp[cur-begin]='\0';
2570 while (*tmp == ' ' || *tmp == '\t')
2572 compose_add_header_entry(compose, header, tmp, pref_type);
2577 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2579 #if !GTK_CHECK_VERSION(3, 0, 0)
2580 static GdkColor yellow;
2581 static GdkColor black;
2582 static gboolean yellow_initialised = FALSE;
2584 static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2585 static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2590 #if !GTK_CHECK_VERSION(3, 0, 0)
2591 if (!yellow_initialised) {
2592 gdk_color_parse("#f5f6be", &yellow);
2593 gdk_color_parse("#000000", &black);
2594 yellow_initialised = gdk_colormap_alloc_color(
2595 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2596 yellow_initialised &= gdk_colormap_alloc_color(
2597 gdk_colormap_get_system(), &black, FALSE, TRUE);
2601 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2602 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2603 if (gtk_entry_get_text(entry) &&
2604 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2605 #if !GTK_CHECK_VERSION(3, 0, 0)
2606 if (yellow_initialised) {
2608 gtk_widget_modify_base(
2609 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2610 GTK_STATE_NORMAL, &yellow);
2611 gtk_widget_modify_text(
2612 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2613 GTK_STATE_NORMAL, &black);
2614 #if !GTK_CHECK_VERSION(3, 0, 0)
2621 void compose_toolbar_cb(gint action, gpointer data)
2623 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2624 Compose *compose = (Compose*)toolbar_item->parent;
2626 cm_return_if_fail(compose != NULL);
2630 compose_send_cb(NULL, compose);
2633 compose_send_later_cb(NULL, compose);
2636 compose_draft(compose, COMPOSE_QUIT_EDITING);
2639 compose_insert_file_cb(NULL, compose);
2642 compose_attach_cb(NULL, compose);
2645 compose_insert_sig(compose, FALSE);
2648 compose_ext_editor_cb(NULL, compose);
2650 case A_LINEWRAP_CURRENT:
2651 compose_beautify_paragraph(compose, NULL, TRUE);
2653 case A_LINEWRAP_ALL:
2654 compose_wrap_all_full(compose, TRUE);
2657 compose_address_cb(NULL, compose);
2660 case A_CHECK_SPELLING:
2661 compose_check_all(NULL, compose);
2669 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2674 gchar *subject = NULL;
2678 gchar **attach = NULL;
2679 gchar *inreplyto = NULL;
2680 MailField mfield = NO_FIELD_PRESENT;
2682 /* get mailto parts but skip from */
2683 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2686 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2687 mfield = TO_FIELD_PRESENT;
2690 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2692 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2694 if (!g_utf8_validate (subject, -1, NULL)) {
2695 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2696 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2699 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2701 mfield = SUBJECT_FIELD_PRESENT;
2704 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2705 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2708 gboolean prev_autowrap = compose->autowrap;
2710 compose->autowrap = FALSE;
2712 mark = gtk_text_buffer_get_insert(buffer);
2713 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2715 if (!g_utf8_validate (body, -1, NULL)) {
2716 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2717 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2720 gtk_text_buffer_insert(buffer, &iter, body, -1);
2722 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2724 compose->autowrap = prev_autowrap;
2725 if (compose->autowrap)
2726 compose_wrap_all(compose);
2727 mfield = BODY_FIELD_PRESENT;
2731 gint i = 0, att = 0;
2732 gchar *warn_files = NULL;
2733 while (attach[i] != NULL) {
2734 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2735 if (utf8_filename) {
2736 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2737 gchar *tmp = g_strdup_printf("%s%s\n",
2738 warn_files?warn_files:"",
2744 g_free(utf8_filename);
2746 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2751 alertpanel_notice(ngettext(
2752 "The following file has been attached: \n%s",
2753 "The following files have been attached: \n%s", att), warn_files);
2758 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2771 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2773 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2774 {"Cc:", NULL, TRUE},
2775 {"References:", NULL, FALSE},
2776 {"Bcc:", NULL, TRUE},
2777 {"Newsgroups:", NULL, TRUE},
2778 {"Followup-To:", NULL, TRUE},
2779 {"List-Post:", NULL, FALSE},
2780 {"X-Priority:", NULL, FALSE},
2781 {NULL, NULL, FALSE}};
2797 cm_return_val_if_fail(msginfo != NULL, -1);
2799 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2800 procheader_get_header_fields(fp, hentry);
2803 if (hentry[H_REPLY_TO].body != NULL) {
2804 if (hentry[H_REPLY_TO].body[0] != '\0') {
2806 conv_unmime_header(hentry[H_REPLY_TO].body,
2809 g_free(hentry[H_REPLY_TO].body);
2810 hentry[H_REPLY_TO].body = NULL;
2812 if (hentry[H_CC].body != NULL) {
2813 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2814 g_free(hentry[H_CC].body);
2815 hentry[H_CC].body = NULL;
2817 if (hentry[H_REFERENCES].body != NULL) {
2818 if (compose->mode == COMPOSE_REEDIT)
2819 compose->references = hentry[H_REFERENCES].body;
2821 compose->references = compose_parse_references
2822 (hentry[H_REFERENCES].body, msginfo->msgid);
2823 g_free(hentry[H_REFERENCES].body);
2825 hentry[H_REFERENCES].body = NULL;
2827 if (hentry[H_BCC].body != NULL) {
2828 if (compose->mode == COMPOSE_REEDIT)
2830 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2831 g_free(hentry[H_BCC].body);
2832 hentry[H_BCC].body = NULL;
2834 if (hentry[H_NEWSGROUPS].body != NULL) {
2835 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2836 hentry[H_NEWSGROUPS].body = NULL;
2838 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2839 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2840 compose->followup_to =
2841 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2844 g_free(hentry[H_FOLLOWUP_TO].body);
2845 hentry[H_FOLLOWUP_TO].body = NULL;
2847 if (hentry[H_LIST_POST].body != NULL) {
2848 gchar *to = NULL, *start = NULL;
2850 extract_address(hentry[H_LIST_POST].body);
2851 if (hentry[H_LIST_POST].body[0] != '\0') {
2852 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2854 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2855 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2858 g_free(compose->ml_post);
2859 compose->ml_post = to;
2862 g_free(hentry[H_LIST_POST].body);
2863 hentry[H_LIST_POST].body = NULL;
2866 /* CLAWS - X-Priority */
2867 if (compose->mode == COMPOSE_REEDIT)
2868 if (hentry[H_X_PRIORITY].body != NULL) {
2871 priority = atoi(hentry[H_X_PRIORITY].body);
2872 g_free(hentry[H_X_PRIORITY].body);
2874 hentry[H_X_PRIORITY].body = NULL;
2876 if (priority < PRIORITY_HIGHEST ||
2877 priority > PRIORITY_LOWEST)
2878 priority = PRIORITY_NORMAL;
2880 compose->priority = priority;
2883 if (compose->mode == COMPOSE_REEDIT) {
2884 if (msginfo->inreplyto && *msginfo->inreplyto)
2885 compose->inreplyto = g_strdup(msginfo->inreplyto);
2889 if (msginfo->msgid && *msginfo->msgid)
2890 compose->inreplyto = g_strdup(msginfo->msgid);
2892 if (!compose->references) {
2893 if (msginfo->msgid && *msginfo->msgid) {
2894 if (msginfo->inreplyto && *msginfo->inreplyto)
2895 compose->references =
2896 g_strdup_printf("<%s>\n\t<%s>",
2900 compose->references =
2901 g_strconcat("<", msginfo->msgid, ">",
2903 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2904 compose->references =
2905 g_strconcat("<", msginfo->inreplyto, ">",
2913 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2918 cm_return_val_if_fail(msginfo != NULL, -1);
2920 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2921 procheader_get_header_fields(fp, entries);
2925 while (he != NULL && he->name != NULL) {
2927 GtkListStore *model = NULL;
2929 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2930 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2931 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2932 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2933 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2940 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2942 GSList *ref_id_list, *cur;
2946 ref_id_list = references_list_append(NULL, ref);
2947 if (!ref_id_list) return NULL;
2948 if (msgid && *msgid)
2949 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2954 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2955 /* "<" + Message-ID + ">" + CR+LF+TAB */
2956 len += strlen((gchar *)cur->data) + 5;
2958 if (len > MAX_REFERENCES_LEN) {
2959 /* remove second message-ID */
2960 if (ref_id_list && ref_id_list->next &&
2961 ref_id_list->next->next) {
2962 g_free(ref_id_list->next->data);
2963 ref_id_list = g_slist_remove
2964 (ref_id_list, ref_id_list->next->data);
2966 slist_free_strings(ref_id_list);
2967 g_slist_free(ref_id_list);
2974 new_ref = g_string_new("");
2975 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2976 if (new_ref->len > 0)
2977 g_string_append(new_ref, "\n\t");
2978 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2981 slist_free_strings(ref_id_list);
2982 g_slist_free(ref_id_list);
2984 new_ref_str = new_ref->str;
2985 g_string_free(new_ref, FALSE);
2990 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2991 const gchar *fmt, const gchar *qmark,
2992 const gchar *body, gboolean rewrap,
2993 gboolean need_unescape,
2994 const gchar *err_msg)
2996 MsgInfo* dummyinfo = NULL;
2997 gchar *quote_str = NULL;
2999 gboolean prev_autowrap;
3000 const gchar *trimmed_body = body;
3001 gint cursor_pos = -1;
3002 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3003 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3008 SIGNAL_BLOCK(buffer);
3011 dummyinfo = compose_msginfo_new_from_compose(compose);
3012 msginfo = dummyinfo;
3015 if (qmark != NULL) {
3017 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3018 compose->gtkaspell);
3020 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3022 quote_fmt_scan_string(qmark);
3025 buf = quote_fmt_get_buffer();
3027 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3029 Xstrdup_a(quote_str, buf, goto error)
3032 if (fmt && *fmt != '\0') {
3035 while (*trimmed_body == '\n')
3039 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3040 compose->gtkaspell);
3042 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3044 if (need_unescape) {
3047 /* decode \-escape sequences in the internal representation of the quote format */
3048 tmp = g_malloc(strlen(fmt)+1);
3049 pref_get_unescaped_pref(tmp, fmt);
3050 quote_fmt_scan_string(tmp);
3054 quote_fmt_scan_string(fmt);
3058 buf = quote_fmt_get_buffer();
3060 gint line = quote_fmt_get_line();
3061 alertpanel_error(err_msg, line);
3067 prev_autowrap = compose->autowrap;
3068 compose->autowrap = FALSE;
3070 mark = gtk_text_buffer_get_insert(buffer);
3071 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3072 if (g_utf8_validate(buf, -1, NULL)) {
3073 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3075 gchar *tmpout = NULL;
3076 tmpout = conv_codeset_strdup
3077 (buf, conv_get_locale_charset_str_no_utf8(),
3079 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3081 tmpout = g_malloc(strlen(buf)*2+1);
3082 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3084 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3088 cursor_pos = quote_fmt_get_cursor_pos();
3089 if (cursor_pos == -1)
3090 cursor_pos = gtk_text_iter_get_offset(&iter);
3091 compose->set_cursor_pos = cursor_pos;
3093 gtk_text_buffer_get_start_iter(buffer, &iter);
3094 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3095 gtk_text_buffer_place_cursor(buffer, &iter);
3097 compose->autowrap = prev_autowrap;
3098 if (compose->autowrap && rewrap)
3099 compose_wrap_all(compose);
3106 SIGNAL_UNBLOCK(buffer);
3108 procmsg_msginfo_free( dummyinfo );
3113 /* if ml_post is of type addr@host and from is of type
3114 * addr-anything@host, return TRUE
3116 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3118 gchar *left_ml = NULL;
3119 gchar *right_ml = NULL;
3120 gchar *left_from = NULL;
3121 gchar *right_from = NULL;
3122 gboolean result = FALSE;
3124 if (!ml_post || !from)
3127 left_ml = g_strdup(ml_post);
3128 if (strstr(left_ml, "@")) {
3129 right_ml = strstr(left_ml, "@")+1;
3130 *(strstr(left_ml, "@")) = '\0';
3133 left_from = g_strdup(from);
3134 if (strstr(left_from, "@")) {
3135 right_from = strstr(left_from, "@")+1;
3136 *(strstr(left_from, "@")) = '\0';
3139 if (left_ml && left_from && right_ml && right_from
3140 && !strncmp(left_from, left_ml, strlen(left_ml))
3141 && !strcmp(right_from, right_ml)) {
3150 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3151 gboolean respect_default_to)
3155 if (!folder || !folder->prefs)
3158 if (respect_default_to && folder->prefs->enable_default_to) {
3159 compose_entry_append(compose, folder->prefs->default_to,
3160 COMPOSE_TO, PREF_FOLDER);
3161 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3163 if (folder->prefs->enable_default_cc)
3164 compose_entry_append(compose, folder->prefs->default_cc,
3165 COMPOSE_CC, PREF_FOLDER);
3166 if (folder->prefs->enable_default_bcc)
3167 compose_entry_append(compose, folder->prefs->default_bcc,
3168 COMPOSE_BCC, PREF_FOLDER);
3169 if (folder->prefs->enable_default_replyto)
3170 compose_entry_append(compose, folder->prefs->default_replyto,
3171 COMPOSE_REPLYTO, PREF_FOLDER);
3174 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3179 if (!compose || !msginfo)
3182 if (msginfo->subject && *msginfo->subject) {
3183 buf = p = g_strdup(msginfo->subject);
3184 p += subject_get_prefix_length(p);
3185 memmove(buf, p, strlen(p) + 1);
3187 buf2 = g_strdup_printf("Re: %s", buf);
3188 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3193 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3196 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3197 gboolean to_all, gboolean to_ml,
3199 gboolean followup_and_reply_to)
3201 GSList *cc_list = NULL;
3204 gchar *replyto = NULL;
3205 gchar *ac_email = NULL;
3207 gboolean reply_to_ml = FALSE;
3208 gboolean default_reply_to = FALSE;
3210 cm_return_if_fail(compose->account != NULL);
3211 cm_return_if_fail(msginfo != NULL);
3213 reply_to_ml = to_ml && compose->ml_post;
3215 default_reply_to = msginfo->folder &&
3216 msginfo->folder->prefs->enable_default_reply_to;
3218 if (compose->account->protocol != A_NNTP) {
3219 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3221 if (reply_to_ml && !default_reply_to) {
3223 gboolean is_subscr = is_subscription(compose->ml_post,
3226 /* normal answer to ml post with a reply-to */
3227 compose_entry_append(compose,
3229 COMPOSE_TO, PREF_ML);
3230 if (compose->replyto)
3231 compose_entry_append(compose,
3233 COMPOSE_CC, PREF_ML);
3235 /* answer to subscription confirmation */
3236 if (compose->replyto)
3237 compose_entry_append(compose,
3239 COMPOSE_TO, PREF_ML);
3240 else if (msginfo->from)
3241 compose_entry_append(compose,
3243 COMPOSE_TO, PREF_ML);
3246 else if (!(to_all || to_sender) && default_reply_to) {
3247 compose_entry_append(compose,
3248 msginfo->folder->prefs->default_reply_to,
3249 COMPOSE_TO, PREF_FOLDER);
3250 compose_entry_mark_default_to(compose,
3251 msginfo->folder->prefs->default_reply_to);
3256 Xstrdup_a(tmp1, msginfo->from, return);
3257 extract_address(tmp1);
3258 if (to_all || to_sender ||
3259 !account_find_from_address(tmp1, FALSE))
3260 compose_entry_append(compose,
3261 (compose->replyto && !to_sender)
3262 ? compose->replyto :
3263 msginfo->from ? msginfo->from : "",
3264 COMPOSE_TO, PREF_NONE);
3265 else if (!to_all && !to_sender) {
3266 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3267 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3268 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3269 if (compose->replyto) {
3270 compose_entry_append(compose,
3272 COMPOSE_TO, PREF_NONE);
3274 compose_entry_append(compose,
3275 msginfo->from ? msginfo->from : "",
3276 COMPOSE_TO, PREF_NONE);
3279 /* replying to own mail, use original recp */
3280 compose_entry_append(compose,
3281 msginfo->to ? msginfo->to : "",
3282 COMPOSE_TO, PREF_NONE);
3283 compose_entry_append(compose,
3284 msginfo->cc ? msginfo->cc : "",
3285 COMPOSE_CC, PREF_NONE);
3290 if (to_sender || (compose->followup_to &&
3291 !strncmp(compose->followup_to, "poster", 6)))
3292 compose_entry_append
3294 (compose->replyto ? compose->replyto :
3295 msginfo->from ? msginfo->from : ""),
3296 COMPOSE_TO, PREF_NONE);
3298 else if (followup_and_reply_to || to_all) {
3299 compose_entry_append
3301 (compose->replyto ? compose->replyto :
3302 msginfo->from ? msginfo->from : ""),
3303 COMPOSE_TO, PREF_NONE);
3305 compose_entry_append
3307 compose->followup_to ? compose->followup_to :
3308 compose->newsgroups ? compose->newsgroups : "",
3309 COMPOSE_NEWSGROUPS, PREF_NONE);
3312 compose_entry_append
3314 compose->followup_to ? compose->followup_to :
3315 compose->newsgroups ? compose->newsgroups : "",
3316 COMPOSE_NEWSGROUPS, PREF_NONE);
3318 compose_reply_set_subject(compose, msginfo);
3320 if (to_ml && compose->ml_post) return;
3321 if (!to_all || compose->account->protocol == A_NNTP) return;
3323 if (compose->replyto) {
3324 Xstrdup_a(replyto, compose->replyto, return);
3325 extract_address(replyto);
3327 if (msginfo->from) {
3328 Xstrdup_a(from, msginfo->from, return);
3329 extract_address(from);
3332 if (replyto && from)
3333 cc_list = address_list_append_with_comments(cc_list, from);
3334 if (to_all && msginfo->folder &&
3335 msginfo->folder->prefs->enable_default_reply_to)
3336 cc_list = address_list_append_with_comments(cc_list,
3337 msginfo->folder->prefs->default_reply_to);
3338 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3339 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3341 ac_email = g_utf8_strdown(compose->account->address, -1);
3344 for (cur = cc_list; cur != NULL; cur = cur->next) {
3345 gchar *addr = g_utf8_strdown(cur->data, -1);
3346 extract_address(addr);
3348 if (strcmp(ac_email, addr))
3349 compose_entry_append(compose, (gchar *)cur->data,
3350 COMPOSE_CC, PREF_NONE);
3352 debug_print("Cc address same as compose account's, ignoring\n");
3357 slist_free_strings(cc_list);
3358 g_slist_free(cc_list);
3364 #define SET_ENTRY(entry, str) \
3367 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3370 #define SET_ADDRESS(type, str) \
3373 compose_entry_append(compose, str, type, PREF_NONE); \
3376 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3378 cm_return_if_fail(msginfo != NULL);
3380 SET_ENTRY(subject_entry, msginfo->subject);
3381 SET_ENTRY(from_name, msginfo->from);
3382 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3383 SET_ADDRESS(COMPOSE_CC, compose->cc);
3384 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3385 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3386 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3387 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3389 compose_update_priority_menu_item(compose);
3390 compose_update_privacy_system_menu_item(compose, FALSE);
3391 compose_show_first_last_header(compose, TRUE);
3397 static void compose_insert_sig(Compose *compose, gboolean replace)
3399 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3400 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3402 GtkTextIter iter, iter_end;
3403 gint cur_pos, ins_pos;
3404 gboolean prev_autowrap;
3405 gboolean found = FALSE;
3406 gboolean exists = FALSE;
3408 cm_return_if_fail(compose->account != NULL);
3412 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3413 G_CALLBACK(compose_changed_cb),
3416 mark = gtk_text_buffer_get_insert(buffer);
3417 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3418 cur_pos = gtk_text_iter_get_offset (&iter);
3421 gtk_text_buffer_get_end_iter(buffer, &iter);
3423 exists = (compose->sig_str != NULL);
3426 GtkTextIter first_iter, start_iter, end_iter;
3428 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3430 if (!exists || compose->sig_str[0] == '\0')
3433 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3434 compose->signature_tag);
3437 /* include previous \n\n */
3438 gtk_text_iter_backward_chars(&first_iter, 1);
3439 start_iter = first_iter;
3440 end_iter = first_iter;
3442 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3443 compose->signature_tag);
3444 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3445 compose->signature_tag);
3447 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3453 g_free(compose->sig_str);
3454 compose->sig_str = account_get_signature_str(compose->account);
3456 cur_pos = gtk_text_iter_get_offset(&iter);
3458 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3459 g_free(compose->sig_str);
3460 compose->sig_str = NULL;
3462 if (compose->sig_inserted == FALSE)
3463 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3464 compose->sig_inserted = TRUE;
3466 cur_pos = gtk_text_iter_get_offset(&iter);
3467 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3469 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3470 gtk_text_iter_forward_chars(&iter, 1);
3471 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3472 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3474 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3475 cur_pos = gtk_text_buffer_get_char_count (buffer);
3478 /* put the cursor where it should be
3479 * either where the quote_fmt says, either where it was */
3480 if (compose->set_cursor_pos < 0)
3481 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3483 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3484 compose->set_cursor_pos);
3486 compose->set_cursor_pos = -1;
3487 gtk_text_buffer_place_cursor(buffer, &iter);
3488 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3489 G_CALLBACK(compose_changed_cb),
3495 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3498 GtkTextBuffer *buffer;
3501 const gchar *cur_encoding;
3502 gchar buf[BUFFSIZE];
3505 gboolean prev_autowrap;
3506 gboolean badtxt = FALSE;
3507 struct stat file_stat;
3510 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3512 /* get the size of the file we are about to insert */
3513 ret = g_stat(file, &file_stat);
3515 gchar *shortfile = g_path_get_basename(file);
3516 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3518 return COMPOSE_INSERT_NO_FILE;
3519 } else if (prefs_common.warn_large_insert == TRUE) {
3521 /* ask user for confirmation if the file is large */
3522 if (prefs_common.warn_large_insert_size < 0 ||
3523 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3527 msg = g_strdup_printf(_("You are about to insert a file of %s "
3528 "in the message body. Are you sure you want to do that?"),
3529 to_human_readable(file_stat.st_size));
3530 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3531 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3534 /* do we ask for confirmation next time? */
3535 if (aval & G_ALERTDISABLE) {
3536 /* no confirmation next time, disable feature in preferences */
3537 aval &= ~G_ALERTDISABLE;
3538 prefs_common.warn_large_insert = FALSE;
3541 /* abort file insertion if user canceled action */
3542 if (aval != G_ALERTALTERNATE) {
3543 return COMPOSE_INSERT_NO_FILE;
3549 if ((fp = g_fopen(file, "rb")) == NULL) {
3550 FILE_OP_ERROR(file, "fopen");
3551 return COMPOSE_INSERT_READ_ERROR;
3554 prev_autowrap = compose->autowrap;
3555 compose->autowrap = FALSE;
3557 text = GTK_TEXT_VIEW(compose->text);
3558 buffer = gtk_text_view_get_buffer(text);
3559 mark = gtk_text_buffer_get_insert(buffer);
3560 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3562 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3563 G_CALLBACK(text_inserted),
3566 cur_encoding = conv_get_locale_charset_str_no_utf8();
3568 while (fgets(buf, sizeof(buf), fp) != NULL) {
3571 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3572 str = g_strdup(buf);
3574 str = conv_codeset_strdup
3575 (buf, cur_encoding, CS_INTERNAL);
3578 /* strip <CR> if DOS/Windows file,
3579 replace <CR> with <LF> if Macintosh file. */
3582 if (len > 0 && str[len - 1] != '\n') {
3584 if (str[len] == '\r') str[len] = '\n';
3587 gtk_text_buffer_insert(buffer, &iter, str, -1);
3591 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3592 G_CALLBACK(text_inserted),
3594 compose->autowrap = prev_autowrap;
3595 if (compose->autowrap)
3596 compose_wrap_all(compose);
3601 return COMPOSE_INSERT_INVALID_CHARACTER;
3603 return COMPOSE_INSERT_SUCCESS;
3606 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3607 const gchar *filename,
3608 const gchar *content_type,
3609 const gchar *charset)
3617 GtkListStore *store;
3619 gboolean has_binary = FALSE;
3621 if (!is_file_exist(file)) {
3622 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3623 gboolean result = FALSE;
3624 if (file_from_uri && is_file_exist(file_from_uri)) {
3625 result = compose_attach_append(
3626 compose, file_from_uri,
3627 filename, content_type,
3630 g_free(file_from_uri);
3633 alertpanel_error("File %s doesn't exist\n", filename);
3636 if ((size = get_file_size(file)) < 0) {
3637 alertpanel_error("Can't get file size of %s\n", filename);
3641 alertpanel_error(_("File %s is empty."), filename);
3644 if ((fp = g_fopen(file, "rb")) == NULL) {
3645 alertpanel_error(_("Can't read %s."), filename);
3650 ainfo = g_new0(AttachInfo, 1);
3651 auto_ainfo = g_auto_pointer_new_with_free
3652 (ainfo, (GFreeFunc) compose_attach_info_free);
3653 ainfo->file = g_strdup(file);
3656 ainfo->content_type = g_strdup(content_type);
3657 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3659 MsgFlags flags = {0, 0};
3661 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3662 ainfo->encoding = ENC_7BIT;
3664 ainfo->encoding = ENC_8BIT;
3666 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3667 if (msginfo && msginfo->subject)
3668 name = g_strdup(msginfo->subject);
3670 name = g_path_get_basename(filename ? filename : file);
3672 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3674 procmsg_msginfo_free(msginfo);
3676 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3677 ainfo->charset = g_strdup(charset);
3678 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3680 ainfo->encoding = ENC_BASE64;
3682 name = g_path_get_basename(filename ? filename : file);
3683 ainfo->name = g_strdup(name);
3687 ainfo->content_type = procmime_get_mime_type(file);
3688 if (!ainfo->content_type) {
3689 ainfo->content_type =
3690 g_strdup("application/octet-stream");
3691 ainfo->encoding = ENC_BASE64;
3692 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3694 procmime_get_encoding_for_text_file(file, &has_binary);
3696 ainfo->encoding = ENC_BASE64;
3697 name = g_path_get_basename(filename ? filename : file);
3698 ainfo->name = g_strdup(name);
3702 if (ainfo->name != NULL
3703 && !strcmp(ainfo->name, ".")) {
3704 g_free(ainfo->name);
3708 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3709 g_free(ainfo->content_type);
3710 ainfo->content_type = g_strdup("application/octet-stream");
3711 g_free(ainfo->charset);
3712 ainfo->charset = NULL;
3715 ainfo->size = (goffset)size;
3716 size_text = to_human_readable((goffset)size);
3718 store = GTK_LIST_STORE(gtk_tree_view_get_model
3719 (GTK_TREE_VIEW(compose->attach_clist)));
3721 gtk_list_store_append(store, &iter);
3722 gtk_list_store_set(store, &iter,
3723 COL_MIMETYPE, ainfo->content_type,
3724 COL_SIZE, size_text,
3725 COL_NAME, ainfo->name,
3726 COL_CHARSET, ainfo->charset,
3728 COL_AUTODATA, auto_ainfo,
3731 g_auto_pointer_free(auto_ainfo);
3732 compose_attach_update_label(compose);
3736 static void compose_use_signing(Compose *compose, gboolean use_signing)
3738 compose->use_signing = use_signing;
3739 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3742 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3744 compose->use_encryption = use_encryption;
3745 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3748 #define NEXT_PART_NOT_CHILD(info) \
3750 node = info->node; \
3751 while (node->children) \
3752 node = g_node_last_child(node); \
3753 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3756 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3760 MimeInfo *firsttext = NULL;
3761 MimeInfo *encrypted = NULL;
3764 const gchar *partname = NULL;
3766 mimeinfo = procmime_scan_message(msginfo);
3767 if (!mimeinfo) return;
3769 if (mimeinfo->node->children == NULL) {
3770 procmime_mimeinfo_free_all(mimeinfo);
3774 /* find first content part */
3775 child = (MimeInfo *) mimeinfo->node->children->data;
3776 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3777 child = (MimeInfo *)child->node->children->data;
3780 if (child->type == MIMETYPE_TEXT) {
3782 debug_print("First text part found\n");
3783 } else if (compose->mode == COMPOSE_REEDIT &&
3784 child->type == MIMETYPE_APPLICATION &&
3785 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3786 encrypted = (MimeInfo *)child->node->parent->data;
3789 child = (MimeInfo *) mimeinfo->node->children->data;
3790 while (child != NULL) {
3793 if (child == encrypted) {
3794 /* skip this part of tree */
3795 NEXT_PART_NOT_CHILD(child);
3799 if (child->type == MIMETYPE_MULTIPART) {
3800 /* get the actual content */
3801 child = procmime_mimeinfo_next(child);
3805 if (child == firsttext) {
3806 child = procmime_mimeinfo_next(child);
3810 outfile = procmime_get_tmp_file_name(child);
3811 if ((err = procmime_get_part(outfile, child)) < 0)
3812 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3814 gchar *content_type;
3816 content_type = procmime_get_content_type_str(child->type, child->subtype);
3818 /* if we meet a pgp signature, we don't attach it, but
3819 * we force signing. */
3820 if ((strcmp(content_type, "application/pgp-signature") &&
3821 strcmp(content_type, "application/pkcs7-signature") &&
3822 strcmp(content_type, "application/x-pkcs7-signature"))
3823 || compose->mode == COMPOSE_REDIRECT) {
3824 partname = procmime_mimeinfo_get_parameter(child, "filename");
3825 if (partname == NULL)
3826 partname = procmime_mimeinfo_get_parameter(child, "name");
3827 if (partname == NULL)
3829 compose_attach_append(compose, outfile,
3830 partname, content_type,
3831 procmime_mimeinfo_get_parameter(child, "charset"));
3833 compose_force_signing(compose, compose->account, NULL);
3835 g_free(content_type);
3838 NEXT_PART_NOT_CHILD(child);
3840 procmime_mimeinfo_free_all(mimeinfo);
3843 #undef NEXT_PART_NOT_CHILD
3848 WAIT_FOR_INDENT_CHAR,
3849 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3852 /* return indent length, we allow:
3853 indent characters followed by indent characters or spaces/tabs,
3854 alphabets and numbers immediately followed by indent characters,
3855 and the repeating sequences of the above
3856 If quote ends with multiple spaces, only the first one is included. */
3857 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3858 const GtkTextIter *start, gint *len)
3860 GtkTextIter iter = *start;
3864 IndentState state = WAIT_FOR_INDENT_CHAR;
3867 gint alnum_count = 0;
3868 gint space_count = 0;
3871 if (prefs_common.quote_chars == NULL) {
3875 while (!gtk_text_iter_ends_line(&iter)) {
3876 wc = gtk_text_iter_get_char(&iter);
3877 if (g_unichar_iswide(wc))
3879 clen = g_unichar_to_utf8(wc, ch);
3883 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3884 is_space = g_unichar_isspace(wc);
3886 if (state == WAIT_FOR_INDENT_CHAR) {
3887 if (!is_indent && !g_unichar_isalnum(wc))
3890 quote_len += alnum_count + space_count + 1;
3891 alnum_count = space_count = 0;
3892 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3895 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3896 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3900 else if (is_indent) {
3901 quote_len += alnum_count + space_count + 1;
3902 alnum_count = space_count = 0;
3905 state = WAIT_FOR_INDENT_CHAR;
3909 gtk_text_iter_forward_char(&iter);
3912 if (quote_len > 0 && space_count > 0)
3918 if (quote_len > 0) {
3920 gtk_text_iter_forward_chars(&iter, quote_len);
3921 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3927 /* return >0 if the line is itemized */
3928 static int compose_itemized_length(GtkTextBuffer *buffer,
3929 const GtkTextIter *start)
3931 GtkTextIter iter = *start;
3936 if (gtk_text_iter_ends_line(&iter))
3941 wc = gtk_text_iter_get_char(&iter);
3942 if (!g_unichar_isspace(wc))
3944 gtk_text_iter_forward_char(&iter);
3945 if (gtk_text_iter_ends_line(&iter))
3949 clen = g_unichar_to_utf8(wc, ch);
3953 if (!strchr("*-+", ch[0]))
3956 gtk_text_iter_forward_char(&iter);
3957 if (gtk_text_iter_ends_line(&iter))
3959 wc = gtk_text_iter_get_char(&iter);
3960 if (g_unichar_isspace(wc)) {
3966 /* return the string at the start of the itemization */
3967 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3968 const GtkTextIter *start)
3970 GtkTextIter iter = *start;
3973 GString *item_chars = g_string_new("");
3976 if (gtk_text_iter_ends_line(&iter))
3981 wc = gtk_text_iter_get_char(&iter);
3982 if (!g_unichar_isspace(wc))
3984 gtk_text_iter_forward_char(&iter);
3985 if (gtk_text_iter_ends_line(&iter))
3987 g_string_append_unichar(item_chars, wc);
3990 str = item_chars->str;
3991 g_string_free(item_chars, FALSE);
3995 /* return the number of spaces at a line's start */
3996 static int compose_left_offset_length(GtkTextBuffer *buffer,
3997 const GtkTextIter *start)
3999 GtkTextIter iter = *start;
4002 if (gtk_text_iter_ends_line(&iter))
4006 wc = gtk_text_iter_get_char(&iter);
4007 if (!g_unichar_isspace(wc))
4010 gtk_text_iter_forward_char(&iter);
4011 if (gtk_text_iter_ends_line(&iter))
4015 gtk_text_iter_forward_char(&iter);
4016 if (gtk_text_iter_ends_line(&iter))
4021 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4022 const GtkTextIter *start,
4023 GtkTextIter *break_pos,
4027 GtkTextIter iter = *start, line_end = *start;
4028 PangoLogAttr *attrs;
4035 gboolean can_break = FALSE;
4036 gboolean do_break = FALSE;
4037 gboolean was_white = FALSE;
4038 gboolean prev_dont_break = FALSE;
4040 gtk_text_iter_forward_to_line_end(&line_end);
4041 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4042 len = g_utf8_strlen(str, -1);
4046 g_warning("compose_get_line_break_pos: len = 0!\n");
4050 /* g_print("breaking line: %d: %s (len = %d)\n",
4051 gtk_text_iter_get_line(&iter), str, len); */
4053 attrs = g_new(PangoLogAttr, len + 1);
4055 pango_default_break(str, -1, NULL, attrs, len + 1);
4059 /* skip quote and leading spaces */
4060 for (i = 0; *p != '\0' && i < len; i++) {
4063 wc = g_utf8_get_char(p);
4064 if (i >= quote_len && !g_unichar_isspace(wc))
4066 if (g_unichar_iswide(wc))
4068 else if (*p == '\t')
4072 p = g_utf8_next_char(p);
4075 for (; *p != '\0' && i < len; i++) {
4076 PangoLogAttr *attr = attrs + i;
4080 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4083 was_white = attr->is_white;
4085 /* don't wrap URI */
4086 if ((uri_len = get_uri_len(p)) > 0) {
4088 if (pos > 0 && col > max_col) {
4098 wc = g_utf8_get_char(p);
4099 if (g_unichar_iswide(wc)) {
4101 if (prev_dont_break && can_break && attr->is_line_break)
4103 } else if (*p == '\t')
4107 if (pos > 0 && col > max_col) {
4112 if (*p == '-' || *p == '/')
4113 prev_dont_break = TRUE;
4115 prev_dont_break = FALSE;
4117 p = g_utf8_next_char(p);
4121 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4126 *break_pos = *start;
4127 gtk_text_iter_set_line_offset(break_pos, pos);
4132 static gboolean compose_join_next_line(Compose *compose,
4133 GtkTextBuffer *buffer,
4135 const gchar *quote_str)
4137 GtkTextIter iter_ = *iter, cur, prev, next, end;
4138 PangoLogAttr attrs[3];
4140 gchar *next_quote_str;
4143 gboolean keep_cursor = FALSE;
4145 if (!gtk_text_iter_forward_line(&iter_) ||
4146 gtk_text_iter_ends_line(&iter_)) {
4149 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4151 if ((quote_str || next_quote_str) &&
4152 strcmp2(quote_str, next_quote_str) != 0) {
4153 g_free(next_quote_str);
4156 g_free(next_quote_str);
4159 if (quote_len > 0) {
4160 gtk_text_iter_forward_chars(&end, quote_len);
4161 if (gtk_text_iter_ends_line(&end)) {
4166 /* don't join itemized lines */
4167 if (compose_itemized_length(buffer, &end) > 0) {
4171 /* don't join signature separator */
4172 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4175 /* delete quote str */
4177 gtk_text_buffer_delete(buffer, &iter_, &end);
4179 /* don't join line breaks put by the user */
4181 gtk_text_iter_backward_char(&cur);
4182 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4183 gtk_text_iter_forward_char(&cur);
4187 gtk_text_iter_forward_char(&cur);
4188 /* delete linebreak and extra spaces */
4189 while (gtk_text_iter_backward_char(&cur)) {
4190 wc1 = gtk_text_iter_get_char(&cur);
4191 if (!g_unichar_isspace(wc1))
4196 while (!gtk_text_iter_ends_line(&cur)) {
4197 wc1 = gtk_text_iter_get_char(&cur);
4198 if (!g_unichar_isspace(wc1))
4200 gtk_text_iter_forward_char(&cur);
4203 if (!gtk_text_iter_equal(&prev, &next)) {
4206 mark = gtk_text_buffer_get_insert(buffer);
4207 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4208 if (gtk_text_iter_equal(&prev, &cur))
4210 gtk_text_buffer_delete(buffer, &prev, &next);
4214 /* insert space if required */
4215 gtk_text_iter_backward_char(&prev);
4216 wc1 = gtk_text_iter_get_char(&prev);
4217 wc2 = gtk_text_iter_get_char(&next);
4218 gtk_text_iter_forward_char(&next);
4219 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4220 pango_default_break(str, -1, NULL, attrs, 3);
4221 if (!attrs[1].is_line_break ||
4222 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4223 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4225 gtk_text_iter_backward_char(&iter_);
4226 gtk_text_buffer_place_cursor(buffer, &iter_);
4235 #define ADD_TXT_POS(bp_, ep_, pti_) \
4236 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4237 last = last->next; \
4238 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4239 last->next = NULL; \
4241 g_warning("alloc error scanning URIs\n"); \
4244 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4246 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4247 GtkTextBuffer *buffer;
4248 GtkTextIter iter, break_pos, end_of_line;
4249 gchar *quote_str = NULL;
4251 gboolean wrap_quote = prefs_common.linewrap_quote;
4252 gboolean prev_autowrap = compose->autowrap;
4253 gint startq_offset = -1, noq_offset = -1;
4254 gint uri_start = -1, uri_stop = -1;
4255 gint nouri_start = -1, nouri_stop = -1;
4256 gint num_blocks = 0;
4257 gint quotelevel = -1;
4258 gboolean modified = force;
4259 gboolean removed = FALSE;
4260 gboolean modified_before_remove = FALSE;
4262 gboolean start = TRUE;
4263 gint itemized_len = 0, rem_item_len = 0;
4264 gchar *itemized_chars = NULL;
4265 gboolean item_continuation = FALSE;
4270 if (compose->draft_timeout_tag == -2) {
4274 compose->autowrap = FALSE;
4276 buffer = gtk_text_view_get_buffer(text);
4277 undo_wrapping(compose->undostruct, TRUE);
4282 mark = gtk_text_buffer_get_insert(buffer);
4283 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4287 if (compose->draft_timeout_tag == -2) {
4288 if (gtk_text_iter_ends_line(&iter)) {
4289 while (gtk_text_iter_ends_line(&iter) &&
4290 gtk_text_iter_forward_line(&iter))
4293 while (gtk_text_iter_backward_line(&iter)) {
4294 if (gtk_text_iter_ends_line(&iter)) {
4295 gtk_text_iter_forward_line(&iter);
4301 /* move to line start */
4302 gtk_text_iter_set_line_offset(&iter, 0);
4305 itemized_len = compose_itemized_length(buffer, &iter);
4307 if (!itemized_len) {
4308 itemized_len = compose_left_offset_length(buffer, &iter);
4309 item_continuation = TRUE;
4313 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4315 /* go until paragraph end (empty line) */
4316 while (start || !gtk_text_iter_ends_line(&iter)) {
4317 gchar *scanpos = NULL;
4318 /* parse table - in order of priority */
4320 const gchar *needle; /* token */
4322 /* token search function */
4323 gchar *(*search) (const gchar *haystack,
4324 const gchar *needle);
4325 /* part parsing function */
4326 gboolean (*parse) (const gchar *start,
4327 const gchar *scanpos,
4331 /* part to URI function */
4332 gchar *(*build_uri) (const gchar *bp,
4336 static struct table parser[] = {
4337 {"http://", strcasestr, get_uri_part, make_uri_string},
4338 {"https://", strcasestr, get_uri_part, make_uri_string},
4339 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4340 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4341 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4342 {"www.", strcasestr, get_uri_part, make_http_string},
4343 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4344 {"@", strcasestr, get_email_part, make_email_string}
4346 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4347 gint last_index = PARSE_ELEMS;
4349 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4353 if (!prev_autowrap && num_blocks == 0) {
4355 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4356 G_CALLBACK(text_inserted),
4359 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4362 uri_start = uri_stop = -1;
4364 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4367 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4368 if (startq_offset == -1)
4369 startq_offset = gtk_text_iter_get_offset(&iter);
4370 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4371 if (quotelevel > 2) {
4372 /* recycle colors */
4373 if (prefs_common.recycle_quote_colors)
4382 if (startq_offset == -1)
4383 noq_offset = gtk_text_iter_get_offset(&iter);
4387 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4390 if (gtk_text_iter_ends_line(&iter)) {
4392 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4393 prefs_common.linewrap_len,
4395 GtkTextIter prev, next, cur;
4396 if (prev_autowrap != FALSE || force) {
4397 compose->automatic_break = TRUE;
4399 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4400 compose->automatic_break = FALSE;
4401 if (itemized_len && compose->autoindent) {
4402 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4403 if (!item_continuation)
4404 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4406 } else if (quote_str && wrap_quote) {
4407 compose->automatic_break = TRUE;
4409 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4410 compose->automatic_break = FALSE;
4411 if (itemized_len && compose->autoindent) {
4412 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4413 if (!item_continuation)
4414 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4418 /* remove trailing spaces */
4420 rem_item_len = itemized_len;
4421 while (compose->autoindent && rem_item_len-- > 0)
4422 gtk_text_iter_backward_char(&cur);
4423 gtk_text_iter_backward_char(&cur);
4426 while (!gtk_text_iter_starts_line(&cur)) {
4429 gtk_text_iter_backward_char(&cur);
4430 wc = gtk_text_iter_get_char(&cur);
4431 if (!g_unichar_isspace(wc))
4435 if (!gtk_text_iter_equal(&prev, &next)) {
4436 gtk_text_buffer_delete(buffer, &prev, &next);
4438 gtk_text_iter_forward_char(&break_pos);
4442 gtk_text_buffer_insert(buffer, &break_pos,
4446 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4448 /* move iter to current line start */
4449 gtk_text_iter_set_line_offset(&iter, 0);
4456 /* move iter to next line start */
4462 if (!prev_autowrap && num_blocks > 0) {
4464 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4465 G_CALLBACK(text_inserted),
4469 while (!gtk_text_iter_ends_line(&end_of_line)) {
4470 gtk_text_iter_forward_char(&end_of_line);
4472 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4474 nouri_start = gtk_text_iter_get_offset(&iter);
4475 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4477 walk_pos = gtk_text_iter_get_offset(&iter);
4478 /* FIXME: this looks phony. scanning for anything in the parse table */
4479 for (n = 0; n < PARSE_ELEMS; n++) {
4482 tmp = parser[n].search(walk, parser[n].needle);
4484 if (scanpos == NULL || tmp < scanpos) {
4493 /* check if URI can be parsed */
4494 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4495 (const gchar **)&ep, FALSE)
4496 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4500 strlen(parser[last_index].needle);
4503 uri_start = walk_pos + (bp - o_walk);
4504 uri_stop = walk_pos + (ep - o_walk);
4508 gtk_text_iter_forward_line(&iter);
4511 if (startq_offset != -1) {
4512 GtkTextIter startquote, endquote;
4513 gtk_text_buffer_get_iter_at_offset(
4514 buffer, &startquote, startq_offset);
4517 switch (quotelevel) {
4519 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4520 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4521 gtk_text_buffer_apply_tag_by_name(
4522 buffer, "quote0", &startquote, &endquote);
4523 gtk_text_buffer_remove_tag_by_name(
4524 buffer, "quote1", &startquote, &endquote);
4525 gtk_text_buffer_remove_tag_by_name(
4526 buffer, "quote2", &startquote, &endquote);
4531 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4532 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4533 gtk_text_buffer_apply_tag_by_name(
4534 buffer, "quote1", &startquote, &endquote);
4535 gtk_text_buffer_remove_tag_by_name(
4536 buffer, "quote0", &startquote, &endquote);
4537 gtk_text_buffer_remove_tag_by_name(
4538 buffer, "quote2", &startquote, &endquote);
4543 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4544 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4545 gtk_text_buffer_apply_tag_by_name(
4546 buffer, "quote2", &startquote, &endquote);
4547 gtk_text_buffer_remove_tag_by_name(
4548 buffer, "quote0", &startquote, &endquote);
4549 gtk_text_buffer_remove_tag_by_name(
4550 buffer, "quote1", &startquote, &endquote);
4556 } else if (noq_offset != -1) {
4557 GtkTextIter startnoquote, endnoquote;
4558 gtk_text_buffer_get_iter_at_offset(
4559 buffer, &startnoquote, noq_offset);
4562 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4563 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4564 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4565 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4566 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4567 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4568 gtk_text_buffer_remove_tag_by_name(
4569 buffer, "quote0", &startnoquote, &endnoquote);
4570 gtk_text_buffer_remove_tag_by_name(
4571 buffer, "quote1", &startnoquote, &endnoquote);
4572 gtk_text_buffer_remove_tag_by_name(
4573 buffer, "quote2", &startnoquote, &endnoquote);
4579 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4580 GtkTextIter nouri_start_iter, nouri_end_iter;
4581 gtk_text_buffer_get_iter_at_offset(
4582 buffer, &nouri_start_iter, nouri_start);
4583 gtk_text_buffer_get_iter_at_offset(
4584 buffer, &nouri_end_iter, nouri_stop);
4585 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4586 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4587 gtk_text_buffer_remove_tag_by_name(
4588 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4589 modified_before_remove = modified;
4594 if (uri_start >= 0 && uri_stop > 0) {
4595 GtkTextIter uri_start_iter, uri_end_iter, back;
4596 gtk_text_buffer_get_iter_at_offset(
4597 buffer, &uri_start_iter, uri_start);
4598 gtk_text_buffer_get_iter_at_offset(
4599 buffer, &uri_end_iter, uri_stop);
4600 back = uri_end_iter;
4601 gtk_text_iter_backward_char(&back);
4602 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4603 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4604 gtk_text_buffer_apply_tag_by_name(
4605 buffer, "link", &uri_start_iter, &uri_end_iter);
4607 if (removed && !modified_before_remove) {
4613 // debug_print("not modified, out after %d lines\n", lines);
4617 // debug_print("modified, out after %d lines\n", lines);
4619 g_free(itemized_chars);
4622 undo_wrapping(compose->undostruct, FALSE);
4623 compose->autowrap = prev_autowrap;
4628 void compose_action_cb(void *data)
4630 Compose *compose = (Compose *)data;
4631 compose_wrap_all(compose);
4634 static void compose_wrap_all(Compose *compose)
4636 compose_wrap_all_full(compose, FALSE);
4639 static void compose_wrap_all_full(Compose *compose, gboolean force)
4641 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4642 GtkTextBuffer *buffer;
4644 gboolean modified = TRUE;
4646 buffer = gtk_text_view_get_buffer(text);
4648 gtk_text_buffer_get_start_iter(buffer, &iter);
4649 while (!gtk_text_iter_is_end(&iter) && modified)
4650 modified = compose_beautify_paragraph(compose, &iter, force);
4654 static void compose_set_title(Compose *compose)
4660 edited = compose->modified ? _(" [Edited]") : "";
4662 subject = gtk_editable_get_chars(
4663 GTK_EDITABLE(compose->subject_entry), 0, -1);
4665 #ifndef GENERIC_UMPC
4666 if (subject && strlen(subject))
4667 str = g_strdup_printf(_("%s - Compose message%s"),
4670 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4672 str = g_strdup(_("Compose message"));
4675 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4681 * compose_current_mail_account:
4683 * Find a current mail account (the currently selected account, or the
4684 * default account, if a news account is currently selected). If a
4685 * mail account cannot be found, display an error message.
4687 * Return value: Mail account, or NULL if not found.
4689 static PrefsAccount *
4690 compose_current_mail_account(void)
4694 if (cur_account && cur_account->protocol != A_NNTP)
4697 ac = account_get_default();
4698 if (!ac || ac->protocol == A_NNTP) {
4699 alertpanel_error(_("Account for sending mail is not specified.\n"
4700 "Please select a mail account before sending."));
4707 #define QUOTE_IF_REQUIRED(out, str) \
4709 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4713 len = strlen(str) + 3; \
4714 if ((__tmp = alloca(len)) == NULL) { \
4715 g_warning("can't allocate memory\n"); \
4716 g_string_free(header, TRUE); \
4719 g_snprintf(__tmp, len, "\"%s\"", str); \
4724 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4725 g_warning("can't allocate memory\n"); \
4726 g_string_free(header, TRUE); \
4729 strcpy(__tmp, str); \
4735 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4737 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4741 len = strlen(str) + 3; \
4742 if ((__tmp = alloca(len)) == NULL) { \
4743 g_warning("can't allocate memory\n"); \
4746 g_snprintf(__tmp, len, "\"%s\"", str); \
4751 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4752 g_warning("can't allocate memory\n"); \
4755 strcpy(__tmp, str); \
4761 static void compose_select_account(Compose *compose, PrefsAccount *account,
4764 gchar *from = NULL, *header;
4765 ComposeHeaderEntry *header_entry;
4767 cm_return_if_fail(account != NULL);
4769 compose->account = account;
4770 if (account->name && *account->name) {
4772 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4773 from = g_strdup_printf("%s <%s>",
4774 buf, account->address);
4775 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4777 from = g_strdup_printf("<%s>",
4779 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4784 compose_set_title(compose);
4786 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4787 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4789 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4790 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4791 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4793 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4795 activate_privacy_system(compose, account, FALSE);
4797 if (!init && compose->mode != COMPOSE_REDIRECT) {
4798 undo_block(compose->undostruct);
4799 compose_insert_sig(compose, TRUE);
4800 undo_unblock(compose->undostruct);
4803 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4804 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4806 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4807 if (account->protocol == A_NNTP) {
4808 if (!strcmp(header, _("To:")))
4809 combobox_select_by_text(
4810 GTK_COMBO_BOX(header_entry->combo),
4813 if (!strcmp(header, _("Newsgroups:")))
4814 combobox_select_by_text(
4815 GTK_COMBO_BOX(header_entry->combo),
4823 /* use account's dict info if set */
4824 if (compose->gtkaspell) {
4825 if (account->enable_default_dictionary)
4826 gtkaspell_change_dict(compose->gtkaspell,
4827 account->default_dictionary, FALSE);
4828 if (account->enable_default_alt_dictionary)
4829 gtkaspell_change_alt_dict(compose->gtkaspell,
4830 account->default_alt_dictionary);
4831 if (account->enable_default_dictionary
4832 || account->enable_default_alt_dictionary)
4833 compose_spell_menu_changed(compose);
4838 gboolean compose_check_for_valid_recipient(Compose *compose) {
4839 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4840 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4841 gboolean recipient_found = FALSE;
4845 /* free to and newsgroup list */
4846 slist_free_strings(compose->to_list);
4847 g_slist_free(compose->to_list);
4848 compose->to_list = NULL;
4850 slist_free_strings(compose->newsgroup_list);
4851 g_slist_free(compose->newsgroup_list);
4852 compose->newsgroup_list = NULL;
4854 /* search header entries for to and newsgroup entries */
4855 for (list = compose->header_list; list; list = list->next) {
4858 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4859 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4862 if (entry[0] != '\0') {
4863 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4864 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4865 compose->to_list = address_list_append(compose->to_list, entry);
4866 recipient_found = TRUE;
4869 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4870 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4871 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4872 recipient_found = TRUE;
4879 return recipient_found;
4882 static gboolean compose_check_for_set_recipients(Compose *compose)
4884 if (compose->account->set_autocc && compose->account->auto_cc) {
4885 gboolean found_other = FALSE;
4887 /* search header entries for to and newsgroup entries */
4888 for (list = compose->header_list; list; list = list->next) {
4891 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4892 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4895 if (strcmp(entry, compose->account->auto_cc)
4896 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4906 if (compose->batch) {
4907 gtk_widget_show_all(compose->window);
4909 aval = alertpanel(_("Send"),
4910 _("The only recipient is the default CC address. Send anyway?"),
4911 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4912 if (aval != G_ALERTALTERNATE)
4916 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4917 gboolean found_other = FALSE;
4919 /* search header entries for to and newsgroup entries */
4920 for (list = compose->header_list; list; list = list->next) {
4923 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4924 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4927 if (strcmp(entry, compose->account->auto_bcc)
4928 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4938 if (compose->batch) {
4939 gtk_widget_show_all(compose->window);
4941 aval = alertpanel(_("Send"),
4942 _("The only recipient is the default BCC address. Send anyway?"),
4943 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4944 if (aval != G_ALERTALTERNATE)
4951 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4955 if (compose_check_for_valid_recipient(compose) == FALSE) {
4956 if (compose->batch) {
4957 gtk_widget_show_all(compose->window);
4959 alertpanel_error(_("Recipient is not specified."));
4963 if (compose_check_for_set_recipients(compose) == FALSE) {
4967 if (!compose->batch) {
4968 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4969 if (*str == '\0' && check_everything == TRUE &&
4970 compose->mode != COMPOSE_REDIRECT) {
4972 gchar *button_label;
4975 if (compose->sending)
4976 button_label = _("+_Send");
4978 button_label = _("+_Queue");
4979 message = g_strdup_printf(_("Subject is empty. %s"),
4980 compose->sending?_("Send it anyway?"):
4981 _("Queue it anyway?"));
4983 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4984 GTK_STOCK_CANCEL, button_label, NULL);
4986 if (aval != G_ALERTALTERNATE)
4991 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4997 gint compose_send(Compose *compose)
5000 FolderItem *folder = NULL;
5002 gchar *msgpath = NULL;
5003 gboolean discard_window = FALSE;
5004 gchar *errstr = NULL;
5005 gchar *tmsgid = NULL;
5006 MainWindow *mainwin = mainwindow_get_mainwindow();
5007 gboolean queued_removed = FALSE;
5009 if (prefs_common.send_dialog_invisible
5010 || compose->batch == TRUE)
5011 discard_window = TRUE;
5013 compose_allow_user_actions (compose, FALSE);
5014 compose->sending = TRUE;
5016 if (compose_check_entries(compose, TRUE) == FALSE) {
5017 if (compose->batch) {
5018 gtk_widget_show_all(compose->window);
5024 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5027 if (compose->batch) {
5028 gtk_widget_show_all(compose->window);
5031 alertpanel_error(_("Could not queue message for sending:\n\n"
5032 "Charset conversion failed."));
5033 } else if (val == -5) {
5034 alertpanel_error(_("Could not queue message for sending:\n\n"
5035 "Couldn't get recipient encryption key."));
5036 } else if (val == -6) {
5038 } else if (val == -3) {
5039 if (privacy_peek_error())
5040 alertpanel_error(_("Could not queue message for sending:\n\n"
5041 "Signature failed: %s"), privacy_get_error());
5042 } else if (val == -2 && errno != 0) {
5043 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5045 alertpanel_error(_("Could not queue message for sending."));
5050 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5051 if (discard_window) {
5052 compose->sending = FALSE;
5053 compose_close(compose);
5054 /* No more compose access in the normal codepath
5055 * after this point! */
5060 alertpanel_error(_("The message was queued but could not be "
5061 "sent.\nUse \"Send queued messages\" from "
5062 "the main window to retry."));
5063 if (!discard_window) {
5070 if (msgpath == NULL) {
5071 msgpath = folder_item_fetch_msg(folder, msgnum);
5072 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5075 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5076 claws_unlink(msgpath);
5079 if (!discard_window) {
5081 if (!queued_removed)
5082 folder_item_remove_msg(folder, msgnum);
5083 folder_item_scan(folder);
5085 /* make sure we delete that */
5086 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5088 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5089 folder_item_remove_msg(folder, tmp->msgnum);
5090 procmsg_msginfo_free(tmp);
5097 if (!queued_removed)
5098 folder_item_remove_msg(folder, msgnum);
5099 folder_item_scan(folder);
5101 /* make sure we delete that */
5102 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5104 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5105 folder_item_remove_msg(folder, tmp->msgnum);
5106 procmsg_msginfo_free(tmp);
5109 if (!discard_window) {
5110 compose->sending = FALSE;
5111 compose_allow_user_actions (compose, TRUE);
5112 compose_close(compose);
5116 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5117 "the main window to retry."), errstr);
5120 alertpanel_error_log(_("The message was queued but could not be "
5121 "sent.\nUse \"Send queued messages\" from "
5122 "the main window to retry."));
5124 if (!discard_window) {
5133 toolbar_main_set_sensitive(mainwin);
5134 main_window_set_menu_sensitive(mainwin);
5140 compose_allow_user_actions (compose, TRUE);
5141 compose->sending = FALSE;
5142 compose->modified = TRUE;
5143 toolbar_main_set_sensitive(mainwin);
5144 main_window_set_menu_sensitive(mainwin);
5149 static gboolean compose_use_attach(Compose *compose)
5151 GtkTreeModel *model = gtk_tree_view_get_model
5152 (GTK_TREE_VIEW(compose->attach_clist));
5153 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5156 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5159 gchar buf[BUFFSIZE];
5161 gboolean first_to_address;
5162 gboolean first_cc_address;
5164 ComposeHeaderEntry *headerentry;
5165 const gchar *headerentryname;
5166 const gchar *cc_hdr;
5167 const gchar *to_hdr;
5168 gboolean err = FALSE;
5170 debug_print("Writing redirect header\n");
5172 cc_hdr = prefs_common_translated_header_name("Cc:");
5173 to_hdr = prefs_common_translated_header_name("To:");
5175 first_to_address = TRUE;
5176 for (list = compose->header_list; list; list = list->next) {
5177 headerentry = ((ComposeHeaderEntry *)list->data);
5178 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5180 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5181 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5182 Xstrdup_a(str, entstr, return -1);
5184 if (str[0] != '\0') {
5185 compose_convert_header
5186 (compose, buf, sizeof(buf), str,
5187 strlen("Resent-To") + 2, TRUE);
5189 if (first_to_address) {
5190 err |= (fprintf(fp, "Resent-To: ") < 0);
5191 first_to_address = FALSE;
5193 err |= (fprintf(fp, ",") < 0);
5195 err |= (fprintf(fp, "%s", buf) < 0);
5199 if (!first_to_address) {
5200 err |= (fprintf(fp, "\n") < 0);
5203 first_cc_address = TRUE;
5204 for (list = compose->header_list; list; list = list->next) {
5205 headerentry = ((ComposeHeaderEntry *)list->data);
5206 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5208 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5209 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5210 Xstrdup_a(str, strg, return -1);
5212 if (str[0] != '\0') {
5213 compose_convert_header
5214 (compose, buf, sizeof(buf), str,
5215 strlen("Resent-Cc") + 2, TRUE);
5217 if (first_cc_address) {
5218 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5219 first_cc_address = FALSE;
5221 err |= (fprintf(fp, ",") < 0);
5223 err |= (fprintf(fp, "%s", buf) < 0);
5227 if (!first_cc_address) {
5228 err |= (fprintf(fp, "\n") < 0);
5231 return (err ? -1:0);
5234 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5236 gchar buf[BUFFSIZE];
5238 const gchar *entstr;
5239 /* struct utsname utsbuf; */
5240 gboolean err = FALSE;
5242 cm_return_val_if_fail(fp != NULL, -1);
5243 cm_return_val_if_fail(compose->account != NULL, -1);
5244 cm_return_val_if_fail(compose->account->address != NULL, -1);
5247 get_rfc822_date(buf, sizeof(buf));
5248 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5251 if (compose->account->name && *compose->account->name) {
5252 compose_convert_header
5253 (compose, buf, sizeof(buf), compose->account->name,
5254 strlen("From: "), TRUE);
5255 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5256 buf, compose->account->address) < 0);
5258 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5261 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5262 if (*entstr != '\0') {
5263 Xstrdup_a(str, entstr, return -1);
5266 compose_convert_header(compose, buf, sizeof(buf), str,
5267 strlen("Subject: "), FALSE);
5268 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5272 /* Resent-Message-ID */
5273 if (compose->account->set_domain && compose->account->domain) {
5274 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5275 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5276 g_snprintf(buf, sizeof(buf), "%s",
5277 strchr(compose->account->address, '@') ?
5278 strchr(compose->account->address, '@')+1 :
5279 compose->account->address);
5281 g_snprintf(buf, sizeof(buf), "%s", "");
5284 if (compose->account->gen_msgid) {
5286 if (compose->account->msgid_with_addr) {
5287 addr = compose->account->address;
5289 generate_msgid(buf, sizeof(buf), addr);
5290 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5291 compose->msgid = g_strdup(buf);
5293 compose->msgid = NULL;
5296 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5299 /* separator between header and body */
5300 err |= (fputs("\n", fp) == EOF);
5302 return (err ? -1:0);
5305 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5309 gchar buf[BUFFSIZE];
5311 gboolean skip = FALSE;
5312 gboolean err = FALSE;
5313 gchar *not_included[]={
5314 "Return-Path:", "Delivered-To:", "Received:",
5315 "Subject:", "X-UIDL:", "AF:",
5316 "NF:", "PS:", "SRH:",
5317 "SFN:", "DSR:", "MID:",
5318 "CFG:", "PT:", "S:",
5319 "RQ:", "SSV:", "NSV:",
5320 "SSH:", "R:", "MAID:",
5321 "NAID:", "RMID:", "FMID:",
5322 "SCF:", "RRCPT:", "NG:",
5323 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5324 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5325 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5326 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5327 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5330 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5331 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5335 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5337 for (i = 0; not_included[i] != NULL; i++) {
5338 if (g_ascii_strncasecmp(buf, not_included[i],
5339 strlen(not_included[i])) == 0) {
5346 if (fputs(buf, fdest) == -1)
5349 if (!prefs_common.redirect_keep_from) {
5350 if (g_ascii_strncasecmp(buf, "From:",
5351 strlen("From:")) == 0) {
5352 err |= (fputs(" (by way of ", fdest) == EOF);
5353 if (compose->account->name
5354 && *compose->account->name) {
5355 compose_convert_header
5356 (compose, buf, sizeof(buf),
5357 compose->account->name,
5360 err |= (fprintf(fdest, "%s <%s>",
5362 compose->account->address) < 0);
5364 err |= (fprintf(fdest, "%s",
5365 compose->account->address) < 0);
5366 err |= (fputs(")", fdest) == EOF);
5370 if (fputs("\n", fdest) == -1)
5377 if (compose_redirect_write_headers(compose, fdest))
5380 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5381 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5394 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5396 GtkTextBuffer *buffer;
5397 GtkTextIter start, end;
5400 const gchar *out_codeset;
5401 EncodingType encoding = ENC_UNKNOWN;
5402 MimeInfo *mimemsg, *mimetext;
5404 const gchar *src_codeset = CS_INTERNAL;
5405 gchar *from_addr = NULL;
5406 gchar *from_name = NULL;
5408 if (action == COMPOSE_WRITE_FOR_SEND)
5409 attach_parts = TRUE;
5411 /* create message MimeInfo */
5412 mimemsg = procmime_mimeinfo_new();
5413 mimemsg->type = MIMETYPE_MESSAGE;
5414 mimemsg->subtype = g_strdup("rfc822");
5415 mimemsg->content = MIMECONTENT_MEM;
5416 mimemsg->tmp = TRUE; /* must free content later */
5417 mimemsg->data.mem = compose_get_header(compose);
5419 /* Create text part MimeInfo */
5420 /* get all composed text */
5421 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5422 gtk_text_buffer_get_start_iter(buffer, &start);
5423 gtk_text_buffer_get_end_iter(buffer, &end);
5424 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5426 out_codeset = conv_get_charset_str(compose->out_encoding);
5428 if (!out_codeset && is_ascii_str(chars)) {
5429 out_codeset = CS_US_ASCII;
5430 } else if (prefs_common.outgoing_fallback_to_ascii &&
5431 is_ascii_str(chars)) {
5432 out_codeset = CS_US_ASCII;
5433 encoding = ENC_7BIT;
5437 gchar *test_conv_global_out = NULL;
5438 gchar *test_conv_reply = NULL;
5440 /* automatic mode. be automatic. */
5441 codeconv_set_strict(TRUE);
5443 out_codeset = conv_get_outgoing_charset_str();
5445 debug_print("trying to convert to %s\n", out_codeset);
5446 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5449 if (!test_conv_global_out && compose->orig_charset
5450 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5451 out_codeset = compose->orig_charset;
5452 debug_print("failure; trying to convert to %s\n", out_codeset);
5453 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5456 if (!test_conv_global_out && !test_conv_reply) {
5458 out_codeset = CS_INTERNAL;
5459 debug_print("failure; finally using %s\n", out_codeset);
5461 g_free(test_conv_global_out);
5462 g_free(test_conv_reply);
5463 codeconv_set_strict(FALSE);
5466 if (encoding == ENC_UNKNOWN) {
5467 if (prefs_common.encoding_method == CTE_BASE64)
5468 encoding = ENC_BASE64;
5469 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5470 encoding = ENC_QUOTED_PRINTABLE;
5471 else if (prefs_common.encoding_method == CTE_8BIT)
5472 encoding = ENC_8BIT;
5474 encoding = procmime_get_encoding_for_charset(out_codeset);
5477 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5478 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5480 if (action == COMPOSE_WRITE_FOR_SEND) {
5481 codeconv_set_strict(TRUE);
5482 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5483 codeconv_set_strict(FALSE);
5489 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5490 "to the specified %s charset.\n"
5491 "Send it as %s?"), out_codeset, src_codeset);
5492 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5493 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5496 if (aval != G_ALERTALTERNATE) {
5501 out_codeset = src_codeset;
5507 out_codeset = src_codeset;
5512 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5513 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5514 strstr(buf, "\nFrom ") != NULL) {
5515 encoding = ENC_QUOTED_PRINTABLE;
5519 mimetext = procmime_mimeinfo_new();
5520 mimetext->content = MIMECONTENT_MEM;
5521 mimetext->tmp = TRUE; /* must free content later */
5522 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5523 * and free the data, which we need later. */
5524 mimetext->data.mem = g_strdup(buf);
5525 mimetext->type = MIMETYPE_TEXT;
5526 mimetext->subtype = g_strdup("plain");
5527 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5528 g_strdup(out_codeset));
5530 /* protect trailing spaces when signing message */
5531 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5532 privacy_system_can_sign(compose->privacy_system)) {
5533 encoding = ENC_QUOTED_PRINTABLE;
5536 debug_print("main text: %zd bytes encoded as %s in %d\n",
5537 strlen(buf), out_codeset, encoding);
5539 /* check for line length limit */
5540 if (action == COMPOSE_WRITE_FOR_SEND &&
5541 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5542 check_line_length(buf, 1000, &line) < 0) {
5546 msg = g_strdup_printf
5547 (_("Line %d exceeds the line length limit (998 bytes).\n"
5548 "The contents of the message might be broken on the way to the delivery.\n"
5550 "Send it anyway?"), line + 1);
5551 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5553 if (aval != G_ALERTALTERNATE) {
5559 if (encoding != ENC_UNKNOWN)
5560 procmime_encode_content(mimetext, encoding);
5562 /* append attachment parts */
5563 if (compose_use_attach(compose) && attach_parts) {
5564 MimeInfo *mimempart;
5565 gchar *boundary = NULL;
5566 mimempart = procmime_mimeinfo_new();
5567 mimempart->content = MIMECONTENT_EMPTY;
5568 mimempart->type = MIMETYPE_MULTIPART;
5569 mimempart->subtype = g_strdup("mixed");
5573 boundary = generate_mime_boundary(NULL);
5574 } while (strstr(buf, boundary) != NULL);
5576 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5579 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5581 g_node_append(mimempart->node, mimetext->node);
5582 g_node_append(mimemsg->node, mimempart->node);
5584 if (compose_add_attachments(compose, mimempart) < 0)
5587 g_node_append(mimemsg->node, mimetext->node);
5591 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5592 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5593 /* extract name and address */
5594 if (strstr(spec, " <") && strstr(spec, ">")) {
5595 from_addr = g_strdup(strrchr(spec, '<')+1);
5596 *(strrchr(from_addr, '>')) = '\0';
5597 from_name = g_strdup(spec);
5598 *(strrchr(from_name, '<')) = '\0';
5605 /* sign message if sending */
5606 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5607 privacy_system_can_sign(compose->privacy_system))
5608 if (!privacy_sign(compose->privacy_system, mimemsg,
5609 compose->account, from_addr)) {
5616 procmime_write_mimeinfo(mimemsg, fp);
5618 procmime_mimeinfo_free_all(mimemsg);
5623 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5625 GtkTextBuffer *buffer;
5626 GtkTextIter start, end;
5631 if ((fp = g_fopen(file, "wb")) == NULL) {
5632 FILE_OP_ERROR(file, "fopen");
5636 /* chmod for security */
5637 if (change_file_mode_rw(fp, file) < 0) {
5638 FILE_OP_ERROR(file, "chmod");
5639 g_warning("can't change file mode\n");
5642 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5643 gtk_text_buffer_get_start_iter(buffer, &start);
5644 gtk_text_buffer_get_end_iter(buffer, &end);
5645 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5647 chars = conv_codeset_strdup
5648 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5651 if (!chars) return -1;
5654 len = strlen(chars);
5655 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5656 FILE_OP_ERROR(file, "fwrite");
5665 if (fclose(fp) == EOF) {
5666 FILE_OP_ERROR(file, "fclose");
5673 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5676 MsgInfo *msginfo = compose->targetinfo;
5678 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5679 if (!msginfo) return -1;
5681 if (!force && MSG_IS_LOCKED(msginfo->flags))
5684 item = msginfo->folder;
5685 cm_return_val_if_fail(item != NULL, -1);
5687 if (procmsg_msg_exist(msginfo) &&
5688 (folder_has_parent_of_type(item, F_QUEUE) ||
5689 folder_has_parent_of_type(item, F_DRAFT)
5690 || msginfo == compose->autosaved_draft)) {
5691 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5692 g_warning("can't remove the old message\n");
5695 debug_print("removed reedit target %d\n", msginfo->msgnum);
5702 static void compose_remove_draft(Compose *compose)
5705 MsgInfo *msginfo = compose->targetinfo;
5706 drafts = account_get_special_folder(compose->account, F_DRAFT);
5708 if (procmsg_msg_exist(msginfo)) {
5709 folder_item_remove_msg(drafts, msginfo->msgnum);
5714 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5715 gboolean remove_reedit_target)
5717 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5720 static gboolean compose_warn_encryption(Compose *compose)
5722 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5723 AlertValue val = G_ALERTALTERNATE;
5725 if (warning == NULL)
5728 val = alertpanel_full(_("Encryption warning"), warning,
5729 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5730 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5731 if (val & G_ALERTDISABLE) {
5732 val &= ~G_ALERTDISABLE;
5733 if (val == G_ALERTALTERNATE)
5734 privacy_inhibit_encrypt_warning(compose->privacy_system,
5738 if (val == G_ALERTALTERNATE) {
5745 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5746 gchar **msgpath, gboolean check_subject,
5747 gboolean remove_reedit_target)
5754 PrefsAccount *mailac = NULL, *newsac = NULL;
5755 gboolean err = FALSE;
5757 debug_print("queueing message...\n");
5758 cm_return_val_if_fail(compose->account != NULL, -1);
5760 if (compose_check_entries(compose, check_subject) == FALSE) {
5761 if (compose->batch) {
5762 gtk_widget_show_all(compose->window);
5767 if (!compose->to_list && !compose->newsgroup_list) {
5768 g_warning("can't get recipient list.");
5772 if (compose->to_list) {
5773 if (compose->account->protocol != A_NNTP)
5774 mailac = compose->account;
5775 else if (cur_account && cur_account->protocol != A_NNTP)
5776 mailac = cur_account;
5777 else if (!(mailac = compose_current_mail_account())) {
5778 alertpanel_error(_("No account for sending mails available!"));
5783 if (compose->newsgroup_list) {
5784 if (compose->account->protocol == A_NNTP)
5785 newsac = compose->account;
5787 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5792 /* write queue header */
5793 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5794 G_DIR_SEPARATOR, compose, (guint) rand());
5795 debug_print("queuing to %s\n", tmp);
5796 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5797 FILE_OP_ERROR(tmp, "fopen");
5802 if (change_file_mode_rw(fp, tmp) < 0) {
5803 FILE_OP_ERROR(tmp, "chmod");
5804 g_warning("can't change file mode\n");
5807 /* queueing variables */
5808 err |= (fprintf(fp, "AF:\n") < 0);
5809 err |= (fprintf(fp, "NF:0\n") < 0);
5810 err |= (fprintf(fp, "PS:10\n") < 0);
5811 err |= (fprintf(fp, "SRH:1\n") < 0);
5812 err |= (fprintf(fp, "SFN:\n") < 0);
5813 err |= (fprintf(fp, "DSR:\n") < 0);
5815 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5817 err |= (fprintf(fp, "MID:\n") < 0);
5818 err |= (fprintf(fp, "CFG:\n") < 0);
5819 err |= (fprintf(fp, "PT:0\n") < 0);
5820 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5821 err |= (fprintf(fp, "RQ:\n") < 0);
5823 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5825 err |= (fprintf(fp, "SSV:\n") < 0);
5827 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5829 err |= (fprintf(fp, "NSV:\n") < 0);
5830 err |= (fprintf(fp, "SSH:\n") < 0);
5831 /* write recepient list */
5832 if (compose->to_list) {
5833 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5834 for (cur = compose->to_list->next; cur != NULL;
5836 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5837 err |= (fprintf(fp, "\n") < 0);
5839 /* write newsgroup list */
5840 if (compose->newsgroup_list) {
5841 err |= (fprintf(fp, "NG:") < 0);
5842 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5843 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5844 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5845 err |= (fprintf(fp, "\n") < 0);
5847 /* Sylpheed account IDs */
5849 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5851 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5854 if (compose->privacy_system != NULL) {
5855 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5856 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5857 if (compose->use_encryption) {
5859 if (!compose_warn_encryption(compose)) {
5865 if (mailac && mailac->encrypt_to_self) {
5866 GSList *tmp_list = g_slist_copy(compose->to_list);
5867 tmp_list = g_slist_append(tmp_list, compose->account->address);
5868 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5869 g_slist_free(tmp_list);
5871 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5873 if (encdata != NULL) {
5874 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5875 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5876 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5878 } /* else we finally dont want to encrypt */
5880 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5881 /* and if encdata was null, it means there's been a problem in
5884 g_warning("failed to write queue message");
5894 /* Save copy folder */
5895 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5896 gchar *savefolderid;
5898 savefolderid = compose_get_save_to(compose);
5899 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5900 g_free(savefolderid);
5902 /* Save copy folder */
5903 if (compose->return_receipt) {
5904 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5906 /* Message-ID of message replying to */
5907 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5910 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5911 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5914 /* Message-ID of message forwarding to */
5915 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5918 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5919 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5923 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5924 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5926 /* end of headers */
5927 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5929 if (compose->redirect_filename != NULL) {
5930 if (compose_redirect_write_to_file(compose, fp) < 0) {
5938 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5942 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5946 g_warning("failed to write queue message\n");
5952 if (fclose(fp) == EOF) {
5953 FILE_OP_ERROR(tmp, "fclose");
5959 if (item && *item) {
5962 queue = account_get_special_folder(compose->account, F_QUEUE);
5965 g_warning("can't find queue folder\n");
5970 folder_item_scan(queue);
5971 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5972 g_warning("can't queue the message\n");
5978 if (msgpath == NULL) {
5984 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5985 compose_remove_reedit_target(compose, FALSE);
5988 if ((msgnum != NULL) && (item != NULL)) {
5996 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5999 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6001 struct stat statbuf;
6002 gchar *type, *subtype;
6003 GtkTreeModel *model;
6006 model = gtk_tree_view_get_model(tree_view);
6008 if (!gtk_tree_model_get_iter_first(model, &iter))
6011 gtk_tree_model_get(model, &iter,
6015 if (!is_file_exist(ainfo->file)) {
6016 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6017 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6018 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6020 if (val == G_ALERTDEFAULT) {
6025 mimepart = procmime_mimeinfo_new();
6026 mimepart->content = MIMECONTENT_FILE;
6027 mimepart->data.filename = g_strdup(ainfo->file);
6028 mimepart->tmp = FALSE; /* or we destroy our attachment */
6029 mimepart->offset = 0;
6031 g_stat(ainfo->file, &statbuf);
6032 mimepart->length = statbuf.st_size;
6034 type = g_strdup(ainfo->content_type);
6036 if (!strchr(type, '/')) {
6038 type = g_strdup("application/octet-stream");
6041 subtype = strchr(type, '/') + 1;
6042 *(subtype - 1) = '\0';
6043 mimepart->type = procmime_get_media_type(type);
6044 mimepart->subtype = g_strdup(subtype);
6047 if (mimepart->type == MIMETYPE_MESSAGE &&
6048 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6049 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6050 } else if (mimepart->type == MIMETYPE_TEXT) {
6051 if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6052 /* Text parts with no name come from multipart/alternative
6053 * forwards. Make sure the recipient won't look at the
6054 * original HTML part by mistake. */
6055 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6056 ainfo->name = g_strdup_printf(_("Original %s part"),
6060 g_hash_table_insert(mimepart->typeparameters,
6061 g_strdup("charset"), g_strdup(ainfo->charset));
6063 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6064 if (mimepart->type == MIMETYPE_APPLICATION &&
6065 !strcmp2(mimepart->subtype, "octet-stream"))
6066 g_hash_table_insert(mimepart->typeparameters,
6067 g_strdup("name"), g_strdup(ainfo->name));
6068 g_hash_table_insert(mimepart->dispositionparameters,
6069 g_strdup("filename"), g_strdup(ainfo->name));
6070 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6073 if (mimepart->type == MIMETYPE_MESSAGE
6074 || mimepart->type == MIMETYPE_MULTIPART)
6075 ainfo->encoding = ENC_BINARY;
6076 else if (compose->use_signing) {
6077 if (ainfo->encoding == ENC_7BIT)
6078 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6079 else if (ainfo->encoding == ENC_8BIT)
6080 ainfo->encoding = ENC_BASE64;
6085 procmime_encode_content(mimepart, ainfo->encoding);
6087 g_node_append(parent->node, mimepart->node);
6088 } while (gtk_tree_model_iter_next(model, &iter));
6093 #define IS_IN_CUSTOM_HEADER(header) \
6094 (compose->account->add_customhdr && \
6095 custom_header_find(compose->account->customhdr_list, header) != NULL)
6097 static void compose_add_headerfield_from_headerlist(Compose *compose,
6099 const gchar *fieldname,
6100 const gchar *seperator)
6102 gchar *str, *fieldname_w_colon;
6103 gboolean add_field = FALSE;
6105 ComposeHeaderEntry *headerentry;
6106 const gchar *headerentryname;
6107 const gchar *trans_fieldname;
6110 if (IS_IN_CUSTOM_HEADER(fieldname))
6113 debug_print("Adding %s-fields\n", fieldname);
6115 fieldstr = g_string_sized_new(64);
6117 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6118 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6120 for (list = compose->header_list; list; list = list->next) {
6121 headerentry = ((ComposeHeaderEntry *)list->data);
6122 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6124 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6125 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6127 if (str[0] != '\0') {
6129 g_string_append(fieldstr, seperator);
6130 g_string_append(fieldstr, str);
6139 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6140 compose_convert_header
6141 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6142 strlen(fieldname) + 2, TRUE);
6143 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6147 g_free(fieldname_w_colon);
6148 g_string_free(fieldstr, TRUE);
6153 static gchar *compose_get_manual_headers_info(Compose *compose)
6155 GString *sh_header = g_string_new(" ");
6157 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6159 for (list = compose->header_list; list; list = list->next) {
6160 ComposeHeaderEntry *headerentry;
6163 gchar *headername_wcolon;
6164 const gchar *headername_trans;
6166 gboolean standard_header = FALSE;
6168 headerentry = ((ComposeHeaderEntry *)list->data);
6170 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6172 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6177 if (!strstr(tmp, ":")) {
6178 headername_wcolon = g_strconcat(tmp, ":", NULL);
6179 headername = g_strdup(tmp);
6181 headername_wcolon = g_strdup(tmp);
6182 headername = g_strdup(strtok(tmp, ":"));
6186 string = std_headers;
6187 while (*string != NULL) {
6188 headername_trans = prefs_common_translated_header_name(*string);
6189 if (!strcmp(headername_trans, headername_wcolon))
6190 standard_header = TRUE;
6193 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6194 g_string_append_printf(sh_header, "%s ", headername);
6196 g_free(headername_wcolon);
6198 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6199 return g_string_free(sh_header, FALSE);
6202 static gchar *compose_get_header(Compose *compose)
6204 gchar buf[BUFFSIZE];
6205 const gchar *entry_str;
6209 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6211 gchar *from_name = NULL, *from_address = NULL;
6214 cm_return_val_if_fail(compose->account != NULL, NULL);
6215 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6217 header = g_string_sized_new(64);
6220 get_rfc822_date(buf, sizeof(buf));
6221 g_string_append_printf(header, "Date: %s\n", buf);
6225 if (compose->account->name && *compose->account->name) {
6227 QUOTE_IF_REQUIRED(buf, compose->account->name);
6228 tmp = g_strdup_printf("%s <%s>",
6229 buf, compose->account->address);
6231 tmp = g_strdup_printf("%s",
6232 compose->account->address);
6234 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6235 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6237 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6238 from_address = g_strdup(compose->account->address);
6240 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6241 /* extract name and address */
6242 if (strstr(spec, " <") && strstr(spec, ">")) {
6243 from_address = g_strdup(strrchr(spec, '<')+1);
6244 *(strrchr(from_address, '>')) = '\0';
6245 from_name = g_strdup(spec);
6246 *(strrchr(from_name, '<')) = '\0';
6249 from_address = g_strdup(spec);
6256 if (from_name && *from_name) {
6257 compose_convert_header
6258 (compose, buf, sizeof(buf), from_name,
6259 strlen("From: "), TRUE);
6260 QUOTE_IF_REQUIRED(name, buf);
6262 g_string_append_printf(header, "From: %s <%s>\n",
6263 name, from_address);
6265 g_string_append_printf(header, "From: %s\n", from_address);
6268 g_free(from_address);
6271 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6274 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6277 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6281 * If this account is a NNTP account remove Bcc header from
6282 * message body since it otherwise will be publicly shown
6284 if (compose->account->protocol != A_NNTP)
6285 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6288 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6290 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6293 compose_convert_header(compose, buf, sizeof(buf), str,
6294 strlen("Subject: "), FALSE);
6295 g_string_append_printf(header, "Subject: %s\n", buf);
6301 if (compose->account->set_domain && compose->account->domain) {
6302 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6303 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6304 g_snprintf(buf, sizeof(buf), "%s",
6305 strchr(compose->account->address, '@') ?
6306 strchr(compose->account->address, '@')+1 :
6307 compose->account->address);
6309 g_snprintf(buf, sizeof(buf), "%s", "");
6312 if (compose->account->gen_msgid) {
6314 if (compose->account->msgid_with_addr) {
6315 addr = compose->account->address;
6317 generate_msgid(buf, sizeof(buf), addr);
6318 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6319 compose->msgid = g_strdup(buf);
6321 compose->msgid = NULL;
6324 if (compose->remove_references == FALSE) {
6326 if (compose->inreplyto && compose->to_list)
6327 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6330 if (compose->references)
6331 g_string_append_printf(header, "References: %s\n", compose->references);
6335 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6338 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6341 if (compose->account->organization &&
6342 strlen(compose->account->organization) &&
6343 !IS_IN_CUSTOM_HEADER("Organization")) {
6344 compose_convert_header(compose, buf, sizeof(buf),
6345 compose->account->organization,
6346 strlen("Organization: "), FALSE);
6347 g_string_append_printf(header, "Organization: %s\n", buf);
6350 /* Program version and system info */
6351 if (compose->account->gen_xmailer &&
6352 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6353 !compose->newsgroup_list) {
6354 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6356 gtk_major_version, gtk_minor_version, gtk_micro_version,
6359 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6360 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6362 gtk_major_version, gtk_minor_version, gtk_micro_version,
6366 /* custom headers */
6367 if (compose->account->add_customhdr) {
6370 for (cur = compose->account->customhdr_list; cur != NULL;
6372 CustomHeader *chdr = (CustomHeader *)cur->data;
6374 if (custom_header_is_allowed(chdr->name)
6375 && chdr->value != NULL
6376 && *(chdr->value) != '\0') {
6377 compose_convert_header
6378 (compose, buf, sizeof(buf),
6380 strlen(chdr->name) + 2, FALSE);
6381 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6386 /* Automatic Faces and X-Faces */
6387 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6388 g_string_append_printf(header, "X-Face: %s\n", buf);
6390 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6391 g_string_append_printf(header, "X-Face: %s\n", buf);
6393 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6394 g_string_append_printf(header, "Face: %s\n", buf);
6396 else if (get_default_face (buf, sizeof(buf)) == 0) {
6397 g_string_append_printf(header, "Face: %s\n", buf);
6401 switch (compose->priority) {
6402 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6403 "X-Priority: 1 (Highest)\n");
6405 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6406 "X-Priority: 2 (High)\n");
6408 case PRIORITY_NORMAL: break;
6409 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6410 "X-Priority: 4 (Low)\n");
6412 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6413 "X-Priority: 5 (Lowest)\n");
6415 default: debug_print("compose: priority unknown : %d\n",
6419 /* Request Return Receipt */
6420 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6421 if (compose->return_receipt) {
6422 if (compose->account->name
6423 && *compose->account->name) {
6424 compose_convert_header(compose, buf, sizeof(buf),
6425 compose->account->name,
6426 strlen("Disposition-Notification-To: "),
6428 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6430 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6434 /* get special headers */
6435 for (list = compose->header_list; list; list = list->next) {
6436 ComposeHeaderEntry *headerentry;
6439 gchar *headername_wcolon;
6440 const gchar *headername_trans;
6443 gboolean standard_header = FALSE;
6445 headerentry = ((ComposeHeaderEntry *)list->data);
6447 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6449 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6454 if (!strstr(tmp, ":")) {
6455 headername_wcolon = g_strconcat(tmp, ":", NULL);
6456 headername = g_strdup(tmp);
6458 headername_wcolon = g_strdup(tmp);
6459 headername = g_strdup(strtok(tmp, ":"));
6463 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6464 Xstrdup_a(headervalue, entry_str, return NULL);
6465 subst_char(headervalue, '\r', ' ');
6466 subst_char(headervalue, '\n', ' ');
6467 string = std_headers;
6468 while (*string != NULL) {
6469 headername_trans = prefs_common_translated_header_name(*string);
6470 if (!strcmp(headername_trans, headername_wcolon))
6471 standard_header = TRUE;
6474 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6475 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6478 g_free(headername_wcolon);
6482 g_string_free(header, FALSE);
6487 #undef IS_IN_CUSTOM_HEADER
6489 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6490 gint header_len, gboolean addr_field)
6492 gchar *tmpstr = NULL;
6493 const gchar *out_codeset = NULL;
6495 cm_return_if_fail(src != NULL);
6496 cm_return_if_fail(dest != NULL);
6498 if (len < 1) return;
6500 tmpstr = g_strdup(src);
6502 subst_char(tmpstr, '\n', ' ');
6503 subst_char(tmpstr, '\r', ' ');
6506 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6507 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6508 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6513 codeconv_set_strict(TRUE);
6514 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6515 conv_get_charset_str(compose->out_encoding));
6516 codeconv_set_strict(FALSE);
6518 if (!dest || *dest == '\0') {
6519 gchar *test_conv_global_out = NULL;
6520 gchar *test_conv_reply = NULL;
6522 /* automatic mode. be automatic. */
6523 codeconv_set_strict(TRUE);
6525 out_codeset = conv_get_outgoing_charset_str();
6527 debug_print("trying to convert to %s\n", out_codeset);
6528 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6531 if (!test_conv_global_out && compose->orig_charset
6532 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6533 out_codeset = compose->orig_charset;
6534 debug_print("failure; trying to convert to %s\n", out_codeset);
6535 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6538 if (!test_conv_global_out && !test_conv_reply) {
6540 out_codeset = CS_INTERNAL;
6541 debug_print("finally using %s\n", out_codeset);
6543 g_free(test_conv_global_out);
6544 g_free(test_conv_reply);
6545 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6547 codeconv_set_strict(FALSE);
6552 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6556 cm_return_if_fail(user_data != NULL);
6558 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6559 g_strstrip(address);
6560 if (*address != '\0') {
6561 gchar *name = procheader_get_fromname(address);
6562 extract_address(address);
6563 #ifndef USE_NEW_ADDRBOOK
6564 addressbook_add_contact(name, address, NULL, NULL);
6566 debug_print("%s: %s\n", name, address);
6567 if (addressadd_selection(name, address, NULL, NULL)) {
6568 debug_print( "addressbook_add_contact - added\n" );
6575 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6577 GtkWidget *menuitem;
6580 cm_return_if_fail(menu != NULL);
6581 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6583 menuitem = gtk_separator_menu_item_new();
6584 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6585 gtk_widget_show(menuitem);
6587 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6588 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6590 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6591 g_strstrip(address);
6592 if (*address == '\0') {
6593 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6596 g_signal_connect(G_OBJECT(menuitem), "activate",
6597 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6598 gtk_widget_show(menuitem);
6601 void compose_add_extra_header(gchar *header, GtkListStore *model)
6604 if (strcmp(header, "")) {
6605 COMBOBOX_ADD(model, header, COMPOSE_TO);
6609 void compose_add_extra_header_entries(GtkListStore *model)
6613 gchar buf[BUFFSIZE];
6616 if (extra_headers == NULL) {
6617 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6618 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6619 debug_print("extra headers file not found\n");
6620 goto extra_headers_done;
6622 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6623 lastc = strlen(buf) - 1; /* remove trailing \n */
6624 buf[lastc] = (buf[lastc] == '\n')? '\0': buf[lastc];
6626 if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6627 buf[lastc] = '\0'; /* remove trailing : for comparison */
6628 if (custom_header_is_allowed(buf)) {
6630 extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6633 g_message("disallowed extra header line: %s\n", buf);
6637 g_message("invalid extra header line: %s\n", buf);
6643 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6644 extra_headers = g_slist_reverse(extra_headers);
6646 g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6649 static void compose_create_header_entry(Compose *compose)
6651 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6658 const gchar *header = NULL;
6659 ComposeHeaderEntry *headerentry;
6660 gboolean standard_header = FALSE;
6661 GtkListStore *model;
6663 #if !(GTK_CHECK_VERSION(2,12,0))
6664 GtkTooltips *tips = compose->tooltips;
6667 headerentry = g_new0(ComposeHeaderEntry, 1);
6670 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6671 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6672 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6674 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6676 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6678 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6679 COMPOSE_NEWSGROUPS);
6680 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6682 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6683 COMPOSE_FOLLOWUPTO);
6684 compose_add_extra_header_entries(model);
6686 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6687 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6688 G_CALLBACK(compose_grab_focus_cb), compose);
6689 gtk_widget_show(combo);
6692 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6693 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6696 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6697 compose->header_nextrow, compose->header_nextrow+1,
6698 GTK_SHRINK, GTK_FILL, 0, 0);
6699 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6700 const gchar *last_header_entry = gtk_entry_get_text(
6701 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6703 while (*string != NULL) {
6704 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6705 standard_header = TRUE;
6708 if (standard_header)
6709 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6711 if (!compose->header_last || !standard_header) {
6712 switch(compose->account->protocol) {
6714 header = prefs_common_translated_header_name("Newsgroups:");
6717 header = prefs_common_translated_header_name("To:");
6722 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6724 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6725 G_CALLBACK(compose_grab_focus_cb), compose);
6727 /* Entry field with cleanup button */
6728 button = gtk_button_new();
6729 gtk_button_set_image(GTK_BUTTON(button),
6730 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6731 gtk_widget_show(button);
6732 CLAWS_SET_TIP(button,
6733 _("Delete entry contents"));
6734 entry = gtk_entry_new();
6735 gtk_widget_show(entry);
6736 CLAWS_SET_TIP(entry,
6737 _("Use <tab> to autocomplete from addressbook"));
6738 hbox = gtk_hbox_new (FALSE, 0);
6739 gtk_widget_show(hbox);
6740 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6741 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6742 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6743 compose->header_nextrow, compose->header_nextrow+1,
6744 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6746 g_signal_connect(G_OBJECT(entry), "key-press-event",
6747 G_CALLBACK(compose_headerentry_key_press_event_cb),
6749 g_signal_connect(G_OBJECT(entry), "changed",
6750 G_CALLBACK(compose_headerentry_changed_cb),
6752 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6753 G_CALLBACK(compose_grab_focus_cb), compose);
6755 g_signal_connect(G_OBJECT(button), "clicked",
6756 G_CALLBACK(compose_headerentry_button_clicked_cb),
6760 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6761 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6762 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6763 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6764 G_CALLBACK(compose_header_drag_received_cb),
6766 g_signal_connect(G_OBJECT(entry), "drag-drop",
6767 G_CALLBACK(compose_drag_drop),
6769 g_signal_connect(G_OBJECT(entry), "populate-popup",
6770 G_CALLBACK(compose_entry_popup_extend),
6773 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6775 headerentry->compose = compose;
6776 headerentry->combo = combo;
6777 headerentry->entry = entry;
6778 headerentry->button = button;
6779 headerentry->hbox = hbox;
6780 headerentry->headernum = compose->header_nextrow;
6781 headerentry->type = PREF_NONE;
6783 compose->header_nextrow++;
6784 compose->header_last = headerentry;
6785 compose->header_list =
6786 g_slist_append(compose->header_list,
6790 static void compose_add_header_entry(Compose *compose, const gchar *header,
6791 gchar *text, ComposePrefType pref_type)
6793 ComposeHeaderEntry *last_header = compose->header_last;
6794 gchar *tmp = g_strdup(text), *email;
6795 gboolean replyto_hdr;
6797 replyto_hdr = (!strcasecmp(header,
6798 prefs_common_translated_header_name("Reply-To:")) ||
6800 prefs_common_translated_header_name("Followup-To:")) ||
6802 prefs_common_translated_header_name("In-Reply-To:")));
6804 extract_address(tmp);
6805 email = g_utf8_strdown(tmp, -1);
6807 if (replyto_hdr == FALSE &&
6808 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6810 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6811 header, text, (gint) pref_type);
6817 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6818 gtk_entry_set_text(GTK_ENTRY(
6819 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6821 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6822 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6823 last_header->type = pref_type;
6825 if (replyto_hdr == FALSE)
6826 g_hash_table_insert(compose->email_hashtable, email,
6827 GUINT_TO_POINTER(1));
6834 static void compose_destroy_headerentry(Compose *compose,
6835 ComposeHeaderEntry *headerentry)
6837 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6840 extract_address(text);
6841 email = g_utf8_strdown(text, -1);
6842 g_hash_table_remove(compose->email_hashtable, email);
6846 gtk_widget_destroy(headerentry->combo);
6847 gtk_widget_destroy(headerentry->entry);
6848 gtk_widget_destroy(headerentry->button);
6849 gtk_widget_destroy(headerentry->hbox);
6850 g_free(headerentry);
6853 static void compose_remove_header_entries(Compose *compose)
6856 for (list = compose->header_list; list; list = list->next)
6857 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6859 compose->header_last = NULL;
6860 g_slist_free(compose->header_list);
6861 compose->header_list = NULL;
6862 compose->header_nextrow = 1;
6863 compose_create_header_entry(compose);
6866 static GtkWidget *compose_create_header(Compose *compose)
6868 GtkWidget *from_optmenu_hbox;
6869 GtkWidget *header_scrolledwin_main;
6870 GtkWidget *header_table_main;
6871 GtkWidget *header_scrolledwin;
6872 GtkWidget *header_table;
6874 /* parent with account selection and from header */
6875 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6876 gtk_widget_show(header_scrolledwin_main);
6877 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6879 header_table_main = gtk_table_new(2, 2, FALSE);
6880 gtk_widget_show(header_table_main);
6881 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6882 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6883 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6885 from_optmenu_hbox = compose_account_option_menu_create(compose);
6886 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6887 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6889 /* child with header labels and entries */
6890 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6891 gtk_widget_show(header_scrolledwin);
6892 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6894 header_table = gtk_table_new(2, 2, FALSE);
6895 gtk_widget_show(header_table);
6896 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6897 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6898 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6900 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6901 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6903 compose->header_table = header_table;
6904 compose->header_list = NULL;
6905 compose->header_nextrow = 0;
6907 compose_create_header_entry(compose);
6909 compose->table = NULL;
6911 return header_scrolledwin_main;
6914 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6916 Compose *compose = (Compose *)data;
6917 GdkEventButton event;
6920 event.time = gtk_get_current_event_time();
6922 return attach_button_pressed(compose->attach_clist, &event, compose);
6925 static GtkWidget *compose_create_attach(Compose *compose)
6927 GtkWidget *attach_scrwin;
6928 GtkWidget *attach_clist;
6930 GtkListStore *store;
6931 GtkCellRenderer *renderer;
6932 GtkTreeViewColumn *column;
6933 GtkTreeSelection *selection;
6935 /* attachment list */
6936 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6937 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6938 GTK_POLICY_AUTOMATIC,
6939 GTK_POLICY_AUTOMATIC);
6940 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6942 store = gtk_list_store_new(N_ATTACH_COLS,
6948 G_TYPE_AUTO_POINTER,
6950 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6951 (GTK_TREE_MODEL(store)));
6952 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6953 g_object_unref(store);
6955 renderer = gtk_cell_renderer_text_new();
6956 column = gtk_tree_view_column_new_with_attributes
6957 (_("Mime type"), renderer, "text",
6958 COL_MIMETYPE, NULL);
6959 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6961 renderer = gtk_cell_renderer_text_new();
6962 column = gtk_tree_view_column_new_with_attributes
6963 (_("Size"), renderer, "text",
6965 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6967 renderer = gtk_cell_renderer_text_new();
6968 column = gtk_tree_view_column_new_with_attributes
6969 (_("Name"), renderer, "text",
6971 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6973 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6974 prefs_common.use_stripes_everywhere);
6975 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6976 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6978 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6979 G_CALLBACK(attach_selected), compose);
6980 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6981 G_CALLBACK(attach_button_pressed), compose);
6983 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6984 G_CALLBACK(popup_attach_button_pressed), compose);
6986 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6987 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6988 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6989 G_CALLBACK(popup_attach_button_pressed), compose);
6991 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6992 G_CALLBACK(attach_key_pressed), compose);
6995 gtk_drag_dest_set(attach_clist,
6996 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6997 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6998 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6999 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7000 G_CALLBACK(compose_attach_drag_received_cb),
7002 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7003 G_CALLBACK(compose_drag_drop),
7006 compose->attach_scrwin = attach_scrwin;
7007 compose->attach_clist = attach_clist;
7009 return attach_scrwin;
7012 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7013 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7015 static GtkWidget *compose_create_others(Compose *compose)
7018 GtkWidget *savemsg_checkbtn;
7019 GtkWidget *savemsg_combo;
7020 GtkWidget *savemsg_select;
7023 gchar *folderidentifier;
7025 /* Table for settings */
7026 table = gtk_table_new(3, 1, FALSE);
7027 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7028 gtk_widget_show(table);
7029 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7032 /* Save Message to folder */
7033 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7034 gtk_widget_show(savemsg_checkbtn);
7035 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7036 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7037 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7039 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7040 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7042 savemsg_combo = gtk_combo_box_entry_new_text();
7043 compose->savemsg_checkbtn = savemsg_checkbtn;
7044 compose->savemsg_combo = savemsg_combo;
7045 gtk_widget_show(savemsg_combo);
7047 if (prefs_common.compose_save_to_history)
7048 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7049 prefs_common.compose_save_to_history);
7051 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7052 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7053 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7054 G_CALLBACK(compose_grab_focus_cb), compose);
7055 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7056 folderidentifier = folder_item_get_identifier(account_get_special_folder
7057 (compose->account, F_OUTBOX));
7058 compose_set_save_to(compose, folderidentifier);
7059 g_free(folderidentifier);
7062 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7063 gtk_widget_show(savemsg_select);
7064 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7065 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7066 G_CALLBACK(compose_savemsg_select_cb),
7072 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7074 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7075 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7078 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7083 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7086 path = folder_item_get_identifier(dest);
7088 compose_set_save_to(compose, path);
7092 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7093 GdkAtom clip, GtkTextIter *insert_place);
7096 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7100 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7102 if (event->button == 3) {
7104 GtkTextIter sel_start, sel_end;
7105 gboolean stuff_selected;
7107 /* move the cursor to allow GtkAspell to check the word
7108 * under the mouse */
7109 if (event->x && event->y) {
7110 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7111 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7113 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7116 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7117 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7120 stuff_selected = gtk_text_buffer_get_selection_bounds(
7122 &sel_start, &sel_end);
7124 gtk_text_buffer_place_cursor (buffer, &iter);
7125 /* reselect stuff */
7127 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7128 gtk_text_buffer_select_range(buffer,
7129 &sel_start, &sel_end);
7131 return FALSE; /* pass the event so that the right-click goes through */
7134 if (event->button == 2) {
7139 /* get the middle-click position to paste at the correct place */
7140 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7141 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7143 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7146 entry_paste_clipboard(compose, text,
7147 prefs_common.linewrap_pastes,
7148 GDK_SELECTION_PRIMARY, &iter);
7156 static void compose_spell_menu_changed(void *data)
7158 Compose *compose = (Compose *)data;
7160 GtkWidget *menuitem;
7161 GtkWidget *parent_item;
7162 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7165 if (compose->gtkaspell == NULL)
7168 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7169 "/Menu/Spelling/Options");
7171 /* setting the submenu removes /Spelling/Options from the factory
7172 * so we need to save it */
7174 if (parent_item == NULL) {
7175 parent_item = compose->aspell_options_menu;
7176 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7178 compose->aspell_options_menu = parent_item;
7180 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7182 spell_menu = g_slist_reverse(spell_menu);
7183 for (items = spell_menu;
7184 items; items = items->next) {
7185 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7186 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7187 gtk_widget_show(GTK_WIDGET(menuitem));
7189 g_slist_free(spell_menu);
7191 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7192 gtk_widget_show(parent_item);
7195 static void compose_dict_changed(void *data)
7197 Compose *compose = (Compose *) data;
7199 if(compose->gtkaspell &&
7200 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7203 gtkaspell_highlight_all(compose->gtkaspell);
7204 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7208 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7210 Compose *compose = (Compose *)data;
7211 GdkEventButton event;
7214 event.time = gtk_get_current_event_time();
7218 return text_clicked(compose->text, &event, compose);
7221 static gboolean compose_force_window_origin = TRUE;
7222 static Compose *compose_create(PrefsAccount *account,
7231 GtkWidget *handlebox;
7233 GtkWidget *notebook;
7235 GtkWidget *attach_hbox;
7236 GtkWidget *attach_lab1;
7237 GtkWidget *attach_lab2;
7242 GtkWidget *subject_hbox;
7243 GtkWidget *subject_frame;
7244 GtkWidget *subject_entry;
7248 GtkWidget *edit_vbox;
7249 GtkWidget *ruler_hbox;
7251 GtkWidget *scrolledwin;
7253 GtkTextBuffer *buffer;
7254 GtkClipboard *clipboard;
7256 UndoMain *undostruct;
7258 GtkWidget *popupmenu;
7259 GtkWidget *tmpl_menu;
7260 GtkActionGroup *action_group = NULL;
7263 GtkAspell * gtkaspell = NULL;
7266 static GdkGeometry geometry;
7268 cm_return_val_if_fail(account != NULL, NULL);
7270 debug_print("Creating compose window...\n");
7271 compose = g_new0(Compose, 1);
7273 compose->batch = batch;
7274 compose->account = account;
7275 compose->folder = folder;
7277 compose->mutex = g_mutex_new();
7278 compose->set_cursor_pos = -1;
7280 #if !(GTK_CHECK_VERSION(2,12,0))
7281 compose->tooltips = tips;
7284 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7286 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7287 gtk_widget_set_size_request(window, prefs_common.compose_width,
7288 prefs_common.compose_height);
7290 if (!geometry.max_width) {
7291 geometry.max_width = gdk_screen_width();
7292 geometry.max_height = gdk_screen_height();
7295 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7296 &geometry, GDK_HINT_MAX_SIZE);
7297 if (!geometry.min_width) {
7298 geometry.min_width = 600;
7299 geometry.min_height = 440;
7301 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7302 &geometry, GDK_HINT_MIN_SIZE);
7304 #ifndef GENERIC_UMPC
7305 if (compose_force_window_origin)
7306 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7307 prefs_common.compose_y);
7309 g_signal_connect(G_OBJECT(window), "delete_event",
7310 G_CALLBACK(compose_delete_cb), compose);
7311 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7312 gtk_widget_realize(window);
7314 gtkut_widget_set_composer_icon(window);
7316 vbox = gtk_vbox_new(FALSE, 0);
7317 gtk_container_add(GTK_CONTAINER(window), vbox);
7319 compose->ui_manager = gtk_ui_manager_new();
7320 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7321 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7322 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7323 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7324 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7325 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7326 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7327 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7328 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7329 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7334 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7340 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7344 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7354 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7355 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7362 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7363 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7370 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7372 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7374 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7376 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7383 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7385 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7386 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7388 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7394 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7396 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7398 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7400 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7407 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7408 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7409 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7415 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7418 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7421 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7422 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7429 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7430 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7431 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7436 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7437 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7438 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7439 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7444 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7445 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7446 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7447 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7450 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7451 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)
7452 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)
7453 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7455 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7457 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7458 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)
7459 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)
7461 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7463 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7464 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)
7465 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7467 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7468 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)
7469 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7471 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7473 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7474 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)
7475 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7476 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7477 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7479 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7480 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)
7481 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)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7485 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7486 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7487 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7488 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7489 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7490 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7492 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7493 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7494 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)
7496 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7497 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7498 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7502 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7503 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7504 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7505 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7506 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7507 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7510 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7512 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7513 gtk_widget_show_all(menubar);
7515 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7517 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7519 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7522 if (prefs_common.toolbar_detachable) {
7523 handlebox = gtk_handle_box_new();
7525 handlebox = gtk_hbox_new(FALSE, 0);
7527 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7529 gtk_widget_realize(handlebox);
7531 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7534 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7538 vbox2 = gtk_vbox_new(FALSE, 2);
7539 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7540 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7543 notebook = gtk_notebook_new();
7544 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7545 gtk_widget_show(notebook);
7547 /* header labels and entries */
7548 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7549 compose_create_header(compose),
7550 gtk_label_new_with_mnemonic(_("Hea_der")));
7551 /* attachment list */
7552 attach_hbox = gtk_hbox_new(FALSE, 0);
7553 gtk_widget_show(attach_hbox);
7555 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7556 gtk_widget_show(attach_lab1);
7557 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7559 attach_lab2 = gtk_label_new("");
7560 gtk_widget_show(attach_lab2);
7561 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7563 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7564 compose_create_attach(compose),
7567 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7568 compose_create_others(compose),
7569 gtk_label_new_with_mnemonic(_("Othe_rs")));
7572 subject_hbox = gtk_hbox_new(FALSE, 0);
7573 gtk_widget_show(subject_hbox);
7575 subject_frame = gtk_frame_new(NULL);
7576 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7577 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7578 gtk_widget_show(subject_frame);
7580 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7581 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7582 gtk_widget_show(subject);
7584 label = gtk_label_new(_("Subject:"));
7585 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7586 gtk_widget_show(label);
7589 subject_entry = claws_spell_entry_new();
7591 subject_entry = gtk_entry_new();
7593 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7594 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7595 G_CALLBACK(compose_grab_focus_cb), compose);
7596 gtk_widget_show(subject_entry);
7597 compose->subject_entry = subject_entry;
7598 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7600 edit_vbox = gtk_vbox_new(FALSE, 0);
7602 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7605 ruler_hbox = gtk_hbox_new(FALSE, 0);
7606 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7608 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7609 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7610 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7614 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7615 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7616 GTK_POLICY_AUTOMATIC,
7617 GTK_POLICY_AUTOMATIC);
7618 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7620 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7622 text = gtk_text_view_new();
7623 if (prefs_common.show_compose_margin) {
7624 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7625 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7627 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7628 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7629 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7630 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7631 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7633 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7634 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7635 G_CALLBACK(compose_notebook_size_alloc), compose);
7636 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7637 G_CALLBACK(compose_edit_size_alloc),
7639 g_signal_connect(G_OBJECT(buffer), "changed",
7640 G_CALLBACK(compose_changed_cb), compose);
7641 g_signal_connect(G_OBJECT(text), "grab_focus",
7642 G_CALLBACK(compose_grab_focus_cb), compose);
7643 g_signal_connect(G_OBJECT(buffer), "insert_text",
7644 G_CALLBACK(text_inserted), compose);
7645 g_signal_connect(G_OBJECT(text), "button_press_event",
7646 G_CALLBACK(text_clicked), compose);
7648 g_signal_connect(G_OBJECT(text), "popup-menu",
7649 G_CALLBACK(compose_popup_menu), compose);
7651 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7652 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7653 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7654 G_CALLBACK(compose_popup_menu), compose);
7656 g_signal_connect(G_OBJECT(subject_entry), "changed",
7657 G_CALLBACK(compose_changed_cb), compose);
7660 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7661 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7662 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7663 g_signal_connect(G_OBJECT(text), "drag_data_received",
7664 G_CALLBACK(compose_insert_drag_received_cb),
7666 g_signal_connect(G_OBJECT(text), "drag-drop",
7667 G_CALLBACK(compose_drag_drop),
7669 g_signal_connect(G_OBJECT(text), "key-press-event",
7670 G_CALLBACK(completion_set_focus_to_subject),
7672 gtk_widget_show_all(vbox);
7674 /* pane between attach clist and text */
7675 paned = gtk_vpaned_new();
7676 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7678 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7679 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7681 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7683 gtk_paned_add1(GTK_PANED(paned), notebook);
7684 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7685 gtk_widget_show_all(paned);
7688 if (prefs_common.textfont) {
7689 PangoFontDescription *font_desc;
7691 font_desc = pango_font_description_from_string
7692 (prefs_common.textfont);
7694 gtk_widget_modify_font(text, font_desc);
7695 pango_font_description_free(font_desc);
7699 gtk_action_group_add_actions(action_group, compose_popup_entries,
7700 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7701 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7702 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7703 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7704 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7705 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7706 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7708 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7710 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7711 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7712 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7714 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7716 undostruct = undo_init(text);
7717 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7720 address_completion_start(window);
7722 compose->window = window;
7723 compose->vbox = vbox;
7724 compose->menubar = menubar;
7725 compose->handlebox = handlebox;
7727 compose->vbox2 = vbox2;
7729 compose->paned = paned;
7731 compose->attach_label = attach_lab2;
7733 compose->notebook = notebook;
7734 compose->edit_vbox = edit_vbox;
7735 compose->ruler_hbox = ruler_hbox;
7736 compose->ruler = ruler;
7737 compose->scrolledwin = scrolledwin;
7738 compose->text = text;
7740 compose->focused_editable = NULL;
7742 compose->popupmenu = popupmenu;
7744 compose->tmpl_menu = tmpl_menu;
7746 compose->mode = mode;
7747 compose->rmode = mode;
7749 compose->targetinfo = NULL;
7750 compose->replyinfo = NULL;
7751 compose->fwdinfo = NULL;
7753 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7754 g_str_equal, (GDestroyNotify) g_free, NULL);
7756 compose->replyto = NULL;
7758 compose->bcc = NULL;
7759 compose->followup_to = NULL;
7761 compose->ml_post = NULL;
7763 compose->inreplyto = NULL;
7764 compose->references = NULL;
7765 compose->msgid = NULL;
7766 compose->boundary = NULL;
7768 compose->autowrap = prefs_common.autowrap;
7769 compose->autoindent = prefs_common.auto_indent;
7770 compose->use_signing = FALSE;
7771 compose->use_encryption = FALSE;
7772 compose->privacy_system = NULL;
7774 compose->modified = FALSE;
7776 compose->return_receipt = FALSE;
7778 compose->to_list = NULL;
7779 compose->newsgroup_list = NULL;
7781 compose->undostruct = undostruct;
7783 compose->sig_str = NULL;
7785 compose->exteditor_file = NULL;
7786 compose->exteditor_pid = -1;
7787 compose->exteditor_tag = -1;
7788 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7791 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7792 if (mode != COMPOSE_REDIRECT) {
7793 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7794 strcmp(prefs_common.dictionary, "")) {
7795 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7796 prefs_common.alt_dictionary,
7797 conv_get_locale_charset_str(),
7798 prefs_common.misspelled_col,
7799 prefs_common.check_while_typing,
7800 prefs_common.recheck_when_changing_dict,
7801 prefs_common.use_alternate,
7802 prefs_common.use_both_dicts,
7803 GTK_TEXT_VIEW(text),
7804 GTK_WINDOW(compose->window),
7805 compose_dict_changed,
7806 compose_spell_menu_changed,
7809 alertpanel_error(_("Spell checker could not "
7811 gtkaspell_checkers_strerror());
7812 gtkaspell_checkers_reset_error();
7814 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7818 compose->gtkaspell = gtkaspell;
7819 compose_spell_menu_changed(compose);
7820 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7823 compose_select_account(compose, account, TRUE);
7825 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7826 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7828 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7829 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7831 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7832 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7834 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7835 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7837 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7838 if (account->protocol != A_NNTP)
7839 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7840 prefs_common_translated_header_name("To:"));
7842 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7843 prefs_common_translated_header_name("Newsgroups:"));
7845 #ifndef USE_NEW_ADDRBOOK
7846 addressbook_set_target_compose(compose);
7848 if (mode != COMPOSE_REDIRECT)
7849 compose_set_template_menu(compose);
7851 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7854 compose_list = g_list_append(compose_list, compose);
7856 if (!prefs_common.show_ruler)
7857 gtk_widget_hide(ruler_hbox);
7859 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7862 compose->priority = PRIORITY_NORMAL;
7863 compose_update_priority_menu_item(compose);
7865 compose_set_out_encoding(compose);
7868 compose_update_actions_menu(compose);
7870 /* Privacy Systems menu */
7871 compose_update_privacy_systems_menu(compose);
7873 activate_privacy_system(compose, account, TRUE);
7874 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7876 gtk_widget_realize(window);
7878 gtk_widget_show(window);
7880 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7881 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7888 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7893 GtkWidget *optmenubox;
7896 GtkWidget *from_name = NULL;
7897 #if !(GTK_CHECK_VERSION(2,12,0))
7898 GtkTooltips *tips = compose->tooltips;
7901 gint num = 0, def_menu = 0;
7903 accounts = account_get_list();
7904 cm_return_val_if_fail(accounts != NULL, NULL);
7906 optmenubox = gtk_event_box_new();
7907 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7908 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7910 hbox = gtk_hbox_new(FALSE, 6);
7911 from_name = gtk_entry_new();
7913 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7914 G_CALLBACK(compose_grab_focus_cb), compose);
7916 for (; accounts != NULL; accounts = accounts->next, num++) {
7917 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7918 gchar *name, *from = NULL;
7920 if (ac == compose->account) def_menu = num;
7922 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7925 if (ac == compose->account) {
7926 if (ac->name && *ac->name) {
7928 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7929 from = g_strdup_printf("%s <%s>",
7931 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7933 from = g_strdup_printf("%s",
7935 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7938 COMBOBOX_ADD(menu, name, ac->account_id);
7943 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7945 g_signal_connect(G_OBJECT(optmenu), "changed",
7946 G_CALLBACK(account_activated),
7948 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7949 G_CALLBACK(compose_entry_popup_extend),
7952 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7953 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7955 CLAWS_SET_TIP(optmenubox,
7956 _("Account to use for this email"));
7957 CLAWS_SET_TIP(from_name,
7958 _("Sender address to be used"));
7960 compose->account_combo = optmenu;
7961 compose->from_name = from_name;
7966 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7968 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7969 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7970 Compose *compose = (Compose *) data;
7972 compose->priority = value;
7976 static void compose_reply_change_mode(Compose *compose,
7979 gboolean was_modified = compose->modified;
7981 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7983 cm_return_if_fail(compose->replyinfo != NULL);
7985 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7987 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7989 if (action == COMPOSE_REPLY_TO_ALL)
7991 if (action == COMPOSE_REPLY_TO_SENDER)
7993 if (action == COMPOSE_REPLY_TO_LIST)
7996 compose_remove_header_entries(compose);
7997 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7998 if (compose->account->set_autocc && compose->account->auto_cc)
7999 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8001 if (compose->account->set_autobcc && compose->account->auto_bcc)
8002 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8004 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8005 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8006 compose_show_first_last_header(compose, TRUE);
8007 compose->modified = was_modified;
8008 compose_set_title(compose);
8011 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8013 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8014 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8015 Compose *compose = (Compose *) data;
8018 compose_reply_change_mode(compose, value);
8021 static void compose_update_priority_menu_item(Compose * compose)
8023 GtkWidget *menuitem = NULL;
8024 switch (compose->priority) {
8025 case PRIORITY_HIGHEST:
8026 menuitem = gtk_ui_manager_get_widget
8027 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8030 menuitem = gtk_ui_manager_get_widget
8031 (compose->ui_manager, "/Menu/Options/Priority/High");
8033 case PRIORITY_NORMAL:
8034 menuitem = gtk_ui_manager_get_widget
8035 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8038 menuitem = gtk_ui_manager_get_widget
8039 (compose->ui_manager, "/Menu/Options/Priority/Low");
8041 case PRIORITY_LOWEST:
8042 menuitem = gtk_ui_manager_get_widget
8043 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8046 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8049 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8051 Compose *compose = (Compose *) data;
8053 gboolean can_sign = FALSE, can_encrypt = FALSE;
8055 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8057 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8060 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8061 g_free(compose->privacy_system);
8062 compose->privacy_system = NULL;
8063 if (systemid != NULL) {
8064 compose->privacy_system = g_strdup(systemid);
8066 can_sign = privacy_system_can_sign(systemid);
8067 can_encrypt = privacy_system_can_encrypt(systemid);
8070 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8072 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8073 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8076 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8078 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8079 GtkWidget *menuitem = NULL;
8080 GList *children, *amenu;
8081 gboolean can_sign = FALSE, can_encrypt = FALSE;
8082 gboolean found = FALSE;
8084 if (compose->privacy_system != NULL) {
8086 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8087 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8088 cm_return_if_fail(menuitem != NULL);
8090 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8093 while (amenu != NULL) {
8094 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8095 if (systemid != NULL) {
8096 if (strcmp(systemid, compose->privacy_system) == 0 &&
8097 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8098 menuitem = GTK_WIDGET(amenu->data);
8100 can_sign = privacy_system_can_sign(systemid);
8101 can_encrypt = privacy_system_can_encrypt(systemid);
8105 } else if (strlen(compose->privacy_system) == 0 &&
8106 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8107 menuitem = GTK_WIDGET(amenu->data);
8110 can_encrypt = FALSE;
8115 amenu = amenu->next;
8117 g_list_free(children);
8118 if (menuitem != NULL)
8119 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8121 if (warn && !found && strlen(compose->privacy_system)) {
8122 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8123 "will not be able to sign or encrypt this message."),
8124 compose->privacy_system);
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_set_out_encoding(Compose *compose)
8134 CharSet out_encoding;
8135 const gchar *branch = NULL;
8136 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8138 switch(out_encoding) {
8139 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8140 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8141 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8142 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8143 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8144 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8145 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8146 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8147 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8148 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8149 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8150 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8151 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8152 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8153 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8154 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8155 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8156 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8157 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8158 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8159 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8160 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8161 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8162 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8163 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8164 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8165 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8166 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8167 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8168 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8169 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8170 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8171 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8173 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8176 static void compose_set_template_menu(Compose *compose)
8178 GSList *tmpl_list, *cur;
8182 tmpl_list = template_get_config();
8184 menu = gtk_menu_new();
8186 gtk_menu_set_accel_group (GTK_MENU (menu),
8187 gtk_ui_manager_get_accel_group(compose->ui_manager));
8188 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8189 Template *tmpl = (Template *)cur->data;
8190 gchar *accel_path = NULL;
8191 item = gtk_menu_item_new_with_label(tmpl->name);
8192 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8193 g_signal_connect(G_OBJECT(item), "activate",
8194 G_CALLBACK(compose_template_activate_cb),
8196 g_object_set_data(G_OBJECT(item), "template", tmpl);
8197 gtk_widget_show(item);
8198 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8199 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8203 gtk_widget_show(menu);
8204 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8207 void compose_update_actions_menu(Compose *compose)
8209 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8212 static void compose_update_privacy_systems_menu(Compose *compose)
8214 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8215 GSList *systems, *cur;
8217 GtkWidget *system_none;
8219 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8220 GtkWidget *privacy_menu = gtk_menu_new();
8222 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8223 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8225 g_signal_connect(G_OBJECT(system_none), "activate",
8226 G_CALLBACK(compose_set_privacy_system_cb), compose);
8228 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8229 gtk_widget_show(system_none);
8231 systems = privacy_get_system_ids();
8232 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8233 gchar *systemid = cur->data;
8235 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8236 widget = gtk_radio_menu_item_new_with_label(group,
8237 privacy_system_get_name(systemid));
8238 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8239 g_strdup(systemid), g_free);
8240 g_signal_connect(G_OBJECT(widget), "activate",
8241 G_CALLBACK(compose_set_privacy_system_cb), compose);
8243 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8244 gtk_widget_show(widget);
8247 g_slist_free(systems);
8248 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8249 gtk_widget_show_all(privacy_menu);
8250 gtk_widget_show_all(privacy_menuitem);
8253 void compose_reflect_prefs_all(void)
8258 for (cur = compose_list; cur != NULL; cur = cur->next) {
8259 compose = (Compose *)cur->data;
8260 compose_set_template_menu(compose);
8264 void compose_reflect_prefs_pixmap_theme(void)
8269 for (cur = compose_list; cur != NULL; cur = cur->next) {
8270 compose = (Compose *)cur->data;
8271 toolbar_update(TOOLBAR_COMPOSE, compose);
8275 static const gchar *compose_quote_char_from_context(Compose *compose)
8277 const gchar *qmark = NULL;
8279 cm_return_val_if_fail(compose != NULL, NULL);
8281 switch (compose->mode) {
8282 /* use forward-specific quote char */
8283 case COMPOSE_FORWARD:
8284 case COMPOSE_FORWARD_AS_ATTACH:
8285 case COMPOSE_FORWARD_INLINE:
8286 if (compose->folder && compose->folder->prefs &&
8287 compose->folder->prefs->forward_with_format)
8288 qmark = compose->folder->prefs->forward_quotemark;
8289 else if (compose->account->forward_with_format)
8290 qmark = compose->account->forward_quotemark;
8292 qmark = prefs_common.fw_quotemark;
8295 /* use reply-specific quote char in all other modes */
8297 if (compose->folder && compose->folder->prefs &&
8298 compose->folder->prefs->reply_with_format)
8299 qmark = compose->folder->prefs->reply_quotemark;
8300 else if (compose->account->reply_with_format)
8301 qmark = compose->account->reply_quotemark;
8303 qmark = prefs_common.quotemark;
8307 if (qmark == NULL || *qmark == '\0')
8313 static void compose_template_apply(Compose *compose, Template *tmpl,
8317 GtkTextBuffer *buffer;
8321 gchar *parsed_str = NULL;
8322 gint cursor_pos = 0;
8323 const gchar *err_msg = _("The body of the template has an error at line %d.");
8326 /* process the body */
8328 text = GTK_TEXT_VIEW(compose->text);
8329 buffer = gtk_text_view_get_buffer(text);
8332 qmark = compose_quote_char_from_context(compose);
8334 if (compose->replyinfo != NULL) {
8337 gtk_text_buffer_set_text(buffer, "", -1);
8338 mark = gtk_text_buffer_get_insert(buffer);
8339 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8341 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8342 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8344 } else if (compose->fwdinfo != NULL) {
8347 gtk_text_buffer_set_text(buffer, "", -1);
8348 mark = gtk_text_buffer_get_insert(buffer);
8349 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8351 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8352 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8355 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8357 GtkTextIter start, end;
8360 gtk_text_buffer_get_start_iter(buffer, &start);
8361 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8362 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8364 /* clear the buffer now */
8366 gtk_text_buffer_set_text(buffer, "", -1);
8368 parsed_str = compose_quote_fmt(compose, dummyinfo,
8369 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8370 procmsg_msginfo_free( dummyinfo );
8376 gtk_text_buffer_set_text(buffer, "", -1);
8377 mark = gtk_text_buffer_get_insert(buffer);
8378 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8381 if (replace && parsed_str && compose->account->auto_sig)
8382 compose_insert_sig(compose, FALSE);
8384 if (replace && parsed_str) {
8385 gtk_text_buffer_get_start_iter(buffer, &iter);
8386 gtk_text_buffer_place_cursor(buffer, &iter);
8390 cursor_pos = quote_fmt_get_cursor_pos();
8391 compose->set_cursor_pos = cursor_pos;
8392 if (cursor_pos == -1)
8394 gtk_text_buffer_get_start_iter(buffer, &iter);
8395 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8396 gtk_text_buffer_place_cursor(buffer, &iter);
8399 /* process the other fields */
8401 compose_template_apply_fields(compose, tmpl);
8402 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8403 quote_fmt_reset_vartable();
8404 compose_changed_cb(NULL, compose);
8407 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8408 gtkaspell_highlight_all(compose->gtkaspell);
8412 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8414 MsgInfo* dummyinfo = NULL;
8415 MsgInfo *msginfo = NULL;
8418 if (compose->replyinfo != NULL)
8419 msginfo = compose->replyinfo;
8420 else if (compose->fwdinfo != NULL)
8421 msginfo = compose->fwdinfo;
8423 dummyinfo = compose_msginfo_new_from_compose(compose);
8424 msginfo = dummyinfo;
8427 if (tmpl->from && *tmpl->from != '\0') {
8429 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8430 compose->gtkaspell);
8432 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8434 quote_fmt_scan_string(tmpl->from);
8437 buf = quote_fmt_get_buffer();
8439 alertpanel_error(_("Template From format error."));
8441 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8445 if (tmpl->to && *tmpl->to != '\0') {
8447 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8448 compose->gtkaspell);
8450 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8452 quote_fmt_scan_string(tmpl->to);
8455 buf = quote_fmt_get_buffer();
8457 alertpanel_error(_("Template To format error."));
8459 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8463 if (tmpl->cc && *tmpl->cc != '\0') {
8465 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8466 compose->gtkaspell);
8468 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8470 quote_fmt_scan_string(tmpl->cc);
8473 buf = quote_fmt_get_buffer();
8475 alertpanel_error(_("Template Cc format error."));
8477 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8481 if (tmpl->bcc && *tmpl->bcc != '\0') {
8483 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8484 compose->gtkaspell);
8486 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8488 quote_fmt_scan_string(tmpl->bcc);
8491 buf = quote_fmt_get_buffer();
8493 alertpanel_error(_("Template Bcc format error."));
8495 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8499 /* process the subject */
8500 if (tmpl->subject && *tmpl->subject != '\0') {
8502 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8503 compose->gtkaspell);
8505 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8507 quote_fmt_scan_string(tmpl->subject);
8510 buf = quote_fmt_get_buffer();
8512 alertpanel_error(_("Template subject format error."));
8514 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8518 procmsg_msginfo_free( dummyinfo );
8521 static void compose_destroy(Compose *compose)
8523 GtkAllocation allocation;
8524 GtkTextBuffer *buffer;
8525 GtkClipboard *clipboard;
8527 compose_list = g_list_remove(compose_list, compose);
8529 if (compose->updating) {
8530 debug_print("danger, not destroying anything now\n");
8531 compose->deferred_destroy = TRUE;
8534 /* NOTE: address_completion_end() does nothing with the window
8535 * however this may change. */
8536 address_completion_end(compose->window);
8538 slist_free_strings(compose->to_list);
8539 g_slist_free(compose->to_list);
8540 slist_free_strings(compose->newsgroup_list);
8541 g_slist_free(compose->newsgroup_list);
8542 slist_free_strings(compose->header_list);
8543 g_slist_free(compose->header_list);
8545 slist_free_strings(extra_headers);
8546 extra_headers = NULL;
8548 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8550 g_hash_table_destroy(compose->email_hashtable);
8552 procmsg_msginfo_free(compose->targetinfo);
8553 procmsg_msginfo_free(compose->replyinfo);
8554 procmsg_msginfo_free(compose->fwdinfo);
8556 g_free(compose->replyto);
8557 g_free(compose->cc);
8558 g_free(compose->bcc);
8559 g_free(compose->newsgroups);
8560 g_free(compose->followup_to);
8562 g_free(compose->ml_post);
8564 g_free(compose->inreplyto);
8565 g_free(compose->references);
8566 g_free(compose->msgid);
8567 g_free(compose->boundary);
8569 g_free(compose->redirect_filename);
8570 if (compose->undostruct)
8571 undo_destroy(compose->undostruct);
8573 g_free(compose->sig_str);
8575 g_free(compose->exteditor_file);
8577 g_free(compose->orig_charset);
8579 g_free(compose->privacy_system);
8581 #ifndef USE_NEW_ADDRBOOK
8582 if (addressbook_get_target_compose() == compose)
8583 addressbook_set_target_compose(NULL);
8586 if (compose->gtkaspell) {
8587 gtkaspell_delete(compose->gtkaspell);
8588 compose->gtkaspell = NULL;
8592 if (!compose->batch) {
8593 gtk_widget_get_allocation(compose->window, &allocation);
8594 prefs_common.compose_width = allocation.width;
8595 prefs_common.compose_height = allocation.height;
8598 if (!gtk_widget_get_parent(compose->paned))
8599 gtk_widget_destroy(compose->paned);
8600 gtk_widget_destroy(compose->popupmenu);
8602 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8603 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8604 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8606 gtk_widget_destroy(compose->window);
8607 toolbar_destroy(compose->toolbar);
8608 g_free(compose->toolbar);
8609 g_mutex_free(compose->mutex);
8613 static void compose_attach_info_free(AttachInfo *ainfo)
8615 g_free(ainfo->file);
8616 g_free(ainfo->content_type);
8617 g_free(ainfo->name);
8618 g_free(ainfo->charset);
8622 static void compose_attach_update_label(Compose *compose)
8627 GtkTreeModel *model;
8632 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8633 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8634 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8638 while(gtk_tree_model_iter_next(model, &iter))
8641 text = g_strdup_printf("(%d)", i);
8642 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8646 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8648 Compose *compose = (Compose *)data;
8649 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8650 GtkTreeSelection *selection;
8652 GtkTreeModel *model;
8654 selection = gtk_tree_view_get_selection(tree_view);
8655 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8660 for (cur = sel; cur != NULL; cur = cur->next) {
8661 GtkTreePath *path = cur->data;
8662 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8665 gtk_tree_path_free(path);
8668 for (cur = sel; cur != NULL; cur = cur->next) {
8669 GtkTreeRowReference *ref = cur->data;
8670 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8673 if (gtk_tree_model_get_iter(model, &iter, path))
8674 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8676 gtk_tree_path_free(path);
8677 gtk_tree_row_reference_free(ref);
8681 compose_attach_update_label(compose);
8684 static struct _AttachProperty
8687 GtkWidget *mimetype_entry;
8688 GtkWidget *encoding_optmenu;
8689 GtkWidget *path_entry;
8690 GtkWidget *filename_entry;
8692 GtkWidget *cancel_btn;
8695 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8697 gtk_tree_path_free((GtkTreePath *)ptr);
8700 static void compose_attach_property(GtkAction *action, gpointer data)
8702 Compose *compose = (Compose *)data;
8703 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8705 GtkComboBox *optmenu;
8706 GtkTreeSelection *selection;
8708 GtkTreeModel *model;
8711 static gboolean cancelled;
8713 /* only if one selected */
8714 selection = gtk_tree_view_get_selection(tree_view);
8715 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8718 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8722 path = (GtkTreePath *) sel->data;
8723 gtk_tree_model_get_iter(model, &iter, path);
8724 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8727 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8733 if (!attach_prop.window)
8734 compose_attach_property_create(&cancelled);
8735 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8736 gtk_widget_grab_focus(attach_prop.ok_btn);
8737 gtk_widget_show(attach_prop.window);
8738 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8739 GTK_WINDOW(compose->window));
8741 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8742 if (ainfo->encoding == ENC_UNKNOWN)
8743 combobox_select_by_data(optmenu, ENC_BASE64);
8745 combobox_select_by_data(optmenu, ainfo->encoding);
8747 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8748 ainfo->content_type ? ainfo->content_type : "");
8749 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8750 ainfo->file ? ainfo->file : "");
8751 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8752 ainfo->name ? ainfo->name : "");
8755 const gchar *entry_text;
8757 gchar *cnttype = NULL;
8764 gtk_widget_hide(attach_prop.window);
8765 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8770 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8771 if (*entry_text != '\0') {
8774 text = g_strstrip(g_strdup(entry_text));
8775 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8776 cnttype = g_strdup(text);
8779 alertpanel_error(_("Invalid MIME type."));
8785 ainfo->encoding = combobox_get_active_data(optmenu);
8787 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8788 if (*entry_text != '\0') {
8789 if (is_file_exist(entry_text) &&
8790 (size = get_file_size(entry_text)) > 0)
8791 file = g_strdup(entry_text);
8794 (_("File doesn't exist or is empty."));
8800 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8801 if (*entry_text != '\0') {
8802 g_free(ainfo->name);
8803 ainfo->name = g_strdup(entry_text);
8807 g_free(ainfo->content_type);
8808 ainfo->content_type = cnttype;
8811 g_free(ainfo->file);
8815 ainfo->size = (goffset)size;
8817 /* update tree store */
8818 text = to_human_readable(ainfo->size);
8819 gtk_tree_model_get_iter(model, &iter, path);
8820 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8821 COL_MIMETYPE, ainfo->content_type,
8823 COL_NAME, ainfo->name,
8824 COL_CHARSET, ainfo->charset,
8830 gtk_tree_path_free(path);
8833 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8835 label = gtk_label_new(str); \
8836 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8837 GTK_FILL, 0, 0, 0); \
8838 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8840 entry = gtk_entry_new(); \
8841 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8842 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8845 static void compose_attach_property_create(gboolean *cancelled)
8851 GtkWidget *mimetype_entry;
8854 GtkListStore *optmenu_menu;
8855 GtkWidget *path_entry;
8856 GtkWidget *filename_entry;
8859 GtkWidget *cancel_btn;
8860 GList *mime_type_list, *strlist;
8863 debug_print("Creating attach_property window...\n");
8865 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8866 gtk_widget_set_size_request(window, 480, -1);
8867 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8868 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8869 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8870 g_signal_connect(G_OBJECT(window), "delete_event",
8871 G_CALLBACK(attach_property_delete_event),
8873 g_signal_connect(G_OBJECT(window), "key_press_event",
8874 G_CALLBACK(attach_property_key_pressed),
8877 vbox = gtk_vbox_new(FALSE, 8);
8878 gtk_container_add(GTK_CONTAINER(window), vbox);
8880 table = gtk_table_new(4, 2, FALSE);
8881 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8882 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8883 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8885 label = gtk_label_new(_("MIME type"));
8886 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8888 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8889 mimetype_entry = gtk_combo_box_entry_new_text();
8890 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8891 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8893 /* stuff with list */
8894 mime_type_list = procmime_get_mime_type_list();
8896 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8897 MimeType *type = (MimeType *) mime_type_list->data;
8900 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8902 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8905 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8906 (GCompareFunc)strcmp2);
8909 for (mime_type_list = strlist; mime_type_list != NULL;
8910 mime_type_list = mime_type_list->next) {
8911 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8912 g_free(mime_type_list->data);
8914 g_list_free(strlist);
8915 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8916 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8918 label = gtk_label_new(_("Encoding"));
8919 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8921 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8923 hbox = gtk_hbox_new(FALSE, 0);
8924 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8925 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8927 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8928 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8930 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8931 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8932 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8933 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8934 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8936 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8938 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8939 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8941 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8942 &ok_btn, GTK_STOCK_OK,
8944 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8945 gtk_widget_grab_default(ok_btn);
8947 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8948 G_CALLBACK(attach_property_ok),
8950 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8951 G_CALLBACK(attach_property_cancel),
8954 gtk_widget_show_all(vbox);
8956 attach_prop.window = window;
8957 attach_prop.mimetype_entry = mimetype_entry;
8958 attach_prop.encoding_optmenu = optmenu;
8959 attach_prop.path_entry = path_entry;
8960 attach_prop.filename_entry = filename_entry;
8961 attach_prop.ok_btn = ok_btn;
8962 attach_prop.cancel_btn = cancel_btn;
8965 #undef SET_LABEL_AND_ENTRY
8967 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8973 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8979 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8980 gboolean *cancelled)
8988 static gboolean attach_property_key_pressed(GtkWidget *widget,
8990 gboolean *cancelled)
8992 if (event && event->keyval == GDK_KEY_Escape) {
8996 if (event && event->keyval == GDK_KEY_Return) {
9004 static void compose_exec_ext_editor(Compose *compose)
9011 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9012 G_DIR_SEPARATOR, compose);
9014 if (pipe(pipe_fds) < 0) {
9020 if ((pid = fork()) < 0) {
9027 /* close the write side of the pipe */
9030 compose->exteditor_file = g_strdup(tmp);
9031 compose->exteditor_pid = pid;
9033 compose_set_ext_editor_sensitive(compose, FALSE);
9036 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9038 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9040 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9044 } else { /* process-monitoring process */
9050 /* close the read side of the pipe */
9053 if (compose_write_body_to_file(compose, tmp) < 0) {
9054 fd_write_all(pipe_fds[1], "2\n", 2);
9058 pid_ed = compose_exec_ext_editor_real(tmp);
9060 fd_write_all(pipe_fds[1], "1\n", 2);
9064 /* wait until editor is terminated */
9065 waitpid(pid_ed, NULL, 0);
9067 fd_write_all(pipe_fds[1], "0\n", 2);
9074 #endif /* G_OS_UNIX */
9078 static gint compose_exec_ext_editor_real(const gchar *file)
9085 cm_return_val_if_fail(file != NULL, -1);
9087 if ((pid = fork()) < 0) {
9092 if (pid != 0) return pid;
9094 /* grandchild process */
9096 if (setpgid(0, getppid()))
9099 if (prefs_common_get_ext_editor_cmd() &&
9100 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9101 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9102 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9104 if (prefs_common_get_ext_editor_cmd())
9105 g_warning("External editor command-line is invalid: '%s'\n",
9106 prefs_common_get_ext_editor_cmd());
9107 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9110 cmdline = strsplit_with_quote(buf, " ", 1024);
9111 execvp(cmdline[0], cmdline);
9114 g_strfreev(cmdline);
9119 static gboolean compose_ext_editor_kill(Compose *compose)
9121 pid_t pgid = compose->exteditor_pid * -1;
9124 ret = kill(pgid, 0);
9126 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9130 msg = g_strdup_printf
9131 (_("The external editor is still working.\n"
9132 "Force terminating the process?\n"
9133 "process group id: %d"), -pgid);
9134 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9135 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9139 if (val == G_ALERTALTERNATE) {
9140 g_source_remove(compose->exteditor_tag);
9141 g_io_channel_shutdown(compose->exteditor_ch,
9143 g_io_channel_unref(compose->exteditor_ch);
9145 if (kill(pgid, SIGTERM) < 0) perror("kill");
9146 waitpid(compose->exteditor_pid, NULL, 0);
9148 g_warning("Terminated process group id: %d", -pgid);
9149 g_warning("Temporary file: %s",
9150 compose->exteditor_file);
9152 compose_set_ext_editor_sensitive(compose, TRUE);
9154 g_free(compose->exteditor_file);
9155 compose->exteditor_file = NULL;
9156 compose->exteditor_pid = -1;
9157 compose->exteditor_ch = NULL;
9158 compose->exteditor_tag = -1;
9166 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9170 Compose *compose = (Compose *)data;
9173 debug_print("Compose: input from monitoring process\n");
9175 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9177 g_io_channel_shutdown(source, FALSE, NULL);
9178 g_io_channel_unref(source);
9180 waitpid(compose->exteditor_pid, NULL, 0);
9182 if (buf[0] == '0') { /* success */
9183 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9184 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9186 gtk_text_buffer_set_text(buffer, "", -1);
9187 compose_insert_file(compose, compose->exteditor_file);
9188 compose_changed_cb(NULL, compose);
9189 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9191 if (claws_unlink(compose->exteditor_file) < 0)
9192 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9193 } else if (buf[0] == '1') { /* failed */
9194 g_warning("Couldn't exec external editor\n");
9195 if (claws_unlink(compose->exteditor_file) < 0)
9196 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9197 } else if (buf[0] == '2') {
9198 g_warning("Couldn't write to file\n");
9199 } else if (buf[0] == '3') {
9200 g_warning("Pipe read failed\n");
9203 compose_set_ext_editor_sensitive(compose, TRUE);
9205 g_free(compose->exteditor_file);
9206 compose->exteditor_file = NULL;
9207 compose->exteditor_pid = -1;
9208 compose->exteditor_ch = NULL;
9209 compose->exteditor_tag = -1;
9214 static void compose_set_ext_editor_sensitive(Compose *compose,
9217 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9218 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9219 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9220 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9221 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9222 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9223 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9225 gtk_widget_set_sensitive(compose->text, sensitive);
9226 if (compose->toolbar->send_btn)
9227 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9228 if (compose->toolbar->sendl_btn)
9229 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9230 if (compose->toolbar->draft_btn)
9231 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9232 if (compose->toolbar->insert_btn)
9233 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9234 if (compose->toolbar->sig_btn)
9235 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9236 if (compose->toolbar->exteditor_btn)
9237 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9238 if (compose->toolbar->linewrap_current_btn)
9239 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9240 if (compose->toolbar->linewrap_all_btn)
9241 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9243 #endif /* G_OS_UNIX */
9246 * compose_undo_state_changed:
9248 * Change the sensivity of the menuentries undo and redo
9250 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9251 gint redo_state, gpointer data)
9253 Compose *compose = (Compose *)data;
9255 switch (undo_state) {
9256 case UNDO_STATE_TRUE:
9257 if (!undostruct->undo_state) {
9258 undostruct->undo_state = TRUE;
9259 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9262 case UNDO_STATE_FALSE:
9263 if (undostruct->undo_state) {
9264 undostruct->undo_state = FALSE;
9265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9268 case UNDO_STATE_UNCHANGED:
9270 case UNDO_STATE_REFRESH:
9271 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9274 g_warning("Undo state not recognized");
9278 switch (redo_state) {
9279 case UNDO_STATE_TRUE:
9280 if (!undostruct->redo_state) {
9281 undostruct->redo_state = TRUE;
9282 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9285 case UNDO_STATE_FALSE:
9286 if (undostruct->redo_state) {
9287 undostruct->redo_state = FALSE;
9288 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9291 case UNDO_STATE_UNCHANGED:
9293 case UNDO_STATE_REFRESH:
9294 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9297 g_warning("Redo state not recognized");
9302 /* callback functions */
9304 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9305 GtkAllocation *allocation,
9308 prefs_common.compose_notebook_height = allocation->height;
9311 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9312 * includes "non-client" (windows-izm) in calculation, so this calculation
9313 * may not be accurate.
9315 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9316 GtkAllocation *allocation,
9317 GtkSHRuler *shruler)
9319 if (prefs_common.show_ruler) {
9320 gint char_width = 0, char_height = 0;
9321 gint line_width_in_chars;
9323 gtkut_get_font_size(GTK_WIDGET(widget),
9324 &char_width, &char_height);
9325 line_width_in_chars =
9326 (allocation->width - allocation->x) / char_width;
9328 /* got the maximum */
9329 gtk_shruler_set_range(GTK_SHRULER(shruler),
9330 0.0, line_width_in_chars, 0);
9339 ComposePrefType type;
9340 gboolean entry_marked;
9343 static void account_activated(GtkComboBox *optmenu, gpointer data)
9345 Compose *compose = (Compose *)data;
9348 gchar *folderidentifier;
9349 gint account_id = 0;
9352 GSList *list, *saved_list = NULL;
9353 HeaderEntryState *state;
9354 GtkRcStyle *style = NULL;
9355 static GdkColor yellow;
9356 static gboolean color_set = FALSE;
9358 /* Get ID of active account in the combo box */
9359 menu = gtk_combo_box_get_model(optmenu);
9360 gtk_combo_box_get_active_iter(optmenu, &iter);
9361 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9363 ac = account_find_from_id(account_id);
9364 cm_return_if_fail(ac != NULL);
9366 if (ac != compose->account) {
9367 compose_select_account(compose, ac, FALSE);
9369 for (list = compose->header_list; list; list = list->next) {
9370 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9372 if (hentry->type == PREF_ACCOUNT || !list->next) {
9373 compose_destroy_headerentry(compose, hentry);
9377 state = g_malloc0(sizeof(HeaderEntryState));
9378 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9379 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9380 state->entry = gtk_editable_get_chars(
9381 GTK_EDITABLE(hentry->entry), 0, -1);
9382 state->type = hentry->type;
9385 gdk_color_parse("#f5f6be", &yellow);
9386 color_set = gdk_colormap_alloc_color(
9387 gdk_colormap_get_system(),
9388 &yellow, FALSE, TRUE);
9391 style = gtk_widget_get_modifier_style(hentry->entry);
9392 state->entry_marked = gdk_color_equal(&yellow,
9393 &style->base[GTK_STATE_NORMAL]);
9395 saved_list = g_slist_append(saved_list, state);
9396 compose_destroy_headerentry(compose, hentry);
9399 compose->header_last = NULL;
9400 g_slist_free(compose->header_list);
9401 compose->header_list = NULL;
9402 compose->header_nextrow = 1;
9403 compose_create_header_entry(compose);
9405 if (ac->set_autocc && ac->auto_cc)
9406 compose_entry_append(compose, ac->auto_cc,
9407 COMPOSE_CC, PREF_ACCOUNT);
9409 if (ac->set_autobcc && ac->auto_bcc)
9410 compose_entry_append(compose, ac->auto_bcc,
9411 COMPOSE_BCC, PREF_ACCOUNT);
9413 if (ac->set_autoreplyto && ac->auto_replyto)
9414 compose_entry_append(compose, ac->auto_replyto,
9415 COMPOSE_REPLYTO, PREF_ACCOUNT);
9417 for (list = saved_list; list; list = list->next) {
9418 state = (HeaderEntryState *) list->data;
9420 compose_add_header_entry(compose, state->header,
9421 state->entry, state->type);
9422 if (state->entry_marked)
9423 compose_entry_mark_default_to(compose, state->entry);
9425 g_free(state->header);
9426 g_free(state->entry);
9429 g_slist_free(saved_list);
9431 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9432 (ac->protocol == A_NNTP) ?
9433 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9436 /* Set message save folder */
9437 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9438 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9440 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9441 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9443 compose_set_save_to(compose, NULL);
9444 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9445 folderidentifier = folder_item_get_identifier(account_get_special_folder
9446 (compose->account, F_OUTBOX));
9447 compose_set_save_to(compose, folderidentifier);
9448 g_free(folderidentifier);
9452 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9453 GtkTreeViewColumn *column, Compose *compose)
9455 compose_attach_property(NULL, compose);
9458 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9461 Compose *compose = (Compose *)data;
9462 GtkTreeSelection *attach_selection;
9463 gint attach_nr_selected;
9465 if (!event) return FALSE;
9467 if (event->button == 3) {
9468 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9469 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9471 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9472 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9474 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9475 NULL, NULL, event->button, event->time);
9482 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9485 Compose *compose = (Compose *)data;
9487 if (!event) return FALSE;
9489 switch (event->keyval) {
9490 case GDK_KEY_Delete:
9491 compose_attach_remove_selected(NULL, compose);
9497 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9499 toolbar_comp_set_sensitive(compose, allow);
9500 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9501 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9503 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9505 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9506 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9507 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9509 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9513 static void compose_send_cb(GtkAction *action, gpointer data)
9515 Compose *compose = (Compose *)data;
9517 if (prefs_common.work_offline &&
9518 !inc_offline_should_override(TRUE,
9519 _("Claws Mail needs network access in order "
9520 "to send this email.")))
9523 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9524 g_source_remove(compose->draft_timeout_tag);
9525 compose->draft_timeout_tag = -1;
9528 compose_send(compose);
9531 static void compose_send_later_cb(GtkAction *action, gpointer data)
9533 Compose *compose = (Compose *)data;
9537 compose_allow_user_actions(compose, FALSE);
9538 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9539 compose_allow_user_actions(compose, TRUE);
9543 compose_close(compose);
9544 } else if (val == -1) {
9545 alertpanel_error(_("Could not queue message."));
9546 } else if (val == -2) {
9547 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9548 } else if (val == -3) {
9549 if (privacy_peek_error())
9550 alertpanel_error(_("Could not queue message for sending:\n\n"
9551 "Signature failed: %s"), privacy_get_error());
9552 } else if (val == -4) {
9553 alertpanel_error(_("Could not queue message for sending:\n\n"
9554 "Charset conversion failed."));
9555 } else if (val == -5) {
9556 alertpanel_error(_("Could not queue message for sending:\n\n"
9557 "Couldn't get recipient encryption key."));
9558 } else if (val == -6) {
9561 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9564 #define DRAFTED_AT_EXIT "drafted_at_exit"
9565 static void compose_register_draft(MsgInfo *info)
9567 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9568 DRAFTED_AT_EXIT, NULL);
9569 FILE *fp = g_fopen(filepath, "ab");
9572 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9580 gboolean compose_draft (gpointer data, guint action)
9582 Compose *compose = (Compose *)data;
9587 MsgFlags flag = {0, 0};
9588 static gboolean lock = FALSE;
9589 MsgInfo *newmsginfo;
9591 gboolean target_locked = FALSE;
9592 gboolean err = FALSE;
9594 if (lock) return FALSE;
9596 if (compose->sending)
9599 draft = account_get_special_folder(compose->account, F_DRAFT);
9600 cm_return_val_if_fail(draft != NULL, FALSE);
9602 if (!g_mutex_trylock(compose->mutex)) {
9603 /* we don't want to lock the mutex once it's available,
9604 * because as the only other part of compose.c locking
9605 * it is compose_close - which means once unlocked,
9606 * the compose struct will be freed */
9607 debug_print("couldn't lock mutex, probably sending\n");
9613 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9614 G_DIR_SEPARATOR, compose);
9615 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9616 FILE_OP_ERROR(tmp, "fopen");
9620 /* chmod for security */
9621 if (change_file_mode_rw(fp, tmp) < 0) {
9622 FILE_OP_ERROR(tmp, "chmod");
9623 g_warning("can't change file mode\n");
9626 /* Save draft infos */
9627 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9628 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9630 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9631 gchar *savefolderid;
9633 savefolderid = compose_get_save_to(compose);
9634 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9635 g_free(savefolderid);
9637 if (compose->return_receipt) {
9638 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9640 if (compose->privacy_system) {
9641 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9642 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9643 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9646 /* Message-ID of message replying to */
9647 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9650 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9651 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9654 /* Message-ID of message forwarding to */
9655 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9658 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9659 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9663 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9664 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9666 sheaders = compose_get_manual_headers_info(compose);
9667 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9670 /* end of headers */
9671 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9678 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9682 if (fclose(fp) == EOF) {
9686 if (compose->targetinfo) {
9687 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9688 flag.perm_flags = target_locked?MSG_LOCKED:0;
9690 flag.tmp_flags = MSG_DRAFT;
9692 folder_item_scan(draft);
9693 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9694 MsgInfo *tmpinfo = NULL;
9695 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9696 if (compose->msgid) {
9697 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9700 msgnum = tmpinfo->msgnum;
9701 procmsg_msginfo_free(tmpinfo);
9702 debug_print("got draft msgnum %d from scanning\n", msgnum);
9704 debug_print("didn't get draft msgnum after scanning\n");
9707 debug_print("got draft msgnum %d from adding\n", msgnum);
9713 if (action != COMPOSE_AUTO_SAVE) {
9714 if (action != COMPOSE_DRAFT_FOR_EXIT)
9715 alertpanel_error(_("Could not save draft."));
9718 gtkut_window_popup(compose->window);
9719 val = alertpanel_full(_("Could not save draft"),
9720 _("Could not save draft.\n"
9721 "Do you want to cancel exit or discard this email?"),
9722 _("_Cancel exit"), _("_Discard email"), NULL,
9723 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9724 if (val == G_ALERTALTERNATE) {
9726 g_mutex_unlock(compose->mutex); /* must be done before closing */
9727 compose_close(compose);
9731 g_mutex_unlock(compose->mutex); /* must be done before closing */
9740 if (compose->mode == COMPOSE_REEDIT) {
9741 compose_remove_reedit_target(compose, TRUE);
9744 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9747 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9749 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9751 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9752 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9753 procmsg_msginfo_set_flags(newmsginfo, 0,
9754 MSG_HAS_ATTACHMENT);
9756 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9757 compose_register_draft(newmsginfo);
9759 procmsg_msginfo_free(newmsginfo);
9762 folder_item_scan(draft);
9764 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9766 g_mutex_unlock(compose->mutex); /* must be done before closing */
9767 compose_close(compose);
9773 path = folder_item_fetch_msg(draft, msgnum);
9775 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9778 if (g_stat(path, &s) < 0) {
9779 FILE_OP_ERROR(path, "stat");
9785 procmsg_msginfo_free(compose->targetinfo);
9786 compose->targetinfo = procmsg_msginfo_new();
9787 compose->targetinfo->msgnum = msgnum;
9788 compose->targetinfo->size = (goffset)s.st_size;
9789 compose->targetinfo->mtime = s.st_mtime;
9790 compose->targetinfo->folder = draft;
9792 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9793 compose->mode = COMPOSE_REEDIT;
9795 if (action == COMPOSE_AUTO_SAVE) {
9796 compose->autosaved_draft = compose->targetinfo;
9798 compose->modified = FALSE;
9799 compose_set_title(compose);
9803 g_mutex_unlock(compose->mutex);
9807 void compose_clear_exit_drafts(void)
9809 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9810 DRAFTED_AT_EXIT, NULL);
9811 if (is_file_exist(filepath))
9812 claws_unlink(filepath);
9817 void compose_reopen_exit_drafts(void)
9819 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9820 DRAFTED_AT_EXIT, NULL);
9821 FILE *fp = g_fopen(filepath, "rb");
9825 while (fgets(buf, sizeof(buf), fp)) {
9826 gchar **parts = g_strsplit(buf, "\t", 2);
9827 const gchar *folder = parts[0];
9828 int msgnum = parts[1] ? atoi(parts[1]):-1;
9830 if (folder && *folder && msgnum > -1) {
9831 FolderItem *item = folder_find_item_from_identifier(folder);
9832 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9834 compose_reedit(info, FALSE);
9841 compose_clear_exit_drafts();
9844 static void compose_save_cb(GtkAction *action, gpointer data)
9846 Compose *compose = (Compose *)data;
9847 compose_draft(compose, COMPOSE_KEEP_EDITING);
9848 compose->rmode = COMPOSE_REEDIT;
9851 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9853 if (compose && file_list) {
9856 for ( tmp = file_list; tmp; tmp = tmp->next) {
9857 gchar *file = (gchar *) tmp->data;
9858 gchar *utf8_filename = conv_filename_to_utf8(file);
9859 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9860 compose_changed_cb(NULL, compose);
9865 g_free(utf8_filename);
9870 static void compose_attach_cb(GtkAction *action, gpointer data)
9872 Compose *compose = (Compose *)data;
9875 if (compose->redirect_filename != NULL)
9878 /* Set focus_window properly, in case we were called via popup menu,
9879 * which unsets it (via focus_out_event callback on compose window). */
9880 manage_window_focus_in(compose->window, NULL, NULL);
9882 file_list = filesel_select_multiple_files_open(_("Select file"));
9885 compose_attach_from_list(compose, file_list, TRUE);
9886 g_list_free(file_list);
9890 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9892 Compose *compose = (Compose *)data;
9894 gint files_inserted = 0;
9896 file_list = filesel_select_multiple_files_open(_("Select file"));
9901 for ( tmp = file_list; tmp; tmp = tmp->next) {
9902 gchar *file = (gchar *) tmp->data;
9903 gchar *filedup = g_strdup(file);
9904 gchar *shortfile = g_path_get_basename(filedup);
9905 ComposeInsertResult res;
9906 /* insert the file if the file is short or if the user confirmed that
9907 he/she wants to insert the large file */
9908 res = compose_insert_file(compose, file);
9909 if (res == COMPOSE_INSERT_READ_ERROR) {
9910 alertpanel_error(_("File '%s' could not be read."), shortfile);
9911 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9912 alertpanel_error(_("File '%s' contained invalid characters\n"
9913 "for the current encoding, insertion may be incorrect."),
9915 } else if (res == COMPOSE_INSERT_SUCCESS)
9922 g_list_free(file_list);
9926 if (files_inserted > 0 && compose->gtkaspell &&
9927 compose->gtkaspell->check_while_typing)
9928 gtkaspell_highlight_all(compose->gtkaspell);
9932 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9934 Compose *compose = (Compose *)data;
9936 compose_insert_sig(compose, FALSE);
9939 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9943 Compose *compose = (Compose *)data;
9945 gtkut_widget_get_uposition(widget, &x, &y);
9946 if (!compose->batch) {
9947 prefs_common.compose_x = x;
9948 prefs_common.compose_y = y;
9950 if (compose->sending || compose->updating)
9952 compose_close_cb(NULL, compose);
9956 void compose_close_toolbar(Compose *compose)
9958 compose_close_cb(NULL, compose);
9961 static void compose_close_cb(GtkAction *action, gpointer data)
9963 Compose *compose = (Compose *)data;
9967 if (compose->exteditor_tag != -1) {
9968 if (!compose_ext_editor_kill(compose))
9973 if (compose->modified) {
9974 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9975 if (!g_mutex_trylock(compose->mutex)) {
9976 /* we don't want to lock the mutex once it's available,
9977 * because as the only other part of compose.c locking
9978 * it is compose_close - which means once unlocked,
9979 * the compose struct will be freed */
9980 debug_print("couldn't lock mutex, probably sending\n");
9984 val = alertpanel(_("Discard message"),
9985 _("This message has been modified. Discard it?"),
9986 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9988 val = alertpanel(_("Save changes"),
9989 _("This message has been modified. Save the latest changes?"),
9990 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9992 g_mutex_unlock(compose->mutex);
9994 case G_ALERTDEFAULT:
9995 if (prefs_common.autosave && !reedit)
9996 compose_remove_draft(compose);
9998 case G_ALERTALTERNATE:
9999 compose_draft(data, COMPOSE_QUIT_EDITING);
10006 compose_close(compose);
10009 static void compose_print_cb(GtkAction *action, gpointer data)
10011 Compose *compose = (Compose *) data;
10013 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10014 if (compose->targetinfo)
10015 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10018 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10020 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10021 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10022 Compose *compose = (Compose *) data;
10025 compose->out_encoding = (CharSet)value;
10028 static void compose_address_cb(GtkAction *action, gpointer data)
10030 Compose *compose = (Compose *)data;
10032 #ifndef USE_NEW_ADDRBOOK
10033 addressbook_open(compose);
10035 GError* error = NULL;
10036 addressbook_connect_signals(compose);
10037 addressbook_dbus_open(TRUE, &error);
10039 g_warning("%s", error->message);
10040 g_error_free(error);
10045 static void about_show_cb(GtkAction *action, gpointer data)
10050 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10052 Compose *compose = (Compose *)data;
10057 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10058 cm_return_if_fail(tmpl != NULL);
10060 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10062 val = alertpanel(_("Apply template"), msg,
10063 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10066 if (val == G_ALERTDEFAULT)
10067 compose_template_apply(compose, tmpl, TRUE);
10068 else if (val == G_ALERTALTERNATE)
10069 compose_template_apply(compose, tmpl, FALSE);
10072 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10074 Compose *compose = (Compose *)data;
10076 compose_exec_ext_editor(compose);
10079 static void compose_undo_cb(GtkAction *action, gpointer data)
10081 Compose *compose = (Compose *)data;
10082 gboolean prev_autowrap = compose->autowrap;
10084 compose->autowrap = FALSE;
10085 undo_undo(compose->undostruct);
10086 compose->autowrap = prev_autowrap;
10089 static void compose_redo_cb(GtkAction *action, gpointer data)
10091 Compose *compose = (Compose *)data;
10092 gboolean prev_autowrap = compose->autowrap;
10094 compose->autowrap = FALSE;
10095 undo_redo(compose->undostruct);
10096 compose->autowrap = prev_autowrap;
10099 static void entry_cut_clipboard(GtkWidget *entry)
10101 if (GTK_IS_EDITABLE(entry))
10102 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10103 else if (GTK_IS_TEXT_VIEW(entry))
10104 gtk_text_buffer_cut_clipboard(
10105 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10106 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10110 static void entry_copy_clipboard(GtkWidget *entry)
10112 if (GTK_IS_EDITABLE(entry))
10113 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10114 else if (GTK_IS_TEXT_VIEW(entry))
10115 gtk_text_buffer_copy_clipboard(
10116 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10117 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10120 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10121 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10123 if (GTK_IS_TEXT_VIEW(entry)) {
10124 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10125 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10126 GtkTextIter start_iter, end_iter;
10128 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10130 if (contents == NULL)
10133 /* we shouldn't delete the selection when middle-click-pasting, or we
10134 * can't mid-click-paste our own selection */
10135 if (clip != GDK_SELECTION_PRIMARY) {
10136 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10137 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10140 if (insert_place == NULL) {
10141 /* if insert_place isn't specified, insert at the cursor.
10142 * used for Ctrl-V pasting */
10143 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10144 start = gtk_text_iter_get_offset(&start_iter);
10145 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10147 /* if insert_place is specified, paste here.
10148 * used for mid-click-pasting */
10149 start = gtk_text_iter_get_offset(insert_place);
10150 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10151 if (prefs_common.primary_paste_unselects)
10152 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10156 /* paste unwrapped: mark the paste so it's not wrapped later */
10157 end = start + strlen(contents);
10158 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10159 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10160 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10161 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10162 /* rewrap paragraph now (after a mid-click-paste) */
10163 mark_start = gtk_text_buffer_get_insert(buffer);
10164 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10165 gtk_text_iter_backward_char(&start_iter);
10166 compose_beautify_paragraph(compose, &start_iter, TRUE);
10168 } else if (GTK_IS_EDITABLE(entry))
10169 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10171 compose->modified = TRUE;
10174 static void entry_allsel(GtkWidget *entry)
10176 if (GTK_IS_EDITABLE(entry))
10177 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10178 else if (GTK_IS_TEXT_VIEW(entry)) {
10179 GtkTextIter startiter, enditer;
10180 GtkTextBuffer *textbuf;
10182 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10183 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10184 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10186 gtk_text_buffer_move_mark_by_name(textbuf,
10187 "selection_bound", &startiter);
10188 gtk_text_buffer_move_mark_by_name(textbuf,
10189 "insert", &enditer);
10193 static void compose_cut_cb(GtkAction *action, gpointer data)
10195 Compose *compose = (Compose *)data;
10196 if (compose->focused_editable
10197 #ifndef GENERIC_UMPC
10198 && gtk_widget_has_focus(compose->focused_editable)
10201 entry_cut_clipboard(compose->focused_editable);
10204 static void compose_copy_cb(GtkAction *action, gpointer data)
10206 Compose *compose = (Compose *)data;
10207 if (compose->focused_editable
10208 #ifndef GENERIC_UMPC
10209 && gtk_widget_has_focus(compose->focused_editable)
10212 entry_copy_clipboard(compose->focused_editable);
10215 static void compose_paste_cb(GtkAction *action, gpointer data)
10217 Compose *compose = (Compose *)data;
10218 gint prev_autowrap;
10219 GtkTextBuffer *buffer;
10221 if (compose->focused_editable &&
10222 #ifndef GENERIC_UMPC
10223 gtk_widget_has_focus(compose->focused_editable)
10226 entry_paste_clipboard(compose, compose->focused_editable,
10227 prefs_common.linewrap_pastes,
10228 GDK_SELECTION_CLIPBOARD, NULL);
10233 #ifndef GENERIC_UMPC
10234 gtk_widget_has_focus(compose->text) &&
10236 compose->gtkaspell &&
10237 compose->gtkaspell->check_while_typing)
10238 gtkaspell_highlight_all(compose->gtkaspell);
10242 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10244 Compose *compose = (Compose *)data;
10245 gint wrap_quote = prefs_common.linewrap_quote;
10246 if (compose->focused_editable
10247 #ifndef GENERIC_UMPC
10248 && gtk_widget_has_focus(compose->focused_editable)
10251 /* let text_insert() (called directly or at a later time
10252 * after the gtk_editable_paste_clipboard) know that
10253 * text is to be inserted as a quotation. implemented
10254 * by using a simple refcount... */
10255 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10256 G_OBJECT(compose->focused_editable),
10257 "paste_as_quotation"));
10258 g_object_set_data(G_OBJECT(compose->focused_editable),
10259 "paste_as_quotation",
10260 GINT_TO_POINTER(paste_as_quotation + 1));
10261 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10262 entry_paste_clipboard(compose, compose->focused_editable,
10263 prefs_common.linewrap_pastes,
10264 GDK_SELECTION_CLIPBOARD, NULL);
10265 prefs_common.linewrap_quote = wrap_quote;
10269 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10271 Compose *compose = (Compose *)data;
10272 gint prev_autowrap;
10273 GtkTextBuffer *buffer;
10275 if (compose->focused_editable
10276 #ifndef GENERIC_UMPC
10277 && gtk_widget_has_focus(compose->focused_editable)
10280 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10281 GDK_SELECTION_CLIPBOARD, NULL);
10286 #ifndef GENERIC_UMPC
10287 gtk_widget_has_focus(compose->text) &&
10289 compose->gtkaspell &&
10290 compose->gtkaspell->check_while_typing)
10291 gtkaspell_highlight_all(compose->gtkaspell);
10295 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10297 Compose *compose = (Compose *)data;
10298 gint prev_autowrap;
10299 GtkTextBuffer *buffer;
10301 if (compose->focused_editable
10302 #ifndef GENERIC_UMPC
10303 && gtk_widget_has_focus(compose->focused_editable)
10306 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10307 GDK_SELECTION_CLIPBOARD, NULL);
10312 #ifndef GENERIC_UMPC
10313 gtk_widget_has_focus(compose->text) &&
10315 compose->gtkaspell &&
10316 compose->gtkaspell->check_while_typing)
10317 gtkaspell_highlight_all(compose->gtkaspell);
10321 static void compose_allsel_cb(GtkAction *action, gpointer data)
10323 Compose *compose = (Compose *)data;
10324 if (compose->focused_editable
10325 #ifndef GENERIC_UMPC
10326 && gtk_widget_has_focus(compose->focused_editable)
10329 entry_allsel(compose->focused_editable);
10332 static void textview_move_beginning_of_line (GtkTextView *text)
10334 GtkTextBuffer *buffer;
10338 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10340 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10341 mark = gtk_text_buffer_get_insert(buffer);
10342 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10343 gtk_text_iter_set_line_offset(&ins, 0);
10344 gtk_text_buffer_place_cursor(buffer, &ins);
10347 static void textview_move_forward_character (GtkTextView *text)
10349 GtkTextBuffer *buffer;
10353 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10355 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10356 mark = gtk_text_buffer_get_insert(buffer);
10357 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10358 if (gtk_text_iter_forward_cursor_position(&ins))
10359 gtk_text_buffer_place_cursor(buffer, &ins);
10362 static void textview_move_backward_character (GtkTextView *text)
10364 GtkTextBuffer *buffer;
10368 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10370 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10371 mark = gtk_text_buffer_get_insert(buffer);
10372 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10373 if (gtk_text_iter_backward_cursor_position(&ins))
10374 gtk_text_buffer_place_cursor(buffer, &ins);
10377 static void textview_move_forward_word (GtkTextView *text)
10379 GtkTextBuffer *buffer;
10384 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10386 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10387 mark = gtk_text_buffer_get_insert(buffer);
10388 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10389 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10390 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10391 gtk_text_iter_backward_word_start(&ins);
10392 gtk_text_buffer_place_cursor(buffer, &ins);
10396 static void textview_move_backward_word (GtkTextView *text)
10398 GtkTextBuffer *buffer;
10402 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10404 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10405 mark = gtk_text_buffer_get_insert(buffer);
10406 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10407 if (gtk_text_iter_backward_word_starts(&ins, 1))
10408 gtk_text_buffer_place_cursor(buffer, &ins);
10411 static void textview_move_end_of_line (GtkTextView *text)
10413 GtkTextBuffer *buffer;
10417 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10419 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10420 mark = gtk_text_buffer_get_insert(buffer);
10421 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10422 if (gtk_text_iter_forward_to_line_end(&ins))
10423 gtk_text_buffer_place_cursor(buffer, &ins);
10426 static void textview_move_next_line (GtkTextView *text)
10428 GtkTextBuffer *buffer;
10433 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10435 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10436 mark = gtk_text_buffer_get_insert(buffer);
10437 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10438 offset = gtk_text_iter_get_line_offset(&ins);
10439 if (gtk_text_iter_forward_line(&ins)) {
10440 gtk_text_iter_set_line_offset(&ins, offset);
10441 gtk_text_buffer_place_cursor(buffer, &ins);
10445 static void textview_move_previous_line (GtkTextView *text)
10447 GtkTextBuffer *buffer;
10452 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10454 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10455 mark = gtk_text_buffer_get_insert(buffer);
10456 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10457 offset = gtk_text_iter_get_line_offset(&ins);
10458 if (gtk_text_iter_backward_line(&ins)) {
10459 gtk_text_iter_set_line_offset(&ins, offset);
10460 gtk_text_buffer_place_cursor(buffer, &ins);
10464 static void textview_delete_forward_character (GtkTextView *text)
10466 GtkTextBuffer *buffer;
10468 GtkTextIter ins, end_iter;
10470 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10472 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10473 mark = gtk_text_buffer_get_insert(buffer);
10474 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10476 if (gtk_text_iter_forward_char(&end_iter)) {
10477 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10481 static void textview_delete_backward_character (GtkTextView *text)
10483 GtkTextBuffer *buffer;
10485 GtkTextIter ins, end_iter;
10487 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10489 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10490 mark = gtk_text_buffer_get_insert(buffer);
10491 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10493 if (gtk_text_iter_backward_char(&end_iter)) {
10494 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10498 static void textview_delete_forward_word (GtkTextView *text)
10500 GtkTextBuffer *buffer;
10502 GtkTextIter ins, end_iter;
10504 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10506 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10507 mark = gtk_text_buffer_get_insert(buffer);
10508 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10510 if (gtk_text_iter_forward_word_end(&end_iter)) {
10511 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10515 static void textview_delete_backward_word (GtkTextView *text)
10517 GtkTextBuffer *buffer;
10519 GtkTextIter ins, end_iter;
10521 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10523 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10524 mark = gtk_text_buffer_get_insert(buffer);
10525 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10527 if (gtk_text_iter_backward_word_start(&end_iter)) {
10528 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10532 static void textview_delete_line (GtkTextView *text)
10534 GtkTextBuffer *buffer;
10536 GtkTextIter ins, start_iter, end_iter;
10538 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10540 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10541 mark = gtk_text_buffer_get_insert(buffer);
10542 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10545 gtk_text_iter_set_line_offset(&start_iter, 0);
10548 if (gtk_text_iter_ends_line(&end_iter)){
10549 if (!gtk_text_iter_forward_char(&end_iter))
10550 gtk_text_iter_backward_char(&start_iter);
10553 gtk_text_iter_forward_to_line_end(&end_iter);
10554 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10557 static void textview_delete_to_line_end (GtkTextView *text)
10559 GtkTextBuffer *buffer;
10561 GtkTextIter ins, end_iter;
10563 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10565 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10566 mark = gtk_text_buffer_get_insert(buffer);
10567 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10569 if (gtk_text_iter_ends_line(&end_iter))
10570 gtk_text_iter_forward_char(&end_iter);
10572 gtk_text_iter_forward_to_line_end(&end_iter);
10573 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10576 #define DO_ACTION(name, act) { \
10577 if(!strcmp(name, a_name)) { \
10581 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10583 const gchar *a_name = gtk_action_get_name(action);
10584 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10585 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10586 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10587 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10588 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10589 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10590 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10591 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10592 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10593 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10594 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10595 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10596 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10597 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10601 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10603 Compose *compose = (Compose *)data;
10604 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10605 ComposeCallAdvancedAction action = -1;
10607 action = compose_call_advanced_action_from_path(gaction);
10610 void (*do_action) (GtkTextView *text);
10611 } action_table[] = {
10612 {textview_move_beginning_of_line},
10613 {textview_move_forward_character},
10614 {textview_move_backward_character},
10615 {textview_move_forward_word},
10616 {textview_move_backward_word},
10617 {textview_move_end_of_line},
10618 {textview_move_next_line},
10619 {textview_move_previous_line},
10620 {textview_delete_forward_character},
10621 {textview_delete_backward_character},
10622 {textview_delete_forward_word},
10623 {textview_delete_backward_word},
10624 {textview_delete_line},
10625 {textview_delete_to_line_end}
10628 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10630 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10631 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10632 if (action_table[action].do_action)
10633 action_table[action].do_action(text);
10635 g_warning("Not implemented yet.");
10639 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10641 GtkAllocation allocation;
10645 if (GTK_IS_EDITABLE(widget)) {
10646 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10647 gtk_editable_set_position(GTK_EDITABLE(widget),
10650 if ((parent = gtk_widget_get_parent(widget))
10651 && (parent = gtk_widget_get_parent(parent))
10652 && (parent = gtk_widget_get_parent(parent))) {
10653 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10654 gtk_widget_get_allocation(widget, &allocation);
10655 gint y = allocation.y;
10656 gint height = allocation.height;
10657 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10658 (GTK_SCROLLED_WINDOW(parent));
10660 gfloat value = gtk_adjustment_get_value(shown);
10661 gfloat upper = gtk_adjustment_get_upper(shown);
10662 gfloat page_size = gtk_adjustment_get_page_size(shown);
10663 if (y < (int)value) {
10664 gtk_adjustment_set_value(shown, y - 1);
10666 if ((y + height) > ((int)value + (int)page_size)) {
10667 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10668 gtk_adjustment_set_value(shown,
10669 y + height - (int)page_size - 1);
10671 gtk_adjustment_set_value(shown,
10672 (int)upper - (int)page_size - 1);
10679 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10680 compose->focused_editable = widget;
10682 #ifdef GENERIC_UMPC
10683 if (GTK_IS_TEXT_VIEW(widget)
10684 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10685 g_object_ref(compose->notebook);
10686 g_object_ref(compose->edit_vbox);
10687 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10688 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10689 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10690 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10691 g_object_unref(compose->notebook);
10692 g_object_unref(compose->edit_vbox);
10693 g_signal_handlers_block_by_func(G_OBJECT(widget),
10694 G_CALLBACK(compose_grab_focus_cb),
10696 gtk_widget_grab_focus(widget);
10697 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10698 G_CALLBACK(compose_grab_focus_cb),
10700 } else if (!GTK_IS_TEXT_VIEW(widget)
10701 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10702 g_object_ref(compose->notebook);
10703 g_object_ref(compose->edit_vbox);
10704 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10705 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10706 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10707 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10708 g_object_unref(compose->notebook);
10709 g_object_unref(compose->edit_vbox);
10710 g_signal_handlers_block_by_func(G_OBJECT(widget),
10711 G_CALLBACK(compose_grab_focus_cb),
10713 gtk_widget_grab_focus(widget);
10714 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10715 G_CALLBACK(compose_grab_focus_cb),
10721 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10723 compose->modified = TRUE;
10724 // compose_beautify_paragraph(compose, NULL, TRUE);
10725 #ifndef GENERIC_UMPC
10726 compose_set_title(compose);
10730 static void compose_wrap_cb(GtkAction *action, gpointer data)
10732 Compose *compose = (Compose *)data;
10733 compose_beautify_paragraph(compose, NULL, TRUE);
10736 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10738 Compose *compose = (Compose *)data;
10739 compose_wrap_all_full(compose, TRUE);
10742 static void compose_find_cb(GtkAction *action, gpointer data)
10744 Compose *compose = (Compose *)data;
10746 message_search_compose(compose);
10749 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10752 Compose *compose = (Compose *)data;
10753 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10754 if (compose->autowrap)
10755 compose_wrap_all_full(compose, TRUE);
10756 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10759 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10762 Compose *compose = (Compose *)data;
10763 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10766 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10768 Compose *compose = (Compose *)data;
10770 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10773 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10775 Compose *compose = (Compose *)data;
10777 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10780 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10782 g_free(compose->privacy_system);
10784 compose->privacy_system = g_strdup(account->default_privacy_system);
10785 compose_update_privacy_system_menu_item(compose, warn);
10788 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10790 Compose *compose = (Compose *)data;
10792 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10793 gtk_widget_show(compose->ruler_hbox);
10794 prefs_common.show_ruler = TRUE;
10796 gtk_widget_hide(compose->ruler_hbox);
10797 gtk_widget_queue_resize(compose->edit_vbox);
10798 prefs_common.show_ruler = FALSE;
10802 static void compose_attach_drag_received_cb (GtkWidget *widget,
10803 GdkDragContext *context,
10806 GtkSelectionData *data,
10809 gpointer user_data)
10811 Compose *compose = (Compose *)user_data;
10815 type = gtk_selection_data_get_data_type(data);
10816 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10818 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10820 ) && gtk_drag_get_source_widget(context) !=
10821 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10822 list = uri_list_extract_filenames(
10823 (const gchar *)gtk_selection_data_get_data(data));
10824 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10825 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10826 compose_attach_append
10827 (compose, (const gchar *)tmp->data,
10828 utf8_filename, NULL, NULL);
10829 g_free(utf8_filename);
10831 if (list) compose_changed_cb(NULL, compose);
10832 list_free_strings(list);
10834 } else if (gtk_drag_get_source_widget(context)
10835 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10836 /* comes from our summaryview */
10837 SummaryView * summaryview = NULL;
10838 GSList * list = NULL, *cur = NULL;
10840 if (mainwindow_get_mainwindow())
10841 summaryview = mainwindow_get_mainwindow()->summaryview;
10844 list = summary_get_selected_msg_list(summaryview);
10846 for (cur = list; cur; cur = cur->next) {
10847 MsgInfo *msginfo = (MsgInfo *)cur->data;
10848 gchar *file = NULL;
10850 file = procmsg_get_message_file_full(msginfo,
10853 compose_attach_append(compose, (const gchar *)file,
10854 (const gchar *)file, "message/rfc822", NULL);
10858 g_slist_free(list);
10862 static gboolean compose_drag_drop(GtkWidget *widget,
10863 GdkDragContext *drag_context,
10865 guint time, gpointer user_data)
10867 /* not handling this signal makes compose_insert_drag_received_cb
10872 static gboolean completion_set_focus_to_subject
10873 (GtkWidget *widget,
10874 GdkEventKey *event,
10877 cm_return_val_if_fail(compose != NULL, FALSE);
10879 /* make backtab move to subject field */
10880 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10881 gtk_widget_grab_focus(compose->subject_entry);
10887 static void compose_insert_drag_received_cb (GtkWidget *widget,
10888 GdkDragContext *drag_context,
10891 GtkSelectionData *data,
10894 gpointer user_data)
10896 Compose *compose = (Compose *)user_data;
10900 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10902 type = gtk_selection_data_get_data_type(data);
10904 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10906 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10908 AlertValue val = G_ALERTDEFAULT;
10909 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10911 list = uri_list_extract_filenames(ddata);
10912 if (list == NULL && strstr(ddata, "://")) {
10913 /* Assume a list of no files, and data has ://, is a remote link */
10914 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10915 gchar *tmpfile = get_tmp_file();
10916 str_write_to_file(tmpdata, tmpfile);
10918 compose_insert_file(compose, tmpfile);
10919 claws_unlink(tmpfile);
10921 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10922 compose_beautify_paragraph(compose, NULL, TRUE);
10925 switch (prefs_common.compose_dnd_mode) {
10926 case COMPOSE_DND_ASK:
10927 val = alertpanel_full(_("Insert or attach?"),
10928 _("Do you want to insert the contents of the file(s) "
10929 "into the message body, or attach it to the email?"),
10930 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10931 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10933 case COMPOSE_DND_INSERT:
10934 val = G_ALERTALTERNATE;
10936 case COMPOSE_DND_ATTACH:
10937 val = G_ALERTOTHER;
10940 /* unexpected case */
10941 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10944 if (val & G_ALERTDISABLE) {
10945 val &= ~G_ALERTDISABLE;
10946 /* remember what action to perform by default, only if we don't click Cancel */
10947 if (val == G_ALERTALTERNATE)
10948 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10949 else if (val == G_ALERTOTHER)
10950 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10953 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10954 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10955 list_free_strings(list);
10958 } else if (val == G_ALERTOTHER) {
10959 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10960 list_free_strings(list);
10965 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10966 compose_insert_file(compose, (const gchar *)tmp->data);
10968 list_free_strings(list);
10970 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10975 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10978 static void compose_header_drag_received_cb (GtkWidget *widget,
10979 GdkDragContext *drag_context,
10982 GtkSelectionData *data,
10985 gpointer user_data)
10987 GtkEditable *entry = (GtkEditable *)user_data;
10988 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10990 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10993 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10994 gchar *decoded=g_new(gchar, strlen(email));
10997 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10998 gtk_editable_delete_text(entry, 0, -1);
10999 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11000 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11004 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11007 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11009 Compose *compose = (Compose *)data;
11011 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11012 compose->return_receipt = TRUE;
11014 compose->return_receipt = FALSE;
11017 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11019 Compose *compose = (Compose *)data;
11021 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11022 compose->remove_references = TRUE;
11024 compose->remove_references = FALSE;
11027 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11028 ComposeHeaderEntry *headerentry)
11030 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11034 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11035 GdkEventKey *event,
11036 ComposeHeaderEntry *headerentry)
11038 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11039 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11040 !(event->state & GDK_MODIFIER_MASK) &&
11041 (event->keyval == GDK_KEY_BackSpace) &&
11042 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11043 gtk_container_remove
11044 (GTK_CONTAINER(headerentry->compose->header_table),
11045 headerentry->combo);
11046 gtk_container_remove
11047 (GTK_CONTAINER(headerentry->compose->header_table),
11048 headerentry->entry);
11049 headerentry->compose->header_list =
11050 g_slist_remove(headerentry->compose->header_list,
11052 g_free(headerentry);
11053 } else if (event->keyval == GDK_KEY_Tab) {
11054 if (headerentry->compose->header_last == headerentry) {
11055 /* Override default next focus, and give it to subject_entry
11056 * instead of notebook tabs
11058 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11059 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11066 static gboolean scroll_postpone(gpointer data)
11068 Compose *compose = (Compose *)data;
11070 cm_return_val_if_fail(!compose->batch, FALSE);
11072 GTK_EVENTS_FLUSH();
11073 compose_show_first_last_header(compose, FALSE);
11077 static void compose_headerentry_changed_cb(GtkWidget *entry,
11078 ComposeHeaderEntry *headerentry)
11080 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11081 compose_create_header_entry(headerentry->compose);
11082 g_signal_handlers_disconnect_matched
11083 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11084 0, 0, NULL, NULL, headerentry);
11086 if (!headerentry->compose->batch)
11087 g_timeout_add(0, scroll_postpone, headerentry->compose);
11091 static gboolean compose_defer_auto_save_draft(Compose *compose)
11093 compose->draft_timeout_tag = -1;
11094 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11098 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11100 GtkAdjustment *vadj;
11102 cm_return_if_fail(compose);
11103 cm_return_if_fail(!compose->batch);
11104 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11105 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11106 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11107 gtk_widget_get_parent(compose->header_table)));
11108 gtk_adjustment_set_value(vadj, (show_first ?
11109 gtk_adjustment_get_lower(vadj) :
11110 (gtk_adjustment_get_upper(vadj) -
11111 gtk_adjustment_get_page_size(vadj))));
11112 gtk_adjustment_changed(vadj);
11115 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11116 const gchar *text, gint len, Compose *compose)
11118 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11119 (G_OBJECT(compose->text), "paste_as_quotation"));
11122 cm_return_if_fail(text != NULL);
11124 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11125 G_CALLBACK(text_inserted),
11127 if (paste_as_quotation) {
11129 const gchar *qmark;
11131 GtkTextIter start_iter;
11134 len = strlen(text);
11136 new_text = g_strndup(text, len);
11138 qmark = compose_quote_char_from_context(compose);
11140 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11141 gtk_text_buffer_place_cursor(buffer, iter);
11143 pos = gtk_text_iter_get_offset(iter);
11145 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11146 _("Quote format error at line %d."));
11147 quote_fmt_reset_vartable();
11149 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11150 GINT_TO_POINTER(paste_as_quotation - 1));
11152 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11153 gtk_text_buffer_place_cursor(buffer, iter);
11154 gtk_text_buffer_delete_mark(buffer, mark);
11156 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11157 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11158 compose_beautify_paragraph(compose, &start_iter, FALSE);
11159 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11160 gtk_text_buffer_delete_mark(buffer, mark);
11162 if (strcmp(text, "\n") || compose->automatic_break
11163 || gtk_text_iter_starts_line(iter)) {
11164 GtkTextIter before_ins;
11165 gtk_text_buffer_insert(buffer, iter, text, len);
11166 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11167 before_ins = *iter;
11168 gtk_text_iter_backward_chars(&before_ins, len);
11169 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11172 /* check if the preceding is just whitespace or quote */
11173 GtkTextIter start_line;
11174 gchar *tmp = NULL, *quote = NULL;
11175 gint quote_len = 0, is_normal = 0;
11176 start_line = *iter;
11177 gtk_text_iter_set_line_offset(&start_line, 0);
11178 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11181 if (*tmp == '\0') {
11184 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11192 gtk_text_buffer_insert(buffer, iter, text, len);
11194 gtk_text_buffer_insert_with_tags_by_name(buffer,
11195 iter, text, len, "no_join", NULL);
11200 if (!paste_as_quotation) {
11201 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11202 compose_beautify_paragraph(compose, iter, FALSE);
11203 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11204 gtk_text_buffer_delete_mark(buffer, mark);
11207 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11208 G_CALLBACK(text_inserted),
11210 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11212 if (prefs_common.autosave &&
11213 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11214 compose->draft_timeout_tag != -2 /* disabled while loading */)
11215 compose->draft_timeout_tag = g_timeout_add
11216 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11220 static void compose_check_all(GtkAction *action, gpointer data)
11222 Compose *compose = (Compose *)data;
11223 if (!compose->gtkaspell)
11226 if (gtk_widget_has_focus(compose->subject_entry))
11227 claws_spell_entry_check_all(
11228 CLAWS_SPELL_ENTRY(compose->subject_entry));
11230 gtkaspell_check_all(compose->gtkaspell);
11233 static void compose_highlight_all(GtkAction *action, gpointer data)
11235 Compose *compose = (Compose *)data;
11236 if (compose->gtkaspell) {
11237 claws_spell_entry_recheck_all(
11238 CLAWS_SPELL_ENTRY(compose->subject_entry));
11239 gtkaspell_highlight_all(compose->gtkaspell);
11243 static void compose_check_backwards(GtkAction *action, gpointer data)
11245 Compose *compose = (Compose *)data;
11246 if (!compose->gtkaspell) {
11247 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11251 if (gtk_widget_has_focus(compose->subject_entry))
11252 claws_spell_entry_check_backwards(
11253 CLAWS_SPELL_ENTRY(compose->subject_entry));
11255 gtkaspell_check_backwards(compose->gtkaspell);
11258 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11260 Compose *compose = (Compose *)data;
11261 if (!compose->gtkaspell) {
11262 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11266 if (gtk_widget_has_focus(compose->subject_entry))
11267 claws_spell_entry_check_forwards_go(
11268 CLAWS_SPELL_ENTRY(compose->subject_entry));
11270 gtkaspell_check_forwards_go(compose->gtkaspell);
11275 *\brief Guess originating forward account from MsgInfo and several
11276 * "common preference" settings. Return NULL if no guess.
11278 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11280 PrefsAccount *account = NULL;
11282 cm_return_val_if_fail(msginfo, NULL);
11283 cm_return_val_if_fail(msginfo->folder, NULL);
11284 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11286 if (msginfo->folder->prefs->enable_default_account)
11287 account = account_find_from_id(msginfo->folder->prefs->default_account);
11290 account = msginfo->folder->folder->account;
11292 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11294 Xstrdup_a(to, msginfo->to, return NULL);
11295 extract_address(to);
11296 account = account_find_from_address(to, FALSE);
11299 if (!account && prefs_common.forward_account_autosel) {
11300 gchar cc[BUFFSIZE];
11301 if (!procheader_get_header_from_msginfo
11302 (msginfo, cc,sizeof cc , "Cc:")) {
11303 gchar *buf = cc + strlen("Cc:");
11304 extract_address(buf);
11305 account = account_find_from_address(buf, FALSE);
11309 if (!account && prefs_common.forward_account_autosel) {
11310 gchar deliveredto[BUFFSIZE];
11311 if (!procheader_get_header_from_msginfo
11312 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11313 gchar *buf = deliveredto + strlen("Delivered-To:");
11314 extract_address(buf);
11315 account = account_find_from_address(buf, FALSE);
11322 gboolean compose_close(Compose *compose)
11326 if (!g_mutex_trylock(compose->mutex)) {
11327 /* we have to wait for the (possibly deferred by auto-save)
11328 * drafting to be done, before destroying the compose under
11330 debug_print("waiting for drafting to finish...\n");
11331 compose_allow_user_actions(compose, FALSE);
11332 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11335 cm_return_val_if_fail(compose, FALSE);
11336 gtkut_widget_get_uposition(compose->window, &x, &y);
11337 if (!compose->batch) {
11338 prefs_common.compose_x = x;
11339 prefs_common.compose_y = y;
11341 g_mutex_unlock(compose->mutex);
11342 compose_destroy(compose);
11347 * Add entry field for each address in list.
11348 * \param compose E-Mail composition object.
11349 * \param listAddress List of (formatted) E-Mail addresses.
11351 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11354 node = listAddress;
11356 addr = ( gchar * ) node->data;
11357 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11358 node = g_list_next( node );
11362 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11363 guint action, gboolean opening_multiple)
11365 gchar *body = NULL;
11366 GSList *new_msglist = NULL;
11367 MsgInfo *tmp_msginfo = NULL;
11368 gboolean originally_enc = FALSE;
11369 gboolean originally_sig = FALSE;
11370 Compose *compose = NULL;
11371 gchar *s_system = NULL;
11373 cm_return_if_fail(msgview != NULL);
11375 cm_return_if_fail(msginfo_list != NULL);
11377 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11378 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11379 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11381 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11382 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11383 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11384 orig_msginfo, mimeinfo);
11385 if (tmp_msginfo != NULL) {
11386 new_msglist = g_slist_append(NULL, tmp_msginfo);
11388 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11389 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11390 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11392 tmp_msginfo->folder = orig_msginfo->folder;
11393 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11394 if (orig_msginfo->tags) {
11395 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11396 tmp_msginfo->folder->tags_dirty = TRUE;
11402 if (!opening_multiple)
11403 body = messageview_get_selection(msgview);
11406 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11407 procmsg_msginfo_free(tmp_msginfo);
11408 g_slist_free(new_msglist);
11410 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11412 if (compose && originally_enc) {
11413 compose_force_encryption(compose, compose->account, FALSE, s_system);
11416 if (compose && originally_sig && compose->account->default_sign_reply) {
11417 compose_force_signing(compose, compose->account, s_system);
11421 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11424 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11427 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11428 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11429 GSList *cur = msginfo_list;
11430 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11431 "messages. Opening the windows "
11432 "could take some time. Do you "
11433 "want to continue?"),
11434 g_slist_length(msginfo_list));
11435 if (g_slist_length(msginfo_list) > 9
11436 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11437 != G_ALERTALTERNATE) {
11442 /* We'll open multiple compose windows */
11443 /* let the WM place the next windows */
11444 compose_force_window_origin = FALSE;
11445 for (; cur; cur = cur->next) {
11447 tmplist.data = cur->data;
11448 tmplist.next = NULL;
11449 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11451 compose_force_window_origin = TRUE;
11453 /* forwarding multiple mails as attachments is done via a
11454 * single compose window */
11455 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11459 void compose_check_for_email_account(Compose *compose)
11461 PrefsAccount *ac = NULL, *curr = NULL;
11467 if (compose->account && compose->account->protocol == A_NNTP) {
11468 ac = account_get_cur_account();
11469 if (ac->protocol == A_NNTP) {
11470 list = account_get_list();
11472 for( ; list != NULL ; list = g_list_next(list)) {
11473 curr = (PrefsAccount *) list->data;
11474 if (curr->protocol != A_NNTP) {
11480 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11485 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11486 const gchar *address)
11488 GSList *msginfo_list = NULL;
11489 gchar *body = messageview_get_selection(msgview);
11492 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11494 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11495 compose_check_for_email_account(compose);
11496 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11497 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11498 compose_reply_set_subject(compose, msginfo);
11501 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11504 void compose_set_position(Compose *compose, gint pos)
11506 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11508 gtkut_text_view_set_position(text, pos);
11511 gboolean compose_search_string(Compose *compose,
11512 const gchar *str, gboolean case_sens)
11514 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11516 return gtkut_text_view_search_string(text, str, case_sens);
11519 gboolean compose_search_string_backward(Compose *compose,
11520 const gchar *str, gboolean case_sens)
11522 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11524 return gtkut_text_view_search_string_backward(text, str, case_sens);
11527 /* allocate a msginfo structure and populate its data from a compose data structure */
11528 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11530 MsgInfo *newmsginfo;
11532 gchar buf[BUFFSIZE];
11534 cm_return_val_if_fail( compose != NULL, NULL );
11536 newmsginfo = procmsg_msginfo_new();
11539 get_rfc822_date(buf, sizeof(buf));
11540 newmsginfo->date = g_strdup(buf);
11543 if (compose->from_name) {
11544 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11545 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11549 if (compose->subject_entry)
11550 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11552 /* to, cc, reply-to, newsgroups */
11553 for (list = compose->header_list; list; list = list->next) {
11554 gchar *header = gtk_editable_get_chars(
11556 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11557 gchar *entry = gtk_editable_get_chars(
11558 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11560 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11561 if ( newmsginfo->to == NULL ) {
11562 newmsginfo->to = g_strdup(entry);
11563 } else if (entry && *entry) {
11564 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11565 g_free(newmsginfo->to);
11566 newmsginfo->to = tmp;
11569 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11570 if ( newmsginfo->cc == NULL ) {
11571 newmsginfo->cc = g_strdup(entry);
11572 } else if (entry && *entry) {
11573 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11574 g_free(newmsginfo->cc);
11575 newmsginfo->cc = tmp;
11578 if ( strcasecmp(header,
11579 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11580 if ( newmsginfo->newsgroups == NULL ) {
11581 newmsginfo->newsgroups = g_strdup(entry);
11582 } else if (entry && *entry) {
11583 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11584 g_free(newmsginfo->newsgroups);
11585 newmsginfo->newsgroups = tmp;
11593 /* other data is unset */
11599 /* update compose's dictionaries from folder dict settings */
11600 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11601 FolderItem *folder_item)
11603 cm_return_if_fail(compose != NULL);
11605 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11606 FolderItemPrefs *prefs = folder_item->prefs;
11608 if (prefs->enable_default_dictionary)
11609 gtkaspell_change_dict(compose->gtkaspell,
11610 prefs->default_dictionary, FALSE);
11611 if (folder_item->prefs->enable_default_alt_dictionary)
11612 gtkaspell_change_alt_dict(compose->gtkaspell,
11613 prefs->default_alt_dictionary);
11614 if (prefs->enable_default_dictionary
11615 || prefs->enable_default_alt_dictionary)
11616 compose_spell_menu_changed(compose);