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_full(ref_id_list);
2973 new_ref = g_string_new("");
2974 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2975 if (new_ref->len > 0)
2976 g_string_append(new_ref, "\n\t");
2977 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2980 slist_free_strings_full(ref_id_list);
2982 new_ref_str = new_ref->str;
2983 g_string_free(new_ref, FALSE);
2988 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2989 const gchar *fmt, const gchar *qmark,
2990 const gchar *body, gboolean rewrap,
2991 gboolean need_unescape,
2992 const gchar *err_msg)
2994 MsgInfo* dummyinfo = NULL;
2995 gchar *quote_str = NULL;
2997 gboolean prev_autowrap;
2998 const gchar *trimmed_body = body;
2999 gint cursor_pos = -1;
3000 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3001 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3006 SIGNAL_BLOCK(buffer);
3009 dummyinfo = compose_msginfo_new_from_compose(compose);
3010 msginfo = dummyinfo;
3013 if (qmark != NULL) {
3015 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3016 compose->gtkaspell);
3018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3020 quote_fmt_scan_string(qmark);
3023 buf = quote_fmt_get_buffer();
3025 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3027 Xstrdup_a(quote_str, buf, goto error)
3030 if (fmt && *fmt != '\0') {
3033 while (*trimmed_body == '\n')
3037 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3038 compose->gtkaspell);
3040 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3042 if (need_unescape) {
3045 /* decode \-escape sequences in the internal representation of the quote format */
3046 tmp = g_malloc(strlen(fmt)+1);
3047 pref_get_unescaped_pref(tmp, fmt);
3048 quote_fmt_scan_string(tmp);
3052 quote_fmt_scan_string(fmt);
3056 buf = quote_fmt_get_buffer();
3058 gint line = quote_fmt_get_line();
3059 alertpanel_error(err_msg, line);
3065 prev_autowrap = compose->autowrap;
3066 compose->autowrap = FALSE;
3068 mark = gtk_text_buffer_get_insert(buffer);
3069 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3070 if (g_utf8_validate(buf, -1, NULL)) {
3071 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3073 gchar *tmpout = NULL;
3074 tmpout = conv_codeset_strdup
3075 (buf, conv_get_locale_charset_str_no_utf8(),
3077 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3079 tmpout = g_malloc(strlen(buf)*2+1);
3080 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3082 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3086 cursor_pos = quote_fmt_get_cursor_pos();
3087 if (cursor_pos == -1)
3088 cursor_pos = gtk_text_iter_get_offset(&iter);
3089 compose->set_cursor_pos = cursor_pos;
3091 gtk_text_buffer_get_start_iter(buffer, &iter);
3092 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3093 gtk_text_buffer_place_cursor(buffer, &iter);
3095 compose->autowrap = prev_autowrap;
3096 if (compose->autowrap && rewrap)
3097 compose_wrap_all(compose);
3104 SIGNAL_UNBLOCK(buffer);
3106 procmsg_msginfo_free( dummyinfo );
3111 /* if ml_post is of type addr@host and from is of type
3112 * addr-anything@host, return TRUE
3114 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3116 gchar *left_ml = NULL;
3117 gchar *right_ml = NULL;
3118 gchar *left_from = NULL;
3119 gchar *right_from = NULL;
3120 gboolean result = FALSE;
3122 if (!ml_post || !from)
3125 left_ml = g_strdup(ml_post);
3126 if (strstr(left_ml, "@")) {
3127 right_ml = strstr(left_ml, "@")+1;
3128 *(strstr(left_ml, "@")) = '\0';
3131 left_from = g_strdup(from);
3132 if (strstr(left_from, "@")) {
3133 right_from = strstr(left_from, "@")+1;
3134 *(strstr(left_from, "@")) = '\0';
3137 if (left_ml && left_from && right_ml && right_from
3138 && !strncmp(left_from, left_ml, strlen(left_ml))
3139 && !strcmp(right_from, right_ml)) {
3148 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3149 gboolean respect_default_to)
3153 if (!folder || !folder->prefs)
3156 if (respect_default_to && folder->prefs->enable_default_to) {
3157 compose_entry_append(compose, folder->prefs->default_to,
3158 COMPOSE_TO, PREF_FOLDER);
3159 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3161 if (folder->prefs->enable_default_cc)
3162 compose_entry_append(compose, folder->prefs->default_cc,
3163 COMPOSE_CC, PREF_FOLDER);
3164 if (folder->prefs->enable_default_bcc)
3165 compose_entry_append(compose, folder->prefs->default_bcc,
3166 COMPOSE_BCC, PREF_FOLDER);
3167 if (folder->prefs->enable_default_replyto)
3168 compose_entry_append(compose, folder->prefs->default_replyto,
3169 COMPOSE_REPLYTO, PREF_FOLDER);
3172 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3177 if (!compose || !msginfo)
3180 if (msginfo->subject && *msginfo->subject) {
3181 buf = p = g_strdup(msginfo->subject);
3182 p += subject_get_prefix_length(p);
3183 memmove(buf, p, strlen(p) + 1);
3185 buf2 = g_strdup_printf("Re: %s", buf);
3186 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3191 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3194 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3195 gboolean to_all, gboolean to_ml,
3197 gboolean followup_and_reply_to)
3199 GSList *cc_list = NULL;
3202 gchar *replyto = NULL;
3203 gchar *ac_email = NULL;
3205 gboolean reply_to_ml = FALSE;
3206 gboolean default_reply_to = FALSE;
3208 cm_return_if_fail(compose->account != NULL);
3209 cm_return_if_fail(msginfo != NULL);
3211 reply_to_ml = to_ml && compose->ml_post;
3213 default_reply_to = msginfo->folder &&
3214 msginfo->folder->prefs->enable_default_reply_to;
3216 if (compose->account->protocol != A_NNTP) {
3217 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3219 if (reply_to_ml && !default_reply_to) {
3221 gboolean is_subscr = is_subscription(compose->ml_post,
3224 /* normal answer to ml post with a reply-to */
3225 compose_entry_append(compose,
3227 COMPOSE_TO, PREF_ML);
3228 if (compose->replyto)
3229 compose_entry_append(compose,
3231 COMPOSE_CC, PREF_ML);
3233 /* answer to subscription confirmation */
3234 if (compose->replyto)
3235 compose_entry_append(compose,
3237 COMPOSE_TO, PREF_ML);
3238 else if (msginfo->from)
3239 compose_entry_append(compose,
3241 COMPOSE_TO, PREF_ML);
3244 else if (!(to_all || to_sender) && default_reply_to) {
3245 compose_entry_append(compose,
3246 msginfo->folder->prefs->default_reply_to,
3247 COMPOSE_TO, PREF_FOLDER);
3248 compose_entry_mark_default_to(compose,
3249 msginfo->folder->prefs->default_reply_to);
3254 Xstrdup_a(tmp1, msginfo->from, return);
3255 extract_address(tmp1);
3256 if (to_all || to_sender ||
3257 !account_find_from_address(tmp1, FALSE))
3258 compose_entry_append(compose,
3259 (compose->replyto && !to_sender)
3260 ? compose->replyto :
3261 msginfo->from ? msginfo->from : "",
3262 COMPOSE_TO, PREF_NONE);
3263 else if (!to_all && !to_sender) {
3264 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3265 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3266 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3267 if (compose->replyto) {
3268 compose_entry_append(compose,
3270 COMPOSE_TO, PREF_NONE);
3272 compose_entry_append(compose,
3273 msginfo->from ? msginfo->from : "",
3274 COMPOSE_TO, PREF_NONE);
3277 /* replying to own mail, use original recp */
3278 compose_entry_append(compose,
3279 msginfo->to ? msginfo->to : "",
3280 COMPOSE_TO, PREF_NONE);
3281 compose_entry_append(compose,
3282 msginfo->cc ? msginfo->cc : "",
3283 COMPOSE_CC, PREF_NONE);
3288 if (to_sender || (compose->followup_to &&
3289 !strncmp(compose->followup_to, "poster", 6)))
3290 compose_entry_append
3292 (compose->replyto ? compose->replyto :
3293 msginfo->from ? msginfo->from : ""),
3294 COMPOSE_TO, PREF_NONE);
3296 else if (followup_and_reply_to || to_all) {
3297 compose_entry_append
3299 (compose->replyto ? compose->replyto :
3300 msginfo->from ? msginfo->from : ""),
3301 COMPOSE_TO, PREF_NONE);
3303 compose_entry_append
3305 compose->followup_to ? compose->followup_to :
3306 compose->newsgroups ? compose->newsgroups : "",
3307 COMPOSE_NEWSGROUPS, PREF_NONE);
3310 compose_entry_append
3312 compose->followup_to ? compose->followup_to :
3313 compose->newsgroups ? compose->newsgroups : "",
3314 COMPOSE_NEWSGROUPS, PREF_NONE);
3316 compose_reply_set_subject(compose, msginfo);
3318 if (to_ml && compose->ml_post) return;
3319 if (!to_all || compose->account->protocol == A_NNTP) return;
3321 if (compose->replyto) {
3322 Xstrdup_a(replyto, compose->replyto, return);
3323 extract_address(replyto);
3325 if (msginfo->from) {
3326 Xstrdup_a(from, msginfo->from, return);
3327 extract_address(from);
3330 if (replyto && from)
3331 cc_list = address_list_append_with_comments(cc_list, from);
3332 if (to_all && msginfo->folder &&
3333 msginfo->folder->prefs->enable_default_reply_to)
3334 cc_list = address_list_append_with_comments(cc_list,
3335 msginfo->folder->prefs->default_reply_to);
3336 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3337 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3339 ac_email = g_utf8_strdown(compose->account->address, -1);
3342 for (cur = cc_list; cur != NULL; cur = cur->next) {
3343 gchar *addr = g_utf8_strdown(cur->data, -1);
3344 extract_address(addr);
3346 if (strcmp(ac_email, addr))
3347 compose_entry_append(compose, (gchar *)cur->data,
3348 COMPOSE_CC, PREF_NONE);
3350 debug_print("Cc address same as compose account's, ignoring\n");
3355 slist_free_strings_full(cc_list);
3361 #define SET_ENTRY(entry, str) \
3364 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3367 #define SET_ADDRESS(type, str) \
3370 compose_entry_append(compose, str, type, PREF_NONE); \
3373 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3375 cm_return_if_fail(msginfo != NULL);
3377 SET_ENTRY(subject_entry, msginfo->subject);
3378 SET_ENTRY(from_name, msginfo->from);
3379 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3380 SET_ADDRESS(COMPOSE_CC, compose->cc);
3381 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3382 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3383 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3384 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3386 compose_update_priority_menu_item(compose);
3387 compose_update_privacy_system_menu_item(compose, FALSE);
3388 compose_show_first_last_header(compose, TRUE);
3394 static void compose_insert_sig(Compose *compose, gboolean replace)
3396 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3397 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3399 GtkTextIter iter, iter_end;
3400 gint cur_pos, ins_pos;
3401 gboolean prev_autowrap;
3402 gboolean found = FALSE;
3403 gboolean exists = FALSE;
3405 cm_return_if_fail(compose->account != NULL);
3409 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3410 G_CALLBACK(compose_changed_cb),
3413 mark = gtk_text_buffer_get_insert(buffer);
3414 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3415 cur_pos = gtk_text_iter_get_offset (&iter);
3418 gtk_text_buffer_get_end_iter(buffer, &iter);
3420 exists = (compose->sig_str != NULL);
3423 GtkTextIter first_iter, start_iter, end_iter;
3425 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3427 if (!exists || compose->sig_str[0] == '\0')
3430 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3431 compose->signature_tag);
3434 /* include previous \n\n */
3435 gtk_text_iter_backward_chars(&first_iter, 1);
3436 start_iter = first_iter;
3437 end_iter = first_iter;
3439 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3440 compose->signature_tag);
3441 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3442 compose->signature_tag);
3444 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3450 g_free(compose->sig_str);
3451 compose->sig_str = account_get_signature_str(compose->account);
3453 cur_pos = gtk_text_iter_get_offset(&iter);
3455 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3456 g_free(compose->sig_str);
3457 compose->sig_str = NULL;
3459 if (compose->sig_inserted == FALSE)
3460 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3461 compose->sig_inserted = TRUE;
3463 cur_pos = gtk_text_iter_get_offset(&iter);
3464 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3466 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3467 gtk_text_iter_forward_chars(&iter, 1);
3468 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3469 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3471 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3472 cur_pos = gtk_text_buffer_get_char_count (buffer);
3475 /* put the cursor where it should be
3476 * either where the quote_fmt says, either where it was */
3477 if (compose->set_cursor_pos < 0)
3478 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3480 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3481 compose->set_cursor_pos);
3483 compose->set_cursor_pos = -1;
3484 gtk_text_buffer_place_cursor(buffer, &iter);
3485 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3486 G_CALLBACK(compose_changed_cb),
3492 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3495 GtkTextBuffer *buffer;
3498 const gchar *cur_encoding;
3499 gchar buf[BUFFSIZE];
3502 gboolean prev_autowrap;
3503 gboolean badtxt = FALSE;
3504 struct stat file_stat;
3507 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3509 /* get the size of the file we are about to insert */
3510 ret = g_stat(file, &file_stat);
3512 gchar *shortfile = g_path_get_basename(file);
3513 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3515 return COMPOSE_INSERT_NO_FILE;
3516 } else if (prefs_common.warn_large_insert == TRUE) {
3518 /* ask user for confirmation if the file is large */
3519 if (prefs_common.warn_large_insert_size < 0 ||
3520 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3524 msg = g_strdup_printf(_("You are about to insert a file of %s "
3525 "in the message body. Are you sure you want to do that?"),
3526 to_human_readable(file_stat.st_size));
3527 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3528 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3531 /* do we ask for confirmation next time? */
3532 if (aval & G_ALERTDISABLE) {
3533 /* no confirmation next time, disable feature in preferences */
3534 aval &= ~G_ALERTDISABLE;
3535 prefs_common.warn_large_insert = FALSE;
3538 /* abort file insertion if user canceled action */
3539 if (aval != G_ALERTALTERNATE) {
3540 return COMPOSE_INSERT_NO_FILE;
3546 if ((fp = g_fopen(file, "rb")) == NULL) {
3547 FILE_OP_ERROR(file, "fopen");
3548 return COMPOSE_INSERT_READ_ERROR;
3551 prev_autowrap = compose->autowrap;
3552 compose->autowrap = FALSE;
3554 text = GTK_TEXT_VIEW(compose->text);
3555 buffer = gtk_text_view_get_buffer(text);
3556 mark = gtk_text_buffer_get_insert(buffer);
3557 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3559 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3560 G_CALLBACK(text_inserted),
3563 cur_encoding = conv_get_locale_charset_str_no_utf8();
3565 while (fgets(buf, sizeof(buf), fp) != NULL) {
3568 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3569 str = g_strdup(buf);
3571 str = conv_codeset_strdup
3572 (buf, cur_encoding, CS_INTERNAL);
3575 /* strip <CR> if DOS/Windows file,
3576 replace <CR> with <LF> if Macintosh file. */
3579 if (len > 0 && str[len - 1] != '\n') {
3581 if (str[len] == '\r') str[len] = '\n';
3584 gtk_text_buffer_insert(buffer, &iter, str, -1);
3588 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3589 G_CALLBACK(text_inserted),
3591 compose->autowrap = prev_autowrap;
3592 if (compose->autowrap)
3593 compose_wrap_all(compose);
3598 return COMPOSE_INSERT_INVALID_CHARACTER;
3600 return COMPOSE_INSERT_SUCCESS;
3603 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3604 const gchar *filename,
3605 const gchar *content_type,
3606 const gchar *charset)
3614 GtkListStore *store;
3616 gboolean has_binary = FALSE;
3618 if (!is_file_exist(file)) {
3619 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3620 gboolean result = FALSE;
3621 if (file_from_uri && is_file_exist(file_from_uri)) {
3622 result = compose_attach_append(
3623 compose, file_from_uri,
3624 filename, content_type,
3627 g_free(file_from_uri);
3630 alertpanel_error("File %s doesn't exist\n", filename);
3633 if ((size = get_file_size(file)) < 0) {
3634 alertpanel_error("Can't get file size of %s\n", filename);
3638 alertpanel_error(_("File %s is empty."), filename);
3641 if ((fp = g_fopen(file, "rb")) == NULL) {
3642 alertpanel_error(_("Can't read %s."), filename);
3647 ainfo = g_new0(AttachInfo, 1);
3648 auto_ainfo = g_auto_pointer_new_with_free
3649 (ainfo, (GFreeFunc) compose_attach_info_free);
3650 ainfo->file = g_strdup(file);
3653 ainfo->content_type = g_strdup(content_type);
3654 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3656 MsgFlags flags = {0, 0};
3658 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3659 ainfo->encoding = ENC_7BIT;
3661 ainfo->encoding = ENC_8BIT;
3663 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3664 if (msginfo && msginfo->subject)
3665 name = g_strdup(msginfo->subject);
3667 name = g_path_get_basename(filename ? filename : file);
3669 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3671 procmsg_msginfo_free(msginfo);
3673 if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3674 ainfo->charset = g_strdup(charset);
3675 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3677 ainfo->encoding = ENC_BASE64;
3679 name = g_path_get_basename(filename ? filename : file);
3680 ainfo->name = g_strdup(name);
3684 ainfo->content_type = procmime_get_mime_type(file);
3685 if (!ainfo->content_type) {
3686 ainfo->content_type =
3687 g_strdup("application/octet-stream");
3688 ainfo->encoding = ENC_BASE64;
3689 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3691 procmime_get_encoding_for_text_file(file, &has_binary);
3693 ainfo->encoding = ENC_BASE64;
3694 name = g_path_get_basename(filename ? filename : file);
3695 ainfo->name = g_strdup(name);
3699 if (ainfo->name != NULL
3700 && !strcmp(ainfo->name, ".")) {
3701 g_free(ainfo->name);
3705 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3706 g_free(ainfo->content_type);
3707 ainfo->content_type = g_strdup("application/octet-stream");
3708 g_free(ainfo->charset);
3709 ainfo->charset = NULL;
3712 ainfo->size = (goffset)size;
3713 size_text = to_human_readable((goffset)size);
3715 store = GTK_LIST_STORE(gtk_tree_view_get_model
3716 (GTK_TREE_VIEW(compose->attach_clist)));
3718 gtk_list_store_append(store, &iter);
3719 gtk_list_store_set(store, &iter,
3720 COL_MIMETYPE, ainfo->content_type,
3721 COL_SIZE, size_text,
3722 COL_NAME, ainfo->name,
3723 COL_CHARSET, ainfo->charset,
3725 COL_AUTODATA, auto_ainfo,
3728 g_auto_pointer_free(auto_ainfo);
3729 compose_attach_update_label(compose);
3733 static void compose_use_signing(Compose *compose, gboolean use_signing)
3735 compose->use_signing = use_signing;
3736 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3739 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3741 compose->use_encryption = use_encryption;
3742 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3745 #define NEXT_PART_NOT_CHILD(info) \
3747 node = info->node; \
3748 while (node->children) \
3749 node = g_node_last_child(node); \
3750 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3753 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3757 MimeInfo *firsttext = NULL;
3758 MimeInfo *encrypted = NULL;
3761 const gchar *partname = NULL;
3763 mimeinfo = procmime_scan_message(msginfo);
3764 if (!mimeinfo) return;
3766 if (mimeinfo->node->children == NULL) {
3767 procmime_mimeinfo_free_all(mimeinfo);
3771 /* find first content part */
3772 child = (MimeInfo *) mimeinfo->node->children->data;
3773 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3774 child = (MimeInfo *)child->node->children->data;
3777 if (child->type == MIMETYPE_TEXT) {
3779 debug_print("First text part found\n");
3780 } else if (compose->mode == COMPOSE_REEDIT &&
3781 child->type == MIMETYPE_APPLICATION &&
3782 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3783 encrypted = (MimeInfo *)child->node->parent->data;
3786 child = (MimeInfo *) mimeinfo->node->children->data;
3787 while (child != NULL) {
3790 if (child == encrypted) {
3791 /* skip this part of tree */
3792 NEXT_PART_NOT_CHILD(child);
3796 if (child->type == MIMETYPE_MULTIPART) {
3797 /* get the actual content */
3798 child = procmime_mimeinfo_next(child);
3802 if (child == firsttext) {
3803 child = procmime_mimeinfo_next(child);
3807 outfile = procmime_get_tmp_file_name(child);
3808 if ((err = procmime_get_part(outfile, child)) < 0)
3809 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3811 gchar *content_type;
3813 content_type = procmime_get_content_type_str(child->type, child->subtype);
3815 /* if we meet a pgp signature, we don't attach it, but
3816 * we force signing. */
3817 if ((strcmp(content_type, "application/pgp-signature") &&
3818 strcmp(content_type, "application/pkcs7-signature") &&
3819 strcmp(content_type, "application/x-pkcs7-signature"))
3820 || compose->mode == COMPOSE_REDIRECT) {
3821 partname = procmime_mimeinfo_get_parameter(child, "filename");
3822 if (partname == NULL)
3823 partname = procmime_mimeinfo_get_parameter(child, "name");
3824 if (partname == NULL)
3826 compose_attach_append(compose, outfile,
3827 partname, content_type,
3828 procmime_mimeinfo_get_parameter(child, "charset"));
3830 compose_force_signing(compose, compose->account, NULL);
3832 g_free(content_type);
3835 NEXT_PART_NOT_CHILD(child);
3837 procmime_mimeinfo_free_all(mimeinfo);
3840 #undef NEXT_PART_NOT_CHILD
3845 WAIT_FOR_INDENT_CHAR,
3846 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3849 /* return indent length, we allow:
3850 indent characters followed by indent characters or spaces/tabs,
3851 alphabets and numbers immediately followed by indent characters,
3852 and the repeating sequences of the above
3853 If quote ends with multiple spaces, only the first one is included. */
3854 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3855 const GtkTextIter *start, gint *len)
3857 GtkTextIter iter = *start;
3861 IndentState state = WAIT_FOR_INDENT_CHAR;
3864 gint alnum_count = 0;
3865 gint space_count = 0;
3868 if (prefs_common.quote_chars == NULL) {
3872 while (!gtk_text_iter_ends_line(&iter)) {
3873 wc = gtk_text_iter_get_char(&iter);
3874 if (g_unichar_iswide(wc))
3876 clen = g_unichar_to_utf8(wc, ch);
3880 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3881 is_space = g_unichar_isspace(wc);
3883 if (state == WAIT_FOR_INDENT_CHAR) {
3884 if (!is_indent && !g_unichar_isalnum(wc))
3887 quote_len += alnum_count + space_count + 1;
3888 alnum_count = space_count = 0;
3889 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3892 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3893 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3897 else if (is_indent) {
3898 quote_len += alnum_count + space_count + 1;
3899 alnum_count = space_count = 0;
3902 state = WAIT_FOR_INDENT_CHAR;
3906 gtk_text_iter_forward_char(&iter);
3909 if (quote_len > 0 && space_count > 0)
3915 if (quote_len > 0) {
3917 gtk_text_iter_forward_chars(&iter, quote_len);
3918 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3924 /* return >0 if the line is itemized */
3925 static int compose_itemized_length(GtkTextBuffer *buffer,
3926 const GtkTextIter *start)
3928 GtkTextIter iter = *start;
3933 if (gtk_text_iter_ends_line(&iter))
3938 wc = gtk_text_iter_get_char(&iter);
3939 if (!g_unichar_isspace(wc))
3941 gtk_text_iter_forward_char(&iter);
3942 if (gtk_text_iter_ends_line(&iter))
3946 clen = g_unichar_to_utf8(wc, ch);
3950 if (!strchr("*-+", ch[0]))
3953 gtk_text_iter_forward_char(&iter);
3954 if (gtk_text_iter_ends_line(&iter))
3956 wc = gtk_text_iter_get_char(&iter);
3957 if (g_unichar_isspace(wc)) {
3963 /* return the string at the start of the itemization */
3964 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3965 const GtkTextIter *start)
3967 GtkTextIter iter = *start;
3970 GString *item_chars = g_string_new("");
3973 if (gtk_text_iter_ends_line(&iter))
3978 wc = gtk_text_iter_get_char(&iter);
3979 if (!g_unichar_isspace(wc))
3981 gtk_text_iter_forward_char(&iter);
3982 if (gtk_text_iter_ends_line(&iter))
3984 g_string_append_unichar(item_chars, wc);
3987 str = item_chars->str;
3988 g_string_free(item_chars, FALSE);
3992 /* return the number of spaces at a line's start */
3993 static int compose_left_offset_length(GtkTextBuffer *buffer,
3994 const GtkTextIter *start)
3996 GtkTextIter iter = *start;
3999 if (gtk_text_iter_ends_line(&iter))
4003 wc = gtk_text_iter_get_char(&iter);
4004 if (!g_unichar_isspace(wc))
4007 gtk_text_iter_forward_char(&iter);
4008 if (gtk_text_iter_ends_line(&iter))
4012 gtk_text_iter_forward_char(&iter);
4013 if (gtk_text_iter_ends_line(&iter))
4018 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4019 const GtkTextIter *start,
4020 GtkTextIter *break_pos,
4024 GtkTextIter iter = *start, line_end = *start;
4025 PangoLogAttr *attrs;
4032 gboolean can_break = FALSE;
4033 gboolean do_break = FALSE;
4034 gboolean was_white = FALSE;
4035 gboolean prev_dont_break = FALSE;
4037 gtk_text_iter_forward_to_line_end(&line_end);
4038 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4039 len = g_utf8_strlen(str, -1);
4043 g_warning("compose_get_line_break_pos: len = 0!\n");
4047 /* g_print("breaking line: %d: %s (len = %d)\n",
4048 gtk_text_iter_get_line(&iter), str, len); */
4050 attrs = g_new(PangoLogAttr, len + 1);
4052 pango_default_break(str, -1, NULL, attrs, len + 1);
4056 /* skip quote and leading spaces */
4057 for (i = 0; *p != '\0' && i < len; i++) {
4060 wc = g_utf8_get_char(p);
4061 if (i >= quote_len && !g_unichar_isspace(wc))
4063 if (g_unichar_iswide(wc))
4065 else if (*p == '\t')
4069 p = g_utf8_next_char(p);
4072 for (; *p != '\0' && i < len; i++) {
4073 PangoLogAttr *attr = attrs + i;
4077 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4080 was_white = attr->is_white;
4082 /* don't wrap URI */
4083 if ((uri_len = get_uri_len(p)) > 0) {
4085 if (pos > 0 && col > max_col) {
4095 wc = g_utf8_get_char(p);
4096 if (g_unichar_iswide(wc)) {
4098 if (prev_dont_break && can_break && attr->is_line_break)
4100 } else if (*p == '\t')
4104 if (pos > 0 && col > max_col) {
4109 if (*p == '-' || *p == '/')
4110 prev_dont_break = TRUE;
4112 prev_dont_break = FALSE;
4114 p = g_utf8_next_char(p);
4118 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4123 *break_pos = *start;
4124 gtk_text_iter_set_line_offset(break_pos, pos);
4129 static gboolean compose_join_next_line(Compose *compose,
4130 GtkTextBuffer *buffer,
4132 const gchar *quote_str)
4134 GtkTextIter iter_ = *iter, cur, prev, next, end;
4135 PangoLogAttr attrs[3];
4137 gchar *next_quote_str;
4140 gboolean keep_cursor = FALSE;
4142 if (!gtk_text_iter_forward_line(&iter_) ||
4143 gtk_text_iter_ends_line(&iter_)) {
4146 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4148 if ((quote_str || next_quote_str) &&
4149 strcmp2(quote_str, next_quote_str) != 0) {
4150 g_free(next_quote_str);
4153 g_free(next_quote_str);
4156 if (quote_len > 0) {
4157 gtk_text_iter_forward_chars(&end, quote_len);
4158 if (gtk_text_iter_ends_line(&end)) {
4163 /* don't join itemized lines */
4164 if (compose_itemized_length(buffer, &end) > 0) {
4168 /* don't join signature separator */
4169 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4172 /* delete quote str */
4174 gtk_text_buffer_delete(buffer, &iter_, &end);
4176 /* don't join line breaks put by the user */
4178 gtk_text_iter_backward_char(&cur);
4179 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4180 gtk_text_iter_forward_char(&cur);
4184 gtk_text_iter_forward_char(&cur);
4185 /* delete linebreak and extra spaces */
4186 while (gtk_text_iter_backward_char(&cur)) {
4187 wc1 = gtk_text_iter_get_char(&cur);
4188 if (!g_unichar_isspace(wc1))
4193 while (!gtk_text_iter_ends_line(&cur)) {
4194 wc1 = gtk_text_iter_get_char(&cur);
4195 if (!g_unichar_isspace(wc1))
4197 gtk_text_iter_forward_char(&cur);
4200 if (!gtk_text_iter_equal(&prev, &next)) {
4203 mark = gtk_text_buffer_get_insert(buffer);
4204 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4205 if (gtk_text_iter_equal(&prev, &cur))
4207 gtk_text_buffer_delete(buffer, &prev, &next);
4211 /* insert space if required */
4212 gtk_text_iter_backward_char(&prev);
4213 wc1 = gtk_text_iter_get_char(&prev);
4214 wc2 = gtk_text_iter_get_char(&next);
4215 gtk_text_iter_forward_char(&next);
4216 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4217 pango_default_break(str, -1, NULL, attrs, 3);
4218 if (!attrs[1].is_line_break ||
4219 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4220 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4222 gtk_text_iter_backward_char(&iter_);
4223 gtk_text_buffer_place_cursor(buffer, &iter_);
4232 #define ADD_TXT_POS(bp_, ep_, pti_) \
4233 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4234 last = last->next; \
4235 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4236 last->next = NULL; \
4238 g_warning("alloc error scanning URIs\n"); \
4241 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4243 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4244 GtkTextBuffer *buffer;
4245 GtkTextIter iter, break_pos, end_of_line;
4246 gchar *quote_str = NULL;
4248 gboolean wrap_quote = prefs_common.linewrap_quote;
4249 gboolean prev_autowrap = compose->autowrap;
4250 gint startq_offset = -1, noq_offset = -1;
4251 gint uri_start = -1, uri_stop = -1;
4252 gint nouri_start = -1, nouri_stop = -1;
4253 gint num_blocks = 0;
4254 gint quotelevel = -1;
4255 gboolean modified = force;
4256 gboolean removed = FALSE;
4257 gboolean modified_before_remove = FALSE;
4259 gboolean start = TRUE;
4260 gint itemized_len = 0, rem_item_len = 0;
4261 gchar *itemized_chars = NULL;
4262 gboolean item_continuation = FALSE;
4267 if (compose->draft_timeout_tag == -2) {
4271 compose->autowrap = FALSE;
4273 buffer = gtk_text_view_get_buffer(text);
4274 undo_wrapping(compose->undostruct, TRUE);
4279 mark = gtk_text_buffer_get_insert(buffer);
4280 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4284 if (compose->draft_timeout_tag == -2) {
4285 if (gtk_text_iter_ends_line(&iter)) {
4286 while (gtk_text_iter_ends_line(&iter) &&
4287 gtk_text_iter_forward_line(&iter))
4290 while (gtk_text_iter_backward_line(&iter)) {
4291 if (gtk_text_iter_ends_line(&iter)) {
4292 gtk_text_iter_forward_line(&iter);
4298 /* move to line start */
4299 gtk_text_iter_set_line_offset(&iter, 0);
4302 itemized_len = compose_itemized_length(buffer, &iter);
4304 if (!itemized_len) {
4305 itemized_len = compose_left_offset_length(buffer, &iter);
4306 item_continuation = TRUE;
4310 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4312 /* go until paragraph end (empty line) */
4313 while (start || !gtk_text_iter_ends_line(&iter)) {
4314 gchar *scanpos = NULL;
4315 /* parse table - in order of priority */
4317 const gchar *needle; /* token */
4319 /* token search function */
4320 gchar *(*search) (const gchar *haystack,
4321 const gchar *needle);
4322 /* part parsing function */
4323 gboolean (*parse) (const gchar *start,
4324 const gchar *scanpos,
4328 /* part to URI function */
4329 gchar *(*build_uri) (const gchar *bp,
4333 static struct table parser[] = {
4334 {"http://", strcasestr, get_uri_part, make_uri_string},
4335 {"https://", strcasestr, get_uri_part, make_uri_string},
4336 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4337 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4338 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4339 {"www.", strcasestr, get_uri_part, make_http_string},
4340 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4341 {"@", strcasestr, get_email_part, make_email_string}
4343 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4344 gint last_index = PARSE_ELEMS;
4346 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4350 if (!prev_autowrap && num_blocks == 0) {
4352 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4353 G_CALLBACK(text_inserted),
4356 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4359 uri_start = uri_stop = -1;
4361 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4364 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4365 if (startq_offset == -1)
4366 startq_offset = gtk_text_iter_get_offset(&iter);
4367 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4368 if (quotelevel > 2) {
4369 /* recycle colors */
4370 if (prefs_common.recycle_quote_colors)
4379 if (startq_offset == -1)
4380 noq_offset = gtk_text_iter_get_offset(&iter);
4384 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4387 if (gtk_text_iter_ends_line(&iter)) {
4389 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4390 prefs_common.linewrap_len,
4392 GtkTextIter prev, next, cur;
4393 if (prev_autowrap != FALSE || force) {
4394 compose->automatic_break = TRUE;
4396 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4397 compose->automatic_break = FALSE;
4398 if (itemized_len && compose->autoindent) {
4399 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4400 if (!item_continuation)
4401 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4403 } else if (quote_str && wrap_quote) {
4404 compose->automatic_break = TRUE;
4406 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4407 compose->automatic_break = FALSE;
4408 if (itemized_len && compose->autoindent) {
4409 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4410 if (!item_continuation)
4411 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4415 /* remove trailing spaces */
4417 rem_item_len = itemized_len;
4418 while (compose->autoindent && rem_item_len-- > 0)
4419 gtk_text_iter_backward_char(&cur);
4420 gtk_text_iter_backward_char(&cur);
4423 while (!gtk_text_iter_starts_line(&cur)) {
4426 gtk_text_iter_backward_char(&cur);
4427 wc = gtk_text_iter_get_char(&cur);
4428 if (!g_unichar_isspace(wc))
4432 if (!gtk_text_iter_equal(&prev, &next)) {
4433 gtk_text_buffer_delete(buffer, &prev, &next);
4435 gtk_text_iter_forward_char(&break_pos);
4439 gtk_text_buffer_insert(buffer, &break_pos,
4443 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4445 /* move iter to current line start */
4446 gtk_text_iter_set_line_offset(&iter, 0);
4453 /* move iter to next line start */
4459 if (!prev_autowrap && num_blocks > 0) {
4461 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4462 G_CALLBACK(text_inserted),
4466 while (!gtk_text_iter_ends_line(&end_of_line)) {
4467 gtk_text_iter_forward_char(&end_of_line);
4469 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4471 nouri_start = gtk_text_iter_get_offset(&iter);
4472 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4474 walk_pos = gtk_text_iter_get_offset(&iter);
4475 /* FIXME: this looks phony. scanning for anything in the parse table */
4476 for (n = 0; n < PARSE_ELEMS; n++) {
4479 tmp = parser[n].search(walk, parser[n].needle);
4481 if (scanpos == NULL || tmp < scanpos) {
4490 /* check if URI can be parsed */
4491 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4492 (const gchar **)&ep, FALSE)
4493 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4497 strlen(parser[last_index].needle);
4500 uri_start = walk_pos + (bp - o_walk);
4501 uri_stop = walk_pos + (ep - o_walk);
4505 gtk_text_iter_forward_line(&iter);
4508 if (startq_offset != -1) {
4509 GtkTextIter startquote, endquote;
4510 gtk_text_buffer_get_iter_at_offset(
4511 buffer, &startquote, startq_offset);
4514 switch (quotelevel) {
4516 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4517 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4518 gtk_text_buffer_apply_tag_by_name(
4519 buffer, "quote0", &startquote, &endquote);
4520 gtk_text_buffer_remove_tag_by_name(
4521 buffer, "quote1", &startquote, &endquote);
4522 gtk_text_buffer_remove_tag_by_name(
4523 buffer, "quote2", &startquote, &endquote);
4528 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4529 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4530 gtk_text_buffer_apply_tag_by_name(
4531 buffer, "quote1", &startquote, &endquote);
4532 gtk_text_buffer_remove_tag_by_name(
4533 buffer, "quote0", &startquote, &endquote);
4534 gtk_text_buffer_remove_tag_by_name(
4535 buffer, "quote2", &startquote, &endquote);
4540 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4541 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4542 gtk_text_buffer_apply_tag_by_name(
4543 buffer, "quote2", &startquote, &endquote);
4544 gtk_text_buffer_remove_tag_by_name(
4545 buffer, "quote0", &startquote, &endquote);
4546 gtk_text_buffer_remove_tag_by_name(
4547 buffer, "quote1", &startquote, &endquote);
4553 } else if (noq_offset != -1) {
4554 GtkTextIter startnoquote, endnoquote;
4555 gtk_text_buffer_get_iter_at_offset(
4556 buffer, &startnoquote, noq_offset);
4559 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4560 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4561 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4562 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4563 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4564 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4565 gtk_text_buffer_remove_tag_by_name(
4566 buffer, "quote0", &startnoquote, &endnoquote);
4567 gtk_text_buffer_remove_tag_by_name(
4568 buffer, "quote1", &startnoquote, &endnoquote);
4569 gtk_text_buffer_remove_tag_by_name(
4570 buffer, "quote2", &startnoquote, &endnoquote);
4576 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4577 GtkTextIter nouri_start_iter, nouri_end_iter;
4578 gtk_text_buffer_get_iter_at_offset(
4579 buffer, &nouri_start_iter, nouri_start);
4580 gtk_text_buffer_get_iter_at_offset(
4581 buffer, &nouri_end_iter, nouri_stop);
4582 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4583 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4584 gtk_text_buffer_remove_tag_by_name(
4585 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4586 modified_before_remove = modified;
4591 if (uri_start >= 0 && uri_stop > 0) {
4592 GtkTextIter uri_start_iter, uri_end_iter, back;
4593 gtk_text_buffer_get_iter_at_offset(
4594 buffer, &uri_start_iter, uri_start);
4595 gtk_text_buffer_get_iter_at_offset(
4596 buffer, &uri_end_iter, uri_stop);
4597 back = uri_end_iter;
4598 gtk_text_iter_backward_char(&back);
4599 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4600 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4601 gtk_text_buffer_apply_tag_by_name(
4602 buffer, "link", &uri_start_iter, &uri_end_iter);
4604 if (removed && !modified_before_remove) {
4610 // debug_print("not modified, out after %d lines\n", lines);
4614 // debug_print("modified, out after %d lines\n", lines);
4616 g_free(itemized_chars);
4619 undo_wrapping(compose->undostruct, FALSE);
4620 compose->autowrap = prev_autowrap;
4625 void compose_action_cb(void *data)
4627 Compose *compose = (Compose *)data;
4628 compose_wrap_all(compose);
4631 static void compose_wrap_all(Compose *compose)
4633 compose_wrap_all_full(compose, FALSE);
4636 static void compose_wrap_all_full(Compose *compose, gboolean force)
4638 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4639 GtkTextBuffer *buffer;
4641 gboolean modified = TRUE;
4643 buffer = gtk_text_view_get_buffer(text);
4645 gtk_text_buffer_get_start_iter(buffer, &iter);
4646 while (!gtk_text_iter_is_end(&iter) && modified)
4647 modified = compose_beautify_paragraph(compose, &iter, force);
4651 static void compose_set_title(Compose *compose)
4657 edited = compose->modified ? _(" [Edited]") : "";
4659 subject = gtk_editable_get_chars(
4660 GTK_EDITABLE(compose->subject_entry), 0, -1);
4662 #ifndef GENERIC_UMPC
4663 if (subject && strlen(subject))
4664 str = g_strdup_printf(_("%s - Compose message%s"),
4667 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4669 str = g_strdup(_("Compose message"));
4672 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4678 * compose_current_mail_account:
4680 * Find a current mail account (the currently selected account, or the
4681 * default account, if a news account is currently selected). If a
4682 * mail account cannot be found, display an error message.
4684 * Return value: Mail account, or NULL if not found.
4686 static PrefsAccount *
4687 compose_current_mail_account(void)
4691 if (cur_account && cur_account->protocol != A_NNTP)
4694 ac = account_get_default();
4695 if (!ac || ac->protocol == A_NNTP) {
4696 alertpanel_error(_("Account for sending mail is not specified.\n"
4697 "Please select a mail account before sending."));
4704 #define QUOTE_IF_REQUIRED(out, str) \
4706 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4710 len = strlen(str) + 3; \
4711 if ((__tmp = alloca(len)) == NULL) { \
4712 g_warning("can't allocate memory\n"); \
4713 g_string_free(header, TRUE); \
4716 g_snprintf(__tmp, len, "\"%s\"", str); \
4721 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4722 g_warning("can't allocate memory\n"); \
4723 g_string_free(header, TRUE); \
4726 strcpy(__tmp, str); \
4732 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4734 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4738 len = strlen(str) + 3; \
4739 if ((__tmp = alloca(len)) == NULL) { \
4740 g_warning("can't allocate memory\n"); \
4743 g_snprintf(__tmp, len, "\"%s\"", str); \
4748 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4749 g_warning("can't allocate memory\n"); \
4752 strcpy(__tmp, str); \
4758 static void compose_select_account(Compose *compose, PrefsAccount *account,
4761 gchar *from = NULL, *header;
4762 ComposeHeaderEntry *header_entry;
4764 cm_return_if_fail(account != NULL);
4766 compose->account = account;
4767 if (account->name && *account->name) {
4769 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4770 from = g_strdup_printf("%s <%s>",
4771 buf, account->address);
4772 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4774 from = g_strdup_printf("<%s>",
4776 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4781 compose_set_title(compose);
4783 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4784 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4786 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4787 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4788 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4790 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4792 activate_privacy_system(compose, account, FALSE);
4794 if (!init && compose->mode != COMPOSE_REDIRECT) {
4795 undo_block(compose->undostruct);
4796 compose_insert_sig(compose, TRUE);
4797 undo_unblock(compose->undostruct);
4800 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4801 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4803 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4804 if (account->protocol == A_NNTP) {
4805 if (!strcmp(header, _("To:")))
4806 combobox_select_by_text(
4807 GTK_COMBO_BOX(header_entry->combo),
4810 if (!strcmp(header, _("Newsgroups:")))
4811 combobox_select_by_text(
4812 GTK_COMBO_BOX(header_entry->combo),
4820 /* use account's dict info if set */
4821 if (compose->gtkaspell) {
4822 if (account->enable_default_dictionary)
4823 gtkaspell_change_dict(compose->gtkaspell,
4824 account->default_dictionary, FALSE);
4825 if (account->enable_default_alt_dictionary)
4826 gtkaspell_change_alt_dict(compose->gtkaspell,
4827 account->default_alt_dictionary);
4828 if (account->enable_default_dictionary
4829 || account->enable_default_alt_dictionary)
4830 compose_spell_menu_changed(compose);
4835 gboolean compose_check_for_valid_recipient(Compose *compose) {
4836 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4837 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4838 gboolean recipient_found = FALSE;
4842 /* free to and newsgroup list */
4843 slist_free_strings_full(compose->to_list);
4844 compose->to_list = NULL;
4846 slist_free_strings_full(compose->newsgroup_list);
4847 compose->newsgroup_list = NULL;
4849 /* search header entries for to and newsgroup entries */
4850 for (list = compose->header_list; list; list = list->next) {
4853 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4854 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4857 if (entry[0] != '\0') {
4858 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4859 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4860 compose->to_list = address_list_append(compose->to_list, entry);
4861 recipient_found = TRUE;
4864 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4865 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4866 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4867 recipient_found = TRUE;
4874 return recipient_found;
4877 static gboolean compose_check_for_set_recipients(Compose *compose)
4879 if (compose->account->set_autocc && compose->account->auto_cc) {
4880 gboolean found_other = FALSE;
4882 /* search header entries for to and newsgroup entries */
4883 for (list = compose->header_list; list; list = list->next) {
4886 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4887 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4890 if (strcmp(entry, compose->account->auto_cc)
4891 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4901 if (compose->batch) {
4902 gtk_widget_show_all(compose->window);
4904 aval = alertpanel(_("Send"),
4905 _("The only recipient is the default CC address. Send anyway?"),
4906 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4907 if (aval != G_ALERTALTERNATE)
4911 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4912 gboolean found_other = FALSE;
4914 /* search header entries for to and newsgroup entries */
4915 for (list = compose->header_list; list; list = list->next) {
4918 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4919 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4922 if (strcmp(entry, compose->account->auto_bcc)
4923 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4933 if (compose->batch) {
4934 gtk_widget_show_all(compose->window);
4936 aval = alertpanel(_("Send"),
4937 _("The only recipient is the default BCC address. Send anyway?"),
4938 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4939 if (aval != G_ALERTALTERNATE)
4946 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4950 if (compose_check_for_valid_recipient(compose) == FALSE) {
4951 if (compose->batch) {
4952 gtk_widget_show_all(compose->window);
4954 alertpanel_error(_("Recipient is not specified."));
4958 if (compose_check_for_set_recipients(compose) == FALSE) {
4962 if (!compose->batch) {
4963 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4964 if (*str == '\0' && check_everything == TRUE &&
4965 compose->mode != COMPOSE_REDIRECT) {
4967 gchar *button_label;
4970 if (compose->sending)
4971 button_label = _("+_Send");
4973 button_label = _("+_Queue");
4974 message = g_strdup_printf(_("Subject is empty. %s"),
4975 compose->sending?_("Send it anyway?"):
4976 _("Queue it anyway?"));
4978 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4979 GTK_STOCK_CANCEL, button_label, NULL);
4981 if (aval != G_ALERTALTERNATE)
4986 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4992 gint compose_send(Compose *compose)
4995 FolderItem *folder = NULL;
4997 gchar *msgpath = NULL;
4998 gboolean discard_window = FALSE;
4999 gchar *errstr = NULL;
5000 gchar *tmsgid = NULL;
5001 MainWindow *mainwin = mainwindow_get_mainwindow();
5002 gboolean queued_removed = FALSE;
5004 if (prefs_common.send_dialog_invisible
5005 || compose->batch == TRUE)
5006 discard_window = TRUE;
5008 compose_allow_user_actions (compose, FALSE);
5009 compose->sending = TRUE;
5011 if (compose_check_entries(compose, TRUE) == FALSE) {
5012 if (compose->batch) {
5013 gtk_widget_show_all(compose->window);
5019 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5022 if (compose->batch) {
5023 gtk_widget_show_all(compose->window);
5026 alertpanel_error(_("Could not queue message for sending:\n\n"
5027 "Charset conversion failed."));
5028 } else if (val == -5) {
5029 alertpanel_error(_("Could not queue message for sending:\n\n"
5030 "Couldn't get recipient encryption key."));
5031 } else if (val == -6) {
5033 } else if (val == -3) {
5034 if (privacy_peek_error())
5035 alertpanel_error(_("Could not queue message for sending:\n\n"
5036 "Signature failed: %s"), privacy_get_error());
5037 } else if (val == -2 && errno != 0) {
5038 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
5040 alertpanel_error(_("Could not queue message for sending."));
5045 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5046 if (discard_window) {
5047 compose->sending = FALSE;
5048 compose_close(compose);
5049 /* No more compose access in the normal codepath
5050 * after this point! */
5055 alertpanel_error(_("The message was queued but could not be "
5056 "sent.\nUse \"Send queued messages\" from "
5057 "the main window to retry."));
5058 if (!discard_window) {
5065 if (msgpath == NULL) {
5066 msgpath = folder_item_fetch_msg(folder, msgnum);
5067 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5070 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5071 claws_unlink(msgpath);
5074 if (!discard_window) {
5076 if (!queued_removed)
5077 folder_item_remove_msg(folder, msgnum);
5078 folder_item_scan(folder);
5080 /* make sure we delete that */
5081 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5083 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5084 folder_item_remove_msg(folder, tmp->msgnum);
5085 procmsg_msginfo_free(tmp);
5092 if (!queued_removed)
5093 folder_item_remove_msg(folder, msgnum);
5094 folder_item_scan(folder);
5096 /* make sure we delete that */
5097 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5099 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5100 folder_item_remove_msg(folder, tmp->msgnum);
5101 procmsg_msginfo_free(tmp);
5104 if (!discard_window) {
5105 compose->sending = FALSE;
5106 compose_allow_user_actions (compose, TRUE);
5107 compose_close(compose);
5111 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5112 "the main window to retry."), errstr);
5115 alertpanel_error_log(_("The message was queued but could not be "
5116 "sent.\nUse \"Send queued messages\" from "
5117 "the main window to retry."));
5119 if (!discard_window) {
5128 toolbar_main_set_sensitive(mainwin);
5129 main_window_set_menu_sensitive(mainwin);
5135 compose_allow_user_actions (compose, TRUE);
5136 compose->sending = FALSE;
5137 compose->modified = TRUE;
5138 toolbar_main_set_sensitive(mainwin);
5139 main_window_set_menu_sensitive(mainwin);
5144 static gboolean compose_use_attach(Compose *compose)
5146 GtkTreeModel *model = gtk_tree_view_get_model
5147 (GTK_TREE_VIEW(compose->attach_clist));
5148 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5151 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5154 gchar buf[BUFFSIZE];
5156 gboolean first_to_address;
5157 gboolean first_cc_address;
5159 ComposeHeaderEntry *headerentry;
5160 const gchar *headerentryname;
5161 const gchar *cc_hdr;
5162 const gchar *to_hdr;
5163 gboolean err = FALSE;
5165 debug_print("Writing redirect header\n");
5167 cc_hdr = prefs_common_translated_header_name("Cc:");
5168 to_hdr = prefs_common_translated_header_name("To:");
5170 first_to_address = TRUE;
5171 for (list = compose->header_list; list; list = list->next) {
5172 headerentry = ((ComposeHeaderEntry *)list->data);
5173 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5175 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5176 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5177 Xstrdup_a(str, entstr, return -1);
5179 if (str[0] != '\0') {
5180 compose_convert_header
5181 (compose, buf, sizeof(buf), str,
5182 strlen("Resent-To") + 2, TRUE);
5184 if (first_to_address) {
5185 err |= (fprintf(fp, "Resent-To: ") < 0);
5186 first_to_address = FALSE;
5188 err |= (fprintf(fp, ",") < 0);
5190 err |= (fprintf(fp, "%s", buf) < 0);
5194 if (!first_to_address) {
5195 err |= (fprintf(fp, "\n") < 0);
5198 first_cc_address = TRUE;
5199 for (list = compose->header_list; list; list = list->next) {
5200 headerentry = ((ComposeHeaderEntry *)list->data);
5201 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5203 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5204 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5205 Xstrdup_a(str, strg, return -1);
5207 if (str[0] != '\0') {
5208 compose_convert_header
5209 (compose, buf, sizeof(buf), str,
5210 strlen("Resent-Cc") + 2, TRUE);
5212 if (first_cc_address) {
5213 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5214 first_cc_address = FALSE;
5216 err |= (fprintf(fp, ",") < 0);
5218 err |= (fprintf(fp, "%s", buf) < 0);
5222 if (!first_cc_address) {
5223 err |= (fprintf(fp, "\n") < 0);
5226 return (err ? -1:0);
5229 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5231 gchar buf[BUFFSIZE];
5233 const gchar *entstr;
5234 /* struct utsname utsbuf; */
5235 gboolean err = FALSE;
5237 cm_return_val_if_fail(fp != NULL, -1);
5238 cm_return_val_if_fail(compose->account != NULL, -1);
5239 cm_return_val_if_fail(compose->account->address != NULL, -1);
5242 get_rfc822_date(buf, sizeof(buf));
5243 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5246 if (compose->account->name && *compose->account->name) {
5247 compose_convert_header
5248 (compose, buf, sizeof(buf), compose->account->name,
5249 strlen("From: "), TRUE);
5250 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5251 buf, compose->account->address) < 0);
5253 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5256 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5257 if (*entstr != '\0') {
5258 Xstrdup_a(str, entstr, return -1);
5261 compose_convert_header(compose, buf, sizeof(buf), str,
5262 strlen("Subject: "), FALSE);
5263 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5267 /* Resent-Message-ID */
5268 if (compose->account->set_domain && compose->account->domain) {
5269 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5270 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5271 g_snprintf(buf, sizeof(buf), "%s",
5272 strchr(compose->account->address, '@') ?
5273 strchr(compose->account->address, '@')+1 :
5274 compose->account->address);
5276 g_snprintf(buf, sizeof(buf), "%s", "");
5279 if (compose->account->gen_msgid) {
5281 if (compose->account->msgid_with_addr) {
5282 addr = compose->account->address;
5284 generate_msgid(buf, sizeof(buf), addr);
5285 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5286 compose->msgid = g_strdup(buf);
5288 compose->msgid = NULL;
5291 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5294 /* separator between header and body */
5295 err |= (fputs("\n", fp) == EOF);
5297 return (err ? -1:0);
5300 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5304 gchar buf[BUFFSIZE];
5306 gboolean skip = FALSE;
5307 gboolean err = FALSE;
5308 gchar *not_included[]={
5309 "Return-Path:", "Delivered-To:", "Received:",
5310 "Subject:", "X-UIDL:", "AF:",
5311 "NF:", "PS:", "SRH:",
5312 "SFN:", "DSR:", "MID:",
5313 "CFG:", "PT:", "S:",
5314 "RQ:", "SSV:", "NSV:",
5315 "SSH:", "R:", "MAID:",
5316 "NAID:", "RMID:", "FMID:",
5317 "SCF:", "RRCPT:", "NG:",
5318 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5319 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5320 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5321 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5322 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5325 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5326 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5330 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5332 for (i = 0; not_included[i] != NULL; i++) {
5333 if (g_ascii_strncasecmp(buf, not_included[i],
5334 strlen(not_included[i])) == 0) {
5341 if (fputs(buf, fdest) == -1)
5344 if (!prefs_common.redirect_keep_from) {
5345 if (g_ascii_strncasecmp(buf, "From:",
5346 strlen("From:")) == 0) {
5347 err |= (fputs(" (by way of ", fdest) == EOF);
5348 if (compose->account->name
5349 && *compose->account->name) {
5350 compose_convert_header
5351 (compose, buf, sizeof(buf),
5352 compose->account->name,
5355 err |= (fprintf(fdest, "%s <%s>",
5357 compose->account->address) < 0);
5359 err |= (fprintf(fdest, "%s",
5360 compose->account->address) < 0);
5361 err |= (fputs(")", fdest) == EOF);
5365 if (fputs("\n", fdest) == -1)
5372 if (compose_redirect_write_headers(compose, fdest))
5375 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5376 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5389 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5391 GtkTextBuffer *buffer;
5392 GtkTextIter start, end;
5395 const gchar *out_codeset;
5396 EncodingType encoding = ENC_UNKNOWN;
5397 MimeInfo *mimemsg, *mimetext;
5399 const gchar *src_codeset = CS_INTERNAL;
5400 gchar *from_addr = NULL;
5401 gchar *from_name = NULL;
5403 if (action == COMPOSE_WRITE_FOR_SEND)
5404 attach_parts = TRUE;
5406 /* create message MimeInfo */
5407 mimemsg = procmime_mimeinfo_new();
5408 mimemsg->type = MIMETYPE_MESSAGE;
5409 mimemsg->subtype = g_strdup("rfc822");
5410 mimemsg->content = MIMECONTENT_MEM;
5411 mimemsg->tmp = TRUE; /* must free content later */
5412 mimemsg->data.mem = compose_get_header(compose);
5414 /* Create text part MimeInfo */
5415 /* get all composed text */
5416 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5417 gtk_text_buffer_get_start_iter(buffer, &start);
5418 gtk_text_buffer_get_end_iter(buffer, &end);
5419 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5421 out_codeset = conv_get_charset_str(compose->out_encoding);
5423 if (!out_codeset && is_ascii_str(chars)) {
5424 out_codeset = CS_US_ASCII;
5425 } else if (prefs_common.outgoing_fallback_to_ascii &&
5426 is_ascii_str(chars)) {
5427 out_codeset = CS_US_ASCII;
5428 encoding = ENC_7BIT;
5432 gchar *test_conv_global_out = NULL;
5433 gchar *test_conv_reply = NULL;
5435 /* automatic mode. be automatic. */
5436 codeconv_set_strict(TRUE);
5438 out_codeset = conv_get_outgoing_charset_str();
5440 debug_print("trying to convert to %s\n", out_codeset);
5441 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5444 if (!test_conv_global_out && compose->orig_charset
5445 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5446 out_codeset = compose->orig_charset;
5447 debug_print("failure; trying to convert to %s\n", out_codeset);
5448 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5451 if (!test_conv_global_out && !test_conv_reply) {
5453 out_codeset = CS_INTERNAL;
5454 debug_print("failure; finally using %s\n", out_codeset);
5456 g_free(test_conv_global_out);
5457 g_free(test_conv_reply);
5458 codeconv_set_strict(FALSE);
5461 if (encoding == ENC_UNKNOWN) {
5462 if (prefs_common.encoding_method == CTE_BASE64)
5463 encoding = ENC_BASE64;
5464 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5465 encoding = ENC_QUOTED_PRINTABLE;
5466 else if (prefs_common.encoding_method == CTE_8BIT)
5467 encoding = ENC_8BIT;
5469 encoding = procmime_get_encoding_for_charset(out_codeset);
5472 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5473 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5475 if (action == COMPOSE_WRITE_FOR_SEND) {
5476 codeconv_set_strict(TRUE);
5477 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5478 codeconv_set_strict(FALSE);
5484 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5485 "to the specified %s charset.\n"
5486 "Send it as %s?"), out_codeset, src_codeset);
5487 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5488 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5491 if (aval != G_ALERTALTERNATE) {
5496 out_codeset = src_codeset;
5502 out_codeset = src_codeset;
5507 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5508 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5509 strstr(buf, "\nFrom ") != NULL) {
5510 encoding = ENC_QUOTED_PRINTABLE;
5514 mimetext = procmime_mimeinfo_new();
5515 mimetext->content = MIMECONTENT_MEM;
5516 mimetext->tmp = TRUE; /* must free content later */
5517 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5518 * and free the data, which we need later. */
5519 mimetext->data.mem = g_strdup(buf);
5520 mimetext->type = MIMETYPE_TEXT;
5521 mimetext->subtype = g_strdup("plain");
5522 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5523 g_strdup(out_codeset));
5525 /* protect trailing spaces when signing message */
5526 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5527 privacy_system_can_sign(compose->privacy_system)) {
5528 encoding = ENC_QUOTED_PRINTABLE;
5531 debug_print("main text: %zd bytes encoded as %s in %d\n",
5532 strlen(buf), out_codeset, encoding);
5534 /* check for line length limit */
5535 if (action == COMPOSE_WRITE_FOR_SEND &&
5536 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5537 check_line_length(buf, 1000, &line) < 0) {
5541 msg = g_strdup_printf
5542 (_("Line %d exceeds the line length limit (998 bytes).\n"
5543 "The contents of the message might be broken on the way to the delivery.\n"
5545 "Send it anyway?"), line + 1);
5546 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5548 if (aval != G_ALERTALTERNATE) {
5554 if (encoding != ENC_UNKNOWN)
5555 procmime_encode_content(mimetext, encoding);
5557 /* append attachment parts */
5558 if (compose_use_attach(compose) && attach_parts) {
5559 MimeInfo *mimempart;
5560 gchar *boundary = NULL;
5561 mimempart = procmime_mimeinfo_new();
5562 mimempart->content = MIMECONTENT_EMPTY;
5563 mimempart->type = MIMETYPE_MULTIPART;
5564 mimempart->subtype = g_strdup("mixed");
5568 boundary = generate_mime_boundary(NULL);
5569 } while (strstr(buf, boundary) != NULL);
5571 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5574 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5576 g_node_append(mimempart->node, mimetext->node);
5577 g_node_append(mimemsg->node, mimempart->node);
5579 if (compose_add_attachments(compose, mimempart) < 0)
5582 g_node_append(mimemsg->node, mimetext->node);
5586 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5587 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5588 /* extract name and address */
5589 if (strstr(spec, " <") && strstr(spec, ">")) {
5590 from_addr = g_strdup(strrchr(spec, '<')+1);
5591 *(strrchr(from_addr, '>')) = '\0';
5592 from_name = g_strdup(spec);
5593 *(strrchr(from_name, '<')) = '\0';
5600 /* sign message if sending */
5601 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5602 privacy_system_can_sign(compose->privacy_system))
5603 if (!privacy_sign(compose->privacy_system, mimemsg,
5604 compose->account, from_addr)) {
5611 procmime_write_mimeinfo(mimemsg, fp);
5613 procmime_mimeinfo_free_all(mimemsg);
5618 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5620 GtkTextBuffer *buffer;
5621 GtkTextIter start, end;
5626 if ((fp = g_fopen(file, "wb")) == NULL) {
5627 FILE_OP_ERROR(file, "fopen");
5631 /* chmod for security */
5632 if (change_file_mode_rw(fp, file) < 0) {
5633 FILE_OP_ERROR(file, "chmod");
5634 g_warning("can't change file mode\n");
5637 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5638 gtk_text_buffer_get_start_iter(buffer, &start);
5639 gtk_text_buffer_get_end_iter(buffer, &end);
5640 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5642 chars = conv_codeset_strdup
5643 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5646 if (!chars) return -1;
5649 len = strlen(chars);
5650 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5651 FILE_OP_ERROR(file, "fwrite");
5660 if (fclose(fp) == EOF) {
5661 FILE_OP_ERROR(file, "fclose");
5668 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5671 MsgInfo *msginfo = compose->targetinfo;
5673 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5674 if (!msginfo) return -1;
5676 if (!force && MSG_IS_LOCKED(msginfo->flags))
5679 item = msginfo->folder;
5680 cm_return_val_if_fail(item != NULL, -1);
5682 if (procmsg_msg_exist(msginfo) &&
5683 (folder_has_parent_of_type(item, F_QUEUE) ||
5684 folder_has_parent_of_type(item, F_DRAFT)
5685 || msginfo == compose->autosaved_draft)) {
5686 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5687 g_warning("can't remove the old message\n");
5690 debug_print("removed reedit target %d\n", msginfo->msgnum);
5697 static void compose_remove_draft(Compose *compose)
5700 MsgInfo *msginfo = compose->targetinfo;
5701 drafts = account_get_special_folder(compose->account, F_DRAFT);
5703 if (procmsg_msg_exist(msginfo)) {
5704 folder_item_remove_msg(drafts, msginfo->msgnum);
5709 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5710 gboolean remove_reedit_target)
5712 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5715 static gboolean compose_warn_encryption(Compose *compose)
5717 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5718 AlertValue val = G_ALERTALTERNATE;
5720 if (warning == NULL)
5723 val = alertpanel_full(_("Encryption warning"), warning,
5724 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5725 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5726 if (val & G_ALERTDISABLE) {
5727 val &= ~G_ALERTDISABLE;
5728 if (val == G_ALERTALTERNATE)
5729 privacy_inhibit_encrypt_warning(compose->privacy_system,
5733 if (val == G_ALERTALTERNATE) {
5740 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5741 gchar **msgpath, gboolean check_subject,
5742 gboolean remove_reedit_target)
5749 PrefsAccount *mailac = NULL, *newsac = NULL;
5750 gboolean err = FALSE;
5752 debug_print("queueing message...\n");
5753 cm_return_val_if_fail(compose->account != NULL, -1);
5755 if (compose_check_entries(compose, check_subject) == FALSE) {
5756 if (compose->batch) {
5757 gtk_widget_show_all(compose->window);
5762 if (!compose->to_list && !compose->newsgroup_list) {
5763 g_warning("can't get recipient list.");
5767 if (compose->to_list) {
5768 if (compose->account->protocol != A_NNTP)
5769 mailac = compose->account;
5770 else if (cur_account && cur_account->protocol != A_NNTP)
5771 mailac = cur_account;
5772 else if (!(mailac = compose_current_mail_account())) {
5773 alertpanel_error(_("No account for sending mails available!"));
5778 if (compose->newsgroup_list) {
5779 if (compose->account->protocol == A_NNTP)
5780 newsac = compose->account;
5782 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5787 /* write queue header */
5788 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5789 G_DIR_SEPARATOR, compose, (guint) rand());
5790 debug_print("queuing to %s\n", tmp);
5791 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5792 FILE_OP_ERROR(tmp, "fopen");
5797 if (change_file_mode_rw(fp, tmp) < 0) {
5798 FILE_OP_ERROR(tmp, "chmod");
5799 g_warning("can't change file mode\n");
5802 /* queueing variables */
5803 err |= (fprintf(fp, "AF:\n") < 0);
5804 err |= (fprintf(fp, "NF:0\n") < 0);
5805 err |= (fprintf(fp, "PS:10\n") < 0);
5806 err |= (fprintf(fp, "SRH:1\n") < 0);
5807 err |= (fprintf(fp, "SFN:\n") < 0);
5808 err |= (fprintf(fp, "DSR:\n") < 0);
5810 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5812 err |= (fprintf(fp, "MID:\n") < 0);
5813 err |= (fprintf(fp, "CFG:\n") < 0);
5814 err |= (fprintf(fp, "PT:0\n") < 0);
5815 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5816 err |= (fprintf(fp, "RQ:\n") < 0);
5818 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5820 err |= (fprintf(fp, "SSV:\n") < 0);
5822 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5824 err |= (fprintf(fp, "NSV:\n") < 0);
5825 err |= (fprintf(fp, "SSH:\n") < 0);
5826 /* write recepient list */
5827 if (compose->to_list) {
5828 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5829 for (cur = compose->to_list->next; cur != NULL;
5831 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5832 err |= (fprintf(fp, "\n") < 0);
5834 /* write newsgroup list */
5835 if (compose->newsgroup_list) {
5836 err |= (fprintf(fp, "NG:") < 0);
5837 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5838 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5839 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5840 err |= (fprintf(fp, "\n") < 0);
5842 /* Sylpheed account IDs */
5844 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5846 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5849 if (compose->privacy_system != NULL) {
5850 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5851 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5852 if (compose->use_encryption) {
5854 if (!compose_warn_encryption(compose)) {
5860 if (mailac && mailac->encrypt_to_self) {
5861 GSList *tmp_list = g_slist_copy(compose->to_list);
5862 tmp_list = g_slist_append(tmp_list, compose->account->address);
5863 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5864 g_slist_free(tmp_list);
5866 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5868 if (encdata != NULL) {
5869 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5870 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5871 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5873 } /* else we finally dont want to encrypt */
5875 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5876 /* and if encdata was null, it means there's been a problem in
5879 g_warning("failed to write queue message");
5889 /* Save copy folder */
5890 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5891 gchar *savefolderid;
5893 savefolderid = compose_get_save_to(compose);
5894 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5895 g_free(savefolderid);
5897 /* Save copy folder */
5898 if (compose->return_receipt) {
5899 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5901 /* Message-ID of message replying to */
5902 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5905 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5906 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5909 /* Message-ID of message forwarding to */
5910 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5913 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5914 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5918 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5919 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5921 /* end of headers */
5922 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5924 if (compose->redirect_filename != NULL) {
5925 if (compose_redirect_write_to_file(compose, fp) < 0) {
5933 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5937 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5941 g_warning("failed to write queue message\n");
5947 if (fclose(fp) == EOF) {
5948 FILE_OP_ERROR(tmp, "fclose");
5954 if (item && *item) {
5957 queue = account_get_special_folder(compose->account, F_QUEUE);
5960 g_warning("can't find queue folder\n");
5965 folder_item_scan(queue);
5966 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5967 g_warning("can't queue the message\n");
5973 if (msgpath == NULL) {
5979 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5980 compose_remove_reedit_target(compose, FALSE);
5983 if ((msgnum != NULL) && (item != NULL)) {
5991 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5994 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5996 struct stat statbuf;
5997 gchar *type, *subtype;
5998 GtkTreeModel *model;
6001 model = gtk_tree_view_get_model(tree_view);
6003 if (!gtk_tree_model_get_iter_first(model, &iter))
6006 gtk_tree_model_get(model, &iter,
6010 if (!is_file_exist(ainfo->file)) {
6011 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6012 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6013 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6015 if (val == G_ALERTDEFAULT) {
6020 mimepart = procmime_mimeinfo_new();
6021 mimepart->content = MIMECONTENT_FILE;
6022 mimepart->data.filename = g_strdup(ainfo->file);
6023 mimepart->tmp = FALSE; /* or we destroy our attachment */
6024 mimepart->offset = 0;
6026 g_stat(ainfo->file, &statbuf);
6027 mimepart->length = statbuf.st_size;
6029 type = g_strdup(ainfo->content_type);
6031 if (!strchr(type, '/')) {
6033 type = g_strdup("application/octet-stream");
6036 subtype = strchr(type, '/') + 1;
6037 *(subtype - 1) = '\0';
6038 mimepart->type = procmime_get_media_type(type);
6039 mimepart->subtype = g_strdup(subtype);
6042 if (mimepart->type == MIMETYPE_MESSAGE &&
6043 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6044 mimepart->disposition = DISPOSITIONTYPE_INLINE;
6045 } else if (mimepart->type == MIMETYPE_TEXT) {
6046 if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6047 /* Text parts with no name come from multipart/alternative
6048 * forwards. Make sure the recipient won't look at the
6049 * original HTML part by mistake. */
6050 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6051 ainfo->name = g_strdup_printf(_("Original %s part"),
6055 g_hash_table_insert(mimepart->typeparameters,
6056 g_strdup("charset"), g_strdup(ainfo->charset));
6058 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6059 if (mimepart->type == MIMETYPE_APPLICATION &&
6060 !strcmp2(mimepart->subtype, "octet-stream"))
6061 g_hash_table_insert(mimepart->typeparameters,
6062 g_strdup("name"), g_strdup(ainfo->name));
6063 g_hash_table_insert(mimepart->dispositionparameters,
6064 g_strdup("filename"), g_strdup(ainfo->name));
6065 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6068 if (mimepart->type == MIMETYPE_MESSAGE
6069 || mimepart->type == MIMETYPE_MULTIPART)
6070 ainfo->encoding = ENC_BINARY;
6071 else if (compose->use_signing) {
6072 if (ainfo->encoding == ENC_7BIT)
6073 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6074 else if (ainfo->encoding == ENC_8BIT)
6075 ainfo->encoding = ENC_BASE64;
6080 procmime_encode_content(mimepart, ainfo->encoding);
6082 g_node_append(parent->node, mimepart->node);
6083 } while (gtk_tree_model_iter_next(model, &iter));
6088 #define IS_IN_CUSTOM_HEADER(header) \
6089 (compose->account->add_customhdr && \
6090 custom_header_find(compose->account->customhdr_list, header) != NULL)
6092 static void compose_add_headerfield_from_headerlist(Compose *compose,
6094 const gchar *fieldname,
6095 const gchar *seperator)
6097 gchar *str, *fieldname_w_colon;
6098 gboolean add_field = FALSE;
6100 ComposeHeaderEntry *headerentry;
6101 const gchar *headerentryname;
6102 const gchar *trans_fieldname;
6105 if (IS_IN_CUSTOM_HEADER(fieldname))
6108 debug_print("Adding %s-fields\n", fieldname);
6110 fieldstr = g_string_sized_new(64);
6112 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6113 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6115 for (list = compose->header_list; list; list = list->next) {
6116 headerentry = ((ComposeHeaderEntry *)list->data);
6117 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6119 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6120 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6122 if (str[0] != '\0') {
6124 g_string_append(fieldstr, seperator);
6125 g_string_append(fieldstr, str);
6134 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6135 compose_convert_header
6136 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6137 strlen(fieldname) + 2, TRUE);
6138 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6142 g_free(fieldname_w_colon);
6143 g_string_free(fieldstr, TRUE);
6148 static gchar *compose_get_manual_headers_info(Compose *compose)
6150 GString *sh_header = g_string_new(" ");
6152 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6154 for (list = compose->header_list; list; list = list->next) {
6155 ComposeHeaderEntry *headerentry;
6158 gchar *headername_wcolon;
6159 const gchar *headername_trans;
6161 gboolean standard_header = FALSE;
6163 headerentry = ((ComposeHeaderEntry *)list->data);
6165 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6167 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6172 if (!strstr(tmp, ":")) {
6173 headername_wcolon = g_strconcat(tmp, ":", NULL);
6174 headername = g_strdup(tmp);
6176 headername_wcolon = g_strdup(tmp);
6177 headername = g_strdup(strtok(tmp, ":"));
6181 string = std_headers;
6182 while (*string != NULL) {
6183 headername_trans = prefs_common_translated_header_name(*string);
6184 if (!strcmp(headername_trans, headername_wcolon))
6185 standard_header = TRUE;
6188 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6189 g_string_append_printf(sh_header, "%s ", headername);
6191 g_free(headername_wcolon);
6193 g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6194 return g_string_free(sh_header, FALSE);
6197 static gchar *compose_get_header(Compose *compose)
6199 gchar buf[BUFFSIZE];
6200 const gchar *entry_str;
6204 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6206 gchar *from_name = NULL, *from_address = NULL;
6209 cm_return_val_if_fail(compose->account != NULL, NULL);
6210 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6212 header = g_string_sized_new(64);
6215 get_rfc822_date(buf, sizeof(buf));
6216 g_string_append_printf(header, "Date: %s\n", buf);
6220 if (compose->account->name && *compose->account->name) {
6222 QUOTE_IF_REQUIRED(buf, compose->account->name);
6223 tmp = g_strdup_printf("%s <%s>",
6224 buf, compose->account->address);
6226 tmp = g_strdup_printf("%s",
6227 compose->account->address);
6229 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6230 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6232 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6233 from_address = g_strdup(compose->account->address);
6235 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6236 /* extract name and address */
6237 if (strstr(spec, " <") && strstr(spec, ">")) {
6238 from_address = g_strdup(strrchr(spec, '<')+1);
6239 *(strrchr(from_address, '>')) = '\0';
6240 from_name = g_strdup(spec);
6241 *(strrchr(from_name, '<')) = '\0';
6244 from_address = g_strdup(spec);
6251 if (from_name && *from_name) {
6252 compose_convert_header
6253 (compose, buf, sizeof(buf), from_name,
6254 strlen("From: "), TRUE);
6255 QUOTE_IF_REQUIRED(name, buf);
6257 g_string_append_printf(header, "From: %s <%s>\n",
6258 name, from_address);
6260 g_string_append_printf(header, "From: %s\n", from_address);
6263 g_free(from_address);
6266 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6269 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6272 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6276 * If this account is a NNTP account remove Bcc header from
6277 * message body since it otherwise will be publicly shown
6279 if (compose->account->protocol != A_NNTP)
6280 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6283 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6285 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6288 compose_convert_header(compose, buf, sizeof(buf), str,
6289 strlen("Subject: "), FALSE);
6290 g_string_append_printf(header, "Subject: %s\n", buf);
6296 if (compose->account->set_domain && compose->account->domain) {
6297 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6298 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6299 g_snprintf(buf, sizeof(buf), "%s",
6300 strchr(compose->account->address, '@') ?
6301 strchr(compose->account->address, '@')+1 :
6302 compose->account->address);
6304 g_snprintf(buf, sizeof(buf), "%s", "");
6307 if (compose->account->gen_msgid) {
6309 if (compose->account->msgid_with_addr) {
6310 addr = compose->account->address;
6312 generate_msgid(buf, sizeof(buf), addr);
6313 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6314 compose->msgid = g_strdup(buf);
6316 compose->msgid = NULL;
6319 if (compose->remove_references == FALSE) {
6321 if (compose->inreplyto && compose->to_list)
6322 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6325 if (compose->references)
6326 g_string_append_printf(header, "References: %s\n", compose->references);
6330 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6333 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6336 if (compose->account->organization &&
6337 strlen(compose->account->organization) &&
6338 !IS_IN_CUSTOM_HEADER("Organization")) {
6339 compose_convert_header(compose, buf, sizeof(buf),
6340 compose->account->organization,
6341 strlen("Organization: "), FALSE);
6342 g_string_append_printf(header, "Organization: %s\n", buf);
6345 /* Program version and system info */
6346 if (compose->account->gen_xmailer &&
6347 g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6348 !compose->newsgroup_list) {
6349 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6351 gtk_major_version, gtk_minor_version, gtk_micro_version,
6354 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6355 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6357 gtk_major_version, gtk_minor_version, gtk_micro_version,
6361 /* custom headers */
6362 if (compose->account->add_customhdr) {
6365 for (cur = compose->account->customhdr_list; cur != NULL;
6367 CustomHeader *chdr = (CustomHeader *)cur->data;
6369 if (custom_header_is_allowed(chdr->name)
6370 && chdr->value != NULL
6371 && *(chdr->value) != '\0') {
6372 compose_convert_header
6373 (compose, buf, sizeof(buf),
6375 strlen(chdr->name) + 2, FALSE);
6376 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6381 /* Automatic Faces and X-Faces */
6382 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6383 g_string_append_printf(header, "X-Face: %s\n", buf);
6385 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6386 g_string_append_printf(header, "X-Face: %s\n", buf);
6388 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6389 g_string_append_printf(header, "Face: %s\n", buf);
6391 else if (get_default_face (buf, sizeof(buf)) == 0) {
6392 g_string_append_printf(header, "Face: %s\n", buf);
6396 switch (compose->priority) {
6397 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6398 "X-Priority: 1 (Highest)\n");
6400 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6401 "X-Priority: 2 (High)\n");
6403 case PRIORITY_NORMAL: break;
6404 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6405 "X-Priority: 4 (Low)\n");
6407 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6408 "X-Priority: 5 (Lowest)\n");
6410 default: debug_print("compose: priority unknown : %d\n",
6414 /* Request Return Receipt */
6415 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6416 if (compose->return_receipt) {
6417 if (compose->account->name
6418 && *compose->account->name) {
6419 compose_convert_header(compose, buf, sizeof(buf),
6420 compose->account->name,
6421 strlen("Disposition-Notification-To: "),
6423 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6425 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6429 /* get special headers */
6430 for (list = compose->header_list; list; list = list->next) {
6431 ComposeHeaderEntry *headerentry;
6434 gchar *headername_wcolon;
6435 const gchar *headername_trans;
6438 gboolean standard_header = FALSE;
6440 headerentry = ((ComposeHeaderEntry *)list->data);
6442 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6444 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6449 if (!strstr(tmp, ":")) {
6450 headername_wcolon = g_strconcat(tmp, ":", NULL);
6451 headername = g_strdup(tmp);
6453 headername_wcolon = g_strdup(tmp);
6454 headername = g_strdup(strtok(tmp, ":"));
6458 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6459 Xstrdup_a(headervalue, entry_str, return NULL);
6460 subst_char(headervalue, '\r', ' ');
6461 subst_char(headervalue, '\n', ' ');
6462 string = std_headers;
6463 while (*string != NULL) {
6464 headername_trans = prefs_common_translated_header_name(*string);
6465 if (!strcmp(headername_trans, headername_wcolon))
6466 standard_header = TRUE;
6469 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6470 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6473 g_free(headername_wcolon);
6477 g_string_free(header, FALSE);
6482 #undef IS_IN_CUSTOM_HEADER
6484 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6485 gint header_len, gboolean addr_field)
6487 gchar *tmpstr = NULL;
6488 const gchar *out_codeset = NULL;
6490 cm_return_if_fail(src != NULL);
6491 cm_return_if_fail(dest != NULL);
6493 if (len < 1) return;
6495 tmpstr = g_strdup(src);
6497 subst_char(tmpstr, '\n', ' ');
6498 subst_char(tmpstr, '\r', ' ');
6501 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6502 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6503 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6508 codeconv_set_strict(TRUE);
6509 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6510 conv_get_charset_str(compose->out_encoding));
6511 codeconv_set_strict(FALSE);
6513 if (!dest || *dest == '\0') {
6514 gchar *test_conv_global_out = NULL;
6515 gchar *test_conv_reply = NULL;
6517 /* automatic mode. be automatic. */
6518 codeconv_set_strict(TRUE);
6520 out_codeset = conv_get_outgoing_charset_str();
6522 debug_print("trying to convert to %s\n", out_codeset);
6523 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6526 if (!test_conv_global_out && compose->orig_charset
6527 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6528 out_codeset = compose->orig_charset;
6529 debug_print("failure; trying to convert to %s\n", out_codeset);
6530 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6533 if (!test_conv_global_out && !test_conv_reply) {
6535 out_codeset = CS_INTERNAL;
6536 debug_print("finally using %s\n", out_codeset);
6538 g_free(test_conv_global_out);
6539 g_free(test_conv_reply);
6540 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6542 codeconv_set_strict(FALSE);
6547 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6551 cm_return_if_fail(user_data != NULL);
6553 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6554 g_strstrip(address);
6555 if (*address != '\0') {
6556 gchar *name = procheader_get_fromname(address);
6557 extract_address(address);
6558 #ifndef USE_NEW_ADDRBOOK
6559 addressbook_add_contact(name, address, NULL, NULL);
6561 debug_print("%s: %s\n", name, address);
6562 if (addressadd_selection(name, address, NULL, NULL)) {
6563 debug_print( "addressbook_add_contact - added\n" );
6570 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6572 GtkWidget *menuitem;
6575 cm_return_if_fail(menu != NULL);
6576 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6578 menuitem = gtk_separator_menu_item_new();
6579 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6580 gtk_widget_show(menuitem);
6582 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6583 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6585 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6586 g_strstrip(address);
6587 if (*address == '\0') {
6588 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6591 g_signal_connect(G_OBJECT(menuitem), "activate",
6592 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6593 gtk_widget_show(menuitem);
6596 void compose_add_extra_header(gchar *header, GtkListStore *model)
6599 if (strcmp(header, "")) {
6600 COMBOBOX_ADD(model, header, COMPOSE_TO);
6604 void compose_add_extra_header_entries(GtkListStore *model)
6608 gchar buf[BUFFSIZE];
6611 if (extra_headers == NULL) {
6612 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6613 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6614 debug_print("extra headers file not found\n");
6615 goto extra_headers_done;
6617 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6618 lastc = strlen(buf) - 1; /* remove trailing \n */
6619 buf[lastc] = (buf[lastc] == '\n')? '\0': buf[lastc];
6621 if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6622 buf[lastc] = '\0'; /* remove trailing : for comparison */
6623 if (custom_header_is_allowed(buf)) {
6625 extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6628 g_message("disallowed extra header line: %s\n", buf);
6632 g_message("invalid extra header line: %s\n", buf);
6638 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6639 extra_headers = g_slist_reverse(extra_headers);
6641 g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6644 static void compose_create_header_entry(Compose *compose)
6646 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6653 const gchar *header = NULL;
6654 ComposeHeaderEntry *headerentry;
6655 gboolean standard_header = FALSE;
6656 GtkListStore *model;
6658 #if !(GTK_CHECK_VERSION(2,12,0))
6659 GtkTooltips *tips = compose->tooltips;
6662 headerentry = g_new0(ComposeHeaderEntry, 1);
6665 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6666 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6667 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6669 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6671 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6673 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6674 COMPOSE_NEWSGROUPS);
6675 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6677 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6678 COMPOSE_FOLLOWUPTO);
6679 compose_add_extra_header_entries(model);
6681 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6682 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6683 G_CALLBACK(compose_grab_focus_cb), compose);
6684 gtk_widget_show(combo);
6687 l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6688 gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6691 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6692 compose->header_nextrow, compose->header_nextrow+1,
6693 GTK_SHRINK, GTK_FILL, 0, 0);
6694 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6695 const gchar *last_header_entry = gtk_entry_get_text(
6696 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6698 while (*string != NULL) {
6699 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6700 standard_header = TRUE;
6703 if (standard_header)
6704 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6706 if (!compose->header_last || !standard_header) {
6707 switch(compose->account->protocol) {
6709 header = prefs_common_translated_header_name("Newsgroups:");
6712 header = prefs_common_translated_header_name("To:");
6717 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6719 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6720 G_CALLBACK(compose_grab_focus_cb), compose);
6722 /* Entry field with cleanup button */
6723 button = gtk_button_new();
6724 gtk_button_set_image(GTK_BUTTON(button),
6725 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6726 gtk_widget_show(button);
6727 CLAWS_SET_TIP(button,
6728 _("Delete entry contents"));
6729 entry = gtk_entry_new();
6730 gtk_widget_show(entry);
6731 CLAWS_SET_TIP(entry,
6732 _("Use <tab> to autocomplete from addressbook"));
6733 hbox = gtk_hbox_new (FALSE, 0);
6734 gtk_widget_show(hbox);
6735 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6736 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6737 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6738 compose->header_nextrow, compose->header_nextrow+1,
6739 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6741 g_signal_connect(G_OBJECT(entry), "key-press-event",
6742 G_CALLBACK(compose_headerentry_key_press_event_cb),
6744 g_signal_connect(G_OBJECT(entry), "changed",
6745 G_CALLBACK(compose_headerentry_changed_cb),
6747 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6748 G_CALLBACK(compose_grab_focus_cb), compose);
6750 g_signal_connect(G_OBJECT(button), "clicked",
6751 G_CALLBACK(compose_headerentry_button_clicked_cb),
6755 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6756 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6757 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6758 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6759 G_CALLBACK(compose_header_drag_received_cb),
6761 g_signal_connect(G_OBJECT(entry), "drag-drop",
6762 G_CALLBACK(compose_drag_drop),
6764 g_signal_connect(G_OBJECT(entry), "populate-popup",
6765 G_CALLBACK(compose_entry_popup_extend),
6768 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6770 headerentry->compose = compose;
6771 headerentry->combo = combo;
6772 headerentry->entry = entry;
6773 headerentry->button = button;
6774 headerentry->hbox = hbox;
6775 headerentry->headernum = compose->header_nextrow;
6776 headerentry->type = PREF_NONE;
6778 compose->header_nextrow++;
6779 compose->header_last = headerentry;
6780 compose->header_list =
6781 g_slist_append(compose->header_list,
6785 static void compose_add_header_entry(Compose *compose, const gchar *header,
6786 gchar *text, ComposePrefType pref_type)
6788 ComposeHeaderEntry *last_header = compose->header_last;
6789 gchar *tmp = g_strdup(text), *email;
6790 gboolean replyto_hdr;
6792 replyto_hdr = (!strcasecmp(header,
6793 prefs_common_translated_header_name("Reply-To:")) ||
6795 prefs_common_translated_header_name("Followup-To:")) ||
6797 prefs_common_translated_header_name("In-Reply-To:")));
6799 extract_address(tmp);
6800 email = g_utf8_strdown(tmp, -1);
6802 if (replyto_hdr == FALSE &&
6803 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6805 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6806 header, text, (gint) pref_type);
6812 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6813 gtk_entry_set_text(GTK_ENTRY(
6814 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6816 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6817 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6818 last_header->type = pref_type;
6820 if (replyto_hdr == FALSE)
6821 g_hash_table_insert(compose->email_hashtable, email,
6822 GUINT_TO_POINTER(1));
6829 static void compose_destroy_headerentry(Compose *compose,
6830 ComposeHeaderEntry *headerentry)
6832 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6835 extract_address(text);
6836 email = g_utf8_strdown(text, -1);
6837 g_hash_table_remove(compose->email_hashtable, email);
6841 gtk_widget_destroy(headerentry->combo);
6842 gtk_widget_destroy(headerentry->entry);
6843 gtk_widget_destroy(headerentry->button);
6844 gtk_widget_destroy(headerentry->hbox);
6845 g_free(headerentry);
6848 static void compose_remove_header_entries(Compose *compose)
6851 for (list = compose->header_list; list; list = list->next)
6852 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6854 compose->header_last = NULL;
6855 g_slist_free(compose->header_list);
6856 compose->header_list = NULL;
6857 compose->header_nextrow = 1;
6858 compose_create_header_entry(compose);
6861 static GtkWidget *compose_create_header(Compose *compose)
6863 GtkWidget *from_optmenu_hbox;
6864 GtkWidget *header_scrolledwin_main;
6865 GtkWidget *header_table_main;
6866 GtkWidget *header_scrolledwin;
6867 GtkWidget *header_table;
6869 /* parent with account selection and from header */
6870 header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL);
6871 gtk_widget_show(header_scrolledwin_main);
6872 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6874 header_table_main = gtk_table_new(2, 2, FALSE);
6875 gtk_widget_show(header_table_main);
6876 gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
6877 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main);
6878 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE);
6880 from_optmenu_hbox = compose_account_option_menu_create(compose);
6881 gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
6882 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6884 /* child with header labels and entries */
6885 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6886 gtk_widget_show(header_scrolledwin);
6887 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6889 header_table = gtk_table_new(2, 2, FALSE);
6890 gtk_widget_show(header_table);
6891 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6892 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6893 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6895 gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
6896 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
6898 compose->header_table = header_table;
6899 compose->header_list = NULL;
6900 compose->header_nextrow = 0;
6902 compose_create_header_entry(compose);
6904 compose->table = NULL;
6906 return header_scrolledwin_main;
6909 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6911 Compose *compose = (Compose *)data;
6912 GdkEventButton event;
6915 event.time = gtk_get_current_event_time();
6917 return attach_button_pressed(compose->attach_clist, &event, compose);
6920 static GtkWidget *compose_create_attach(Compose *compose)
6922 GtkWidget *attach_scrwin;
6923 GtkWidget *attach_clist;
6925 GtkListStore *store;
6926 GtkCellRenderer *renderer;
6927 GtkTreeViewColumn *column;
6928 GtkTreeSelection *selection;
6930 /* attachment list */
6931 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6932 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6933 GTK_POLICY_AUTOMATIC,
6934 GTK_POLICY_AUTOMATIC);
6935 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6937 store = gtk_list_store_new(N_ATTACH_COLS,
6943 G_TYPE_AUTO_POINTER,
6945 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6946 (GTK_TREE_MODEL(store)));
6947 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6948 g_object_unref(store);
6950 renderer = gtk_cell_renderer_text_new();
6951 column = gtk_tree_view_column_new_with_attributes
6952 (_("Mime type"), renderer, "text",
6953 COL_MIMETYPE, NULL);
6954 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6956 renderer = gtk_cell_renderer_text_new();
6957 column = gtk_tree_view_column_new_with_attributes
6958 (_("Size"), renderer, "text",
6960 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6962 renderer = gtk_cell_renderer_text_new();
6963 column = gtk_tree_view_column_new_with_attributes
6964 (_("Name"), renderer, "text",
6966 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6968 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6969 prefs_common.use_stripes_everywhere);
6970 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6971 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6973 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6974 G_CALLBACK(attach_selected), compose);
6975 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6976 G_CALLBACK(attach_button_pressed), compose);
6978 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6979 G_CALLBACK(popup_attach_button_pressed), compose);
6981 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6982 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6983 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6984 G_CALLBACK(popup_attach_button_pressed), compose);
6986 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6987 G_CALLBACK(attach_key_pressed), compose);
6990 gtk_drag_dest_set(attach_clist,
6991 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6992 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6993 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6994 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6995 G_CALLBACK(compose_attach_drag_received_cb),
6997 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6998 G_CALLBACK(compose_drag_drop),
7001 compose->attach_scrwin = attach_scrwin;
7002 compose->attach_clist = attach_clist;
7004 return attach_scrwin;
7007 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7008 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7010 static GtkWidget *compose_create_others(Compose *compose)
7013 GtkWidget *savemsg_checkbtn;
7014 GtkWidget *savemsg_combo;
7015 GtkWidget *savemsg_select;
7018 gchar *folderidentifier;
7020 /* Table for settings */
7021 table = gtk_table_new(3, 1, FALSE);
7022 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7023 gtk_widget_show(table);
7024 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7027 /* Save Message to folder */
7028 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7029 gtk_widget_show(savemsg_checkbtn);
7030 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7031 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7032 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7034 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7035 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7037 savemsg_combo = gtk_combo_box_entry_new_text();
7038 compose->savemsg_checkbtn = savemsg_checkbtn;
7039 compose->savemsg_combo = savemsg_combo;
7040 gtk_widget_show(savemsg_combo);
7042 if (prefs_common.compose_save_to_history)
7043 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7044 prefs_common.compose_save_to_history);
7046 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7047 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7048 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7049 G_CALLBACK(compose_grab_focus_cb), compose);
7050 if (account_get_special_folder(compose->account, F_OUTBOX)) {
7051 folderidentifier = folder_item_get_identifier(account_get_special_folder
7052 (compose->account, F_OUTBOX));
7053 compose_set_save_to(compose, folderidentifier);
7054 g_free(folderidentifier);
7057 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7058 gtk_widget_show(savemsg_select);
7059 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7060 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7061 G_CALLBACK(compose_savemsg_select_cb),
7067 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
7069 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7070 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7073 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7078 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7081 path = folder_item_get_identifier(dest);
7083 compose_set_save_to(compose, path);
7087 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7088 GdkAtom clip, GtkTextIter *insert_place);
7091 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7095 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7097 if (event->button == 3) {
7099 GtkTextIter sel_start, sel_end;
7100 gboolean stuff_selected;
7102 /* move the cursor to allow GtkAspell to check the word
7103 * under the mouse */
7104 if (event->x && event->y) {
7105 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7106 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7108 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7111 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7112 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7115 stuff_selected = gtk_text_buffer_get_selection_bounds(
7117 &sel_start, &sel_end);
7119 gtk_text_buffer_place_cursor (buffer, &iter);
7120 /* reselect stuff */
7122 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7123 gtk_text_buffer_select_range(buffer,
7124 &sel_start, &sel_end);
7126 return FALSE; /* pass the event so that the right-click goes through */
7129 if (event->button == 2) {
7134 /* get the middle-click position to paste at the correct place */
7135 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7136 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7138 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7141 entry_paste_clipboard(compose, text,
7142 prefs_common.linewrap_pastes,
7143 GDK_SELECTION_PRIMARY, &iter);
7151 static void compose_spell_menu_changed(void *data)
7153 Compose *compose = (Compose *)data;
7155 GtkWidget *menuitem;
7156 GtkWidget *parent_item;
7157 GtkMenu *menu = GTK_MENU(gtk_menu_new());
7160 if (compose->gtkaspell == NULL)
7163 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
7164 "/Menu/Spelling/Options");
7166 /* setting the submenu removes /Spelling/Options from the factory
7167 * so we need to save it */
7169 if (parent_item == NULL) {
7170 parent_item = compose->aspell_options_menu;
7171 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7173 compose->aspell_options_menu = parent_item;
7175 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7177 spell_menu = g_slist_reverse(spell_menu);
7178 for (items = spell_menu;
7179 items; items = items->next) {
7180 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7181 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7182 gtk_widget_show(GTK_WIDGET(menuitem));
7184 g_slist_free(spell_menu);
7186 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7187 gtk_widget_show(parent_item);
7190 static void compose_dict_changed(void *data)
7192 Compose *compose = (Compose *) data;
7194 if(compose->gtkaspell &&
7195 compose->gtkaspell->recheck_when_changing_dict == FALSE)
7198 gtkaspell_highlight_all(compose->gtkaspell);
7199 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7203 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7205 Compose *compose = (Compose *)data;
7206 GdkEventButton event;
7209 event.time = gtk_get_current_event_time();
7213 return text_clicked(compose->text, &event, compose);
7216 static gboolean compose_force_window_origin = TRUE;
7217 static Compose *compose_create(PrefsAccount *account,
7226 GtkWidget *handlebox;
7228 GtkWidget *notebook;
7230 GtkWidget *attach_hbox;
7231 GtkWidget *attach_lab1;
7232 GtkWidget *attach_lab2;
7237 GtkWidget *subject_hbox;
7238 GtkWidget *subject_frame;
7239 GtkWidget *subject_entry;
7243 GtkWidget *edit_vbox;
7244 GtkWidget *ruler_hbox;
7246 GtkWidget *scrolledwin;
7248 GtkTextBuffer *buffer;
7249 GtkClipboard *clipboard;
7251 UndoMain *undostruct;
7253 GtkWidget *popupmenu;
7254 GtkWidget *tmpl_menu;
7255 GtkActionGroup *action_group = NULL;
7258 GtkAspell * gtkaspell = NULL;
7261 static GdkGeometry geometry;
7263 cm_return_val_if_fail(account != NULL, NULL);
7265 debug_print("Creating compose window...\n");
7266 compose = g_new0(Compose, 1);
7268 compose->batch = batch;
7269 compose->account = account;
7270 compose->folder = folder;
7272 compose->mutex = cm_mutex_new();
7273 compose->set_cursor_pos = -1;
7275 #if !(GTK_CHECK_VERSION(2,12,0))
7276 compose->tooltips = tips;
7279 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7281 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7282 gtk_widget_set_size_request(window, prefs_common.compose_width,
7283 prefs_common.compose_height);
7285 if (!geometry.max_width) {
7286 geometry.max_width = gdk_screen_width();
7287 geometry.max_height = gdk_screen_height();
7290 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7291 &geometry, GDK_HINT_MAX_SIZE);
7292 if (!geometry.min_width) {
7293 geometry.min_width = 600;
7294 geometry.min_height = 440;
7296 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7297 &geometry, GDK_HINT_MIN_SIZE);
7299 #ifndef GENERIC_UMPC
7300 if (compose_force_window_origin)
7301 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7302 prefs_common.compose_y);
7304 g_signal_connect(G_OBJECT(window), "delete_event",
7305 G_CALLBACK(compose_delete_cb), compose);
7306 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7307 gtk_widget_realize(window);
7309 gtkut_widget_set_composer_icon(window);
7311 vbox = gtk_vbox_new(FALSE, 0);
7312 gtk_container_add(GTK_CONTAINER(window), vbox);
7314 compose->ui_manager = gtk_ui_manager_new();
7315 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7316 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7317 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7318 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7319 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7320 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7321 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7322 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7323 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7324 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7327 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7329 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7332 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7333 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7335 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7337 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7338 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7339 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7342 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7343 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7344 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7345 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7346 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7347 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7348 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7349 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7350 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7351 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7352 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7353 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7356 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7357 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7358 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7360 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7361 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7362 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7364 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7365 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7366 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7367 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7369 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7371 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7372 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7373 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7374 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7375 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7376 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7377 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7378 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7379 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7380 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7381 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7382 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7383 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7384 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7385 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7387 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7389 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7390 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7391 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7392 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7393 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7395 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7397 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7401 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7402 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7403 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7404 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7405 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7406 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7410 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7411 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7412 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7413 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7414 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7416 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7417 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7418 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7419 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7420 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7423 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7424 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7425 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7426 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7427 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7428 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7429 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7431 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7432 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7433 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7434 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7435 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7437 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7439 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7440 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7441 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7442 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7443 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7445 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7446 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)
7447 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)
7448 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7450 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7452 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7453 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)
7454 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)
7456 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7458 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7459 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)
7460 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7462 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7463 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)
7464 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7466 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7468 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7469 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)
7470 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7471 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7472 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7474 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7475 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)
7476 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)
7477 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7478 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7480 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7481 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7484 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7485 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7487 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7488 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7489 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)
7491 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7492 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7493 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7497 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7498 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7499 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7500 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7501 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7502 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7505 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7507 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7508 gtk_widget_show_all(menubar);
7510 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7512 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7514 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7517 if (prefs_common.toolbar_detachable) {
7518 handlebox = gtk_handle_box_new();
7520 handlebox = gtk_hbox_new(FALSE, 0);
7522 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7524 gtk_widget_realize(handlebox);
7526 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7529 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7533 vbox2 = gtk_vbox_new(FALSE, 2);
7534 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7535 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7538 notebook = gtk_notebook_new();
7539 gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height);
7540 gtk_widget_show(notebook);
7542 /* header labels and entries */
7543 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7544 compose_create_header(compose),
7545 gtk_label_new_with_mnemonic(_("Hea_der")));
7546 /* attachment list */
7547 attach_hbox = gtk_hbox_new(FALSE, 0);
7548 gtk_widget_show(attach_hbox);
7550 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7551 gtk_widget_show(attach_lab1);
7552 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7554 attach_lab2 = gtk_label_new("");
7555 gtk_widget_show(attach_lab2);
7556 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7558 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7559 compose_create_attach(compose),
7562 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7563 compose_create_others(compose),
7564 gtk_label_new_with_mnemonic(_("Othe_rs")));
7567 subject_hbox = gtk_hbox_new(FALSE, 0);
7568 gtk_widget_show(subject_hbox);
7570 subject_frame = gtk_frame_new(NULL);
7571 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7572 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7573 gtk_widget_show(subject_frame);
7575 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7576 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7577 gtk_widget_show(subject);
7579 label = gtk_label_new(_("Subject:"));
7580 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7581 gtk_widget_show(label);
7584 subject_entry = claws_spell_entry_new();
7586 subject_entry = gtk_entry_new();
7588 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7589 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7590 G_CALLBACK(compose_grab_focus_cb), compose);
7591 gtk_widget_show(subject_entry);
7592 compose->subject_entry = subject_entry;
7593 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7595 edit_vbox = gtk_vbox_new(FALSE, 0);
7597 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7600 ruler_hbox = gtk_hbox_new(FALSE, 0);
7601 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7603 ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7604 gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7605 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7609 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7610 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7611 GTK_POLICY_AUTOMATIC,
7612 GTK_POLICY_AUTOMATIC);
7613 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7615 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7617 text = gtk_text_view_new();
7618 if (prefs_common.show_compose_margin) {
7619 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7620 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7622 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7623 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7624 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7625 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7626 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7628 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7629 g_signal_connect(G_OBJECT(notebook), "size_allocate",
7630 G_CALLBACK(compose_notebook_size_alloc), compose);
7631 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7632 G_CALLBACK(compose_edit_size_alloc),
7634 g_signal_connect(G_OBJECT(buffer), "changed",
7635 G_CALLBACK(compose_changed_cb), compose);
7636 g_signal_connect(G_OBJECT(text), "grab_focus",
7637 G_CALLBACK(compose_grab_focus_cb), compose);
7638 g_signal_connect(G_OBJECT(buffer), "insert_text",
7639 G_CALLBACK(text_inserted), compose);
7640 g_signal_connect(G_OBJECT(text), "button_press_event",
7641 G_CALLBACK(text_clicked), compose);
7643 g_signal_connect(G_OBJECT(text), "popup-menu",
7644 G_CALLBACK(compose_popup_menu), compose);
7646 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7647 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7648 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7649 G_CALLBACK(compose_popup_menu), compose);
7651 g_signal_connect(G_OBJECT(subject_entry), "changed",
7652 G_CALLBACK(compose_changed_cb), compose);
7655 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7656 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7657 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7658 g_signal_connect(G_OBJECT(text), "drag_data_received",
7659 G_CALLBACK(compose_insert_drag_received_cb),
7661 g_signal_connect(G_OBJECT(text), "drag-drop",
7662 G_CALLBACK(compose_drag_drop),
7664 g_signal_connect(G_OBJECT(text), "key-press-event",
7665 G_CALLBACK(completion_set_focus_to_subject),
7667 gtk_widget_show_all(vbox);
7669 /* pane between attach clist and text */
7670 paned = gtk_vpaned_new();
7671 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7673 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7674 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7676 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7678 gtk_paned_add1(GTK_PANED(paned), notebook);
7679 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7680 gtk_widget_show_all(paned);
7683 if (prefs_common.textfont) {
7684 PangoFontDescription *font_desc;
7686 font_desc = pango_font_description_from_string
7687 (prefs_common.textfont);
7689 gtk_widget_modify_font(text, font_desc);
7690 pango_font_description_free(font_desc);
7694 gtk_action_group_add_actions(action_group, compose_popup_entries,
7695 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7696 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7697 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7698 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7699 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7700 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7701 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7703 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7705 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7706 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7707 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7709 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7711 undostruct = undo_init(text);
7712 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7715 address_completion_start(window);
7717 compose->window = window;
7718 compose->vbox = vbox;
7719 compose->menubar = menubar;
7720 compose->handlebox = handlebox;
7722 compose->vbox2 = vbox2;
7724 compose->paned = paned;
7726 compose->attach_label = attach_lab2;
7728 compose->notebook = notebook;
7729 compose->edit_vbox = edit_vbox;
7730 compose->ruler_hbox = ruler_hbox;
7731 compose->ruler = ruler;
7732 compose->scrolledwin = scrolledwin;
7733 compose->text = text;
7735 compose->focused_editable = NULL;
7737 compose->popupmenu = popupmenu;
7739 compose->tmpl_menu = tmpl_menu;
7741 compose->mode = mode;
7742 compose->rmode = mode;
7744 compose->targetinfo = NULL;
7745 compose->replyinfo = NULL;
7746 compose->fwdinfo = NULL;
7748 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7749 g_str_equal, (GDestroyNotify) g_free, NULL);
7751 compose->replyto = NULL;
7753 compose->bcc = NULL;
7754 compose->followup_to = NULL;
7756 compose->ml_post = NULL;
7758 compose->inreplyto = NULL;
7759 compose->references = NULL;
7760 compose->msgid = NULL;
7761 compose->boundary = NULL;
7763 compose->autowrap = prefs_common.autowrap;
7764 compose->autoindent = prefs_common.auto_indent;
7765 compose->use_signing = FALSE;
7766 compose->use_encryption = FALSE;
7767 compose->privacy_system = NULL;
7769 compose->modified = FALSE;
7771 compose->return_receipt = FALSE;
7773 compose->to_list = NULL;
7774 compose->newsgroup_list = NULL;
7776 compose->undostruct = undostruct;
7778 compose->sig_str = NULL;
7780 compose->exteditor_file = NULL;
7781 compose->exteditor_pid = -1;
7782 compose->exteditor_tag = -1;
7783 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7786 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7787 if (mode != COMPOSE_REDIRECT) {
7788 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7789 strcmp(prefs_common.dictionary, "")) {
7790 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7791 prefs_common.alt_dictionary,
7792 conv_get_locale_charset_str(),
7793 prefs_common.misspelled_col,
7794 prefs_common.check_while_typing,
7795 prefs_common.recheck_when_changing_dict,
7796 prefs_common.use_alternate,
7797 prefs_common.use_both_dicts,
7798 GTK_TEXT_VIEW(text),
7799 GTK_WINDOW(compose->window),
7800 compose_dict_changed,
7801 compose_spell_menu_changed,
7804 alertpanel_error(_("Spell checker could not "
7806 gtkaspell_checkers_strerror());
7807 gtkaspell_checkers_reset_error();
7809 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7813 compose->gtkaspell = gtkaspell;
7814 compose_spell_menu_changed(compose);
7815 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7818 compose_select_account(compose, account, TRUE);
7820 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7821 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7823 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7824 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7826 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7827 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7829 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7830 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7832 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7833 if (account->protocol != A_NNTP)
7834 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7835 prefs_common_translated_header_name("To:"));
7837 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7838 prefs_common_translated_header_name("Newsgroups:"));
7840 #ifndef USE_NEW_ADDRBOOK
7841 addressbook_set_target_compose(compose);
7843 if (mode != COMPOSE_REDIRECT)
7844 compose_set_template_menu(compose);
7846 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7849 compose_list = g_list_append(compose_list, compose);
7851 if (!prefs_common.show_ruler)
7852 gtk_widget_hide(ruler_hbox);
7854 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7857 compose->priority = PRIORITY_NORMAL;
7858 compose_update_priority_menu_item(compose);
7860 compose_set_out_encoding(compose);
7863 compose_update_actions_menu(compose);
7865 /* Privacy Systems menu */
7866 compose_update_privacy_systems_menu(compose);
7868 activate_privacy_system(compose, account, TRUE);
7869 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7871 gtk_widget_realize(window);
7873 gtk_widget_show(window);
7875 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7876 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7883 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7888 GtkWidget *optmenubox;
7891 GtkWidget *from_name = NULL;
7892 #if !(GTK_CHECK_VERSION(2,12,0))
7893 GtkTooltips *tips = compose->tooltips;
7896 gint num = 0, def_menu = 0;
7898 accounts = account_get_list();
7899 cm_return_val_if_fail(accounts != NULL, NULL);
7901 optmenubox = gtk_event_box_new();
7902 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7903 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7905 hbox = gtk_hbox_new(FALSE, 6);
7906 from_name = gtk_entry_new();
7908 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7909 G_CALLBACK(compose_grab_focus_cb), compose);
7911 for (; accounts != NULL; accounts = accounts->next, num++) {
7912 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7913 gchar *name, *from = NULL;
7915 if (ac == compose->account) def_menu = num;
7917 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7920 if (ac == compose->account) {
7921 if (ac->name && *ac->name) {
7923 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7924 from = g_strdup_printf("%s <%s>",
7926 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7928 from = g_strdup_printf("%s",
7930 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7933 COMBOBOX_ADD(menu, name, ac->account_id);
7938 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7940 g_signal_connect(G_OBJECT(optmenu), "changed",
7941 G_CALLBACK(account_activated),
7943 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7944 G_CALLBACK(compose_entry_popup_extend),
7947 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7948 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7950 CLAWS_SET_TIP(optmenubox,
7951 _("Account to use for this email"));
7952 CLAWS_SET_TIP(from_name,
7953 _("Sender address to be used"));
7955 compose->account_combo = optmenu;
7956 compose->from_name = from_name;
7961 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7963 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7964 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7965 Compose *compose = (Compose *) data;
7967 compose->priority = value;
7971 static void compose_reply_change_mode(Compose *compose,
7974 gboolean was_modified = compose->modified;
7976 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7978 cm_return_if_fail(compose->replyinfo != NULL);
7980 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7982 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7984 if (action == COMPOSE_REPLY_TO_ALL)
7986 if (action == COMPOSE_REPLY_TO_SENDER)
7988 if (action == COMPOSE_REPLY_TO_LIST)
7991 compose_remove_header_entries(compose);
7992 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7993 if (compose->account->set_autocc && compose->account->auto_cc)
7994 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7996 if (compose->account->set_autobcc && compose->account->auto_bcc)
7997 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7999 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8000 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8001 compose_show_first_last_header(compose, TRUE);
8002 compose->modified = was_modified;
8003 compose_set_title(compose);
8006 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8008 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8009 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8010 Compose *compose = (Compose *) data;
8013 compose_reply_change_mode(compose, value);
8016 static void compose_update_priority_menu_item(Compose * compose)
8018 GtkWidget *menuitem = NULL;
8019 switch (compose->priority) {
8020 case PRIORITY_HIGHEST:
8021 menuitem = gtk_ui_manager_get_widget
8022 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8025 menuitem = gtk_ui_manager_get_widget
8026 (compose->ui_manager, "/Menu/Options/Priority/High");
8028 case PRIORITY_NORMAL:
8029 menuitem = gtk_ui_manager_get_widget
8030 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8033 menuitem = gtk_ui_manager_get_widget
8034 (compose->ui_manager, "/Menu/Options/Priority/Low");
8036 case PRIORITY_LOWEST:
8037 menuitem = gtk_ui_manager_get_widget
8038 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8041 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8044 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8046 Compose *compose = (Compose *) data;
8048 gboolean can_sign = FALSE, can_encrypt = FALSE;
8050 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8052 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8055 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8056 g_free(compose->privacy_system);
8057 compose->privacy_system = NULL;
8058 if (systemid != NULL) {
8059 compose->privacy_system = g_strdup(systemid);
8061 can_sign = privacy_system_can_sign(systemid);
8062 can_encrypt = privacy_system_can_encrypt(systemid);
8065 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8067 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8068 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8071 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8073 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8074 GtkWidget *menuitem = NULL;
8075 GList *children, *amenu;
8076 gboolean can_sign = FALSE, can_encrypt = FALSE;
8077 gboolean found = FALSE;
8079 if (compose->privacy_system != NULL) {
8081 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8082 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8083 cm_return_if_fail(menuitem != NULL);
8085 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8088 while (amenu != NULL) {
8089 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8090 if (systemid != NULL) {
8091 if (strcmp(systemid, compose->privacy_system) == 0 &&
8092 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8093 menuitem = GTK_WIDGET(amenu->data);
8095 can_sign = privacy_system_can_sign(systemid);
8096 can_encrypt = privacy_system_can_encrypt(systemid);
8100 } else if (strlen(compose->privacy_system) == 0 &&
8101 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8102 menuitem = GTK_WIDGET(amenu->data);
8105 can_encrypt = FALSE;
8110 amenu = amenu->next;
8112 g_list_free(children);
8113 if (menuitem != NULL)
8114 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8116 if (warn && !found && strlen(compose->privacy_system)) {
8117 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8118 "will not be able to sign or encrypt this message."),
8119 compose->privacy_system);
8123 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8124 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8127 static void compose_set_out_encoding(Compose *compose)
8129 CharSet out_encoding;
8130 const gchar *branch = NULL;
8131 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8133 switch(out_encoding) {
8134 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8135 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8136 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8137 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8138 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8139 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8140 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8141 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8142 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8143 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8144 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8145 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8146 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8147 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8148 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8149 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8150 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8151 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8152 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8153 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8154 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8155 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8156 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8157 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8158 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8159 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8160 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8161 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8162 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8163 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8164 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8165 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8166 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8168 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8171 static void compose_set_template_menu(Compose *compose)
8173 GSList *tmpl_list, *cur;
8177 tmpl_list = template_get_config();
8179 menu = gtk_menu_new();
8181 gtk_menu_set_accel_group (GTK_MENU (menu),
8182 gtk_ui_manager_get_accel_group(compose->ui_manager));
8183 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8184 Template *tmpl = (Template *)cur->data;
8185 gchar *accel_path = NULL;
8186 item = gtk_menu_item_new_with_label(tmpl->name);
8187 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8188 g_signal_connect(G_OBJECT(item), "activate",
8189 G_CALLBACK(compose_template_activate_cb),
8191 g_object_set_data(G_OBJECT(item), "template", tmpl);
8192 gtk_widget_show(item);
8193 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8194 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8198 gtk_widget_show(menu);
8199 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8202 void compose_update_actions_menu(Compose *compose)
8204 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8207 static void compose_update_privacy_systems_menu(Compose *compose)
8209 static gchar *branch_path = "/Menu/Options/PrivacySystem";
8210 GSList *systems, *cur;
8212 GtkWidget *system_none;
8214 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8215 GtkWidget *privacy_menu = gtk_menu_new();
8217 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8218 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8220 g_signal_connect(G_OBJECT(system_none), "activate",
8221 G_CALLBACK(compose_set_privacy_system_cb), compose);
8223 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8224 gtk_widget_show(system_none);
8226 systems = privacy_get_system_ids();
8227 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8228 gchar *systemid = cur->data;
8230 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8231 widget = gtk_radio_menu_item_new_with_label(group,
8232 privacy_system_get_name(systemid));
8233 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8234 g_strdup(systemid), g_free);
8235 g_signal_connect(G_OBJECT(widget), "activate",
8236 G_CALLBACK(compose_set_privacy_system_cb), compose);
8238 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8239 gtk_widget_show(widget);
8242 g_slist_free(systems);
8243 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8244 gtk_widget_show_all(privacy_menu);
8245 gtk_widget_show_all(privacy_menuitem);
8248 void compose_reflect_prefs_all(void)
8253 for (cur = compose_list; cur != NULL; cur = cur->next) {
8254 compose = (Compose *)cur->data;
8255 compose_set_template_menu(compose);
8259 void compose_reflect_prefs_pixmap_theme(void)
8264 for (cur = compose_list; cur != NULL; cur = cur->next) {
8265 compose = (Compose *)cur->data;
8266 toolbar_update(TOOLBAR_COMPOSE, compose);
8270 static const gchar *compose_quote_char_from_context(Compose *compose)
8272 const gchar *qmark = NULL;
8274 cm_return_val_if_fail(compose != NULL, NULL);
8276 switch (compose->mode) {
8277 /* use forward-specific quote char */
8278 case COMPOSE_FORWARD:
8279 case COMPOSE_FORWARD_AS_ATTACH:
8280 case COMPOSE_FORWARD_INLINE:
8281 if (compose->folder && compose->folder->prefs &&
8282 compose->folder->prefs->forward_with_format)
8283 qmark = compose->folder->prefs->forward_quotemark;
8284 else if (compose->account->forward_with_format)
8285 qmark = compose->account->forward_quotemark;
8287 qmark = prefs_common.fw_quotemark;
8290 /* use reply-specific quote char in all other modes */
8292 if (compose->folder && compose->folder->prefs &&
8293 compose->folder->prefs->reply_with_format)
8294 qmark = compose->folder->prefs->reply_quotemark;
8295 else if (compose->account->reply_with_format)
8296 qmark = compose->account->reply_quotemark;
8298 qmark = prefs_common.quotemark;
8302 if (qmark == NULL || *qmark == '\0')
8308 static void compose_template_apply(Compose *compose, Template *tmpl,
8312 GtkTextBuffer *buffer;
8316 gchar *parsed_str = NULL;
8317 gint cursor_pos = 0;
8318 const gchar *err_msg = _("The body of the template has an error at line %d.");
8321 /* process the body */
8323 text = GTK_TEXT_VIEW(compose->text);
8324 buffer = gtk_text_view_get_buffer(text);
8327 qmark = compose_quote_char_from_context(compose);
8329 if (compose->replyinfo != NULL) {
8332 gtk_text_buffer_set_text(buffer, "", -1);
8333 mark = gtk_text_buffer_get_insert(buffer);
8334 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8336 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8337 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8339 } else if (compose->fwdinfo != NULL) {
8342 gtk_text_buffer_set_text(buffer, "", -1);
8343 mark = gtk_text_buffer_get_insert(buffer);
8344 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8346 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8347 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8350 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8352 GtkTextIter start, end;
8355 gtk_text_buffer_get_start_iter(buffer, &start);
8356 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8357 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8359 /* clear the buffer now */
8361 gtk_text_buffer_set_text(buffer, "", -1);
8363 parsed_str = compose_quote_fmt(compose, dummyinfo,
8364 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8365 procmsg_msginfo_free( dummyinfo );
8371 gtk_text_buffer_set_text(buffer, "", -1);
8372 mark = gtk_text_buffer_get_insert(buffer);
8373 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8376 if (replace && parsed_str && compose->account->auto_sig)
8377 compose_insert_sig(compose, FALSE);
8379 if (replace && parsed_str) {
8380 gtk_text_buffer_get_start_iter(buffer, &iter);
8381 gtk_text_buffer_place_cursor(buffer, &iter);
8385 cursor_pos = quote_fmt_get_cursor_pos();
8386 compose->set_cursor_pos = cursor_pos;
8387 if (cursor_pos == -1)
8389 gtk_text_buffer_get_start_iter(buffer, &iter);
8390 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8391 gtk_text_buffer_place_cursor(buffer, &iter);
8394 /* process the other fields */
8396 compose_template_apply_fields(compose, tmpl);
8397 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8398 quote_fmt_reset_vartable();
8399 compose_changed_cb(NULL, compose);
8402 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8403 gtkaspell_highlight_all(compose->gtkaspell);
8407 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8409 MsgInfo* dummyinfo = NULL;
8410 MsgInfo *msginfo = NULL;
8413 if (compose->replyinfo != NULL)
8414 msginfo = compose->replyinfo;
8415 else if (compose->fwdinfo != NULL)
8416 msginfo = compose->fwdinfo;
8418 dummyinfo = compose_msginfo_new_from_compose(compose);
8419 msginfo = dummyinfo;
8422 if (tmpl->from && *tmpl->from != '\0') {
8424 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8425 compose->gtkaspell);
8427 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8429 quote_fmt_scan_string(tmpl->from);
8432 buf = quote_fmt_get_buffer();
8434 alertpanel_error(_("Template From format error."));
8436 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8440 if (tmpl->to && *tmpl->to != '\0') {
8442 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8443 compose->gtkaspell);
8445 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8447 quote_fmt_scan_string(tmpl->to);
8450 buf = quote_fmt_get_buffer();
8452 alertpanel_error(_("Template To format error."));
8454 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8458 if (tmpl->cc && *tmpl->cc != '\0') {
8460 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8461 compose->gtkaspell);
8463 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8465 quote_fmt_scan_string(tmpl->cc);
8468 buf = quote_fmt_get_buffer();
8470 alertpanel_error(_("Template Cc format error."));
8472 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8476 if (tmpl->bcc && *tmpl->bcc != '\0') {
8478 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8479 compose->gtkaspell);
8481 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8483 quote_fmt_scan_string(tmpl->bcc);
8486 buf = quote_fmt_get_buffer();
8488 alertpanel_error(_("Template Bcc format error."));
8490 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8494 /* process the subject */
8495 if (tmpl->subject && *tmpl->subject != '\0') {
8497 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8498 compose->gtkaspell);
8500 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8502 quote_fmt_scan_string(tmpl->subject);
8505 buf = quote_fmt_get_buffer();
8507 alertpanel_error(_("Template subject format error."));
8509 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8513 procmsg_msginfo_free( dummyinfo );
8516 static void compose_destroy(Compose *compose)
8518 GtkAllocation allocation;
8519 GtkTextBuffer *buffer;
8520 GtkClipboard *clipboard;
8522 compose_list = g_list_remove(compose_list, compose);
8524 if (compose->updating) {
8525 debug_print("danger, not destroying anything now\n");
8526 compose->deferred_destroy = TRUE;
8529 /* NOTE: address_completion_end() does nothing with the window
8530 * however this may change. */
8531 address_completion_end(compose->window);
8533 slist_free_strings_full(compose->to_list);
8534 slist_free_strings_full(compose->newsgroup_list);
8535 slist_free_strings_full(compose->header_list);
8537 slist_free_strings_full(extra_headers);
8538 extra_headers = NULL;
8540 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8542 g_hash_table_destroy(compose->email_hashtable);
8544 procmsg_msginfo_free(compose->targetinfo);
8545 procmsg_msginfo_free(compose->replyinfo);
8546 procmsg_msginfo_free(compose->fwdinfo);
8548 g_free(compose->replyto);
8549 g_free(compose->cc);
8550 g_free(compose->bcc);
8551 g_free(compose->newsgroups);
8552 g_free(compose->followup_to);
8554 g_free(compose->ml_post);
8556 g_free(compose->inreplyto);
8557 g_free(compose->references);
8558 g_free(compose->msgid);
8559 g_free(compose->boundary);
8561 g_free(compose->redirect_filename);
8562 if (compose->undostruct)
8563 undo_destroy(compose->undostruct);
8565 g_free(compose->sig_str);
8567 g_free(compose->exteditor_file);
8569 g_free(compose->orig_charset);
8571 g_free(compose->privacy_system);
8573 #ifndef USE_NEW_ADDRBOOK
8574 if (addressbook_get_target_compose() == compose)
8575 addressbook_set_target_compose(NULL);
8578 if (compose->gtkaspell) {
8579 gtkaspell_delete(compose->gtkaspell);
8580 compose->gtkaspell = NULL;
8584 if (!compose->batch) {
8585 gtk_widget_get_allocation(compose->window, &allocation);
8586 prefs_common.compose_width = allocation.width;
8587 prefs_common.compose_height = allocation.height;
8590 if (!gtk_widget_get_parent(compose->paned))
8591 gtk_widget_destroy(compose->paned);
8592 gtk_widget_destroy(compose->popupmenu);
8594 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8595 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8596 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8598 gtk_widget_destroy(compose->window);
8599 toolbar_destroy(compose->toolbar);
8600 g_free(compose->toolbar);
8601 cm_mutex_free(compose->mutex);
8605 static void compose_attach_info_free(AttachInfo *ainfo)
8607 g_free(ainfo->file);
8608 g_free(ainfo->content_type);
8609 g_free(ainfo->name);
8610 g_free(ainfo->charset);
8614 static void compose_attach_update_label(Compose *compose)
8619 GtkTreeModel *model;
8624 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8625 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8626 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8630 while(gtk_tree_model_iter_next(model, &iter))
8633 text = g_strdup_printf("(%d)", i);
8634 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8638 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8640 Compose *compose = (Compose *)data;
8641 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8642 GtkTreeSelection *selection;
8644 GtkTreeModel *model;
8646 selection = gtk_tree_view_get_selection(tree_view);
8647 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8652 for (cur = sel; cur != NULL; cur = cur->next) {
8653 GtkTreePath *path = cur->data;
8654 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8657 gtk_tree_path_free(path);
8660 for (cur = sel; cur != NULL; cur = cur->next) {
8661 GtkTreeRowReference *ref = cur->data;
8662 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8665 if (gtk_tree_model_get_iter(model, &iter, path))
8666 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8668 gtk_tree_path_free(path);
8669 gtk_tree_row_reference_free(ref);
8673 compose_attach_update_label(compose);
8676 static struct _AttachProperty
8679 GtkWidget *mimetype_entry;
8680 GtkWidget *encoding_optmenu;
8681 GtkWidget *path_entry;
8682 GtkWidget *filename_entry;
8684 GtkWidget *cancel_btn;
8687 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8689 gtk_tree_path_free((GtkTreePath *)ptr);
8692 static void compose_attach_property(GtkAction *action, gpointer data)
8694 Compose *compose = (Compose *)data;
8695 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8697 GtkComboBox *optmenu;
8698 GtkTreeSelection *selection;
8700 GtkTreeModel *model;
8703 static gboolean cancelled;
8705 /* only if one selected */
8706 selection = gtk_tree_view_get_selection(tree_view);
8707 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8710 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8714 path = (GtkTreePath *) sel->data;
8715 gtk_tree_model_get_iter(model, &iter, path);
8716 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8719 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8725 if (!attach_prop.window)
8726 compose_attach_property_create(&cancelled);
8727 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8728 gtk_widget_grab_focus(attach_prop.ok_btn);
8729 gtk_widget_show(attach_prop.window);
8730 gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
8731 GTK_WINDOW(compose->window));
8733 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8734 if (ainfo->encoding == ENC_UNKNOWN)
8735 combobox_select_by_data(optmenu, ENC_BASE64);
8737 combobox_select_by_data(optmenu, ainfo->encoding);
8739 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8740 ainfo->content_type ? ainfo->content_type : "");
8741 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8742 ainfo->file ? ainfo->file : "");
8743 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8744 ainfo->name ? ainfo->name : "");
8747 const gchar *entry_text;
8749 gchar *cnttype = NULL;
8756 gtk_widget_hide(attach_prop.window);
8757 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8762 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8763 if (*entry_text != '\0') {
8766 text = g_strstrip(g_strdup(entry_text));
8767 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8768 cnttype = g_strdup(text);
8771 alertpanel_error(_("Invalid MIME type."));
8777 ainfo->encoding = combobox_get_active_data(optmenu);
8779 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8780 if (*entry_text != '\0') {
8781 if (is_file_exist(entry_text) &&
8782 (size = get_file_size(entry_text)) > 0)
8783 file = g_strdup(entry_text);
8786 (_("File doesn't exist or is empty."));
8792 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8793 if (*entry_text != '\0') {
8794 g_free(ainfo->name);
8795 ainfo->name = g_strdup(entry_text);
8799 g_free(ainfo->content_type);
8800 ainfo->content_type = cnttype;
8803 g_free(ainfo->file);
8807 ainfo->size = (goffset)size;
8809 /* update tree store */
8810 text = to_human_readable(ainfo->size);
8811 gtk_tree_model_get_iter(model, &iter, path);
8812 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8813 COL_MIMETYPE, ainfo->content_type,
8815 COL_NAME, ainfo->name,
8816 COL_CHARSET, ainfo->charset,
8822 gtk_tree_path_free(path);
8825 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8827 label = gtk_label_new(str); \
8828 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8829 GTK_FILL, 0, 0, 0); \
8830 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8832 entry = gtk_entry_new(); \
8833 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8834 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8837 static void compose_attach_property_create(gboolean *cancelled)
8843 GtkWidget *mimetype_entry;
8846 GtkListStore *optmenu_menu;
8847 GtkWidget *path_entry;
8848 GtkWidget *filename_entry;
8851 GtkWidget *cancel_btn;
8852 GList *mime_type_list, *strlist;
8855 debug_print("Creating attach_property window...\n");
8857 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8858 gtk_widget_set_size_request(window, 480, -1);
8859 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8860 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8861 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8862 g_signal_connect(G_OBJECT(window), "delete_event",
8863 G_CALLBACK(attach_property_delete_event),
8865 g_signal_connect(G_OBJECT(window), "key_press_event",
8866 G_CALLBACK(attach_property_key_pressed),
8869 vbox = gtk_vbox_new(FALSE, 8);
8870 gtk_container_add(GTK_CONTAINER(window), vbox);
8872 table = gtk_table_new(4, 2, FALSE);
8873 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8874 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8875 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8877 label = gtk_label_new(_("MIME type"));
8878 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8880 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8881 mimetype_entry = gtk_combo_box_entry_new_text();
8882 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8883 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8885 /* stuff with list */
8886 mime_type_list = procmime_get_mime_type_list();
8888 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8889 MimeType *type = (MimeType *) mime_type_list->data;
8892 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8894 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8897 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8898 (GCompareFunc)strcmp2);
8901 for (mime_type_list = strlist; mime_type_list != NULL;
8902 mime_type_list = mime_type_list->next) {
8903 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8904 g_free(mime_type_list->data);
8906 g_list_free(strlist);
8907 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8908 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8910 label = gtk_label_new(_("Encoding"));
8911 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8913 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8915 hbox = gtk_hbox_new(FALSE, 0);
8916 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8917 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8919 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8920 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8922 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8923 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8924 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8925 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8926 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8928 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8930 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8931 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8933 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8934 &ok_btn, GTK_STOCK_OK,
8936 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8937 gtk_widget_grab_default(ok_btn);
8939 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8940 G_CALLBACK(attach_property_ok),
8942 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8943 G_CALLBACK(attach_property_cancel),
8946 gtk_widget_show_all(vbox);
8948 attach_prop.window = window;
8949 attach_prop.mimetype_entry = mimetype_entry;
8950 attach_prop.encoding_optmenu = optmenu;
8951 attach_prop.path_entry = path_entry;
8952 attach_prop.filename_entry = filename_entry;
8953 attach_prop.ok_btn = ok_btn;
8954 attach_prop.cancel_btn = cancel_btn;
8957 #undef SET_LABEL_AND_ENTRY
8959 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8965 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8971 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8972 gboolean *cancelled)
8980 static gboolean attach_property_key_pressed(GtkWidget *widget,
8982 gboolean *cancelled)
8984 if (event && event->keyval == GDK_KEY_Escape) {
8988 if (event && event->keyval == GDK_KEY_Return) {
8996 static void compose_exec_ext_editor(Compose *compose)
9003 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9004 G_DIR_SEPARATOR, compose);
9006 if (pipe(pipe_fds) < 0) {
9012 if ((pid = fork()) < 0) {
9019 /* close the write side of the pipe */
9022 compose->exteditor_file = g_strdup(tmp);
9023 compose->exteditor_pid = pid;
9025 compose_set_ext_editor_sensitive(compose, FALSE);
9028 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9030 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9032 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9036 } else { /* process-monitoring process */
9042 /* close the read side of the pipe */
9045 if (compose_write_body_to_file(compose, tmp) < 0) {
9046 fd_write_all(pipe_fds[1], "2\n", 2);
9050 pid_ed = compose_exec_ext_editor_real(tmp);
9052 fd_write_all(pipe_fds[1], "1\n", 2);
9056 /* wait until editor is terminated */
9057 waitpid(pid_ed, NULL, 0);
9059 fd_write_all(pipe_fds[1], "0\n", 2);
9066 #endif /* G_OS_UNIX */
9070 static gint compose_exec_ext_editor_real(const gchar *file)
9077 cm_return_val_if_fail(file != NULL, -1);
9079 if ((pid = fork()) < 0) {
9084 if (pid != 0) return pid;
9086 /* grandchild process */
9088 if (setpgid(0, getppid()))
9091 if (prefs_common_get_ext_editor_cmd() &&
9092 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
9093 *(p + 1) == 's' && !strchr(p + 2, '%')) {
9094 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
9096 if (prefs_common_get_ext_editor_cmd())
9097 g_warning("External editor command-line is invalid: '%s'\n",
9098 prefs_common_get_ext_editor_cmd());
9099 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
9102 cmdline = strsplit_with_quote(buf, " ", 1024);
9103 execvp(cmdline[0], cmdline);
9106 g_strfreev(cmdline);
9111 static gboolean compose_ext_editor_kill(Compose *compose)
9113 pid_t pgid = compose->exteditor_pid * -1;
9116 ret = kill(pgid, 0);
9118 if (ret == 0 || (ret == -1 && EPERM == errno)) {
9122 msg = g_strdup_printf
9123 (_("The external editor is still working.\n"
9124 "Force terminating the process?\n"
9125 "process group id: %d"), -pgid);
9126 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9127 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9131 if (val == G_ALERTALTERNATE) {
9132 g_source_remove(compose->exteditor_tag);
9133 g_io_channel_shutdown(compose->exteditor_ch,
9135 g_io_channel_unref(compose->exteditor_ch);
9137 if (kill(pgid, SIGTERM) < 0) perror("kill");
9138 waitpid(compose->exteditor_pid, NULL, 0);
9140 g_warning("Terminated process group id: %d", -pgid);
9141 g_warning("Temporary file: %s",
9142 compose->exteditor_file);
9144 compose_set_ext_editor_sensitive(compose, TRUE);
9146 g_free(compose->exteditor_file);
9147 compose->exteditor_file = NULL;
9148 compose->exteditor_pid = -1;
9149 compose->exteditor_ch = NULL;
9150 compose->exteditor_tag = -1;
9158 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9162 Compose *compose = (Compose *)data;
9165 debug_print("Compose: input from monitoring process\n");
9167 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
9169 g_io_channel_shutdown(source, FALSE, NULL);
9170 g_io_channel_unref(source);
9172 waitpid(compose->exteditor_pid, NULL, 0);
9174 if (buf[0] == '0') { /* success */
9175 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9176 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9178 gtk_text_buffer_set_text(buffer, "", -1);
9179 compose_insert_file(compose, compose->exteditor_file);
9180 compose_changed_cb(NULL, compose);
9181 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9183 if (claws_unlink(compose->exteditor_file) < 0)
9184 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9185 } else if (buf[0] == '1') { /* failed */
9186 g_warning("Couldn't exec external editor\n");
9187 if (claws_unlink(compose->exteditor_file) < 0)
9188 FILE_OP_ERROR(compose->exteditor_file, "unlink");
9189 } else if (buf[0] == '2') {
9190 g_warning("Couldn't write to file\n");
9191 } else if (buf[0] == '3') {
9192 g_warning("Pipe read failed\n");
9195 compose_set_ext_editor_sensitive(compose, TRUE);
9197 g_free(compose->exteditor_file);
9198 compose->exteditor_file = NULL;
9199 compose->exteditor_pid = -1;
9200 compose->exteditor_ch = NULL;
9201 compose->exteditor_tag = -1;
9206 static void compose_set_ext_editor_sensitive(Compose *compose,
9209 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
9210 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
9211 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
9212 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
9213 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
9214 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
9215 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
9217 gtk_widget_set_sensitive(compose->text, sensitive);
9218 if (compose->toolbar->send_btn)
9219 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
9220 if (compose->toolbar->sendl_btn)
9221 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
9222 if (compose->toolbar->draft_btn)
9223 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9224 if (compose->toolbar->insert_btn)
9225 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9226 if (compose->toolbar->sig_btn)
9227 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9228 if (compose->toolbar->exteditor_btn)
9229 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9230 if (compose->toolbar->linewrap_current_btn)
9231 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9232 if (compose->toolbar->linewrap_all_btn)
9233 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9235 #endif /* G_OS_UNIX */
9238 * compose_undo_state_changed:
9240 * Change the sensivity of the menuentries undo and redo
9242 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9243 gint redo_state, gpointer data)
9245 Compose *compose = (Compose *)data;
9247 switch (undo_state) {
9248 case UNDO_STATE_TRUE:
9249 if (!undostruct->undo_state) {
9250 undostruct->undo_state = TRUE;
9251 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9254 case UNDO_STATE_FALSE:
9255 if (undostruct->undo_state) {
9256 undostruct->undo_state = FALSE;
9257 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9260 case UNDO_STATE_UNCHANGED:
9262 case UNDO_STATE_REFRESH:
9263 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9266 g_warning("Undo state not recognized");
9270 switch (redo_state) {
9271 case UNDO_STATE_TRUE:
9272 if (!undostruct->redo_state) {
9273 undostruct->redo_state = TRUE;
9274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9277 case UNDO_STATE_FALSE:
9278 if (undostruct->redo_state) {
9279 undostruct->redo_state = FALSE;
9280 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9283 case UNDO_STATE_UNCHANGED:
9285 case UNDO_STATE_REFRESH:
9286 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9289 g_warning("Redo state not recognized");
9294 /* callback functions */
9296 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9297 GtkAllocation *allocation,
9300 prefs_common.compose_notebook_height = allocation->height;
9303 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9304 * includes "non-client" (windows-izm) in calculation, so this calculation
9305 * may not be accurate.
9307 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9308 GtkAllocation *allocation,
9309 GtkSHRuler *shruler)
9311 if (prefs_common.show_ruler) {
9312 gint char_width = 0, char_height = 0;
9313 gint line_width_in_chars;
9315 gtkut_get_font_size(GTK_WIDGET(widget),
9316 &char_width, &char_height);
9317 line_width_in_chars =
9318 (allocation->width - allocation->x) / char_width;
9320 /* got the maximum */
9321 gtk_shruler_set_range(GTK_SHRULER(shruler),
9322 0.0, line_width_in_chars, 0);
9331 ComposePrefType type;
9332 gboolean entry_marked;
9335 static void account_activated(GtkComboBox *optmenu, gpointer data)
9337 Compose *compose = (Compose *)data;
9340 gchar *folderidentifier;
9341 gint account_id = 0;
9344 GSList *list, *saved_list = NULL;
9345 HeaderEntryState *state;
9346 GtkRcStyle *style = NULL;
9347 static GdkColor yellow;
9348 static gboolean color_set = FALSE;
9350 /* Get ID of active account in the combo box */
9351 menu = gtk_combo_box_get_model(optmenu);
9352 gtk_combo_box_get_active_iter(optmenu, &iter);
9353 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9355 ac = account_find_from_id(account_id);
9356 cm_return_if_fail(ac != NULL);
9358 if (ac != compose->account) {
9359 compose_select_account(compose, ac, FALSE);
9361 for (list = compose->header_list; list; list = list->next) {
9362 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9364 if (hentry->type == PREF_ACCOUNT || !list->next) {
9365 compose_destroy_headerentry(compose, hentry);
9369 state = g_malloc0(sizeof(HeaderEntryState));
9370 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9371 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9372 state->entry = gtk_editable_get_chars(
9373 GTK_EDITABLE(hentry->entry), 0, -1);
9374 state->type = hentry->type;
9377 gdk_color_parse("#f5f6be", &yellow);
9378 color_set = gdk_colormap_alloc_color(
9379 gdk_colormap_get_system(),
9380 &yellow, FALSE, TRUE);
9383 style = gtk_widget_get_modifier_style(hentry->entry);
9384 state->entry_marked = gdk_color_equal(&yellow,
9385 &style->base[GTK_STATE_NORMAL]);
9387 saved_list = g_slist_append(saved_list, state);
9388 compose_destroy_headerentry(compose, hentry);
9391 compose->header_last = NULL;
9392 g_slist_free(compose->header_list);
9393 compose->header_list = NULL;
9394 compose->header_nextrow = 1;
9395 compose_create_header_entry(compose);
9397 if (ac->set_autocc && ac->auto_cc)
9398 compose_entry_append(compose, ac->auto_cc,
9399 COMPOSE_CC, PREF_ACCOUNT);
9401 if (ac->set_autobcc && ac->auto_bcc)
9402 compose_entry_append(compose, ac->auto_bcc,
9403 COMPOSE_BCC, PREF_ACCOUNT);
9405 if (ac->set_autoreplyto && ac->auto_replyto)
9406 compose_entry_append(compose, ac->auto_replyto,
9407 COMPOSE_REPLYTO, PREF_ACCOUNT);
9409 for (list = saved_list; list; list = list->next) {
9410 state = (HeaderEntryState *) list->data;
9412 compose_add_header_entry(compose, state->header,
9413 state->entry, state->type);
9414 if (state->entry_marked)
9415 compose_entry_mark_default_to(compose, state->entry);
9417 g_free(state->header);
9418 g_free(state->entry);
9421 g_slist_free(saved_list);
9423 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9424 (ac->protocol == A_NNTP) ?
9425 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9428 /* Set message save folder */
9429 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9430 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9432 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9433 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9435 compose_set_save_to(compose, NULL);
9436 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9437 folderidentifier = folder_item_get_identifier(account_get_special_folder
9438 (compose->account, F_OUTBOX));
9439 compose_set_save_to(compose, folderidentifier);
9440 g_free(folderidentifier);
9444 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9445 GtkTreeViewColumn *column, Compose *compose)
9447 compose_attach_property(NULL, compose);
9450 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9453 Compose *compose = (Compose *)data;
9454 GtkTreeSelection *attach_selection;
9455 gint attach_nr_selected;
9457 if (!event) return FALSE;
9459 if (event->button == 3) {
9460 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9461 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9463 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9464 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0));
9466 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9467 NULL, NULL, event->button, event->time);
9474 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9477 Compose *compose = (Compose *)data;
9479 if (!event) return FALSE;
9481 switch (event->keyval) {
9482 case GDK_KEY_Delete:
9483 compose_attach_remove_selected(NULL, compose);
9489 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9491 toolbar_comp_set_sensitive(compose, allow);
9492 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9493 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9495 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9497 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9498 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9499 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9501 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9505 static void compose_send_cb(GtkAction *action, gpointer data)
9507 Compose *compose = (Compose *)data;
9509 if (prefs_common.work_offline &&
9510 !inc_offline_should_override(TRUE,
9511 _("Claws Mail needs network access in order "
9512 "to send this email.")))
9515 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9516 g_source_remove(compose->draft_timeout_tag);
9517 compose->draft_timeout_tag = -1;
9520 compose_send(compose);
9523 static void compose_send_later_cb(GtkAction *action, gpointer data)
9525 Compose *compose = (Compose *)data;
9529 compose_allow_user_actions(compose, FALSE);
9530 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9531 compose_allow_user_actions(compose, TRUE);
9535 compose_close(compose);
9536 } else if (val == -1) {
9537 alertpanel_error(_("Could not queue message."));
9538 } else if (val == -2) {
9539 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9540 } else if (val == -3) {
9541 if (privacy_peek_error())
9542 alertpanel_error(_("Could not queue message for sending:\n\n"
9543 "Signature failed: %s"), privacy_get_error());
9544 } else if (val == -4) {
9545 alertpanel_error(_("Could not queue message for sending:\n\n"
9546 "Charset conversion failed."));
9547 } else if (val == -5) {
9548 alertpanel_error(_("Could not queue message for sending:\n\n"
9549 "Couldn't get recipient encryption key."));
9550 } else if (val == -6) {
9553 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9556 #define DRAFTED_AT_EXIT "drafted_at_exit"
9557 static void compose_register_draft(MsgInfo *info)
9559 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9560 DRAFTED_AT_EXIT, NULL);
9561 FILE *fp = g_fopen(filepath, "ab");
9564 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9572 gboolean compose_draft (gpointer data, guint action)
9574 Compose *compose = (Compose *)data;
9579 MsgFlags flag = {0, 0};
9580 static gboolean lock = FALSE;
9581 MsgInfo *newmsginfo;
9583 gboolean target_locked = FALSE;
9584 gboolean err = FALSE;
9586 if (lock) return FALSE;
9588 if (compose->sending)
9591 draft = account_get_special_folder(compose->account, F_DRAFT);
9592 cm_return_val_if_fail(draft != NULL, FALSE);
9594 if (!g_mutex_trylock(compose->mutex)) {
9595 /* we don't want to lock the mutex once it's available,
9596 * because as the only other part of compose.c locking
9597 * it is compose_close - which means once unlocked,
9598 * the compose struct will be freed */
9599 debug_print("couldn't lock mutex, probably sending\n");
9605 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9606 G_DIR_SEPARATOR, compose);
9607 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9608 FILE_OP_ERROR(tmp, "fopen");
9612 /* chmod for security */
9613 if (change_file_mode_rw(fp, tmp) < 0) {
9614 FILE_OP_ERROR(tmp, "chmod");
9615 g_warning("can't change file mode\n");
9618 /* Save draft infos */
9619 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9620 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9622 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9623 gchar *savefolderid;
9625 savefolderid = compose_get_save_to(compose);
9626 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9627 g_free(savefolderid);
9629 if (compose->return_receipt) {
9630 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9632 if (compose->privacy_system) {
9633 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9634 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9635 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9638 /* Message-ID of message replying to */
9639 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9642 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9643 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9646 /* Message-ID of message forwarding to */
9647 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9650 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9651 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9655 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9656 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9658 sheaders = compose_get_manual_headers_info(compose);
9659 err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
9662 /* end of headers */
9663 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9670 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9674 if (fclose(fp) == EOF) {
9678 if (compose->targetinfo) {
9679 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9680 flag.perm_flags = target_locked?MSG_LOCKED:0;
9682 flag.tmp_flags = MSG_DRAFT;
9684 folder_item_scan(draft);
9685 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9686 MsgInfo *tmpinfo = NULL;
9687 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9688 if (compose->msgid) {
9689 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9692 msgnum = tmpinfo->msgnum;
9693 procmsg_msginfo_free(tmpinfo);
9694 debug_print("got draft msgnum %d from scanning\n", msgnum);
9696 debug_print("didn't get draft msgnum after scanning\n");
9699 debug_print("got draft msgnum %d from adding\n", msgnum);
9705 if (action != COMPOSE_AUTO_SAVE) {
9706 if (action != COMPOSE_DRAFT_FOR_EXIT)
9707 alertpanel_error(_("Could not save draft."));
9710 gtkut_window_popup(compose->window);
9711 val = alertpanel_full(_("Could not save draft"),
9712 _("Could not save draft.\n"
9713 "Do you want to cancel exit or discard this email?"),
9714 _("_Cancel exit"), _("_Discard email"), NULL,
9715 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9716 if (val == G_ALERTALTERNATE) {
9718 g_mutex_unlock(compose->mutex); /* must be done before closing */
9719 compose_close(compose);
9723 g_mutex_unlock(compose->mutex); /* must be done before closing */
9732 if (compose->mode == COMPOSE_REEDIT) {
9733 compose_remove_reedit_target(compose, TRUE);
9736 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9739 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9741 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9743 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9744 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9745 procmsg_msginfo_set_flags(newmsginfo, 0,
9746 MSG_HAS_ATTACHMENT);
9748 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9749 compose_register_draft(newmsginfo);
9751 procmsg_msginfo_free(newmsginfo);
9754 folder_item_scan(draft);
9756 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9758 g_mutex_unlock(compose->mutex); /* must be done before closing */
9759 compose_close(compose);
9765 path = folder_item_fetch_msg(draft, msgnum);
9767 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9770 if (g_stat(path, &s) < 0) {
9771 FILE_OP_ERROR(path, "stat");
9777 procmsg_msginfo_free(compose->targetinfo);
9778 compose->targetinfo = procmsg_msginfo_new();
9779 compose->targetinfo->msgnum = msgnum;
9780 compose->targetinfo->size = (goffset)s.st_size;
9781 compose->targetinfo->mtime = s.st_mtime;
9782 compose->targetinfo->folder = draft;
9784 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9785 compose->mode = COMPOSE_REEDIT;
9787 if (action == COMPOSE_AUTO_SAVE) {
9788 compose->autosaved_draft = compose->targetinfo;
9790 compose->modified = FALSE;
9791 compose_set_title(compose);
9795 g_mutex_unlock(compose->mutex);
9799 void compose_clear_exit_drafts(void)
9801 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9802 DRAFTED_AT_EXIT, NULL);
9803 if (is_file_exist(filepath))
9804 claws_unlink(filepath);
9809 void compose_reopen_exit_drafts(void)
9811 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9812 DRAFTED_AT_EXIT, NULL);
9813 FILE *fp = g_fopen(filepath, "rb");
9817 while (fgets(buf, sizeof(buf), fp)) {
9818 gchar **parts = g_strsplit(buf, "\t", 2);
9819 const gchar *folder = parts[0];
9820 int msgnum = parts[1] ? atoi(parts[1]):-1;
9822 if (folder && *folder && msgnum > -1) {
9823 FolderItem *item = folder_find_item_from_identifier(folder);
9824 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9826 compose_reedit(info, FALSE);
9833 compose_clear_exit_drafts();
9836 static void compose_save_cb(GtkAction *action, gpointer data)
9838 Compose *compose = (Compose *)data;
9839 compose_draft(compose, COMPOSE_KEEP_EDITING);
9840 compose->rmode = COMPOSE_REEDIT;
9843 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9845 if (compose && file_list) {
9848 for ( tmp = file_list; tmp; tmp = tmp->next) {
9849 gchar *file = (gchar *) tmp->data;
9850 gchar *utf8_filename = conv_filename_to_utf8(file);
9851 compose_attach_append(compose, file, utf8_filename, NULL, NULL);
9852 compose_changed_cb(NULL, compose);
9857 g_free(utf8_filename);
9862 static void compose_attach_cb(GtkAction *action, gpointer data)
9864 Compose *compose = (Compose *)data;
9867 if (compose->redirect_filename != NULL)
9870 /* Set focus_window properly, in case we were called via popup menu,
9871 * which unsets it (via focus_out_event callback on compose window). */
9872 manage_window_focus_in(compose->window, NULL, NULL);
9874 file_list = filesel_select_multiple_files_open(_("Select file"));
9877 compose_attach_from_list(compose, file_list, TRUE);
9878 g_list_free(file_list);
9882 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9884 Compose *compose = (Compose *)data;
9886 gint files_inserted = 0;
9888 file_list = filesel_select_multiple_files_open(_("Select file"));
9893 for ( tmp = file_list; tmp; tmp = tmp->next) {
9894 gchar *file = (gchar *) tmp->data;
9895 gchar *filedup = g_strdup(file);
9896 gchar *shortfile = g_path_get_basename(filedup);
9897 ComposeInsertResult res;
9898 /* insert the file if the file is short or if the user confirmed that
9899 he/she wants to insert the large file */
9900 res = compose_insert_file(compose, file);
9901 if (res == COMPOSE_INSERT_READ_ERROR) {
9902 alertpanel_error(_("File '%s' could not be read."), shortfile);
9903 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9904 alertpanel_error(_("File '%s' contained invalid characters\n"
9905 "for the current encoding, insertion may be incorrect."),
9907 } else if (res == COMPOSE_INSERT_SUCCESS)
9914 g_list_free(file_list);
9918 if (files_inserted > 0 && compose->gtkaspell &&
9919 compose->gtkaspell->check_while_typing)
9920 gtkaspell_highlight_all(compose->gtkaspell);
9924 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9926 Compose *compose = (Compose *)data;
9928 compose_insert_sig(compose, FALSE);
9931 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9935 Compose *compose = (Compose *)data;
9937 gtkut_widget_get_uposition(widget, &x, &y);
9938 if (!compose->batch) {
9939 prefs_common.compose_x = x;
9940 prefs_common.compose_y = y;
9942 if (compose->sending || compose->updating)
9944 compose_close_cb(NULL, compose);
9948 void compose_close_toolbar(Compose *compose)
9950 compose_close_cb(NULL, compose);
9953 static void compose_close_cb(GtkAction *action, gpointer data)
9955 Compose *compose = (Compose *)data;
9959 if (compose->exteditor_tag != -1) {
9960 if (!compose_ext_editor_kill(compose))
9965 if (compose->modified) {
9966 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9967 if (!g_mutex_trylock(compose->mutex)) {
9968 /* we don't want to lock the mutex once it's available,
9969 * because as the only other part of compose.c locking
9970 * it is compose_close - which means once unlocked,
9971 * the compose struct will be freed */
9972 debug_print("couldn't lock mutex, probably sending\n");
9976 val = alertpanel(_("Discard message"),
9977 _("This message has been modified. Discard it?"),
9978 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9980 val = alertpanel(_("Save changes"),
9981 _("This message has been modified. Save the latest changes?"),
9982 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9984 g_mutex_unlock(compose->mutex);
9986 case G_ALERTDEFAULT:
9987 if (prefs_common.autosave && !reedit)
9988 compose_remove_draft(compose);
9990 case G_ALERTALTERNATE:
9991 compose_draft(data, COMPOSE_QUIT_EDITING);
9998 compose_close(compose);
10001 static void compose_print_cb(GtkAction *action, gpointer data)
10003 Compose *compose = (Compose *) data;
10005 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10006 if (compose->targetinfo)
10007 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10010 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10012 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10013 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10014 Compose *compose = (Compose *) data;
10017 compose->out_encoding = (CharSet)value;
10020 static void compose_address_cb(GtkAction *action, gpointer data)
10022 Compose *compose = (Compose *)data;
10024 #ifndef USE_NEW_ADDRBOOK
10025 addressbook_open(compose);
10027 GError* error = NULL;
10028 addressbook_connect_signals(compose);
10029 addressbook_dbus_open(TRUE, &error);
10031 g_warning("%s", error->message);
10032 g_error_free(error);
10037 static void about_show_cb(GtkAction *action, gpointer data)
10042 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10044 Compose *compose = (Compose *)data;
10049 tmpl = g_object_get_data(G_OBJECT(widget), "template");
10050 cm_return_if_fail(tmpl != NULL);
10052 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
10054 val = alertpanel(_("Apply template"), msg,
10055 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10058 if (val == G_ALERTDEFAULT)
10059 compose_template_apply(compose, tmpl, TRUE);
10060 else if (val == G_ALERTALTERNATE)
10061 compose_template_apply(compose, tmpl, FALSE);
10064 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10066 Compose *compose = (Compose *)data;
10068 compose_exec_ext_editor(compose);
10071 static void compose_undo_cb(GtkAction *action, gpointer data)
10073 Compose *compose = (Compose *)data;
10074 gboolean prev_autowrap = compose->autowrap;
10076 compose->autowrap = FALSE;
10077 undo_undo(compose->undostruct);
10078 compose->autowrap = prev_autowrap;
10081 static void compose_redo_cb(GtkAction *action, gpointer data)
10083 Compose *compose = (Compose *)data;
10084 gboolean prev_autowrap = compose->autowrap;
10086 compose->autowrap = FALSE;
10087 undo_redo(compose->undostruct);
10088 compose->autowrap = prev_autowrap;
10091 static void entry_cut_clipboard(GtkWidget *entry)
10093 if (GTK_IS_EDITABLE(entry))
10094 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10095 else if (GTK_IS_TEXT_VIEW(entry))
10096 gtk_text_buffer_cut_clipboard(
10097 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10098 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10102 static void entry_copy_clipboard(GtkWidget *entry)
10104 if (GTK_IS_EDITABLE(entry))
10105 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10106 else if (GTK_IS_TEXT_VIEW(entry))
10107 gtk_text_buffer_copy_clipboard(
10108 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10109 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10112 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
10113 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10115 if (GTK_IS_TEXT_VIEW(entry)) {
10116 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10117 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10118 GtkTextIter start_iter, end_iter;
10120 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10122 if (contents == NULL)
10125 /* we shouldn't delete the selection when middle-click-pasting, or we
10126 * can't mid-click-paste our own selection */
10127 if (clip != GDK_SELECTION_PRIMARY) {
10128 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10129 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10132 if (insert_place == NULL) {
10133 /* if insert_place isn't specified, insert at the cursor.
10134 * used for Ctrl-V pasting */
10135 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10136 start = gtk_text_iter_get_offset(&start_iter);
10137 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10139 /* if insert_place is specified, paste here.
10140 * used for mid-click-pasting */
10141 start = gtk_text_iter_get_offset(insert_place);
10142 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10143 if (prefs_common.primary_paste_unselects)
10144 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10148 /* paste unwrapped: mark the paste so it's not wrapped later */
10149 end = start + strlen(contents);
10150 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10151 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10152 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10153 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10154 /* rewrap paragraph now (after a mid-click-paste) */
10155 mark_start = gtk_text_buffer_get_insert(buffer);
10156 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10157 gtk_text_iter_backward_char(&start_iter);
10158 compose_beautify_paragraph(compose, &start_iter, TRUE);
10160 } else if (GTK_IS_EDITABLE(entry))
10161 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10163 compose->modified = TRUE;
10166 static void entry_allsel(GtkWidget *entry)
10168 if (GTK_IS_EDITABLE(entry))
10169 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10170 else if (GTK_IS_TEXT_VIEW(entry)) {
10171 GtkTextIter startiter, enditer;
10172 GtkTextBuffer *textbuf;
10174 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10175 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10176 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10178 gtk_text_buffer_move_mark_by_name(textbuf,
10179 "selection_bound", &startiter);
10180 gtk_text_buffer_move_mark_by_name(textbuf,
10181 "insert", &enditer);
10185 static void compose_cut_cb(GtkAction *action, gpointer data)
10187 Compose *compose = (Compose *)data;
10188 if (compose->focused_editable
10189 #ifndef GENERIC_UMPC
10190 && gtk_widget_has_focus(compose->focused_editable)
10193 entry_cut_clipboard(compose->focused_editable);
10196 static void compose_copy_cb(GtkAction *action, gpointer data)
10198 Compose *compose = (Compose *)data;
10199 if (compose->focused_editable
10200 #ifndef GENERIC_UMPC
10201 && gtk_widget_has_focus(compose->focused_editable)
10204 entry_copy_clipboard(compose->focused_editable);
10207 static void compose_paste_cb(GtkAction *action, gpointer data)
10209 Compose *compose = (Compose *)data;
10210 gint prev_autowrap;
10211 GtkTextBuffer *buffer;
10213 if (compose->focused_editable &&
10214 #ifndef GENERIC_UMPC
10215 gtk_widget_has_focus(compose->focused_editable)
10218 entry_paste_clipboard(compose, compose->focused_editable,
10219 prefs_common.linewrap_pastes,
10220 GDK_SELECTION_CLIPBOARD, NULL);
10225 #ifndef GENERIC_UMPC
10226 gtk_widget_has_focus(compose->text) &&
10228 compose->gtkaspell &&
10229 compose->gtkaspell->check_while_typing)
10230 gtkaspell_highlight_all(compose->gtkaspell);
10234 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10236 Compose *compose = (Compose *)data;
10237 gint wrap_quote = prefs_common.linewrap_quote;
10238 if (compose->focused_editable
10239 #ifndef GENERIC_UMPC
10240 && gtk_widget_has_focus(compose->focused_editable)
10243 /* let text_insert() (called directly or at a later time
10244 * after the gtk_editable_paste_clipboard) know that
10245 * text is to be inserted as a quotation. implemented
10246 * by using a simple refcount... */
10247 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10248 G_OBJECT(compose->focused_editable),
10249 "paste_as_quotation"));
10250 g_object_set_data(G_OBJECT(compose->focused_editable),
10251 "paste_as_quotation",
10252 GINT_TO_POINTER(paste_as_quotation + 1));
10253 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10254 entry_paste_clipboard(compose, compose->focused_editable,
10255 prefs_common.linewrap_pastes,
10256 GDK_SELECTION_CLIPBOARD, NULL);
10257 prefs_common.linewrap_quote = wrap_quote;
10261 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10263 Compose *compose = (Compose *)data;
10264 gint prev_autowrap;
10265 GtkTextBuffer *buffer;
10267 if (compose->focused_editable
10268 #ifndef GENERIC_UMPC
10269 && gtk_widget_has_focus(compose->focused_editable)
10272 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10273 GDK_SELECTION_CLIPBOARD, NULL);
10278 #ifndef GENERIC_UMPC
10279 gtk_widget_has_focus(compose->text) &&
10281 compose->gtkaspell &&
10282 compose->gtkaspell->check_while_typing)
10283 gtkaspell_highlight_all(compose->gtkaspell);
10287 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10289 Compose *compose = (Compose *)data;
10290 gint prev_autowrap;
10291 GtkTextBuffer *buffer;
10293 if (compose->focused_editable
10294 #ifndef GENERIC_UMPC
10295 && gtk_widget_has_focus(compose->focused_editable)
10298 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10299 GDK_SELECTION_CLIPBOARD, NULL);
10304 #ifndef GENERIC_UMPC
10305 gtk_widget_has_focus(compose->text) &&
10307 compose->gtkaspell &&
10308 compose->gtkaspell->check_while_typing)
10309 gtkaspell_highlight_all(compose->gtkaspell);
10313 static void compose_allsel_cb(GtkAction *action, gpointer data)
10315 Compose *compose = (Compose *)data;
10316 if (compose->focused_editable
10317 #ifndef GENERIC_UMPC
10318 && gtk_widget_has_focus(compose->focused_editable)
10321 entry_allsel(compose->focused_editable);
10324 static void textview_move_beginning_of_line (GtkTextView *text)
10326 GtkTextBuffer *buffer;
10330 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10332 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10333 mark = gtk_text_buffer_get_insert(buffer);
10334 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10335 gtk_text_iter_set_line_offset(&ins, 0);
10336 gtk_text_buffer_place_cursor(buffer, &ins);
10339 static void textview_move_forward_character (GtkTextView *text)
10341 GtkTextBuffer *buffer;
10345 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10347 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10348 mark = gtk_text_buffer_get_insert(buffer);
10349 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10350 if (gtk_text_iter_forward_cursor_position(&ins))
10351 gtk_text_buffer_place_cursor(buffer, &ins);
10354 static void textview_move_backward_character (GtkTextView *text)
10356 GtkTextBuffer *buffer;
10360 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10362 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10363 mark = gtk_text_buffer_get_insert(buffer);
10364 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10365 if (gtk_text_iter_backward_cursor_position(&ins))
10366 gtk_text_buffer_place_cursor(buffer, &ins);
10369 static void textview_move_forward_word (GtkTextView *text)
10371 GtkTextBuffer *buffer;
10376 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10378 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10379 mark = gtk_text_buffer_get_insert(buffer);
10380 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10381 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10382 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10383 gtk_text_iter_backward_word_start(&ins);
10384 gtk_text_buffer_place_cursor(buffer, &ins);
10388 static void textview_move_backward_word (GtkTextView *text)
10390 GtkTextBuffer *buffer;
10394 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10396 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10397 mark = gtk_text_buffer_get_insert(buffer);
10398 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10399 if (gtk_text_iter_backward_word_starts(&ins, 1))
10400 gtk_text_buffer_place_cursor(buffer, &ins);
10403 static void textview_move_end_of_line (GtkTextView *text)
10405 GtkTextBuffer *buffer;
10409 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10411 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10412 mark = gtk_text_buffer_get_insert(buffer);
10413 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10414 if (gtk_text_iter_forward_to_line_end(&ins))
10415 gtk_text_buffer_place_cursor(buffer, &ins);
10418 static void textview_move_next_line (GtkTextView *text)
10420 GtkTextBuffer *buffer;
10425 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10427 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10428 mark = gtk_text_buffer_get_insert(buffer);
10429 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10430 offset = gtk_text_iter_get_line_offset(&ins);
10431 if (gtk_text_iter_forward_line(&ins)) {
10432 gtk_text_iter_set_line_offset(&ins, offset);
10433 gtk_text_buffer_place_cursor(buffer, &ins);
10437 static void textview_move_previous_line (GtkTextView *text)
10439 GtkTextBuffer *buffer;
10444 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10446 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10447 mark = gtk_text_buffer_get_insert(buffer);
10448 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10449 offset = gtk_text_iter_get_line_offset(&ins);
10450 if (gtk_text_iter_backward_line(&ins)) {
10451 gtk_text_iter_set_line_offset(&ins, offset);
10452 gtk_text_buffer_place_cursor(buffer, &ins);
10456 static void textview_delete_forward_character (GtkTextView *text)
10458 GtkTextBuffer *buffer;
10460 GtkTextIter ins, end_iter;
10462 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10464 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10465 mark = gtk_text_buffer_get_insert(buffer);
10466 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10468 if (gtk_text_iter_forward_char(&end_iter)) {
10469 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10473 static void textview_delete_backward_character (GtkTextView *text)
10475 GtkTextBuffer *buffer;
10477 GtkTextIter ins, end_iter;
10479 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10481 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10482 mark = gtk_text_buffer_get_insert(buffer);
10483 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10485 if (gtk_text_iter_backward_char(&end_iter)) {
10486 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10490 static void textview_delete_forward_word (GtkTextView *text)
10492 GtkTextBuffer *buffer;
10494 GtkTextIter ins, end_iter;
10496 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10498 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10499 mark = gtk_text_buffer_get_insert(buffer);
10500 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10502 if (gtk_text_iter_forward_word_end(&end_iter)) {
10503 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10507 static void textview_delete_backward_word (GtkTextView *text)
10509 GtkTextBuffer *buffer;
10511 GtkTextIter ins, end_iter;
10513 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10515 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10516 mark = gtk_text_buffer_get_insert(buffer);
10517 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10519 if (gtk_text_iter_backward_word_start(&end_iter)) {
10520 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10524 static void textview_delete_line (GtkTextView *text)
10526 GtkTextBuffer *buffer;
10528 GtkTextIter ins, start_iter, end_iter;
10530 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10532 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10533 mark = gtk_text_buffer_get_insert(buffer);
10534 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10537 gtk_text_iter_set_line_offset(&start_iter, 0);
10540 if (gtk_text_iter_ends_line(&end_iter)){
10541 if (!gtk_text_iter_forward_char(&end_iter))
10542 gtk_text_iter_backward_char(&start_iter);
10545 gtk_text_iter_forward_to_line_end(&end_iter);
10546 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10549 static void textview_delete_to_line_end (GtkTextView *text)
10551 GtkTextBuffer *buffer;
10553 GtkTextIter ins, end_iter;
10555 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10557 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10558 mark = gtk_text_buffer_get_insert(buffer);
10559 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10561 if (gtk_text_iter_ends_line(&end_iter))
10562 gtk_text_iter_forward_char(&end_iter);
10564 gtk_text_iter_forward_to_line_end(&end_iter);
10565 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10568 #define DO_ACTION(name, act) { \
10569 if(!strcmp(name, a_name)) { \
10573 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10575 const gchar *a_name = gtk_action_get_name(action);
10576 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10577 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10578 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10579 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10580 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10581 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10582 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10583 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10584 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10585 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10586 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10587 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10588 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10589 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10593 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10595 Compose *compose = (Compose *)data;
10596 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10597 ComposeCallAdvancedAction action = -1;
10599 action = compose_call_advanced_action_from_path(gaction);
10602 void (*do_action) (GtkTextView *text);
10603 } action_table[] = {
10604 {textview_move_beginning_of_line},
10605 {textview_move_forward_character},
10606 {textview_move_backward_character},
10607 {textview_move_forward_word},
10608 {textview_move_backward_word},
10609 {textview_move_end_of_line},
10610 {textview_move_next_line},
10611 {textview_move_previous_line},
10612 {textview_delete_forward_character},
10613 {textview_delete_backward_character},
10614 {textview_delete_forward_word},
10615 {textview_delete_backward_word},
10616 {textview_delete_line},
10617 {textview_delete_to_line_end}
10620 if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
10622 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10623 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10624 if (action_table[action].do_action)
10625 action_table[action].do_action(text);
10627 g_warning("Not implemented yet.");
10631 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10633 GtkAllocation allocation;
10637 if (GTK_IS_EDITABLE(widget)) {
10638 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10639 gtk_editable_set_position(GTK_EDITABLE(widget),
10642 if ((parent = gtk_widget_get_parent(widget))
10643 && (parent = gtk_widget_get_parent(parent))
10644 && (parent = gtk_widget_get_parent(parent))) {
10645 if (GTK_IS_SCROLLED_WINDOW(parent)) {
10646 gtk_widget_get_allocation(widget, &allocation);
10647 gint y = allocation.y;
10648 gint height = allocation.height;
10649 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10650 (GTK_SCROLLED_WINDOW(parent));
10652 gfloat value = gtk_adjustment_get_value(shown);
10653 gfloat upper = gtk_adjustment_get_upper(shown);
10654 gfloat page_size = gtk_adjustment_get_page_size(shown);
10655 if (y < (int)value) {
10656 gtk_adjustment_set_value(shown, y - 1);
10658 if ((y + height) > ((int)value + (int)page_size)) {
10659 if ((y - height - 1) < ((int)upper - (int)page_size)) {
10660 gtk_adjustment_set_value(shown,
10661 y + height - (int)page_size - 1);
10663 gtk_adjustment_set_value(shown,
10664 (int)upper - (int)page_size - 1);
10671 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10672 compose->focused_editable = widget;
10674 #ifdef GENERIC_UMPC
10675 if (GTK_IS_TEXT_VIEW(widget)
10676 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10677 g_object_ref(compose->notebook);
10678 g_object_ref(compose->edit_vbox);
10679 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10680 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10681 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10682 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10683 g_object_unref(compose->notebook);
10684 g_object_unref(compose->edit_vbox);
10685 g_signal_handlers_block_by_func(G_OBJECT(widget),
10686 G_CALLBACK(compose_grab_focus_cb),
10688 gtk_widget_grab_focus(widget);
10689 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10690 G_CALLBACK(compose_grab_focus_cb),
10692 } else if (!GTK_IS_TEXT_VIEW(widget)
10693 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10694 g_object_ref(compose->notebook);
10695 g_object_ref(compose->edit_vbox);
10696 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10697 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10698 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10699 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10700 g_object_unref(compose->notebook);
10701 g_object_unref(compose->edit_vbox);
10702 g_signal_handlers_block_by_func(G_OBJECT(widget),
10703 G_CALLBACK(compose_grab_focus_cb),
10705 gtk_widget_grab_focus(widget);
10706 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10707 G_CALLBACK(compose_grab_focus_cb),
10713 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10715 compose->modified = TRUE;
10716 // compose_beautify_paragraph(compose, NULL, TRUE);
10717 #ifndef GENERIC_UMPC
10718 compose_set_title(compose);
10722 static void compose_wrap_cb(GtkAction *action, gpointer data)
10724 Compose *compose = (Compose *)data;
10725 compose_beautify_paragraph(compose, NULL, TRUE);
10728 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10730 Compose *compose = (Compose *)data;
10731 compose_wrap_all_full(compose, TRUE);
10734 static void compose_find_cb(GtkAction *action, gpointer data)
10736 Compose *compose = (Compose *)data;
10738 message_search_compose(compose);
10741 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10744 Compose *compose = (Compose *)data;
10745 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10746 if (compose->autowrap)
10747 compose_wrap_all_full(compose, TRUE);
10748 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10751 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10754 Compose *compose = (Compose *)data;
10755 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10758 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10760 Compose *compose = (Compose *)data;
10762 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10765 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10767 Compose *compose = (Compose *)data;
10769 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10772 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10774 g_free(compose->privacy_system);
10776 compose->privacy_system = g_strdup(account->default_privacy_system);
10777 compose_update_privacy_system_menu_item(compose, warn);
10780 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10782 Compose *compose = (Compose *)data;
10784 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10785 gtk_widget_show(compose->ruler_hbox);
10786 prefs_common.show_ruler = TRUE;
10788 gtk_widget_hide(compose->ruler_hbox);
10789 gtk_widget_queue_resize(compose->edit_vbox);
10790 prefs_common.show_ruler = FALSE;
10794 static void compose_attach_drag_received_cb (GtkWidget *widget,
10795 GdkDragContext *context,
10798 GtkSelectionData *data,
10801 gpointer user_data)
10803 Compose *compose = (Compose *)user_data;
10807 type = gtk_selection_data_get_data_type(data);
10808 if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
10810 || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND"))
10812 ) && gtk_drag_get_source_widget(context) !=
10813 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10814 list = uri_list_extract_filenames(
10815 (const gchar *)gtk_selection_data_get_data(data));
10816 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10817 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10818 compose_attach_append
10819 (compose, (const gchar *)tmp->data,
10820 utf8_filename, NULL, NULL);
10821 g_free(utf8_filename);
10823 if (list) compose_changed_cb(NULL, compose);
10824 list_free_strings(list);
10826 } else if (gtk_drag_get_source_widget(context)
10827 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10828 /* comes from our summaryview */
10829 SummaryView * summaryview = NULL;
10830 GSList * list = NULL, *cur = NULL;
10832 if (mainwindow_get_mainwindow())
10833 summaryview = mainwindow_get_mainwindow()->summaryview;
10836 list = summary_get_selected_msg_list(summaryview);
10838 for (cur = list; cur; cur = cur->next) {
10839 MsgInfo *msginfo = (MsgInfo *)cur->data;
10840 gchar *file = NULL;
10842 file = procmsg_get_message_file_full(msginfo,
10845 compose_attach_append(compose, (const gchar *)file,
10846 (const gchar *)file, "message/rfc822", NULL);
10850 g_slist_free(list);
10854 static gboolean compose_drag_drop(GtkWidget *widget,
10855 GdkDragContext *drag_context,
10857 guint time, gpointer user_data)
10859 /* not handling this signal makes compose_insert_drag_received_cb
10864 static gboolean completion_set_focus_to_subject
10865 (GtkWidget *widget,
10866 GdkEventKey *event,
10869 cm_return_val_if_fail(compose != NULL, FALSE);
10871 /* make backtab move to subject field */
10872 if(event->keyval == GDK_KEY_ISO_Left_Tab) {
10873 gtk_widget_grab_focus(compose->subject_entry);
10879 static void compose_insert_drag_received_cb (GtkWidget *widget,
10880 GdkDragContext *drag_context,
10883 GtkSelectionData *data,
10886 gpointer user_data)
10888 Compose *compose = (Compose *)user_data;
10892 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10894 type = gtk_selection_data_get_data_type(data);
10896 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
10898 if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) {
10900 AlertValue val = G_ALERTDEFAULT;
10901 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
10903 list = uri_list_extract_filenames(ddata);
10904 if (list == NULL && strstr(ddata, "://")) {
10905 /* Assume a list of no files, and data has ://, is a remote link */
10906 gchar *tmpdata = g_strstrip(g_strdup(ddata));
10907 gchar *tmpfile = get_tmp_file();
10908 str_write_to_file(tmpdata, tmpfile);
10910 compose_insert_file(compose, tmpfile);
10911 claws_unlink(tmpfile);
10913 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10914 compose_beautify_paragraph(compose, NULL, TRUE);
10917 switch (prefs_common.compose_dnd_mode) {
10918 case COMPOSE_DND_ASK:
10919 val = alertpanel_full(_("Insert or attach?"),
10920 _("Do you want to insert the contents of the file(s) "
10921 "into the message body, or attach it to the email?"),
10922 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10923 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10925 case COMPOSE_DND_INSERT:
10926 val = G_ALERTALTERNATE;
10928 case COMPOSE_DND_ATTACH:
10929 val = G_ALERTOTHER;
10932 /* unexpected case */
10933 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10936 if (val & G_ALERTDISABLE) {
10937 val &= ~G_ALERTDISABLE;
10938 /* remember what action to perform by default, only if we don't click Cancel */
10939 if (val == G_ALERTALTERNATE)
10940 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10941 else if (val == G_ALERTOTHER)
10942 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10945 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10946 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10947 list_free_strings(list);
10950 } else if (val == G_ALERTOTHER) {
10951 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10952 list_free_strings(list);
10957 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10958 compose_insert_file(compose, (const gchar *)tmp->data);
10960 list_free_strings(list);
10962 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10967 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10970 static void compose_header_drag_received_cb (GtkWidget *widget,
10971 GdkDragContext *drag_context,
10974 GtkSelectionData *data,
10977 gpointer user_data)
10979 GtkEditable *entry = (GtkEditable *)user_data;
10980 const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
10982 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10985 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10986 gchar *decoded=g_new(gchar, strlen(email));
10989 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
10990 gtk_editable_delete_text(entry, 0, -1);
10991 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10992 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10996 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10999 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11001 Compose *compose = (Compose *)data;
11003 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11004 compose->return_receipt = TRUE;
11006 compose->return_receipt = FALSE;
11009 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11011 Compose *compose = (Compose *)data;
11013 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11014 compose->remove_references = TRUE;
11016 compose->remove_references = FALSE;
11019 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11020 ComposeHeaderEntry *headerentry)
11022 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11026 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11027 GdkEventKey *event,
11028 ComposeHeaderEntry *headerentry)
11030 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11031 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11032 !(event->state & GDK_MODIFIER_MASK) &&
11033 (event->keyval == GDK_KEY_BackSpace) &&
11034 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11035 gtk_container_remove
11036 (GTK_CONTAINER(headerentry->compose->header_table),
11037 headerentry->combo);
11038 gtk_container_remove
11039 (GTK_CONTAINER(headerentry->compose->header_table),
11040 headerentry->entry);
11041 headerentry->compose->header_list =
11042 g_slist_remove(headerentry->compose->header_list,
11044 g_free(headerentry);
11045 } else if (event->keyval == GDK_KEY_Tab) {
11046 if (headerentry->compose->header_last == headerentry) {
11047 /* Override default next focus, and give it to subject_entry
11048 * instead of notebook tabs
11050 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
11051 gtk_widget_grab_focus(headerentry->compose->subject_entry);
11058 static gboolean scroll_postpone(gpointer data)
11060 Compose *compose = (Compose *)data;
11062 cm_return_val_if_fail(!compose->batch, FALSE);
11064 GTK_EVENTS_FLUSH();
11065 compose_show_first_last_header(compose, FALSE);
11069 static void compose_headerentry_changed_cb(GtkWidget *entry,
11070 ComposeHeaderEntry *headerentry)
11072 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11073 compose_create_header_entry(headerentry->compose);
11074 g_signal_handlers_disconnect_matched
11075 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11076 0, 0, NULL, NULL, headerentry);
11078 if (!headerentry->compose->batch)
11079 g_timeout_add(0, scroll_postpone, headerentry->compose);
11083 static gboolean compose_defer_auto_save_draft(Compose *compose)
11085 compose->draft_timeout_tag = -1;
11086 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11090 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11092 GtkAdjustment *vadj;
11094 cm_return_if_fail(compose);
11095 cm_return_if_fail(!compose->batch);
11096 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11097 cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11098 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11099 gtk_widget_get_parent(compose->header_table)));
11100 gtk_adjustment_set_value(vadj, (show_first ?
11101 gtk_adjustment_get_lower(vadj) :
11102 (gtk_adjustment_get_upper(vadj) -
11103 gtk_adjustment_get_page_size(vadj))));
11104 gtk_adjustment_changed(vadj);
11107 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11108 const gchar *text, gint len, Compose *compose)
11110 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11111 (G_OBJECT(compose->text), "paste_as_quotation"));
11114 cm_return_if_fail(text != NULL);
11116 g_signal_handlers_block_by_func(G_OBJECT(buffer),
11117 G_CALLBACK(text_inserted),
11119 if (paste_as_quotation) {
11121 const gchar *qmark;
11123 GtkTextIter start_iter;
11126 len = strlen(text);
11128 new_text = g_strndup(text, len);
11130 qmark = compose_quote_char_from_context(compose);
11132 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11133 gtk_text_buffer_place_cursor(buffer, iter);
11135 pos = gtk_text_iter_get_offset(iter);
11137 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11138 _("Quote format error at line %d."));
11139 quote_fmt_reset_vartable();
11141 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11142 GINT_TO_POINTER(paste_as_quotation - 1));
11144 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11145 gtk_text_buffer_place_cursor(buffer, iter);
11146 gtk_text_buffer_delete_mark(buffer, mark);
11148 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11149 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11150 compose_beautify_paragraph(compose, &start_iter, FALSE);
11151 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11152 gtk_text_buffer_delete_mark(buffer, mark);
11154 if (strcmp(text, "\n") || compose->automatic_break
11155 || gtk_text_iter_starts_line(iter)) {
11156 GtkTextIter before_ins;
11157 gtk_text_buffer_insert(buffer, iter, text, len);
11158 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11159 before_ins = *iter;
11160 gtk_text_iter_backward_chars(&before_ins, len);
11161 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11164 /* check if the preceding is just whitespace or quote */
11165 GtkTextIter start_line;
11166 gchar *tmp = NULL, *quote = NULL;
11167 gint quote_len = 0, is_normal = 0;
11168 start_line = *iter;
11169 gtk_text_iter_set_line_offset(&start_line, 0);
11170 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11173 if (*tmp == '\0') {
11176 quote = compose_get_quote_str(buffer, &start_line, "e_len);
11184 gtk_text_buffer_insert(buffer, iter, text, len);
11186 gtk_text_buffer_insert_with_tags_by_name(buffer,
11187 iter, text, len, "no_join", NULL);
11192 if (!paste_as_quotation) {
11193 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11194 compose_beautify_paragraph(compose, iter, FALSE);
11195 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11196 gtk_text_buffer_delete_mark(buffer, mark);
11199 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11200 G_CALLBACK(text_inserted),
11202 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11204 if (prefs_common.autosave &&
11205 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11206 compose->draft_timeout_tag != -2 /* disabled while loading */)
11207 compose->draft_timeout_tag = g_timeout_add
11208 (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11212 static void compose_check_all(GtkAction *action, gpointer data)
11214 Compose *compose = (Compose *)data;
11215 if (!compose->gtkaspell)
11218 if (gtk_widget_has_focus(compose->subject_entry))
11219 claws_spell_entry_check_all(
11220 CLAWS_SPELL_ENTRY(compose->subject_entry));
11222 gtkaspell_check_all(compose->gtkaspell);
11225 static void compose_highlight_all(GtkAction *action, gpointer data)
11227 Compose *compose = (Compose *)data;
11228 if (compose->gtkaspell) {
11229 claws_spell_entry_recheck_all(
11230 CLAWS_SPELL_ENTRY(compose->subject_entry));
11231 gtkaspell_highlight_all(compose->gtkaspell);
11235 static void compose_check_backwards(GtkAction *action, gpointer data)
11237 Compose *compose = (Compose *)data;
11238 if (!compose->gtkaspell) {
11239 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11243 if (gtk_widget_has_focus(compose->subject_entry))
11244 claws_spell_entry_check_backwards(
11245 CLAWS_SPELL_ENTRY(compose->subject_entry));
11247 gtkaspell_check_backwards(compose->gtkaspell);
11250 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11252 Compose *compose = (Compose *)data;
11253 if (!compose->gtkaspell) {
11254 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11258 if (gtk_widget_has_focus(compose->subject_entry))
11259 claws_spell_entry_check_forwards_go(
11260 CLAWS_SPELL_ENTRY(compose->subject_entry));
11262 gtkaspell_check_forwards_go(compose->gtkaspell);
11267 *\brief Guess originating forward account from MsgInfo and several
11268 * "common preference" settings. Return NULL if no guess.
11270 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
11272 PrefsAccount *account = NULL;
11274 cm_return_val_if_fail(msginfo, NULL);
11275 cm_return_val_if_fail(msginfo->folder, NULL);
11276 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11278 if (msginfo->folder->prefs->enable_default_account)
11279 account = account_find_from_id(msginfo->folder->prefs->default_account);
11282 account = msginfo->folder->folder->account;
11284 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11286 Xstrdup_a(to, msginfo->to, return NULL);
11287 extract_address(to);
11288 account = account_find_from_address(to, FALSE);
11291 if (!account && prefs_common.forward_account_autosel) {
11292 gchar cc[BUFFSIZE];
11293 if (!procheader_get_header_from_msginfo
11294 (msginfo, cc,sizeof cc , "Cc:")) {
11295 gchar *buf = cc + strlen("Cc:");
11296 extract_address(buf);
11297 account = account_find_from_address(buf, FALSE);
11301 if (!account && prefs_common.forward_account_autosel) {
11302 gchar deliveredto[BUFFSIZE];
11303 if (!procheader_get_header_from_msginfo
11304 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11305 gchar *buf = deliveredto + strlen("Delivered-To:");
11306 extract_address(buf);
11307 account = account_find_from_address(buf, FALSE);
11314 gboolean compose_close(Compose *compose)
11318 if (!g_mutex_trylock(compose->mutex)) {
11319 /* we have to wait for the (possibly deferred by auto-save)
11320 * drafting to be done, before destroying the compose under
11322 debug_print("waiting for drafting to finish...\n");
11323 compose_allow_user_actions(compose, FALSE);
11324 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11327 cm_return_val_if_fail(compose, FALSE);
11328 gtkut_widget_get_uposition(compose->window, &x, &y);
11329 if (!compose->batch) {
11330 prefs_common.compose_x = x;
11331 prefs_common.compose_y = y;
11333 g_mutex_unlock(compose->mutex);
11334 compose_destroy(compose);
11339 * Add entry field for each address in list.
11340 * \param compose E-Mail composition object.
11341 * \param listAddress List of (formatted) E-Mail addresses.
11343 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11346 node = listAddress;
11348 addr = ( gchar * ) node->data;
11349 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11350 node = g_list_next( node );
11354 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11355 guint action, gboolean opening_multiple)
11357 gchar *body = NULL;
11358 GSList *new_msglist = NULL;
11359 MsgInfo *tmp_msginfo = NULL;
11360 gboolean originally_enc = FALSE;
11361 gboolean originally_sig = FALSE;
11362 Compose *compose = NULL;
11363 gchar *s_system = NULL;
11365 cm_return_if_fail(msgview != NULL);
11367 cm_return_if_fail(msginfo_list != NULL);
11369 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11370 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11371 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11373 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11374 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11375 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11376 orig_msginfo, mimeinfo);
11377 if (tmp_msginfo != NULL) {
11378 new_msglist = g_slist_append(NULL, tmp_msginfo);
11380 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11381 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11382 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11384 tmp_msginfo->folder = orig_msginfo->folder;
11385 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11386 if (orig_msginfo->tags) {
11387 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11388 tmp_msginfo->folder->tags_dirty = TRUE;
11394 if (!opening_multiple)
11395 body = messageview_get_selection(msgview);
11398 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11399 procmsg_msginfo_free(tmp_msginfo);
11400 g_slist_free(new_msglist);
11402 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11404 if (compose && originally_enc) {
11405 compose_force_encryption(compose, compose->account, FALSE, s_system);
11408 if (compose && originally_sig && compose->account->default_sign_reply) {
11409 compose_force_signing(compose, compose->account, s_system);
11413 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11416 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11419 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11420 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11421 GSList *cur = msginfo_list;
11422 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11423 "messages. Opening the windows "
11424 "could take some time. Do you "
11425 "want to continue?"),
11426 g_slist_length(msginfo_list));
11427 if (g_slist_length(msginfo_list) > 9
11428 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11429 != G_ALERTALTERNATE) {
11434 /* We'll open multiple compose windows */
11435 /* let the WM place the next windows */
11436 compose_force_window_origin = FALSE;
11437 for (; cur; cur = cur->next) {
11439 tmplist.data = cur->data;
11440 tmplist.next = NULL;
11441 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11443 compose_force_window_origin = TRUE;
11445 /* forwarding multiple mails as attachments is done via a
11446 * single compose window */
11447 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11451 void compose_check_for_email_account(Compose *compose)
11453 PrefsAccount *ac = NULL, *curr = NULL;
11459 if (compose->account && compose->account->protocol == A_NNTP) {
11460 ac = account_get_cur_account();
11461 if (ac->protocol == A_NNTP) {
11462 list = account_get_list();
11464 for( ; list != NULL ; list = g_list_next(list)) {
11465 curr = (PrefsAccount *) list->data;
11466 if (curr->protocol != A_NNTP) {
11472 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11477 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11478 const gchar *address)
11480 GSList *msginfo_list = NULL;
11481 gchar *body = messageview_get_selection(msgview);
11484 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11486 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11487 compose_check_for_email_account(compose);
11488 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11489 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11490 compose_reply_set_subject(compose, msginfo);
11493 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11496 void compose_set_position(Compose *compose, gint pos)
11498 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11500 gtkut_text_view_set_position(text, pos);
11503 gboolean compose_search_string(Compose *compose,
11504 const gchar *str, gboolean case_sens)
11506 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11508 return gtkut_text_view_search_string(text, str, case_sens);
11511 gboolean compose_search_string_backward(Compose *compose,
11512 const gchar *str, gboolean case_sens)
11514 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11516 return gtkut_text_view_search_string_backward(text, str, case_sens);
11519 /* allocate a msginfo structure and populate its data from a compose data structure */
11520 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11522 MsgInfo *newmsginfo;
11524 gchar buf[BUFFSIZE];
11526 cm_return_val_if_fail( compose != NULL, NULL );
11528 newmsginfo = procmsg_msginfo_new();
11531 get_rfc822_date(buf, sizeof(buf));
11532 newmsginfo->date = g_strdup(buf);
11535 if (compose->from_name) {
11536 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11537 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11541 if (compose->subject_entry)
11542 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11544 /* to, cc, reply-to, newsgroups */
11545 for (list = compose->header_list; list; list = list->next) {
11546 gchar *header = gtk_editable_get_chars(
11548 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11549 gchar *entry = gtk_editable_get_chars(
11550 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11552 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11553 if ( newmsginfo->to == NULL ) {
11554 newmsginfo->to = g_strdup(entry);
11555 } else if (entry && *entry) {
11556 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11557 g_free(newmsginfo->to);
11558 newmsginfo->to = tmp;
11561 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11562 if ( newmsginfo->cc == NULL ) {
11563 newmsginfo->cc = g_strdup(entry);
11564 } else if (entry && *entry) {
11565 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11566 g_free(newmsginfo->cc);
11567 newmsginfo->cc = tmp;
11570 if ( strcasecmp(header,
11571 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11572 if ( newmsginfo->newsgroups == NULL ) {
11573 newmsginfo->newsgroups = g_strdup(entry);
11574 } else if (entry && *entry) {
11575 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11576 g_free(newmsginfo->newsgroups);
11577 newmsginfo->newsgroups = tmp;
11585 /* other data is unset */
11591 /* update compose's dictionaries from folder dict settings */
11592 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11593 FolderItem *folder_item)
11595 cm_return_if_fail(compose != NULL);
11597 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11598 FolderItemPrefs *prefs = folder_item->prefs;
11600 if (prefs->enable_default_dictionary)
11601 gtkaspell_change_dict(compose->gtkaspell,
11602 prefs->default_dictionary, FALSE);
11603 if (folder_item->prefs->enable_default_alt_dictionary)
11604 gtkaspell_change_alt_dict(compose->gtkaspell,
11605 prefs->default_alt_dictionary);
11606 if (prefs->enable_default_dictionary
11607 || prefs->enable_default_alt_dictionary)
11608 compose_spell_menu_changed(compose);