2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
167 SUBJECT_FIELD_PRESENT,
172 #define B64_LINE_SIZE 57
173 #define B64_BUFFSIZE 77
175 #define MAX_REFERENCES_LEN 999
177 static GList *compose_list = NULL;
179 static Compose *compose_generic_new (PrefsAccount *account,
182 GPtrArray *attach_files,
183 GList *listAddress );
185 static Compose *compose_create (PrefsAccount *account,
190 static void compose_entry_mark_default_to (Compose *compose,
191 const gchar *address);
192 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
197 static Compose *compose_forward_multiple (PrefsAccount *account,
198 GSList *msginfo_list);
199 static Compose *compose_reply (MsgInfo *msginfo,
200 ComposeQuoteMode quote_mode,
205 static Compose *compose_reply_mode (ComposeMode mode,
206 GSList *msginfo_list,
208 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
209 static void compose_update_privacy_systems_menu(Compose *compose);
211 static GtkWidget *compose_account_option_menu_create
213 static void compose_set_out_encoding (Compose *compose);
214 static void compose_set_template_menu (Compose *compose);
215 static void compose_destroy (Compose *compose);
217 static MailField compose_entries_set (Compose *compose,
219 ComposeEntryType to_type);
220 static gint compose_parse_header (Compose *compose,
222 static gchar *compose_parse_references (const gchar *ref,
225 static gchar *compose_quote_fmt (Compose *compose,
231 gboolean need_unescape,
232 const gchar *err_msg);
234 static void compose_reply_set_entry (Compose *compose,
240 followup_and_reply_to);
241 static void compose_reedit_set_entry (Compose *compose,
244 static void compose_insert_sig (Compose *compose,
246 static ComposeInsertResult compose_insert_file (Compose *compose,
249 static gboolean compose_attach_append (Compose *compose,
252 const gchar *content_type);
253 static void compose_attach_parts (Compose *compose,
256 static gboolean compose_beautify_paragraph (Compose *compose,
257 GtkTextIter *par_iter,
259 static void compose_wrap_all (Compose *compose);
260 static void compose_wrap_all_full (Compose *compose,
263 static void compose_set_title (Compose *compose);
264 static void compose_select_account (Compose *compose,
265 PrefsAccount *account,
268 static PrefsAccount *compose_current_mail_account(void);
269 /* static gint compose_send (Compose *compose); */
270 static gboolean compose_check_for_valid_recipient
272 static gboolean compose_check_entries (Compose *compose,
273 gboolean check_everything);
274 static gint compose_write_to_file (Compose *compose,
277 gboolean attach_parts);
278 static gint compose_write_body_to_file (Compose *compose,
280 static gint compose_remove_reedit_target (Compose *compose,
282 static void compose_remove_draft (Compose *compose);
283 static gint compose_queue_sub (Compose *compose,
287 gboolean check_subject,
288 gboolean remove_reedit_target);
289 static int compose_add_attachments (Compose *compose,
291 static gchar *compose_get_header (Compose *compose);
293 static void compose_convert_header (Compose *compose,
298 gboolean addr_field);
300 static void compose_attach_info_free (AttachInfo *ainfo);
301 static void compose_attach_remove_selected (GtkAction *action,
304 static void compose_template_apply (Compose *compose,
307 static void compose_attach_property (GtkAction *action,
309 static void compose_attach_property_create (gboolean *cancelled);
310 static void attach_property_ok (GtkWidget *widget,
311 gboolean *cancelled);
312 static void attach_property_cancel (GtkWidget *widget,
313 gboolean *cancelled);
314 static gint attach_property_delete_event (GtkWidget *widget,
316 gboolean *cancelled);
317 static gboolean attach_property_key_pressed (GtkWidget *widget,
319 gboolean *cancelled);
321 static void compose_exec_ext_editor (Compose *compose);
323 static gint compose_exec_ext_editor_real (const gchar *file);
324 static gboolean compose_ext_editor_kill (Compose *compose);
325 static gboolean compose_input_cb (GIOChannel *source,
326 GIOCondition condition,
328 static void compose_set_ext_editor_sensitive (Compose *compose,
330 #endif /* G_OS_UNIX */
332 static void compose_undo_state_changed (UndoMain *undostruct,
337 static void compose_create_header_entry (Compose *compose);
338 static void compose_add_header_entry (Compose *compose, const gchar *header,
339 gchar *text, ComposePrefType pref_type);
340 static void compose_remove_header_entries(Compose *compose);
342 static void compose_update_priority_menu_item(Compose * compose);
344 static void compose_spell_menu_changed (void *data);
345 static void compose_dict_changed (void *data);
347 static void compose_add_field_list ( Compose *compose,
348 GList *listAddress );
350 /* callback functions */
352 static gboolean compose_edit_size_alloc (GtkEditable *widget,
353 GtkAllocation *allocation,
354 GtkSHRuler *shruler);
355 static void account_activated (GtkComboBox *optmenu,
357 static void attach_selected (GtkTreeView *tree_view,
358 GtkTreePath *tree_path,
359 GtkTreeViewColumn *column,
361 static gboolean attach_button_pressed (GtkWidget *widget,
362 GdkEventButton *event,
364 static gboolean attach_key_pressed (GtkWidget *widget,
367 static void compose_send_cb (GtkAction *action, gpointer data);
368 static void compose_send_later_cb (GtkAction *action, gpointer data);
370 static void compose_save_cb (GtkAction *action,
373 static void compose_attach_cb (GtkAction *action,
375 static void compose_insert_file_cb (GtkAction *action,
377 static void compose_insert_sig_cb (GtkAction *action,
380 static void compose_close_cb (GtkAction *action,
383 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
385 static void compose_address_cb (GtkAction *action,
387 static void about_show_cb (GtkAction *action,
389 static void compose_template_activate_cb(GtkWidget *widget,
392 static void compose_ext_editor_cb (GtkAction *action,
395 static gint compose_delete_cb (GtkWidget *widget,
399 static void compose_undo_cb (GtkAction *action,
401 static void compose_redo_cb (GtkAction *action,
403 static void compose_cut_cb (GtkAction *action,
405 static void compose_copy_cb (GtkAction *action,
407 static void compose_paste_cb (GtkAction *action,
409 static void compose_paste_as_quote_cb (GtkAction *action,
411 static void compose_paste_no_wrap_cb (GtkAction *action,
413 static void compose_paste_wrap_cb (GtkAction *action,
415 static void compose_allsel_cb (GtkAction *action,
418 static void compose_advanced_action_cb (GtkAction *action,
421 static void compose_grab_focus_cb (GtkWidget *widget,
424 static void compose_changed_cb (GtkTextBuffer *textbuf,
427 static void compose_wrap_cb (GtkAction *action,
429 static void compose_wrap_all_cb (GtkAction *action,
431 static void compose_find_cb (GtkAction *action,
433 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
435 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
438 static void compose_toggle_ruler_cb (GtkToggleAction *action,
440 static void compose_toggle_sign_cb (GtkToggleAction *action,
442 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
444 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
445 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
446 static void activate_privacy_system (Compose *compose,
447 PrefsAccount *account,
449 static void compose_use_signing(Compose *compose, gboolean use_signing);
450 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
451 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
453 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
455 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
456 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
457 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
459 static void compose_attach_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_insert_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
475 static void compose_header_drag_received_cb (GtkWidget *widget,
476 GdkDragContext *drag_context,
479 GtkSelectionData *data,
484 static gboolean compose_drag_drop (GtkWidget *widget,
485 GdkDragContext *drag_context,
487 guint time, gpointer user_data);
489 static void text_inserted (GtkTextBuffer *buffer,
494 static Compose *compose_generic_reply(MsgInfo *msginfo,
495 ComposeQuoteMode quote_mode,
499 gboolean followup_and_reply_to,
502 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
503 ComposeHeaderEntry *headerentry);
504 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
506 ComposeHeaderEntry *headerentry);
507 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
508 ComposeHeaderEntry *headerentry);
510 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
512 static void compose_allow_user_actions (Compose *compose, gboolean allow);
514 static void compose_nothing_cb (GtkAction *action, gpointer data)
520 static void compose_check_all (GtkAction *action, gpointer data);
521 static void compose_highlight_all (GtkAction *action, gpointer data);
522 static void compose_check_backwards (GtkAction *action, gpointer data);
523 static void compose_check_forwards_go (GtkAction *action, gpointer data);
526 static gint compose_defer_auto_save_draft (Compose *compose);
527 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
529 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
532 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
533 FolderItem *folder_item);
535 static void compose_attach_update_label(Compose *compose);
536 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
537 gboolean respect_default_to);
539 static GtkActionEntry compose_popup_entries[] =
541 {"Compose", NULL, "Compose" },
542 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
543 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
544 {"Compose/---", NULL, "---", NULL, NULL, NULL },
545 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
548 static GtkActionEntry compose_entries[] =
550 {"Menu", NULL, "Menu" },
552 {"Message", NULL, N_("_Message") },
553 {"Edit", NULL, N_("_Edit") },
555 {"Spelling", NULL, N_("_Spelling") },
557 {"Options", NULL, N_("_Options") },
558 {"Tools", NULL, N_("_Tools") },
559 {"Help", NULL, N_("_Help") },
561 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
562 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
563 {"Message/---", NULL, "---" },
565 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
566 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
567 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
568 /* {"Message/---", NULL, "---" }, */
569 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
570 /* {"Message/---", NULL, "---" }, */
571 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
574 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
575 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
576 {"Edit/---", NULL, "---" },
578 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
579 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
580 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
582 {"Edit/SpecialPaste", NULL, N_("Special paste") },
583 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
584 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
585 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
587 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
589 {"Edit/Advanced", NULL, N_("A_dvanced") },
590 {"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*/
591 {"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*/
592 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
593 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
594 {"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*/
595 {"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*/
596 {"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*/
597 {"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*/
598 {"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*/
599 {"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*/
600 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
601 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
602 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
603 {"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*/
605 /* {"Edit/---", NULL, "---" }, */
606 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
608 /* {"Edit/---", NULL, "---" }, */
609 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
610 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
611 /* {"Edit/---", NULL, "---" }, */
612 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
615 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
616 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
617 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
618 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
620 {"Spelling/---", NULL, "---" },
621 {"Spelling/Options", NULL, N_("_Options") },
626 {"Options/ReplyMode", NULL, N_("Reply _mode") },
627 {"Options/---", NULL, "---" },
628 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
629 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
631 /* {"Options/---", NULL, "---" }, */
633 {"Options/Priority", NULL, N_("_Priority") },
635 {"Options/Encoding", NULL, N_("Character _encoding") },
636 {"Options/Encoding/---", NULL, "---" },
637 #define ENC_ACTION(cs_char,c_char,string) \
638 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
640 {"Options/Encoding/Western", NULL, N_("Western European") },
641 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
642 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
643 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
644 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
645 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
646 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
647 {"Options/Encoding/Korean", NULL, N_("Korean") },
648 {"Options/Encoding/Thai", NULL, N_("Thai") },
651 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
653 {"Tools/Template", NULL, N_("_Template") },
654 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
655 {"Tools/Actions", NULL, N_("Actio_ns") },
656 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
659 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
662 static GtkToggleActionEntry compose_toggle_entries[] =
664 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
665 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
666 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
667 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
668 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
669 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
670 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
673 static GtkRadioActionEntry compose_radio_rm_entries[] =
675 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
676 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
677 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
678 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
681 static GtkRadioActionEntry compose_radio_prio_entries[] =
683 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
684 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
685 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
686 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
687 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
690 static GtkRadioActionEntry compose_radio_enc_entries[] =
692 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
715 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
716 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
717 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
718 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
719 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
720 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
721 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
722 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
723 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
726 static GtkTargetEntry compose_mime_types[] =
728 {"text/uri-list", 0, 0},
729 {"UTF8_STRING", 0, 0},
733 static gboolean compose_put_existing_to_front(MsgInfo *info)
735 GList *compose_list = compose_get_compose_list();
739 for (elem = compose_list; elem != NULL && elem->data != NULL;
741 Compose *c = (Compose*)elem->data;
743 if (!c->targetinfo || !c->targetinfo->msgid ||
747 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
748 gtkut_window_popup(c->window);
756 static GdkColor quote_color1 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_color2 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_color3 =
761 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
763 static GdkColor quote_bgcolor1 =
764 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor2 =
766 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor3 =
768 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
770 static GdkColor signature_color = {
777 static GdkColor uri_color = {
784 static void compose_create_tags(GtkTextView *text, Compose *compose)
786 GtkTextBuffer *buffer;
787 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
793 buffer = gtk_text_view_get_buffer(text);
795 if (prefs_common.enable_color) {
796 /* grab the quote colors, converting from an int to a GdkColor */
797 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
799 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
801 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
803 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
805 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
807 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
809 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
811 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
814 signature_color = quote_color1 = quote_color2 = quote_color3 =
815 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
818 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
819 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820 "foreground-gdk", "e_color1,
821 "paragraph-background-gdk", "e_bgcolor1,
823 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
824 "foreground-gdk", "e_color2,
825 "paragraph-background-gdk", "e_bgcolor2,
827 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
828 "foreground-gdk", "e_color3,
829 "paragraph-background-gdk", "e_bgcolor3,
832 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
833 "foreground-gdk", "e_color1,
835 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
836 "foreground-gdk", "e_color2,
838 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
839 "foreground-gdk", "e_color3,
843 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
844 "foreground-gdk", &signature_color,
847 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
848 "foreground-gdk", &uri_color,
850 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
851 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
853 color[0] = quote_color1;
854 color[1] = quote_color2;
855 color[2] = quote_color3;
856 color[3] = quote_bgcolor1;
857 color[4] = quote_bgcolor2;
858 color[5] = quote_bgcolor3;
859 color[6] = signature_color;
860 color[7] = uri_color;
861 cmap = gdk_drawable_get_colormap(compose->window->window);
862 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
864 for (i = 0; i < 8; i++) {
865 if (success[i] == FALSE) {
868 g_warning("Compose: color allocation failed.\n");
869 style = gtk_widget_get_style(GTK_WIDGET(text));
870 quote_color1 = quote_color2 = quote_color3 =
871 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
872 signature_color = uri_color = black;
877 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
878 GPtrArray *attach_files)
880 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
883 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
885 return compose_generic_new(account, mailto, item, NULL, NULL);
888 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
890 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
893 #define SCROLL_TO_CURSOR(compose) { \
894 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
895 gtk_text_view_get_buffer( \
896 GTK_TEXT_VIEW(compose->text))); \
897 gtk_text_view_scroll_mark_onscreen( \
898 GTK_TEXT_VIEW(compose->text), \
902 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
905 if (folderidentifier) {
906 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
907 prefs_common.compose_save_to_history = add_history(
908 prefs_common.compose_save_to_history, folderidentifier);
909 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
910 prefs_common.compose_save_to_history);
913 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
914 if (folderidentifier)
915 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
917 gtk_entry_set_text(GTK_ENTRY(entry), "");
920 static gchar *compose_get_save_to(Compose *compose)
923 gchar *result = NULL;
924 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
925 result = gtk_editable_get_chars(entry, 0, -1);
928 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
929 prefs_common.compose_save_to_history = add_history(
930 prefs_common.compose_save_to_history, result);
931 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
932 prefs_common.compose_save_to_history);
937 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
938 GPtrArray *attach_files, GList *listAddress )
941 GtkTextView *textview;
942 GtkTextBuffer *textbuf;
944 const gchar *subject_format = NULL;
945 const gchar *body_format = NULL;
946 gchar *mailto_from = NULL;
947 PrefsAccount *mailto_account = NULL;
948 MsgInfo* dummyinfo = NULL;
949 MailField mfield = NO_FIELD_PRESENT;
953 /* check if mailto defines a from */
954 if (mailto && *mailto != '\0') {
955 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
956 /* mailto defines a from, check if we can get account prefs from it,
957 if not, the account prefs will be guessed using other ways, but we'll keep
960 mailto_account = account_find_from_address(mailto_from, TRUE);
962 account = mailto_account;
965 /* if no account prefs set from mailto, set if from folder prefs (if any) */
966 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
967 account = account_find_from_id(item->prefs->default_account);
969 /* if no account prefs set, fallback to the current one */
970 if (!account) account = cur_account;
971 cm_return_val_if_fail(account != NULL, NULL);
973 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
975 /* override from name if mailto asked for it */
977 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
980 /* override from name according to folder properties */
981 if (item && item->prefs &&
982 item->prefs->compose_with_format &&
983 item->prefs->compose_override_from_format &&
984 *item->prefs->compose_override_from_format != '\0') {
989 dummyinfo = compose_msginfo_new_from_compose(compose);
991 /* decode \-escape sequences in the internal representation of the quote format */
992 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
993 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
996 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
999 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1001 quote_fmt_scan_string(tmp);
1004 buf = quote_fmt_get_buffer();
1006 alertpanel_error(_("New message From format error."));
1008 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1009 quote_fmt_reset_vartable();
1014 compose->replyinfo = NULL;
1015 compose->fwdinfo = NULL;
1017 textview = GTK_TEXT_VIEW(compose->text);
1018 textbuf = gtk_text_view_get_buffer(textview);
1019 compose_create_tags(textview, compose);
1021 undo_block(compose->undostruct);
1023 compose_set_dictionaries_from_folder_prefs(compose, item);
1026 if (account->auto_sig)
1027 compose_insert_sig(compose, FALSE);
1028 gtk_text_buffer_get_start_iter(textbuf, &iter);
1029 gtk_text_buffer_place_cursor(textbuf, &iter);
1031 if (account->protocol != A_NNTP) {
1032 if (mailto && *mailto != '\0') {
1033 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1036 compose_set_folder_prefs(compose, item, TRUE);
1038 if (item && item->ret_rcpt) {
1039 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1042 if (mailto && *mailto != '\0') {
1043 if (!strchr(mailto, '@'))
1044 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1046 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1047 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1048 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1049 mfield = TO_FIELD_PRESENT;
1052 * CLAWS: just don't allow return receipt request, even if the user
1053 * may want to send an email. simple but foolproof.
1055 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1057 compose_add_field_list( compose, listAddress );
1059 if (item && item->prefs && item->prefs->compose_with_format) {
1060 subject_format = item->prefs->compose_subject_format;
1061 body_format = item->prefs->compose_body_format;
1062 } else if (account->compose_with_format) {
1063 subject_format = account->compose_subject_format;
1064 body_format = account->compose_body_format;
1065 } else if (prefs_common.compose_with_format) {
1066 subject_format = prefs_common.compose_subject_format;
1067 body_format = prefs_common.compose_body_format;
1070 if (subject_format || body_format) {
1073 && *subject_format != '\0' )
1075 gchar *subject = NULL;
1080 dummyinfo = compose_msginfo_new_from_compose(compose);
1082 /* decode \-escape sequences in the internal representation of the quote format */
1083 tmp = malloc(strlen(subject_format)+1);
1084 pref_get_unescaped_pref(tmp, subject_format);
1086 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1088 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1089 compose->gtkaspell);
1091 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1093 quote_fmt_scan_string(tmp);
1096 buf = quote_fmt_get_buffer();
1098 alertpanel_error(_("New message subject format error."));
1100 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1101 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1102 quote_fmt_reset_vartable();
1106 mfield = SUBJECT_FIELD_PRESENT;
1110 && *body_format != '\0' )
1113 GtkTextBuffer *buffer;
1114 GtkTextIter start, end;
1118 dummyinfo = compose_msginfo_new_from_compose(compose);
1120 text = GTK_TEXT_VIEW(compose->text);
1121 buffer = gtk_text_view_get_buffer(text);
1122 gtk_text_buffer_get_start_iter(buffer, &start);
1123 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1124 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1126 compose_quote_fmt(compose, dummyinfo,
1128 NULL, tmp, FALSE, TRUE,
1129 _("The body of the \"New message\" template has an error at line %d."));
1130 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1131 quote_fmt_reset_vartable();
1135 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1136 gtkaspell_highlight_all(compose->gtkaspell);
1138 mfield = BODY_FIELD_PRESENT;
1142 procmsg_msginfo_free( dummyinfo );
1148 for (i = 0; i < attach_files->len; i++) {
1149 file = g_ptr_array_index(attach_files, i);
1150 compose_attach_append(compose, file, file, NULL);
1154 compose_show_first_last_header(compose, TRUE);
1156 /* Set save folder */
1157 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1158 gchar *folderidentifier;
1160 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1161 folderidentifier = folder_item_get_identifier(item);
1162 compose_set_save_to(compose, folderidentifier);
1163 g_free(folderidentifier);
1166 /* Place cursor according to provided input (mfield) */
1168 case NO_FIELD_PRESENT:
1169 if (compose->header_last)
1170 gtk_widget_grab_focus(compose->header_last->entry);
1172 case TO_FIELD_PRESENT:
1173 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1175 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1178 gtk_widget_grab_focus(compose->subject_entry);
1180 case SUBJECT_FIELD_PRESENT:
1181 textview = GTK_TEXT_VIEW(compose->text);
1184 textbuf = gtk_text_view_get_buffer(textview);
1187 mark = gtk_text_buffer_get_insert(textbuf);
1188 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1189 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1191 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1192 * only defers where it comes to the variable body
1193 * is not null. If no body is present compose->text
1194 * will be null in which case you cannot place the
1195 * cursor inside the component so. An empty component
1196 * is therefore created before placing the cursor
1198 case BODY_FIELD_PRESENT:
1199 gtk_widget_grab_focus(compose->text);
1203 undo_unblock(compose->undostruct);
1205 if (prefs_common.auto_exteditor)
1206 compose_exec_ext_editor(compose);
1208 compose->draft_timeout_tag = -1;
1209 SCROLL_TO_CURSOR(compose);
1211 compose->modified = FALSE;
1212 compose_set_title(compose);
1214 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1219 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1220 gboolean override_pref, const gchar *system)
1222 const gchar *privacy = NULL;
1224 cm_return_if_fail(compose != NULL);
1225 cm_return_if_fail(account != NULL);
1227 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1232 else if (account->default_privacy_system
1233 && strlen(account->default_privacy_system)) {
1234 privacy = account->default_privacy_system;
1236 GSList *privacy_avail = privacy_get_system_ids();
1237 if (privacy_avail && g_slist_length(privacy_avail)) {
1238 privacy = (gchar *)(privacy_avail->data);
1241 if (privacy != NULL) {
1243 g_free(compose->privacy_system);
1244 compose->privacy_system = NULL;
1246 if (compose->privacy_system == NULL)
1247 compose->privacy_system = g_strdup(privacy);
1248 else if (*(compose->privacy_system) == '\0') {
1249 g_free(compose->privacy_system);
1250 compose->privacy_system = g_strdup(privacy);
1252 compose_update_privacy_system_menu_item(compose, FALSE);
1253 compose_use_encryption(compose, TRUE);
1257 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1259 const gchar *privacy = NULL;
1263 else if (account->default_privacy_system
1264 && strlen(account->default_privacy_system)) {
1265 privacy = account->default_privacy_system;
1267 GSList *privacy_avail = privacy_get_system_ids();
1268 if (privacy_avail && g_slist_length(privacy_avail)) {
1269 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 compose_update_privacy_system_menu_item(compose, FALSE);
1281 compose_use_signing(compose, TRUE);
1285 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1289 Compose *compose = NULL;
1291 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1293 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1294 cm_return_val_if_fail(msginfo != NULL, NULL);
1296 list_len = g_slist_length(msginfo_list);
1300 case COMPOSE_REPLY_TO_ADDRESS:
1301 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1302 FALSE, prefs_common.default_reply_list, FALSE, body);
1304 case COMPOSE_REPLY_WITH_QUOTE:
1305 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1306 FALSE, prefs_common.default_reply_list, FALSE, body);
1308 case COMPOSE_REPLY_WITHOUT_QUOTE:
1309 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1310 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1312 case COMPOSE_REPLY_TO_SENDER:
1313 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1314 FALSE, FALSE, TRUE, body);
1316 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1317 compose = compose_followup_and_reply_to(msginfo,
1318 COMPOSE_QUOTE_CHECK,
1319 FALSE, FALSE, body);
1321 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1322 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1323 FALSE, FALSE, TRUE, body);
1325 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1326 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1327 FALSE, FALSE, TRUE, NULL);
1329 case COMPOSE_REPLY_TO_ALL:
1330 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1331 TRUE, FALSE, FALSE, body);
1333 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1334 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1335 TRUE, FALSE, FALSE, body);
1337 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1338 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1339 TRUE, FALSE, FALSE, NULL);
1341 case COMPOSE_REPLY_TO_LIST:
1342 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1343 FALSE, TRUE, FALSE, body);
1345 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1346 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1347 FALSE, TRUE, FALSE, body);
1349 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1350 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1351 FALSE, TRUE, FALSE, NULL);
1353 case COMPOSE_FORWARD:
1354 if (prefs_common.forward_as_attachment) {
1355 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1358 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1362 case COMPOSE_FORWARD_INLINE:
1363 /* check if we reply to more than one Message */
1364 if (list_len == 1) {
1365 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1368 /* more messages FALL THROUGH */
1369 case COMPOSE_FORWARD_AS_ATTACH:
1370 compose = compose_forward_multiple(NULL, msginfo_list);
1372 case COMPOSE_REDIRECT:
1373 compose = compose_redirect(NULL, msginfo, FALSE);
1376 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1379 if (compose == NULL) {
1380 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1384 compose->rmode = mode;
1385 switch (compose->rmode) {
1387 case COMPOSE_REPLY_WITH_QUOTE:
1388 case COMPOSE_REPLY_WITHOUT_QUOTE:
1389 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1390 debug_print("reply mode Normal\n");
1391 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1392 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1394 case COMPOSE_REPLY_TO_SENDER:
1395 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1396 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1397 debug_print("reply mode Sender\n");
1398 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1400 case COMPOSE_REPLY_TO_ALL:
1401 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1402 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1403 debug_print("reply mode All\n");
1404 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1406 case COMPOSE_REPLY_TO_LIST:
1407 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1408 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1409 debug_print("reply mode List\n");
1410 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1412 case COMPOSE_REPLY_TO_ADDRESS:
1413 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1421 static Compose *compose_reply(MsgInfo *msginfo,
1422 ComposeQuoteMode quote_mode,
1428 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1429 to_sender, FALSE, body);
1432 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1433 ComposeQuoteMode quote_mode,
1438 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1439 to_sender, TRUE, body);
1442 static void compose_extract_original_charset(Compose *compose)
1444 MsgInfo *info = NULL;
1445 if (compose->replyinfo) {
1446 info = compose->replyinfo;
1447 } else if (compose->fwdinfo) {
1448 info = compose->fwdinfo;
1449 } else if (compose->targetinfo) {
1450 info = compose->targetinfo;
1453 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1454 MimeInfo *partinfo = mimeinfo;
1455 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1456 partinfo = procmime_mimeinfo_next(partinfo);
1458 compose->orig_charset =
1459 g_strdup(procmime_mimeinfo_get_parameter(
1460 partinfo, "charset"));
1462 procmime_mimeinfo_free_all(mimeinfo);
1466 #define SIGNAL_BLOCK(buffer) { \
1467 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1468 G_CALLBACK(compose_changed_cb), \
1470 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1471 G_CALLBACK(text_inserted), \
1475 #define SIGNAL_UNBLOCK(buffer) { \
1476 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1477 G_CALLBACK(compose_changed_cb), \
1479 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1480 G_CALLBACK(text_inserted), \
1484 static Compose *compose_generic_reply(MsgInfo *msginfo,
1485 ComposeQuoteMode quote_mode,
1486 gboolean to_all, gboolean to_ml,
1488 gboolean followup_and_reply_to,
1492 PrefsAccount *account = NULL;
1493 GtkTextView *textview;
1494 GtkTextBuffer *textbuf;
1495 gboolean quote = FALSE;
1496 const gchar *qmark = NULL;
1497 const gchar *body_fmt = NULL;
1498 gchar *s_system = NULL;
1500 cm_return_val_if_fail(msginfo != NULL, NULL);
1501 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1503 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1505 cm_return_val_if_fail(account != NULL, NULL);
1507 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1509 compose->updating = TRUE;
1511 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1512 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1514 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1515 if (!compose->replyinfo)
1516 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1518 compose_extract_original_charset(compose);
1520 if (msginfo->folder && msginfo->folder->ret_rcpt)
1521 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1523 /* Set save folder */
1524 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1525 gchar *folderidentifier;
1527 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1528 folderidentifier = folder_item_get_identifier(msginfo->folder);
1529 compose_set_save_to(compose, folderidentifier);
1530 g_free(folderidentifier);
1533 if (compose_parse_header(compose, msginfo) < 0) {
1534 compose->updating = FALSE;
1535 compose_destroy(compose);
1539 /* override from name according to folder properties */
1540 if (msginfo->folder && msginfo->folder->prefs &&
1541 msginfo->folder->prefs->reply_with_format &&
1542 msginfo->folder->prefs->reply_override_from_format &&
1543 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1548 /* decode \-escape sequences in the internal representation of the quote format */
1549 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1550 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1553 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1554 compose->gtkaspell);
1556 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1558 quote_fmt_scan_string(tmp);
1561 buf = quote_fmt_get_buffer();
1563 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1565 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1566 quote_fmt_reset_vartable();
1571 textview = (GTK_TEXT_VIEW(compose->text));
1572 textbuf = gtk_text_view_get_buffer(textview);
1573 compose_create_tags(textview, compose);
1575 undo_block(compose->undostruct);
1577 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1580 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1581 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1582 /* use the reply format of folder (if enabled), or the account's one
1583 (if enabled) or fallback to the global reply format, which is always
1584 enabled (even if empty), and use the relevant quotemark */
1586 if (msginfo->folder && msginfo->folder->prefs &&
1587 msginfo->folder->prefs->reply_with_format) {
1588 qmark = msginfo->folder->prefs->reply_quotemark;
1589 body_fmt = msginfo->folder->prefs->reply_body_format;
1591 } else if (account->reply_with_format) {
1592 qmark = account->reply_quotemark;
1593 body_fmt = account->reply_body_format;
1596 qmark = prefs_common.quotemark;
1597 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1598 body_fmt = gettext(prefs_common.quotefmt);
1605 /* empty quotemark is not allowed */
1606 if (qmark == NULL || *qmark == '\0')
1608 compose_quote_fmt(compose, compose->replyinfo,
1609 body_fmt, qmark, body, FALSE, TRUE,
1610 _("The body of the \"Reply\" template has an error at line %d."));
1611 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1612 quote_fmt_reset_vartable();
1614 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1615 gtkaspell_highlight_all(compose->gtkaspell);
1619 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1620 compose_force_encryption(compose, account, FALSE, s_system);
1623 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1624 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1625 compose_force_signing(compose, account, s_system);
1629 SIGNAL_BLOCK(textbuf);
1631 if (account->auto_sig)
1632 compose_insert_sig(compose, FALSE);
1634 compose_wrap_all(compose);
1636 SIGNAL_UNBLOCK(textbuf);
1638 gtk_widget_grab_focus(compose->text);
1640 undo_unblock(compose->undostruct);
1642 if (prefs_common.auto_exteditor)
1643 compose_exec_ext_editor(compose);
1645 compose->modified = FALSE;
1646 compose_set_title(compose);
1648 compose->updating = FALSE;
1649 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1650 SCROLL_TO_CURSOR(compose);
1652 if (compose->deferred_destroy) {
1653 compose_destroy(compose);
1661 #define INSERT_FW_HEADER(var, hdr) \
1662 if (msginfo->var && *msginfo->var) { \
1663 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1664 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1665 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1668 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1669 gboolean as_attach, const gchar *body,
1670 gboolean no_extedit,
1674 GtkTextView *textview;
1675 GtkTextBuffer *textbuf;
1678 cm_return_val_if_fail(msginfo != NULL, NULL);
1679 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1682 !(account = compose_guess_forward_account_from_msginfo
1684 account = cur_account;
1686 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1688 compose->updating = TRUE;
1689 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1690 if (!compose->fwdinfo)
1691 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1693 compose_extract_original_charset(compose);
1695 if (msginfo->subject && *msginfo->subject) {
1696 gchar *buf, *buf2, *p;
1698 buf = p = g_strdup(msginfo->subject);
1699 p += subject_get_prefix_length(p);
1700 memmove(buf, p, strlen(p) + 1);
1702 buf2 = g_strdup_printf("Fw: %s", buf);
1703 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1709 /* override from name according to folder properties */
1710 if (msginfo->folder && msginfo->folder->prefs &&
1711 msginfo->folder->prefs->forward_with_format &&
1712 msginfo->folder->prefs->forward_override_from_format &&
1713 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1717 MsgInfo *full_msginfo = NULL;
1720 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1722 full_msginfo = procmsg_msginfo_copy(msginfo);
1724 /* decode \-escape sequences in the internal representation of the quote format */
1725 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1726 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1729 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1730 compose->gtkaspell);
1732 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1734 quote_fmt_scan_string(tmp);
1737 buf = quote_fmt_get_buffer();
1739 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1741 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1742 quote_fmt_reset_vartable();
1745 procmsg_msginfo_free(full_msginfo);
1748 textview = GTK_TEXT_VIEW(compose->text);
1749 textbuf = gtk_text_view_get_buffer(textview);
1750 compose_create_tags(textview, compose);
1752 undo_block(compose->undostruct);
1756 msgfile = procmsg_get_message_file(msginfo);
1757 if (!is_file_exist(msgfile))
1758 g_warning("%s: file not exist\n", msgfile);
1760 compose_attach_append(compose, msgfile, msgfile,
1765 const gchar *qmark = NULL;
1766 const gchar *body_fmt = NULL;
1767 MsgInfo *full_msginfo;
1769 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1770 body_fmt = gettext(prefs_common.fw_quotefmt);
1774 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1776 full_msginfo = procmsg_msginfo_copy(msginfo);
1778 /* use the forward format of folder (if enabled), or the account's one
1779 (if enabled) or fallback to the global forward format, which is always
1780 enabled (even if empty), and use the relevant quotemark */
1781 if (msginfo->folder && msginfo->folder->prefs &&
1782 msginfo->folder->prefs->forward_with_format) {
1783 qmark = msginfo->folder->prefs->forward_quotemark;
1784 body_fmt = msginfo->folder->prefs->forward_body_format;
1786 } else if (account->forward_with_format) {
1787 qmark = account->forward_quotemark;
1788 body_fmt = account->forward_body_format;
1791 qmark = prefs_common.fw_quotemark;
1792 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1793 body_fmt = gettext(prefs_common.fw_quotefmt);
1798 /* empty quotemark is not allowed */
1799 if (qmark == NULL || *qmark == '\0')
1802 compose_quote_fmt(compose, full_msginfo,
1803 body_fmt, qmark, body, FALSE, TRUE,
1804 _("The body of the \"Forward\" template has an error at line %d."));
1805 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1806 quote_fmt_reset_vartable();
1807 compose_attach_parts(compose, msginfo);
1809 procmsg_msginfo_free(full_msginfo);
1811 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1812 gtkaspell_highlight_all(compose->gtkaspell);
1816 SIGNAL_BLOCK(textbuf);
1818 if (account->auto_sig)
1819 compose_insert_sig(compose, FALSE);
1821 compose_wrap_all(compose);
1823 SIGNAL_UNBLOCK(textbuf);
1825 gtk_text_buffer_get_start_iter(textbuf, &iter);
1826 gtk_text_buffer_place_cursor(textbuf, &iter);
1828 gtk_widget_grab_focus(compose->header_last->entry);
1830 if (!no_extedit && prefs_common.auto_exteditor)
1831 compose_exec_ext_editor(compose);
1834 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1835 gchar *folderidentifier;
1837 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1838 folderidentifier = folder_item_get_identifier(msginfo->folder);
1839 compose_set_save_to(compose, folderidentifier);
1840 g_free(folderidentifier);
1843 undo_unblock(compose->undostruct);
1845 compose->modified = FALSE;
1846 compose_set_title(compose);
1848 compose->updating = FALSE;
1849 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1850 SCROLL_TO_CURSOR(compose);
1852 if (compose->deferred_destroy) {
1853 compose_destroy(compose);
1857 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1862 #undef INSERT_FW_HEADER
1864 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1867 GtkTextView *textview;
1868 GtkTextBuffer *textbuf;
1872 gboolean single_mail = TRUE;
1874 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1876 if (g_slist_length(msginfo_list) > 1)
1877 single_mail = FALSE;
1879 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1880 if (((MsgInfo *)msginfo->data)->folder == NULL)
1883 /* guess account from first selected message */
1885 !(account = compose_guess_forward_account_from_msginfo
1886 (msginfo_list->data)))
1887 account = cur_account;
1889 cm_return_val_if_fail(account != NULL, NULL);
1891 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1892 if (msginfo->data) {
1893 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1894 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1898 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1899 g_warning("no msginfo_list");
1903 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1905 compose->updating = TRUE;
1907 /* override from name according to folder properties */
1908 if (msginfo_list->data) {
1909 MsgInfo *msginfo = msginfo_list->data;
1911 if (msginfo->folder && msginfo->folder->prefs &&
1912 msginfo->folder->prefs->forward_with_format &&
1913 msginfo->folder->prefs->forward_override_from_format &&
1914 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1919 /* decode \-escape sequences in the internal representation of the quote format */
1920 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1921 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1924 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1925 compose->gtkaspell);
1927 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1929 quote_fmt_scan_string(tmp);
1932 buf = quote_fmt_get_buffer();
1934 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1936 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1937 quote_fmt_reset_vartable();
1943 textview = GTK_TEXT_VIEW(compose->text);
1944 textbuf = gtk_text_view_get_buffer(textview);
1945 compose_create_tags(textview, compose);
1947 undo_block(compose->undostruct);
1948 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1949 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1951 if (!is_file_exist(msgfile))
1952 g_warning("%s: file not exist\n", msgfile);
1954 compose_attach_append(compose, msgfile, msgfile,
1960 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1961 if (info->subject && *info->subject) {
1962 gchar *buf, *buf2, *p;
1964 buf = p = g_strdup(info->subject);
1965 p += subject_get_prefix_length(p);
1966 memmove(buf, p, strlen(p) + 1);
1968 buf2 = g_strdup_printf("Fw: %s", buf);
1969 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1975 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1976 _("Fw: multiple emails"));
1979 SIGNAL_BLOCK(textbuf);
1981 if (account->auto_sig)
1982 compose_insert_sig(compose, FALSE);
1984 compose_wrap_all(compose);
1986 SIGNAL_UNBLOCK(textbuf);
1988 gtk_text_buffer_get_start_iter(textbuf, &iter);
1989 gtk_text_buffer_place_cursor(textbuf, &iter);
1991 gtk_widget_grab_focus(compose->header_last->entry);
1992 undo_unblock(compose->undostruct);
1993 compose->modified = FALSE;
1994 compose_set_title(compose);
1996 compose->updating = FALSE;
1997 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1998 SCROLL_TO_CURSOR(compose);
2000 if (compose->deferred_destroy) {
2001 compose_destroy(compose);
2005 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2010 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2012 GtkTextIter start = *iter;
2013 GtkTextIter end_iter;
2014 int start_pos = gtk_text_iter_get_offset(&start);
2016 if (!compose->account->sig_sep)
2019 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2020 start_pos+strlen(compose->account->sig_sep));
2022 /* check sig separator */
2023 str = gtk_text_iter_get_text(&start, &end_iter);
2024 if (!strcmp(str, compose->account->sig_sep)) {
2026 /* check end of line (\n) */
2027 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2028 start_pos+strlen(compose->account->sig_sep));
2029 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2030 start_pos+strlen(compose->account->sig_sep)+1);
2031 tmp = gtk_text_iter_get_text(&start, &end_iter);
2032 if (!strcmp(tmp,"\n")) {
2044 static void compose_colorize_signature(Compose *compose)
2046 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2048 GtkTextIter end_iter;
2049 gtk_text_buffer_get_start_iter(buffer, &iter);
2050 while (gtk_text_iter_forward_line(&iter))
2051 if (compose_is_sig_separator(compose, buffer, &iter)) {
2052 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2053 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2057 #define BLOCK_WRAP() { \
2058 prev_autowrap = compose->autowrap; \
2059 buffer = gtk_text_view_get_buffer( \
2060 GTK_TEXT_VIEW(compose->text)); \
2061 compose->autowrap = FALSE; \
2063 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2064 G_CALLBACK(compose_changed_cb), \
2066 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2067 G_CALLBACK(text_inserted), \
2070 #define UNBLOCK_WRAP() { \
2071 compose->autowrap = prev_autowrap; \
2072 if (compose->autowrap) { \
2073 gint old = compose->draft_timeout_tag; \
2074 compose->draft_timeout_tag = -2; \
2075 compose_wrap_all(compose); \
2076 compose->draft_timeout_tag = old; \
2079 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2080 G_CALLBACK(compose_changed_cb), \
2082 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2083 G_CALLBACK(text_inserted), \
2087 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2089 Compose *compose = NULL;
2090 PrefsAccount *account = NULL;
2091 GtkTextView *textview;
2092 GtkTextBuffer *textbuf;
2096 gchar buf[BUFFSIZE];
2097 gboolean use_signing = FALSE;
2098 gboolean use_encryption = FALSE;
2099 gchar *privacy_system = NULL;
2100 int priority = PRIORITY_NORMAL;
2101 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2102 gboolean autowrap = prefs_common.autowrap;
2103 gboolean autoindent = prefs_common.auto_indent;
2105 cm_return_val_if_fail(msginfo != NULL, NULL);
2106 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2108 if (compose_put_existing_to_front(msginfo)) {
2112 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2113 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2114 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2115 gchar queueheader_buf[BUFFSIZE];
2118 /* Select Account from queue headers */
2119 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2120 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2121 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2122 account = account_find_from_id(id);
2124 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2125 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2126 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2127 account = account_find_from_id(id);
2129 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2130 sizeof(queueheader_buf), "NAID:")) {
2131 id = atoi(&queueheader_buf[strlen("NAID:")]);
2132 account = account_find_from_id(id);
2134 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2135 sizeof(queueheader_buf), "MAID:")) {
2136 id = atoi(&queueheader_buf[strlen("MAID:")]);
2137 account = account_find_from_id(id);
2139 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2140 sizeof(queueheader_buf), "S:")) {
2141 account = account_find_from_address(queueheader_buf, FALSE);
2143 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2144 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2145 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2146 use_signing = param;
2149 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2150 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2151 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2152 use_signing = param;
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2156 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2157 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2158 use_encryption = param;
2160 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2161 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2162 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2163 use_encryption = param;
2165 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2166 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2167 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2170 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2171 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2172 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2175 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2176 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2177 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2179 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2180 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2181 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2183 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2184 sizeof(queueheader_buf), "X-Priority: ")) {
2185 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2188 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2189 sizeof(queueheader_buf), "RMID:")) {
2190 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2191 if (tokens[0] && tokens[1] && tokens[2]) {
2192 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2193 if (orig_item != NULL) {
2194 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2199 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2200 sizeof(queueheader_buf), "FMID:")) {
2201 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2202 if (tokens[0] && tokens[1] && tokens[2]) {
2203 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2204 if (orig_item != NULL) {
2205 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2211 account = msginfo->folder->folder->account;
2214 if (!account && prefs_common.reedit_account_autosel) {
2215 gchar from[BUFFSIZE];
2216 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2217 extract_address(from);
2218 account = account_find_from_address(from, FALSE);
2222 account = cur_account;
2224 cm_return_val_if_fail(account != NULL, NULL);
2226 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2228 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2229 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2230 compose->autowrap = autowrap;
2231 compose->replyinfo = replyinfo;
2232 compose->fwdinfo = fwdinfo;
2234 compose->updating = TRUE;
2235 compose->priority = priority;
2237 if (privacy_system != NULL) {
2238 compose->privacy_system = privacy_system;
2239 compose_use_signing(compose, use_signing);
2240 compose_use_encryption(compose, use_encryption);
2241 compose_update_privacy_system_menu_item(compose, FALSE);
2243 activate_privacy_system(compose, account, FALSE);
2246 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2248 compose_extract_original_charset(compose);
2250 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2251 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2252 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2253 gchar queueheader_buf[BUFFSIZE];
2255 /* Set message save folder */
2256 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2257 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2258 compose_set_save_to(compose, &queueheader_buf[4]);
2260 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2261 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2263 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2268 if (compose_parse_header(compose, msginfo) < 0) {
2269 compose->updating = FALSE;
2270 compose_destroy(compose);
2273 compose_reedit_set_entry(compose, msginfo);
2275 textview = GTK_TEXT_VIEW(compose->text);
2276 textbuf = gtk_text_view_get_buffer(textview);
2277 compose_create_tags(textview, compose);
2279 mark = gtk_text_buffer_get_insert(textbuf);
2280 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2282 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2283 G_CALLBACK(compose_changed_cb),
2286 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2287 fp = procmime_get_first_encrypted_text_content(msginfo);
2289 compose_force_encryption(compose, account, TRUE, NULL);
2292 fp = procmime_get_first_text_content(msginfo);
2295 g_warning("Can't get text part\n");
2299 gboolean prev_autowrap = compose->autowrap;
2300 GtkTextBuffer *buffer = textbuf;
2302 while (fgets(buf, sizeof(buf), fp) != NULL) {
2304 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2310 compose_attach_parts(compose, msginfo);
2312 compose_colorize_signature(compose);
2314 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2315 G_CALLBACK(compose_changed_cb),
2318 gtk_widget_grab_focus(compose->text);
2320 if (prefs_common.auto_exteditor) {
2321 compose_exec_ext_editor(compose);
2323 compose->modified = FALSE;
2324 compose_set_title(compose);
2326 compose->updating = FALSE;
2327 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2328 SCROLL_TO_CURSOR(compose);
2330 if (compose->deferred_destroy) {
2331 compose_destroy(compose);
2335 compose->sig_str = account_get_signature_str(compose->account);
2337 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2342 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2349 cm_return_val_if_fail(msginfo != NULL, NULL);
2352 account = account_get_reply_account(msginfo,
2353 prefs_common.reply_account_autosel);
2354 cm_return_val_if_fail(account != NULL, NULL);
2356 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2358 compose->updating = TRUE;
2360 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2361 compose->replyinfo = NULL;
2362 compose->fwdinfo = NULL;
2364 compose_show_first_last_header(compose, TRUE);
2366 gtk_widget_grab_focus(compose->header_last->entry);
2368 filename = procmsg_get_message_file(msginfo);
2370 if (filename == NULL) {
2371 compose->updating = FALSE;
2372 compose_destroy(compose);
2377 compose->redirect_filename = filename;
2379 /* Set save folder */
2380 item = msginfo->folder;
2381 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2382 gchar *folderidentifier;
2384 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2385 folderidentifier = folder_item_get_identifier(item);
2386 compose_set_save_to(compose, folderidentifier);
2387 g_free(folderidentifier);
2390 compose_attach_parts(compose, msginfo);
2392 if (msginfo->subject)
2393 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2395 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2397 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2398 _("The body of the \"Redirect\" template has an error at line %d."));
2399 quote_fmt_reset_vartable();
2400 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2402 compose_colorize_signature(compose);
2405 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2406 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2407 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2409 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2410 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2411 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2412 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2413 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2414 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2415 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2416 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2418 if (compose->toolbar->draft_btn)
2419 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2420 if (compose->toolbar->insert_btn)
2421 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2422 if (compose->toolbar->attach_btn)
2423 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2424 if (compose->toolbar->sig_btn)
2425 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2426 if (compose->toolbar->exteditor_btn)
2427 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2428 if (compose->toolbar->linewrap_current_btn)
2429 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2430 if (compose->toolbar->linewrap_all_btn)
2431 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2433 compose->modified = FALSE;
2434 compose_set_title(compose);
2435 compose->updating = FALSE;
2436 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2437 SCROLL_TO_CURSOR(compose);
2439 if (compose->deferred_destroy) {
2440 compose_destroy(compose);
2444 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2449 GList *compose_get_compose_list(void)
2451 return compose_list;
2454 void compose_entry_append(Compose *compose, const gchar *address,
2455 ComposeEntryType type, ComposePrefType pref_type)
2457 const gchar *header;
2459 gboolean in_quote = FALSE;
2460 if (!address || *address == '\0') return;
2467 header = N_("Bcc:");
2469 case COMPOSE_REPLYTO:
2470 header = N_("Reply-To:");
2472 case COMPOSE_NEWSGROUPS:
2473 header = N_("Newsgroups:");
2475 case COMPOSE_FOLLOWUPTO:
2476 header = N_( "Followup-To:");
2478 case COMPOSE_INREPLYTO:
2479 header = N_( "In-Reply-To:");
2486 header = prefs_common_translated_header_name(header);
2488 cur = begin = (gchar *)address;
2490 /* we separate the line by commas, but not if we're inside a quoted
2492 while (*cur != '\0') {
2494 in_quote = !in_quote;
2495 if (*cur == ',' && !in_quote) {
2496 gchar *tmp = g_strdup(begin);
2498 tmp[cur-begin]='\0';
2501 while (*tmp == ' ' || *tmp == '\t')
2503 compose_add_header_entry(compose, header, tmp, pref_type);
2510 gchar *tmp = g_strdup(begin);
2512 tmp[cur-begin]='\0';
2515 while (*tmp == ' ' || *tmp == '\t')
2517 compose_add_header_entry(compose, header, tmp, pref_type);
2522 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2524 static GdkColor yellow;
2525 static GdkColor black;
2526 static gboolean yellow_initialised = FALSE;
2530 if (!yellow_initialised) {
2531 gdk_color_parse("#f5f6be", &yellow);
2532 gdk_color_parse("#000000", &black);
2533 yellow_initialised = gdk_colormap_alloc_color(
2534 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2535 yellow_initialised &= gdk_colormap_alloc_color(
2536 gdk_colormap_get_system(), &black, FALSE, TRUE);
2539 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2540 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2541 if (gtk_entry_get_text(entry) &&
2542 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2543 if (yellow_initialised) {
2544 gtk_widget_modify_base(
2545 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2546 GTK_STATE_NORMAL, &yellow);
2547 gtk_widget_modify_text(
2548 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2549 GTK_STATE_NORMAL, &black);
2555 void compose_toolbar_cb(gint action, gpointer data)
2557 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2558 Compose *compose = (Compose*)toolbar_item->parent;
2560 cm_return_if_fail(compose != NULL);
2564 compose_send_cb(NULL, compose);
2567 compose_send_later_cb(NULL, compose);
2570 compose_draft(compose, COMPOSE_QUIT_EDITING);
2573 compose_insert_file_cb(NULL, compose);
2576 compose_attach_cb(NULL, compose);
2579 compose_insert_sig(compose, FALSE);
2582 compose_ext_editor_cb(NULL, compose);
2584 case A_LINEWRAP_CURRENT:
2585 compose_beautify_paragraph(compose, NULL, TRUE);
2587 case A_LINEWRAP_ALL:
2588 compose_wrap_all_full(compose, TRUE);
2591 compose_address_cb(NULL, compose);
2594 case A_CHECK_SPELLING:
2595 compose_check_all(NULL, compose);
2603 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2608 gchar *subject = NULL;
2612 gchar **attach = NULL;
2613 gchar *inreplyto = NULL;
2614 MailField mfield = NO_FIELD_PRESENT;
2616 /* get mailto parts but skip from */
2617 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2620 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2621 mfield = TO_FIELD_PRESENT;
2624 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2626 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2628 if (!g_utf8_validate (subject, -1, NULL)) {
2629 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2630 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2633 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2635 mfield = SUBJECT_FIELD_PRESENT;
2638 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2639 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2642 gboolean prev_autowrap = compose->autowrap;
2644 compose->autowrap = FALSE;
2646 mark = gtk_text_buffer_get_insert(buffer);
2647 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2649 if (!g_utf8_validate (body, -1, NULL)) {
2650 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2651 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2654 gtk_text_buffer_insert(buffer, &iter, body, -1);
2656 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2658 compose->autowrap = prev_autowrap;
2659 if (compose->autowrap)
2660 compose_wrap_all(compose);
2661 mfield = BODY_FIELD_PRESENT;
2665 gint i = 0, att = 0;
2666 gchar *warn_files = NULL;
2667 while (attach[i] != NULL) {
2668 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2669 if (utf8_filename) {
2670 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2671 gchar *tmp = g_strdup_printf("%s%s\n",
2672 warn_files?warn_files:"",
2678 g_free(utf8_filename);
2680 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2685 alertpanel_notice(ngettext(
2686 "The following file has been attached: \n%s",
2687 "The following files have been attached: \n%s", att), warn_files);
2692 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2705 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2707 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2708 {"Cc:", NULL, TRUE},
2709 {"References:", NULL, FALSE},
2710 {"Bcc:", NULL, TRUE},
2711 {"Newsgroups:", NULL, TRUE},
2712 {"Followup-To:", NULL, TRUE},
2713 {"List-Post:", NULL, FALSE},
2714 {"X-Priority:", NULL, FALSE},
2715 {NULL, NULL, FALSE}};
2731 cm_return_val_if_fail(msginfo != NULL, -1);
2733 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2734 procheader_get_header_fields(fp, hentry);
2737 if (hentry[H_REPLY_TO].body != NULL) {
2738 if (hentry[H_REPLY_TO].body[0] != '\0') {
2740 conv_unmime_header(hentry[H_REPLY_TO].body,
2743 g_free(hentry[H_REPLY_TO].body);
2744 hentry[H_REPLY_TO].body = NULL;
2746 if (hentry[H_CC].body != NULL) {
2747 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2748 g_free(hentry[H_CC].body);
2749 hentry[H_CC].body = NULL;
2751 if (hentry[H_REFERENCES].body != NULL) {
2752 if (compose->mode == COMPOSE_REEDIT)
2753 compose->references = hentry[H_REFERENCES].body;
2755 compose->references = compose_parse_references
2756 (hentry[H_REFERENCES].body, msginfo->msgid);
2757 g_free(hentry[H_REFERENCES].body);
2759 hentry[H_REFERENCES].body = NULL;
2761 if (hentry[H_BCC].body != NULL) {
2762 if (compose->mode == COMPOSE_REEDIT)
2764 conv_unmime_header(hentry[H_BCC].body, NULL);
2765 g_free(hentry[H_BCC].body);
2766 hentry[H_BCC].body = NULL;
2768 if (hentry[H_NEWSGROUPS].body != NULL) {
2769 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2770 hentry[H_NEWSGROUPS].body = NULL;
2772 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2773 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2774 compose->followup_to =
2775 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2778 g_free(hentry[H_FOLLOWUP_TO].body);
2779 hentry[H_FOLLOWUP_TO].body = NULL;
2781 if (hentry[H_LIST_POST].body != NULL) {
2782 gchar *to = NULL, *start = NULL;
2784 extract_address(hentry[H_LIST_POST].body);
2785 if (hentry[H_LIST_POST].body[0] != '\0') {
2786 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2788 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2789 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2792 g_free(compose->ml_post);
2793 compose->ml_post = to;
2796 g_free(hentry[H_LIST_POST].body);
2797 hentry[H_LIST_POST].body = NULL;
2800 /* CLAWS - X-Priority */
2801 if (compose->mode == COMPOSE_REEDIT)
2802 if (hentry[H_X_PRIORITY].body != NULL) {
2805 priority = atoi(hentry[H_X_PRIORITY].body);
2806 g_free(hentry[H_X_PRIORITY].body);
2808 hentry[H_X_PRIORITY].body = NULL;
2810 if (priority < PRIORITY_HIGHEST ||
2811 priority > PRIORITY_LOWEST)
2812 priority = PRIORITY_NORMAL;
2814 compose->priority = priority;
2817 if (compose->mode == COMPOSE_REEDIT) {
2818 if (msginfo->inreplyto && *msginfo->inreplyto)
2819 compose->inreplyto = g_strdup(msginfo->inreplyto);
2823 if (msginfo->msgid && *msginfo->msgid)
2824 compose->inreplyto = g_strdup(msginfo->msgid);
2826 if (!compose->references) {
2827 if (msginfo->msgid && *msginfo->msgid) {
2828 if (msginfo->inreplyto && *msginfo->inreplyto)
2829 compose->references =
2830 g_strdup_printf("<%s>\n\t<%s>",
2834 compose->references =
2835 g_strconcat("<", msginfo->msgid, ">",
2837 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2838 compose->references =
2839 g_strconcat("<", msginfo->inreplyto, ">",
2847 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2849 GSList *ref_id_list, *cur;
2853 ref_id_list = references_list_append(NULL, ref);
2854 if (!ref_id_list) return NULL;
2855 if (msgid && *msgid)
2856 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2861 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2862 /* "<" + Message-ID + ">" + CR+LF+TAB */
2863 len += strlen((gchar *)cur->data) + 5;
2865 if (len > MAX_REFERENCES_LEN) {
2866 /* remove second message-ID */
2867 if (ref_id_list && ref_id_list->next &&
2868 ref_id_list->next->next) {
2869 g_free(ref_id_list->next->data);
2870 ref_id_list = g_slist_remove
2871 (ref_id_list, ref_id_list->next->data);
2873 slist_free_strings(ref_id_list);
2874 g_slist_free(ref_id_list);
2881 new_ref = g_string_new("");
2882 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2883 if (new_ref->len > 0)
2884 g_string_append(new_ref, "\n\t");
2885 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2888 slist_free_strings(ref_id_list);
2889 g_slist_free(ref_id_list);
2891 new_ref_str = new_ref->str;
2892 g_string_free(new_ref, FALSE);
2897 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2898 const gchar *fmt, const gchar *qmark,
2899 const gchar *body, gboolean rewrap,
2900 gboolean need_unescape,
2901 const gchar *err_msg)
2903 MsgInfo* dummyinfo = NULL;
2904 gchar *quote_str = NULL;
2906 gboolean prev_autowrap;
2907 const gchar *trimmed_body = body;
2908 gint cursor_pos = -1;
2909 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2910 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2915 SIGNAL_BLOCK(buffer);
2918 dummyinfo = compose_msginfo_new_from_compose(compose);
2919 msginfo = dummyinfo;
2922 if (qmark != NULL) {
2924 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2925 compose->gtkaspell);
2927 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2929 quote_fmt_scan_string(qmark);
2932 buf = quote_fmt_get_buffer();
2934 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2936 Xstrdup_a(quote_str, buf, goto error)
2939 if (fmt && *fmt != '\0') {
2942 while (*trimmed_body == '\n')
2946 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2947 compose->gtkaspell);
2949 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2951 if (need_unescape) {
2954 /* decode \-escape sequences in the internal representation of the quote format */
2955 tmp = malloc(strlen(fmt)+1);
2956 pref_get_unescaped_pref(tmp, fmt);
2957 quote_fmt_scan_string(tmp);
2961 quote_fmt_scan_string(fmt);
2965 buf = quote_fmt_get_buffer();
2967 gint line = quote_fmt_get_line();
2968 alertpanel_error(err_msg, line);
2974 prev_autowrap = compose->autowrap;
2975 compose->autowrap = FALSE;
2977 mark = gtk_text_buffer_get_insert(buffer);
2978 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2979 if (g_utf8_validate(buf, -1, NULL)) {
2980 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2982 gchar *tmpout = NULL;
2983 tmpout = conv_codeset_strdup
2984 (buf, conv_get_locale_charset_str_no_utf8(),
2986 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2988 tmpout = g_malloc(strlen(buf)*2+1);
2989 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2991 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2995 cursor_pos = quote_fmt_get_cursor_pos();
2996 if (cursor_pos == -1)
2997 cursor_pos = gtk_text_iter_get_offset(&iter);
2998 compose->set_cursor_pos = cursor_pos;
3000 gtk_text_buffer_get_start_iter(buffer, &iter);
3001 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3003 /* Some quotes may not end with new line - when replying with
3004 * quote from GtkTextBuffer selection for example. Add new
3005 * line character so cursor is at the beginning of the line */
3006 if (!gtk_text_iter_starts_line(&iter)) {
3007 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
3008 gtk_text_buffer_get_end_iter(buffer, &iter);
3009 compose->set_cursor_pos = gtk_text_iter_get_offset(&iter);
3011 gtk_text_buffer_place_cursor(buffer, &iter);
3013 compose->autowrap = prev_autowrap;
3014 if (compose->autowrap && rewrap)
3015 compose_wrap_all(compose);
3022 SIGNAL_UNBLOCK(buffer);
3024 procmsg_msginfo_free( dummyinfo );
3029 /* if ml_post is of type addr@host and from is of type
3030 * addr-anything@host, return TRUE
3032 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3034 gchar *left_ml = NULL;
3035 gchar *right_ml = NULL;
3036 gchar *left_from = NULL;
3037 gchar *right_from = NULL;
3038 gboolean result = FALSE;
3040 if (!ml_post || !from)
3043 left_ml = g_strdup(ml_post);
3044 if (strstr(left_ml, "@")) {
3045 right_ml = strstr(left_ml, "@")+1;
3046 *(strstr(left_ml, "@")) = '\0';
3049 left_from = g_strdup(from);
3050 if (strstr(left_from, "@")) {
3051 right_from = strstr(left_from, "@")+1;
3052 *(strstr(left_from, "@")) = '\0';
3055 if (left_ml && left_from && right_ml && right_from
3056 && !strncmp(left_from, left_ml, strlen(left_ml))
3057 && !strcmp(right_from, right_ml)) {
3066 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3067 gboolean respect_default_to)
3071 if (!folder || !folder->prefs)
3074 if (respect_default_to && folder->prefs->enable_default_to) {
3075 compose_entry_append(compose, folder->prefs->default_to,
3076 COMPOSE_TO, PREF_FOLDER);
3077 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3079 if (folder->prefs->enable_default_cc)
3080 compose_entry_append(compose, folder->prefs->default_cc,
3081 COMPOSE_CC, PREF_FOLDER);
3082 if (folder->prefs->enable_default_bcc)
3083 compose_entry_append(compose, folder->prefs->default_bcc,
3084 COMPOSE_BCC, PREF_FOLDER);
3085 if (folder->prefs->enable_default_replyto)
3086 compose_entry_append(compose, folder->prefs->default_replyto,
3087 COMPOSE_REPLYTO, PREF_FOLDER);
3090 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3095 if (!compose || !msginfo)
3098 if (msginfo->subject && *msginfo->subject) {
3099 buf = p = g_strdup(msginfo->subject);
3100 p += subject_get_prefix_length(p);
3101 memmove(buf, p, strlen(p) + 1);
3103 buf2 = g_strdup_printf("Re: %s", buf);
3104 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3109 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3112 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3113 gboolean to_all, gboolean to_ml,
3115 gboolean followup_and_reply_to)
3117 GSList *cc_list = NULL;
3120 gchar *replyto = NULL;
3121 gchar *ac_email = NULL;
3123 gboolean reply_to_ml = FALSE;
3124 gboolean default_reply_to = FALSE;
3126 cm_return_if_fail(compose->account != NULL);
3127 cm_return_if_fail(msginfo != NULL);
3129 reply_to_ml = to_ml && compose->ml_post;
3131 default_reply_to = msginfo->folder &&
3132 msginfo->folder->prefs->enable_default_reply_to;
3134 if (compose->account->protocol != A_NNTP) {
3135 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3137 if (reply_to_ml && !default_reply_to) {
3139 gboolean is_subscr = is_subscription(compose->ml_post,
3142 /* normal answer to ml post with a reply-to */
3143 compose_entry_append(compose,
3145 COMPOSE_TO, PREF_ML);
3146 if (compose->replyto)
3147 compose_entry_append(compose,
3149 COMPOSE_CC, PREF_ML);
3151 /* answer to subscription confirmation */
3152 if (compose->replyto)
3153 compose_entry_append(compose,
3155 COMPOSE_TO, PREF_ML);
3156 else if (msginfo->from)
3157 compose_entry_append(compose,
3159 COMPOSE_TO, PREF_ML);
3162 else if (!(to_all || to_sender) && default_reply_to) {
3163 compose_entry_append(compose,
3164 msginfo->folder->prefs->default_reply_to,
3165 COMPOSE_TO, PREF_FOLDER);
3166 compose_entry_mark_default_to(compose,
3167 msginfo->folder->prefs->default_reply_to);
3172 Xstrdup_a(tmp1, msginfo->from, return);
3173 extract_address(tmp1);
3174 if (to_all || to_sender ||
3175 !account_find_from_address(tmp1, FALSE))
3176 compose_entry_append(compose,
3177 (compose->replyto && !to_sender)
3178 ? compose->replyto :
3179 msginfo->from ? msginfo->from : "",
3180 COMPOSE_TO, PREF_NONE);
3181 else if (!to_all && !to_sender) {
3182 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3183 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3184 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3185 if (compose->replyto) {
3186 compose_entry_append(compose,
3188 COMPOSE_TO, PREF_NONE);
3190 compose_entry_append(compose,
3191 msginfo->from ? msginfo->from : "",
3192 COMPOSE_TO, PREF_NONE);
3195 /* replying to own mail, use original recp */
3196 compose_entry_append(compose,
3197 msginfo->to ? msginfo->to : "",
3198 COMPOSE_TO, PREF_NONE);
3199 compose_entry_append(compose,
3200 msginfo->cc ? msginfo->cc : "",
3201 COMPOSE_CC, PREF_NONE);
3206 if (to_sender || (compose->followup_to &&
3207 !strncmp(compose->followup_to, "poster", 6)))
3208 compose_entry_append
3210 (compose->replyto ? compose->replyto :
3211 msginfo->from ? msginfo->from : ""),
3212 COMPOSE_TO, PREF_NONE);
3214 else if (followup_and_reply_to || to_all) {
3215 compose_entry_append
3217 (compose->replyto ? compose->replyto :
3218 msginfo->from ? msginfo->from : ""),
3219 COMPOSE_TO, PREF_NONE);
3221 compose_entry_append
3223 compose->followup_to ? compose->followup_to :
3224 compose->newsgroups ? compose->newsgroups : "",
3225 COMPOSE_NEWSGROUPS, PREF_NONE);
3228 compose_entry_append
3230 compose->followup_to ? compose->followup_to :
3231 compose->newsgroups ? compose->newsgroups : "",
3232 COMPOSE_NEWSGROUPS, PREF_NONE);
3234 compose_reply_set_subject(compose, msginfo);
3236 if (to_ml && compose->ml_post) return;
3237 if (!to_all || compose->account->protocol == A_NNTP) return;
3239 if (compose->replyto) {
3240 Xstrdup_a(replyto, compose->replyto, return);
3241 extract_address(replyto);
3243 if (msginfo->from) {
3244 Xstrdup_a(from, msginfo->from, return);
3245 extract_address(from);
3248 if (replyto && from)
3249 cc_list = address_list_append_with_comments(cc_list, from);
3250 if (to_all && msginfo->folder &&
3251 msginfo->folder->prefs->enable_default_reply_to)
3252 cc_list = address_list_append_with_comments(cc_list,
3253 msginfo->folder->prefs->default_reply_to);
3254 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3255 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3257 ac_email = g_utf8_strdown(compose->account->address, -1);
3260 for (cur = cc_list; cur != NULL; cur = cur->next) {
3261 gchar *addr = g_utf8_strdown(cur->data, -1);
3262 extract_address(addr);
3264 if (strcmp(ac_email, addr))
3265 compose_entry_append(compose, (gchar *)cur->data,
3266 COMPOSE_CC, PREF_NONE);
3268 debug_print("Cc address same as compose account's, ignoring\n");
3273 slist_free_strings(cc_list);
3274 g_slist_free(cc_list);
3280 #define SET_ENTRY(entry, str) \
3283 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3286 #define SET_ADDRESS(type, str) \
3289 compose_entry_append(compose, str, type, PREF_NONE); \
3292 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3294 cm_return_if_fail(msginfo != NULL);
3296 SET_ENTRY(subject_entry, msginfo->subject);
3297 SET_ENTRY(from_name, msginfo->from);
3298 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3299 SET_ADDRESS(COMPOSE_CC, compose->cc);
3300 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3301 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3302 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3303 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3305 compose_update_priority_menu_item(compose);
3306 compose_update_privacy_system_menu_item(compose, FALSE);
3307 compose_show_first_last_header(compose, TRUE);
3313 static void compose_insert_sig(Compose *compose, gboolean replace)
3315 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3316 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3318 GtkTextIter iter, iter_end;
3319 gint cur_pos, ins_pos;
3320 gboolean prev_autowrap;
3321 gboolean found = FALSE;
3322 gboolean exists = FALSE;
3324 cm_return_if_fail(compose->account != NULL);
3328 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3329 G_CALLBACK(compose_changed_cb),
3332 mark = gtk_text_buffer_get_insert(buffer);
3333 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3334 cur_pos = gtk_text_iter_get_offset (&iter);
3337 gtk_text_buffer_get_end_iter(buffer, &iter);
3339 exists = (compose->sig_str != NULL);
3342 GtkTextIter first_iter, start_iter, end_iter;
3344 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3346 if (!exists || compose->sig_str[0] == '\0')
3349 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3350 compose->signature_tag);
3353 /* include previous \n\n */
3354 gtk_text_iter_backward_chars(&first_iter, 1);
3355 start_iter = first_iter;
3356 end_iter = first_iter;
3358 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3359 compose->signature_tag);
3360 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3361 compose->signature_tag);
3363 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3369 g_free(compose->sig_str);
3370 compose->sig_str = account_get_signature_str(compose->account);
3372 cur_pos = gtk_text_iter_get_offset(&iter);
3374 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3375 g_free(compose->sig_str);
3376 compose->sig_str = NULL;
3378 if (compose->sig_inserted == FALSE)
3379 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3380 compose->sig_inserted = TRUE;
3382 cur_pos = gtk_text_iter_get_offset(&iter);
3383 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3385 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3386 gtk_text_iter_forward_chars(&iter, 1);
3387 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3388 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3390 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3391 cur_pos = gtk_text_buffer_get_char_count (buffer);
3394 /* put the cursor where it should be
3395 * either where the quote_fmt says, either where it was */
3396 if (compose->set_cursor_pos < 0)
3397 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3399 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3400 compose->set_cursor_pos);
3402 compose->set_cursor_pos = -1;
3403 gtk_text_buffer_place_cursor(buffer, &iter);
3404 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3405 G_CALLBACK(compose_changed_cb),
3411 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3414 GtkTextBuffer *buffer;
3417 const gchar *cur_encoding;
3418 gchar buf[BUFFSIZE];
3421 gboolean prev_autowrap;
3422 gboolean badtxt = FALSE;
3423 struct stat file_stat;
3426 cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3428 /* get the size of the file we are about to insert */
3429 ret = g_stat(file, &file_stat);
3431 gchar *shortfile = g_path_get_basename(file);
3432 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3434 return COMPOSE_INSERT_NO_FILE;
3435 } else if (prefs_common.warn_large_insert == TRUE) {
3437 /* ask user for confirmation if the file is large */
3438 if (prefs_common.warn_large_insert_size < 0 ||
3439 file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3443 msg = g_strdup_printf(_("You are about to insert a file of %s "
3444 "in the message body. Are you sure you want to do that?"),
3445 to_human_readable(file_stat.st_size));
3446 aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3447 _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3450 /* do we ask for confirmation next time? */
3451 if (aval & G_ALERTDISABLE) {
3452 /* no confirmation next time, disable feature in preferences */
3453 aval &= ~G_ALERTDISABLE;
3454 prefs_common.warn_large_insert = FALSE;
3457 /* abort file insertion if user canceled action */
3458 if (aval != G_ALERTALTERNATE) {
3459 return COMPOSE_INSERT_NO_FILE;
3465 if ((fp = g_fopen(file, "rb")) == NULL) {
3466 FILE_OP_ERROR(file, "fopen");
3467 return COMPOSE_INSERT_READ_ERROR;
3470 prev_autowrap = compose->autowrap;
3471 compose->autowrap = FALSE;
3473 text = GTK_TEXT_VIEW(compose->text);
3474 buffer = gtk_text_view_get_buffer(text);
3475 mark = gtk_text_buffer_get_insert(buffer);
3476 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3478 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3479 G_CALLBACK(text_inserted),
3482 cur_encoding = conv_get_locale_charset_str_no_utf8();
3484 while (fgets(buf, sizeof(buf), fp) != NULL) {
3487 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3488 str = g_strdup(buf);
3490 str = conv_codeset_strdup
3491 (buf, cur_encoding, CS_INTERNAL);
3494 /* strip <CR> if DOS/Windows file,
3495 replace <CR> with <LF> if Macintosh file. */
3498 if (len > 0 && str[len - 1] != '\n') {
3500 if (str[len] == '\r') str[len] = '\n';
3503 gtk_text_buffer_insert(buffer, &iter, str, -1);
3507 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3508 G_CALLBACK(text_inserted),
3510 compose->autowrap = prev_autowrap;
3511 if (compose->autowrap)
3512 compose_wrap_all(compose);
3517 return COMPOSE_INSERT_INVALID_CHARACTER;
3519 return COMPOSE_INSERT_SUCCESS;
3522 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3523 const gchar *filename,
3524 const gchar *content_type)
3532 GtkListStore *store;
3534 gboolean has_binary = FALSE;
3536 if (!is_file_exist(file)) {
3537 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3538 gboolean result = FALSE;
3539 if (file_from_uri && is_file_exist(file_from_uri)) {
3540 result = compose_attach_append(
3541 compose, file_from_uri,
3545 g_free(file_from_uri);
3548 alertpanel_error("File %s doesn't exist\n", filename);
3551 if ((size = get_file_size(file)) < 0) {
3552 alertpanel_error("Can't get file size of %s\n", filename);
3556 alertpanel_error(_("File %s is empty."), filename);
3559 if ((fp = g_fopen(file, "rb")) == NULL) {
3560 alertpanel_error(_("Can't read %s."), filename);
3565 ainfo = g_new0(AttachInfo, 1);
3566 auto_ainfo = g_auto_pointer_new_with_free
3567 (ainfo, (GFreeFunc) compose_attach_info_free);
3568 ainfo->file = g_strdup(file);
3571 ainfo->content_type = g_strdup(content_type);
3572 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3574 MsgFlags flags = {0, 0};
3576 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3577 ainfo->encoding = ENC_7BIT;
3579 ainfo->encoding = ENC_8BIT;
3581 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3582 if (msginfo && msginfo->subject)
3583 name = g_strdup(msginfo->subject);
3585 name = g_path_get_basename(filename ? filename : file);
3587 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3589 procmsg_msginfo_free(msginfo);
3591 if (!g_ascii_strncasecmp(content_type, "text", 4))
3592 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3594 ainfo->encoding = ENC_BASE64;
3595 name = g_path_get_basename(filename ? filename : file);
3596 ainfo->name = g_strdup(name);
3600 ainfo->content_type = procmime_get_mime_type(file);
3601 if (!ainfo->content_type) {
3602 ainfo->content_type =
3603 g_strdup("application/octet-stream");
3604 ainfo->encoding = ENC_BASE64;
3605 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3607 procmime_get_encoding_for_text_file(file, &has_binary);
3609 ainfo->encoding = ENC_BASE64;
3610 name = g_path_get_basename(filename ? filename : file);
3611 ainfo->name = g_strdup(name);
3615 if (ainfo->name != NULL
3616 && !strcmp(ainfo->name, ".")) {
3617 g_free(ainfo->name);
3621 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3622 g_free(ainfo->content_type);
3623 ainfo->content_type = g_strdup("application/octet-stream");
3626 ainfo->size = (goffset)size;
3627 size_text = to_human_readable((goffset)size);
3629 store = GTK_LIST_STORE(gtk_tree_view_get_model
3630 (GTK_TREE_VIEW(compose->attach_clist)));
3632 gtk_list_store_append(store, &iter);
3633 gtk_list_store_set(store, &iter,
3634 COL_MIMETYPE, ainfo->content_type,
3635 COL_SIZE, size_text,
3636 COL_NAME, ainfo->name,
3638 COL_AUTODATA, auto_ainfo,
3641 g_auto_pointer_free(auto_ainfo);
3642 compose_attach_update_label(compose);
3646 static void compose_use_signing(Compose *compose, gboolean use_signing)
3648 compose->use_signing = use_signing;
3649 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3652 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3654 compose->use_encryption = use_encryption;
3655 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3658 #define NEXT_PART_NOT_CHILD(info) \
3660 node = info->node; \
3661 while (node->children) \
3662 node = g_node_last_child(node); \
3663 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3666 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3670 MimeInfo *firsttext = NULL;
3671 MimeInfo *encrypted = NULL;
3674 const gchar *partname = NULL;
3676 mimeinfo = procmime_scan_message(msginfo);
3677 if (!mimeinfo) return;
3679 if (mimeinfo->node->children == NULL) {
3680 procmime_mimeinfo_free_all(mimeinfo);
3684 /* find first content part */
3685 child = (MimeInfo *) mimeinfo->node->children->data;
3686 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3687 child = (MimeInfo *)child->node->children->data;
3690 if (child->type == MIMETYPE_TEXT) {
3692 debug_print("First text part found\n");
3693 } else if (compose->mode == COMPOSE_REEDIT &&
3694 child->type == MIMETYPE_APPLICATION &&
3695 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3696 encrypted = (MimeInfo *)child->node->parent->data;
3699 child = (MimeInfo *) mimeinfo->node->children->data;
3700 while (child != NULL) {
3703 if (child == encrypted) {
3704 /* skip this part of tree */
3705 NEXT_PART_NOT_CHILD(child);
3709 if (child->type == MIMETYPE_MULTIPART) {
3710 /* get the actual content */
3711 child = procmime_mimeinfo_next(child);
3715 if (child == firsttext) {
3716 child = procmime_mimeinfo_next(child);
3720 outfile = procmime_get_tmp_file_name(child);
3721 if ((err = procmime_get_part(outfile, child)) < 0)
3722 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3724 gchar *content_type;
3726 content_type = procmime_get_content_type_str(child->type, child->subtype);
3728 /* if we meet a pgp signature, we don't attach it, but
3729 * we force signing. */
3730 if ((strcmp(content_type, "application/pgp-signature") &&
3731 strcmp(content_type, "application/pkcs7-signature") &&
3732 strcmp(content_type, "application/x-pkcs7-signature"))
3733 || compose->mode == COMPOSE_REDIRECT) {
3734 partname = procmime_mimeinfo_get_parameter(child, "filename");
3735 if (partname == NULL)
3736 partname = procmime_mimeinfo_get_parameter(child, "name");
3737 if (partname == NULL)
3739 compose_attach_append(compose, outfile,
3740 partname, content_type);
3742 compose_force_signing(compose, compose->account, NULL);
3744 g_free(content_type);
3747 NEXT_PART_NOT_CHILD(child);
3749 procmime_mimeinfo_free_all(mimeinfo);
3752 #undef NEXT_PART_NOT_CHILD
3757 WAIT_FOR_INDENT_CHAR,
3758 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3761 /* return indent length, we allow:
3762 indent characters followed by indent characters or spaces/tabs,
3763 alphabets and numbers immediately followed by indent characters,
3764 and the repeating sequences of the above
3765 If quote ends with multiple spaces, only the first one is included. */
3766 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3767 const GtkTextIter *start, gint *len)
3769 GtkTextIter iter = *start;
3773 IndentState state = WAIT_FOR_INDENT_CHAR;
3776 gint alnum_count = 0;
3777 gint space_count = 0;
3780 if (prefs_common.quote_chars == NULL) {
3784 while (!gtk_text_iter_ends_line(&iter)) {
3785 wc = gtk_text_iter_get_char(&iter);
3786 if (g_unichar_iswide(wc))
3788 clen = g_unichar_to_utf8(wc, ch);
3792 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3793 is_space = g_unichar_isspace(wc);
3795 if (state == WAIT_FOR_INDENT_CHAR) {
3796 if (!is_indent && !g_unichar_isalnum(wc))
3799 quote_len += alnum_count + space_count + 1;
3800 alnum_count = space_count = 0;
3801 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3804 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3805 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3809 else if (is_indent) {
3810 quote_len += alnum_count + space_count + 1;
3811 alnum_count = space_count = 0;
3814 state = WAIT_FOR_INDENT_CHAR;
3818 gtk_text_iter_forward_char(&iter);
3821 if (quote_len > 0 && space_count > 0)
3827 if (quote_len > 0) {
3829 gtk_text_iter_forward_chars(&iter, quote_len);
3830 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3836 /* return >0 if the line is itemized */
3837 static int compose_itemized_length(GtkTextBuffer *buffer,
3838 const GtkTextIter *start)
3840 GtkTextIter iter = *start;
3845 if (gtk_text_iter_ends_line(&iter))
3850 wc = gtk_text_iter_get_char(&iter);
3851 if (!g_unichar_isspace(wc))
3853 gtk_text_iter_forward_char(&iter);
3854 if (gtk_text_iter_ends_line(&iter))
3858 clen = g_unichar_to_utf8(wc, ch);
3862 if (!strchr("*-+", ch[0]))
3865 gtk_text_iter_forward_char(&iter);
3866 if (gtk_text_iter_ends_line(&iter))
3868 wc = gtk_text_iter_get_char(&iter);
3869 if (g_unichar_isspace(wc)) {
3875 /* return the string at the start of the itemization */
3876 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3877 const GtkTextIter *start)
3879 GtkTextIter iter = *start;
3882 GString *item_chars = g_string_new("");
3885 if (gtk_text_iter_ends_line(&iter))
3890 wc = gtk_text_iter_get_char(&iter);
3891 if (!g_unichar_isspace(wc))
3893 gtk_text_iter_forward_char(&iter);
3894 if (gtk_text_iter_ends_line(&iter))
3896 g_string_append_unichar(item_chars, wc);
3899 str = item_chars->str;
3900 g_string_free(item_chars, FALSE);
3904 /* return the number of spaces at a line's start */
3905 static int compose_left_offset_length(GtkTextBuffer *buffer,
3906 const GtkTextIter *start)
3908 GtkTextIter iter = *start;
3911 if (gtk_text_iter_ends_line(&iter))
3915 wc = gtk_text_iter_get_char(&iter);
3916 if (!g_unichar_isspace(wc))
3919 gtk_text_iter_forward_char(&iter);
3920 if (gtk_text_iter_ends_line(&iter))
3924 gtk_text_iter_forward_char(&iter);
3925 if (gtk_text_iter_ends_line(&iter))
3930 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3931 const GtkTextIter *start,
3932 GtkTextIter *break_pos,
3936 GtkTextIter iter = *start, line_end = *start;
3937 PangoLogAttr *attrs;
3944 gboolean can_break = FALSE;
3945 gboolean do_break = FALSE;
3946 gboolean was_white = FALSE;
3947 gboolean prev_dont_break = FALSE;
3949 gtk_text_iter_forward_to_line_end(&line_end);
3950 str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3951 len = g_utf8_strlen(str, -1);
3955 g_warning("compose_get_line_break_pos: len = 0!\n");
3959 /* g_print("breaking line: %d: %s (len = %d)\n",
3960 gtk_text_iter_get_line(&iter), str, len); */
3962 attrs = g_new(PangoLogAttr, len + 1);
3964 pango_default_break(str, -1, NULL, attrs, len + 1);
3968 /* skip quote and leading spaces */
3969 for (i = 0; *p != '\0' && i < len; i++) {
3972 wc = g_utf8_get_char(p);
3973 if (i >= quote_len && !g_unichar_isspace(wc))
3975 if (g_unichar_iswide(wc))
3977 else if (*p == '\t')
3981 p = g_utf8_next_char(p);
3984 for (; *p != '\0' && i < len; i++) {
3985 PangoLogAttr *attr = attrs + i;
3989 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3992 was_white = attr->is_white;
3994 /* don't wrap URI */
3995 if ((uri_len = get_uri_len(p)) > 0) {
3997 if (pos > 0 && col > max_col) {
4007 wc = g_utf8_get_char(p);
4008 if (g_unichar_iswide(wc)) {
4010 if (prev_dont_break && can_break && attr->is_line_break)
4012 } else if (*p == '\t')
4016 if (pos > 0 && col > max_col) {
4021 if (*p == '-' || *p == '/')
4022 prev_dont_break = TRUE;
4024 prev_dont_break = FALSE;
4026 p = g_utf8_next_char(p);
4030 // debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
4035 *break_pos = *start;
4036 gtk_text_iter_set_line_offset(break_pos, pos);
4041 static gboolean compose_join_next_line(Compose *compose,
4042 GtkTextBuffer *buffer,
4044 const gchar *quote_str)
4046 GtkTextIter iter_ = *iter, cur, prev, next, end;
4047 PangoLogAttr attrs[3];
4049 gchar *next_quote_str;
4052 gboolean keep_cursor = FALSE;
4054 if (!gtk_text_iter_forward_line(&iter_) ||
4055 gtk_text_iter_ends_line(&iter_)) {
4058 next_quote_str = compose_get_quote_str(buffer, &iter_, "e_len);
4060 if ((quote_str || next_quote_str) &&
4061 strcmp2(quote_str, next_quote_str) != 0) {
4062 g_free(next_quote_str);
4065 g_free(next_quote_str);
4068 if (quote_len > 0) {
4069 gtk_text_iter_forward_chars(&end, quote_len);
4070 if (gtk_text_iter_ends_line(&end)) {
4075 /* don't join itemized lines */
4076 if (compose_itemized_length(buffer, &end) > 0) {
4080 /* don't join signature separator */
4081 if (compose_is_sig_separator(compose, buffer, &iter_)) {
4084 /* delete quote str */
4086 gtk_text_buffer_delete(buffer, &iter_, &end);
4088 /* don't join line breaks put by the user */
4090 gtk_text_iter_backward_char(&cur);
4091 if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4092 gtk_text_iter_forward_char(&cur);
4096 gtk_text_iter_forward_char(&cur);
4097 /* delete linebreak and extra spaces */
4098 while (gtk_text_iter_backward_char(&cur)) {
4099 wc1 = gtk_text_iter_get_char(&cur);
4100 if (!g_unichar_isspace(wc1))
4105 while (!gtk_text_iter_ends_line(&cur)) {
4106 wc1 = gtk_text_iter_get_char(&cur);
4107 if (!g_unichar_isspace(wc1))
4109 gtk_text_iter_forward_char(&cur);
4112 if (!gtk_text_iter_equal(&prev, &next)) {
4115 mark = gtk_text_buffer_get_insert(buffer);
4116 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4117 if (gtk_text_iter_equal(&prev, &cur))
4119 gtk_text_buffer_delete(buffer, &prev, &next);
4123 /* insert space if required */
4124 gtk_text_iter_backward_char(&prev);
4125 wc1 = gtk_text_iter_get_char(&prev);
4126 wc2 = gtk_text_iter_get_char(&next);
4127 gtk_text_iter_forward_char(&next);
4128 str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4129 pango_default_break(str, -1, NULL, attrs, 3);
4130 if (!attrs[1].is_line_break ||
4131 (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4132 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4134 gtk_text_iter_backward_char(&iter_);
4135 gtk_text_buffer_place_cursor(buffer, &iter_);
4144 #define ADD_TXT_POS(bp_, ep_, pti_) \
4145 if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4146 last = last->next; \
4147 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4148 last->next = NULL; \
4150 g_warning("alloc error scanning URIs\n"); \
4153 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4155 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4156 GtkTextBuffer *buffer;
4157 GtkTextIter iter, break_pos, end_of_line;
4158 gchar *quote_str = NULL;
4160 gboolean wrap_quote = prefs_common.linewrap_quote;
4161 gboolean prev_autowrap = compose->autowrap;
4162 gint startq_offset = -1, noq_offset = -1;
4163 gint uri_start = -1, uri_stop = -1;
4164 gint nouri_start = -1, nouri_stop = -1;
4165 gint num_blocks = 0;
4166 gint quotelevel = -1;
4167 gboolean modified = force;
4168 gboolean removed = FALSE;
4169 gboolean modified_before_remove = FALSE;
4171 gboolean start = TRUE;
4172 gint itemized_len = 0, rem_item_len = 0;
4173 gchar *itemized_chars = NULL;
4174 gboolean item_continuation = FALSE;
4179 if (compose->draft_timeout_tag == -2) {
4183 compose->autowrap = FALSE;
4185 buffer = gtk_text_view_get_buffer(text);
4186 undo_wrapping(compose->undostruct, TRUE);
4191 mark = gtk_text_buffer_get_insert(buffer);
4192 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4196 if (compose->draft_timeout_tag == -2) {
4197 if (gtk_text_iter_ends_line(&iter)) {
4198 while (gtk_text_iter_ends_line(&iter) &&
4199 gtk_text_iter_forward_line(&iter))
4202 while (gtk_text_iter_backward_line(&iter)) {
4203 if (gtk_text_iter_ends_line(&iter)) {
4204 gtk_text_iter_forward_line(&iter);
4210 /* move to line start */
4211 gtk_text_iter_set_line_offset(&iter, 0);
4214 itemized_len = compose_itemized_length(buffer, &iter);
4216 if (!itemized_len) {
4217 itemized_len = compose_left_offset_length(buffer, &iter);
4218 item_continuation = TRUE;
4222 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4224 /* go until paragraph end (empty line) */
4225 while (start || !gtk_text_iter_ends_line(&iter)) {
4226 gchar *scanpos = NULL;
4227 /* parse table - in order of priority */
4229 const gchar *needle; /* token */
4231 /* token search function */
4232 gchar *(*search) (const gchar *haystack,
4233 const gchar *needle);
4234 /* part parsing function */
4235 gboolean (*parse) (const gchar *start,
4236 const gchar *scanpos,
4240 /* part to URI function */
4241 gchar *(*build_uri) (const gchar *bp,
4245 static struct table parser[] = {
4246 {"http://", strcasestr, get_uri_part, make_uri_string},
4247 {"https://", strcasestr, get_uri_part, make_uri_string},
4248 {"ftp://", strcasestr, get_uri_part, make_uri_string},
4249 {"sftp://", strcasestr, get_uri_part, make_uri_string},
4250 {"gopher://",strcasestr, get_uri_part, make_uri_string},
4251 {"www.", strcasestr, get_uri_part, make_http_string},
4252 {"mailto:", strcasestr, get_uri_part, make_uri_string},
4253 {"@", strcasestr, get_email_part, make_email_string}
4255 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4256 gint last_index = PARSE_ELEMS;
4258 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4262 if (!prev_autowrap && num_blocks == 0) {
4264 g_signal_handlers_block_by_func(G_OBJECT(buffer),
4265 G_CALLBACK(text_inserted),
4268 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4271 uri_start = uri_stop = -1;
4273 quote_str = compose_get_quote_str(buffer, &iter, "e_len);
4276 // debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4277 if (startq_offset == -1)
4278 startq_offset = gtk_text_iter_get_offset(&iter);
4279 quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4280 if (quotelevel > 2) {
4281 /* recycle colors */
4282 if (prefs_common.recycle_quote_colors)
4291 if (startq_offset == -1)
4292 noq_offset = gtk_text_iter_get_offset(&iter);
4296 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4299 if (gtk_text_iter_ends_line(&iter)) {
4301 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4302 prefs_common.linewrap_len,
4304 GtkTextIter prev, next, cur;
4305 if (prev_autowrap != FALSE || force) {
4306 compose->automatic_break = TRUE;
4308 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4309 compose->automatic_break = FALSE;
4310 if (itemized_len && compose->autoindent) {
4311 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4312 if (!item_continuation)
4313 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4315 } else if (quote_str && wrap_quote) {
4316 compose->automatic_break = TRUE;
4318 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4319 compose->automatic_break = FALSE;
4320 if (itemized_len && compose->autoindent) {
4321 gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4322 if (!item_continuation)
4323 gtk_text_buffer_insert(buffer, &break_pos, " ", 2);
4327 /* remove trailing spaces */
4329 rem_item_len = itemized_len;
4330 while (compose->autoindent && rem_item_len-- > 0)
4331 gtk_text_iter_backward_char(&cur);
4332 gtk_text_iter_backward_char(&cur);
4335 while (!gtk_text_iter_starts_line(&cur)) {
4338 gtk_text_iter_backward_char(&cur);
4339 wc = gtk_text_iter_get_char(&cur);
4340 if (!g_unichar_isspace(wc))
4344 if (!gtk_text_iter_equal(&prev, &next)) {
4345 gtk_text_buffer_delete(buffer, &prev, &next);
4347 gtk_text_iter_forward_char(&break_pos);
4351 gtk_text_buffer_insert(buffer, &break_pos,
4355 modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4357 /* move iter to current line start */
4358 gtk_text_iter_set_line_offset(&iter, 0);
4365 /* move iter to next line start */
4371 if (!prev_autowrap && num_blocks > 0) {
4373 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4374 G_CALLBACK(text_inserted),
4378 while (!gtk_text_iter_ends_line(&end_of_line)) {
4379 gtk_text_iter_forward_char(&end_of_line);
4381 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4383 nouri_start = gtk_text_iter_get_offset(&iter);
4384 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4386 walk_pos = gtk_text_iter_get_offset(&iter);
4387 /* FIXME: this looks phony. scanning for anything in the parse table */
4388 for (n = 0; n < PARSE_ELEMS; n++) {
4391 tmp = parser[n].search(walk, parser[n].needle);
4393 if (scanpos == NULL || tmp < scanpos) {
4402 /* check if URI can be parsed */
4403 if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4404 (const gchar **)&ep, FALSE)
4405 && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4409 strlen(parser[last_index].needle);
4412 uri_start = walk_pos + (bp - o_walk);
4413 uri_stop = walk_pos + (ep - o_walk);
4417 gtk_text_iter_forward_line(&iter);
4420 if (startq_offset != -1) {
4421 GtkTextIter startquote, endquote;
4422 gtk_text_buffer_get_iter_at_offset(
4423 buffer, &startquote, startq_offset);
4426 switch (quotelevel) {
4428 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4429 !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4430 gtk_text_buffer_apply_tag_by_name(
4431 buffer, "quote0", &startquote, &endquote);
4432 gtk_text_buffer_remove_tag_by_name(
4433 buffer, "quote1", &startquote, &endquote);
4434 gtk_text_buffer_remove_tag_by_name(
4435 buffer, "quote2", &startquote, &endquote);
4440 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4441 !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4442 gtk_text_buffer_apply_tag_by_name(
4443 buffer, "quote1", &startquote, &endquote);
4444 gtk_text_buffer_remove_tag_by_name(
4445 buffer, "quote0", &startquote, &endquote);
4446 gtk_text_buffer_remove_tag_by_name(
4447 buffer, "quote2", &startquote, &endquote);
4452 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4453 !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4454 gtk_text_buffer_apply_tag_by_name(
4455 buffer, "quote2", &startquote, &endquote);
4456 gtk_text_buffer_remove_tag_by_name(
4457 buffer, "quote0", &startquote, &endquote);
4458 gtk_text_buffer_remove_tag_by_name(
4459 buffer, "quote1", &startquote, &endquote);
4465 } else if (noq_offset != -1) {
4466 GtkTextIter startnoquote, endnoquote;
4467 gtk_text_buffer_get_iter_at_offset(
4468 buffer, &startnoquote, noq_offset);
4471 if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4472 && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4473 (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4474 && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4475 (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4476 && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4477 gtk_text_buffer_remove_tag_by_name(
4478 buffer, "quote0", &startnoquote, &endnoquote);
4479 gtk_text_buffer_remove_tag_by_name(
4480 buffer, "quote1", &startnoquote, &endnoquote);
4481 gtk_text_buffer_remove_tag_by_name(
4482 buffer, "quote2", &startnoquote, &endnoquote);
4488 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4489 GtkTextIter nouri_start_iter, nouri_end_iter;
4490 gtk_text_buffer_get_iter_at_offset(
4491 buffer, &nouri_start_iter, nouri_start);
4492 gtk_text_buffer_get_iter_at_offset(
4493 buffer, &nouri_end_iter, nouri_stop);
4494 if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4495 gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4496 gtk_text_buffer_remove_tag_by_name(
4497 buffer, "link", &nouri_start_iter, &nouri_end_iter);
4498 modified_before_remove = modified;
4503 if (uri_start >= 0 && uri_stop > 0) {
4504 GtkTextIter uri_start_iter, uri_end_iter, back;
4505 gtk_text_buffer_get_iter_at_offset(
4506 buffer, &uri_start_iter, uri_start);
4507 gtk_text_buffer_get_iter_at_offset(
4508 buffer, &uri_end_iter, uri_stop);
4509 back = uri_end_iter;
4510 gtk_text_iter_backward_char(&back);
4511 if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4512 !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4513 gtk_text_buffer_apply_tag_by_name(
4514 buffer, "link", &uri_start_iter, &uri_end_iter);
4516 if (removed && !modified_before_remove) {
4522 // debug_print("not modified, out after %d lines\n", lines);
4526 // debug_print("modified, out after %d lines\n", lines);
4528 g_free(itemized_chars);
4531 undo_wrapping(compose->undostruct, FALSE);
4532 compose->autowrap = prev_autowrap;
4537 void compose_action_cb(void *data)
4539 Compose *compose = (Compose *)data;
4540 compose_wrap_all(compose);
4543 static void compose_wrap_all(Compose *compose)
4545 compose_wrap_all_full(compose, FALSE);
4548 static void compose_wrap_all_full(Compose *compose, gboolean force)
4550 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4551 GtkTextBuffer *buffer;
4553 gboolean modified = TRUE;
4555 buffer = gtk_text_view_get_buffer(text);
4557 gtk_text_buffer_get_start_iter(buffer, &iter);
4558 while (!gtk_text_iter_is_end(&iter) && modified)
4559 modified = compose_beautify_paragraph(compose, &iter, force);
4563 static void compose_set_title(Compose *compose)
4569 edited = compose->modified ? _(" [Edited]") : "";
4571 subject = gtk_editable_get_chars(
4572 GTK_EDITABLE(compose->subject_entry), 0, -1);
4574 #ifndef GENERIC_UMPC
4575 if (subject && strlen(subject))
4576 str = g_strdup_printf(_("%s - Compose message%s"),
4579 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4581 str = g_strdup(_("Compose message"));
4584 gtk_window_set_title(GTK_WINDOW(compose->window), str);
4590 * compose_current_mail_account:
4592 * Find a current mail account (the currently selected account, or the
4593 * default account, if a news account is currently selected). If a
4594 * mail account cannot be found, display an error message.
4596 * Return value: Mail account, or NULL if not found.
4598 static PrefsAccount *
4599 compose_current_mail_account(void)
4603 if (cur_account && cur_account->protocol != A_NNTP)
4606 ac = account_get_default();
4607 if (!ac || ac->protocol == A_NNTP) {
4608 alertpanel_error(_("Account for sending mail is not specified.\n"
4609 "Please select a mail account before sending."));
4616 #define QUOTE_IF_REQUIRED(out, str) \
4618 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4622 len = strlen(str) + 3; \
4623 if ((__tmp = alloca(len)) == NULL) { \
4624 g_warning("can't allocate memory\n"); \
4625 g_string_free(header, TRUE); \
4628 g_snprintf(__tmp, len, "\"%s\"", str); \
4633 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4634 g_warning("can't allocate memory\n"); \
4635 g_string_free(header, TRUE); \
4638 strcpy(__tmp, str); \
4644 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret) \
4646 if (*str != '"' && strpbrk(str, ",.[]<>")) { \
4650 len = strlen(str) + 3; \
4651 if ((__tmp = alloca(len)) == NULL) { \
4652 g_warning("can't allocate memory\n"); \
4655 g_snprintf(__tmp, len, "\"%s\"", str); \
4660 if ((__tmp = alloca(strlen(str) + 1)) == NULL) { \
4661 g_warning("can't allocate memory\n"); \
4664 strcpy(__tmp, str); \
4670 static void compose_select_account(Compose *compose, PrefsAccount *account,
4673 gchar *from = NULL, *header;
4674 ComposeHeaderEntry *header_entry;
4676 cm_return_if_fail(account != NULL);
4678 compose->account = account;
4679 if (account->name && *account->name) {
4681 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4682 from = g_strdup_printf("%s <%s>",
4683 buf, account->address);
4684 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4686 from = g_strdup_printf("<%s>",
4688 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4693 compose_set_title(compose);
4695 if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4696 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4698 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4699 if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4700 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4702 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4704 activate_privacy_system(compose, account, FALSE);
4706 if (!init && compose->mode != COMPOSE_REDIRECT) {
4707 undo_block(compose->undostruct);
4708 compose_insert_sig(compose, TRUE);
4709 undo_unblock(compose->undostruct);
4712 header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4713 header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4715 if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4716 if (account->protocol == A_NNTP) {
4717 if (!strcmp(header, _("To:")))
4718 combobox_select_by_text(
4719 GTK_COMBO_BOX(header_entry->combo),
4722 if (!strcmp(header, _("Newsgroups:")))
4723 combobox_select_by_text(
4724 GTK_COMBO_BOX(header_entry->combo),
4732 /* use account's dict info if set */
4733 if (compose->gtkaspell) {
4734 if (account->enable_default_dictionary)
4735 gtkaspell_change_dict(compose->gtkaspell,
4736 account->default_dictionary, FALSE);
4737 if (account->enable_default_alt_dictionary)
4738 gtkaspell_change_alt_dict(compose->gtkaspell,
4739 account->default_alt_dictionary);
4740 if (account->enable_default_dictionary
4741 || account->enable_default_alt_dictionary)
4742 compose_spell_menu_changed(compose);
4747 gboolean compose_check_for_valid_recipient(Compose *compose) {
4748 gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4749 gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4750 gboolean recipient_found = FALSE;
4754 /* free to and newsgroup list */
4755 slist_free_strings(compose->to_list);
4756 g_slist_free(compose->to_list);
4757 compose->to_list = NULL;
4759 slist_free_strings(compose->newsgroup_list);
4760 g_slist_free(compose->newsgroup_list);
4761 compose->newsgroup_list = NULL;
4763 /* search header entries for to and newsgroup entries */
4764 for (list = compose->header_list; list; list = list->next) {
4767 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4768 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4771 if (entry[0] != '\0') {
4772 for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4773 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4774 compose->to_list = address_list_append(compose->to_list, entry);
4775 recipient_found = TRUE;
4778 for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4779 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
4780 compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4781 recipient_found = TRUE;
4788 return recipient_found;
4791 static gboolean compose_check_for_set_recipients(Compose *compose)
4793 if (compose->account->set_autocc && compose->account->auto_cc) {
4794 gboolean found_other = FALSE;
4796 /* search header entries for to and newsgroup entries */
4797 for (list = compose->header_list; list; list = list->next) {
4800 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4801 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4804 if (strcmp(entry, compose->account->auto_cc)
4805 || strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4815 if (compose->batch) {
4816 gtk_widget_show_all(compose->window);
4818 aval = alertpanel(_("Send"),
4819 _("The only recipient is the default CC address. Send anyway?"),
4820 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4821 if (aval != G_ALERTALTERNATE)
4825 if (compose->account->set_autobcc && compose->account->auto_bcc) {
4826 gboolean found_other = FALSE;
4828 /* search header entries for to and newsgroup entries */
4829 for (list = compose->header_list; list; list = list->next) {
4832 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4833 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4836 if (strcmp(entry, compose->account->auto_bcc)
4837 || strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4847 if (compose->batch) {
4848 gtk_widget_show_all(compose->window);
4850 aval = alertpanel(_("Send"),
4851 _("The only recipient is the default BCC address. Send anyway?"),
4852 GTK_STOCK_CANCEL, _("+_Send"), NULL);
4853 if (aval != G_ALERTALTERNATE)
4860 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4864 if (compose_check_for_valid_recipient(compose) == FALSE) {
4865 if (compose->batch) {
4866 gtk_widget_show_all(compose->window);
4868 alertpanel_error(_("Recipient is not specified."));
4872 if (compose_check_for_set_recipients(compose) == FALSE) {
4876 if (!compose->batch) {
4877 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4878 if (*str == '\0' && check_everything == TRUE &&
4879 compose->mode != COMPOSE_REDIRECT) {
4881 gchar *button_label;
4884 if (compose->sending)
4885 button_label = _("+_Send");
4887 button_label = _("+_Queue");
4888 message = g_strdup_printf(_("Subject is empty. %s"),
4889 compose->sending?_("Send it anyway?"):
4890 _("Queue it anyway?"));
4892 aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4893 GTK_STOCK_CANCEL, button_label, NULL);
4895 if (aval != G_ALERTALTERNATE)
4900 if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4906 gint compose_send(Compose *compose)
4909 FolderItem *folder = NULL;
4911 gchar *msgpath = NULL;
4912 gboolean discard_window = FALSE;
4913 gchar *errstr = NULL;
4914 gchar *tmsgid = NULL;
4915 MainWindow *mainwin = mainwindow_get_mainwindow();
4916 gboolean queued_removed = FALSE;
4918 if (prefs_common.send_dialog_invisible
4919 || compose->batch == TRUE)
4920 discard_window = TRUE;
4922 compose_allow_user_actions (compose, FALSE);
4923 compose->sending = TRUE;
4925 if (compose_check_entries(compose, TRUE) == FALSE) {
4926 if (compose->batch) {
4927 gtk_widget_show_all(compose->window);
4933 val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4936 if (compose->batch) {
4937 gtk_widget_show_all(compose->window);
4940 alertpanel_error(_("Could not queue message for sending:\n\n"
4941 "Charset conversion failed."));
4942 } else if (val == -5) {
4943 alertpanel_error(_("Could not queue message for sending:\n\n"
4944 "Couldn't get recipient encryption key."));
4945 } else if (val == -6) {
4947 } else if (val == -3) {
4948 if (privacy_peek_error())
4949 alertpanel_error(_("Could not queue message for sending:\n\n"
4950 "Signature failed: %s"), privacy_get_error());
4951 } else if (val == -2 && errno != 0) {
4952 alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4954 alertpanel_error(_("Could not queue message for sending."));
4959 tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4960 if (discard_window) {
4961 compose->sending = FALSE;
4962 compose_close(compose);
4963 /* No more compose access in the normal codepath
4964 * after this point! */
4969 alertpanel_error(_("The message was queued but could not be "
4970 "sent.\nUse \"Send queued messages\" from "
4971 "the main window to retry."));
4972 if (!discard_window) {
4979 if (msgpath == NULL) {
4980 msgpath = folder_item_fetch_msg(folder, msgnum);
4981 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4984 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4985 claws_unlink(msgpath);
4988 if (!discard_window) {
4990 if (!queued_removed)
4991 folder_item_remove_msg(folder, msgnum);
4992 folder_item_scan(folder);
4994 /* make sure we delete that */
4995 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4997 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4998 folder_item_remove_msg(folder, tmp->msgnum);
4999 procmsg_msginfo_free(tmp);
5006 if (!queued_removed)
5007 folder_item_remove_msg(folder, msgnum);
5008 folder_item_scan(folder);
5010 /* make sure we delete that */
5011 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5013 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5014 folder_item_remove_msg(folder, tmp->msgnum);
5015 procmsg_msginfo_free(tmp);
5018 if (!discard_window) {
5019 compose->sending = FALSE;
5020 compose_allow_user_actions (compose, TRUE);
5021 compose_close(compose);
5025 alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5026 "the main window to retry."), errstr);
5029 alertpanel_error_log(_("The message was queued but could not be "
5030 "sent.\nUse \"Send queued messages\" from "
5031 "the main window to retry."));
5033 if (!discard_window) {
5042 toolbar_main_set_sensitive(mainwin);
5043 main_window_set_menu_sensitive(mainwin);
5049 compose_allow_user_actions (compose, TRUE);
5050 compose->sending = FALSE;
5051 compose->modified = TRUE;
5052 toolbar_main_set_sensitive(mainwin);
5053 main_window_set_menu_sensitive(mainwin);
5058 static gboolean compose_use_attach(Compose *compose)
5060 GtkTreeModel *model = gtk_tree_view_get_model
5061 (GTK_TREE_VIEW(compose->attach_clist));
5062 return gtk_tree_model_iter_n_children(model, NULL) > 0;
5065 static gint compose_redirect_write_headers_from_headerlist(Compose *compose,
5068 gchar buf[BUFFSIZE];
5070 gboolean first_to_address;
5071 gboolean first_cc_address;
5073 ComposeHeaderEntry *headerentry;
5074 const gchar *headerentryname;
5075 const gchar *cc_hdr;
5076 const gchar *to_hdr;
5077 gboolean err = FALSE;
5079 debug_print("Writing redirect header\n");
5081 cc_hdr = prefs_common_translated_header_name("Cc:");
5082 to_hdr = prefs_common_translated_header_name("To:");
5084 first_to_address = TRUE;
5085 for (list = compose->header_list; list; list = list->next) {
5086 headerentry = ((ComposeHeaderEntry *)list->data);
5087 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5089 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5090 const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5091 Xstrdup_a(str, entstr, return -1);
5093 if (str[0] != '\0') {
5094 compose_convert_header
5095 (compose, buf, sizeof(buf), str,
5096 strlen("Resent-To") + 2, TRUE);
5098 if (first_to_address) {
5099 err |= (fprintf(fp, "Resent-To: ") < 0);
5100 first_to_address = FALSE;
5102 err |= (fprintf(fp, ",") < 0);
5104 err |= (fprintf(fp, "%s", buf) < 0);
5108 if (!first_to_address) {
5109 err |= (fprintf(fp, "\n") < 0);
5112 first_cc_address = TRUE;
5113 for (list = compose->header_list; list; list = list->next) {
5114 headerentry = ((ComposeHeaderEntry *)list->data);
5115 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5117 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5118 const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5119 Xstrdup_a(str, strg, return -1);
5121 if (str[0] != '\0') {
5122 compose_convert_header
5123 (compose, buf, sizeof(buf), str,
5124 strlen("Resent-Cc") + 2, TRUE);
5126 if (first_cc_address) {
5127 err |= (fprintf(fp, "Resent-Cc: ") < 0);
5128 first_cc_address = FALSE;
5130 err |= (fprintf(fp, ",") < 0);
5132 err |= (fprintf(fp, "%s", buf) < 0);
5136 if (!first_cc_address) {
5137 err |= (fprintf(fp, "\n") < 0);
5140 return (err ? -1:0);
5143 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5145 gchar buf[BUFFSIZE];
5147 const gchar *entstr;
5148 /* struct utsname utsbuf; */
5149 gboolean err = FALSE;
5151 cm_return_val_if_fail(fp != NULL, -1);
5152 cm_return_val_if_fail(compose->account != NULL, -1);
5153 cm_return_val_if_fail(compose->account->address != NULL, -1);
5156 get_rfc822_date(buf, sizeof(buf));
5157 err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5160 if (compose->account->name && *compose->account->name) {
5161 compose_convert_header
5162 (compose, buf, sizeof(buf), compose->account->name,
5163 strlen("From: "), TRUE);
5164 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5165 buf, compose->account->address) < 0);
5167 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5170 entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5171 if (*entstr != '\0') {
5172 Xstrdup_a(str, entstr, return -1);
5175 compose_convert_header(compose, buf, sizeof(buf), str,
5176 strlen("Subject: "), FALSE);
5177 err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5181 /* Resent-Message-ID */
5182 if (compose->account->set_domain && compose->account->domain) {
5183 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
5184 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5185 g_snprintf(buf, sizeof(buf), "%s",
5186 strchr(compose->account->address, '@') ?
5187 strchr(compose->account->address, '@')+1 :
5188 compose->account->address);
5190 g_snprintf(buf, sizeof(buf), "%s", "");
5193 if (compose->account->gen_msgid) {
5195 if (compose->account->msgid_with_addr) {
5196 addr = compose->account->address;
5198 generate_msgid(buf, sizeof(buf), addr);
5199 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5200 compose->msgid = g_strdup(buf);
5202 compose->msgid = NULL;
5205 if (compose_redirect_write_headers_from_headerlist(compose, fp))
5208 /* separator between header and body */
5209 err |= (fputs("\n", fp) == EOF);
5211 return (err ? -1:0);
5214 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5218 gchar buf[BUFFSIZE];
5220 gboolean skip = FALSE;
5221 gboolean err = FALSE;
5222 gchar *not_included[]={
5223 "Return-Path:", "Delivered-To:", "Received:",
5224 "Subject:", "X-UIDL:", "AF:",
5225 "NF:", "PS:", "SRH:",
5226 "SFN:", "DSR:", "MID:",
5227 "CFG:", "PT:", "S:",
5228 "RQ:", "SSV:", "NSV:",
5229 "SSH:", "R:", "MAID:",
5230 "NAID:", "RMID:", "FMID:",
5231 "SCF:", "RRCPT:", "NG:",
5232 "X-Claws-Privacy", "X-Claws-Sign:", "X-Claws-Encrypt",
5233 "X-Claws-End-Special-Headers:", "X-Claws-Account-Id:",
5234 "X-Sylpheed-Privacy", "X-Sylpheed-Sign:", "X-Sylpheed-Encrypt",
5235 "X-Sylpheed-End-Special-Headers:", "X-Sylpheed-Account-Id:",
5236 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5239 if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5240 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5244 while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5246 for (i = 0; not_included[i] != NULL; i++) {
5247 if (g_ascii_strncasecmp(buf, not_included[i],
5248 strlen(not_included[i])) == 0) {
5255 if (fputs(buf, fdest) == -1)
5258 if (!prefs_common.redirect_keep_from) {
5259 if (g_ascii_strncasecmp(buf, "From:",
5260 strlen("From:")) == 0) {
5261 err |= (fputs(" (by way of ", fdest) == EOF);
5262 if (compose->account->name
5263 && *compose->account->name) {
5264 compose_convert_header
5265 (compose, buf, sizeof(buf),
5266 compose->account->name,
5269 err |= (fprintf(fdest, "%s <%s>",
5271 compose->account->address) < 0);
5273 err |= (fprintf(fdest, "%s",
5274 compose->account->address) < 0);
5275 err |= (fputs(")", fdest) == EOF);
5279 if (fputs("\n", fdest) == -1)
5286 if (compose_redirect_write_headers(compose, fdest))
5289 while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5290 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5303 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5305 GtkTextBuffer *buffer;
5306 GtkTextIter start, end;
5309 const gchar *out_codeset;
5310 EncodingType encoding = ENC_UNKNOWN;
5311 MimeInfo *mimemsg, *mimetext;
5313 const gchar *src_codeset = CS_INTERNAL;
5314 gchar *from_addr = NULL;
5315 gchar *from_name = NULL;
5317 if (action == COMPOSE_WRITE_FOR_SEND)
5318 attach_parts = TRUE;
5320 /* create message MimeInfo */
5321 mimemsg = procmime_mimeinfo_new();
5322 mimemsg->type = MIMETYPE_MESSAGE;
5323 mimemsg->subtype = g_strdup("rfc822");
5324 mimemsg->content = MIMECONTENT_MEM;
5325 mimemsg->tmp = TRUE; /* must free content later */
5326 mimemsg->data.mem = compose_get_header(compose);
5328 /* Create text part MimeInfo */
5329 /* get all composed text */
5330 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5331 gtk_text_buffer_get_start_iter(buffer, &start);
5332 gtk_text_buffer_get_end_iter(buffer, &end);
5333 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5335 out_codeset = conv_get_charset_str(compose->out_encoding);
5337 if (!out_codeset && is_ascii_str(chars)) {
5338 out_codeset = CS_US_ASCII;
5339 } else if (prefs_common.outgoing_fallback_to_ascii &&
5340 is_ascii_str(chars)) {
5341 out_codeset = CS_US_ASCII;
5342 encoding = ENC_7BIT;
5346 gchar *test_conv_global_out = NULL;
5347 gchar *test_conv_reply = NULL;
5349 /* automatic mode. be automatic. */
5350 codeconv_set_strict(TRUE);
5352 out_codeset = conv_get_outgoing_charset_str();
5354 debug_print("trying to convert to %s\n", out_codeset);
5355 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5358 if (!test_conv_global_out && compose->orig_charset
5359 && strcmp(compose->orig_charset, CS_US_ASCII)) {
5360 out_codeset = compose->orig_charset;
5361 debug_print("failure; trying to convert to %s\n", out_codeset);
5362 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5365 if (!test_conv_global_out && !test_conv_reply) {
5367 out_codeset = CS_INTERNAL;
5368 debug_print("failure; finally using %s\n", out_codeset);
5370 g_free(test_conv_global_out);
5371 g_free(test_conv_reply);
5372 codeconv_set_strict(FALSE);
5375 if (encoding == ENC_UNKNOWN) {
5376 if (prefs_common.encoding_method == CTE_BASE64)
5377 encoding = ENC_BASE64;
5378 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5379 encoding = ENC_QUOTED_PRINTABLE;
5380 else if (prefs_common.encoding_method == CTE_8BIT)
5381 encoding = ENC_8BIT;
5383 encoding = procmime_get_encoding_for_charset(out_codeset);
5386 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5387 src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5389 if (action == COMPOSE_WRITE_FOR_SEND) {
5390 codeconv_set_strict(TRUE);
5391 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5392 codeconv_set_strict(FALSE);
5398 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5399 "to the specified %s charset.\n"
5400 "Send it as %s?"), out_codeset, src_codeset);
5401 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5402 NULL, ALERT_ERROR, G_ALERTDEFAULT);
5405 if (aval != G_ALERTALTERNATE) {
5410 out_codeset = src_codeset;
5416 out_codeset = src_codeset;
5421 if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5422 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5423 strstr(buf, "\nFrom ") != NULL) {
5424 encoding = ENC_QUOTED_PRINTABLE;
5428 mimetext = procmime_mimeinfo_new();
5429 mimetext->content = MIMECONTENT_MEM;
5430 mimetext->tmp = TRUE; /* must free content later */
5431 /* dup'ed because procmime_encode_content can turn it into a tmpfile
5432 * and free the data, which we need later. */
5433 mimetext->data.mem = g_strdup(buf);
5434 mimetext->type = MIMETYPE_TEXT;
5435 mimetext->subtype = g_strdup("plain");
5436 g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5437 g_strdup(out_codeset));
5439 /* protect trailing spaces when signing message */
5440 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5441 privacy_system_can_sign(compose->privacy_system)) {
5442 encoding = ENC_QUOTED_PRINTABLE;
5445 debug_print("main text: %zd bytes encoded as %s in %d\n",
5446 strlen(buf), out_codeset, encoding);
5448 /* check for line length limit */
5449 if (action == COMPOSE_WRITE_FOR_SEND &&
5450 encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5451 check_line_length(buf, 1000, &line) < 0) {
5455 msg = g_strdup_printf
5456 (_("Line %d exceeds the line length limit (998 bytes).\n"
5457 "The contents of the message might be broken on the way to the delivery.\n"
5459 "Send it anyway?"), line + 1);
5460 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5462 if (aval != G_ALERTALTERNATE) {
5468 if (encoding != ENC_UNKNOWN)
5469 procmime_encode_content(mimetext, encoding);
5471 /* append attachment parts */
5472 if (compose_use_attach(compose) && attach_parts) {
5473 MimeInfo *mimempart;
5474 gchar *boundary = NULL;
5475 mimempart = procmime_mimeinfo_new();
5476 mimempart->content = MIMECONTENT_EMPTY;
5477 mimempart->type = MIMETYPE_MULTIPART;
5478 mimempart->subtype = g_strdup("mixed");
5482 boundary = generate_mime_boundary(NULL);
5483 } while (strstr(buf, boundary) != NULL);
5485 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5488 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5490 g_node_append(mimempart->node, mimetext->node);
5491 g_node_append(mimemsg->node, mimempart->node);
5493 if (compose_add_attachments(compose, mimempart) < 0)
5496 g_node_append(mimemsg->node, mimetext->node);
5500 if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5501 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5502 /* extract name and address */
5503 if (strstr(spec, " <") && strstr(spec, ">")) {
5504 from_addr = g_strdup(strrchr(spec, '<')+1);
5505 *(strrchr(from_addr, '>')) = '\0';
5506 from_name = g_strdup(spec);
5507 *(strrchr(from_name, '<')) = '\0';
5514 /* sign message if sending */
5515 if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing &&
5516 privacy_system_can_sign(compose->privacy_system))
5517 if (!privacy_sign(compose->privacy_system, mimemsg,
5518 compose->account, from_addr)) {
5525 procmime_write_mimeinfo(mimemsg, fp);
5527 procmime_mimeinfo_free_all(mimemsg);
5532 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5534 GtkTextBuffer *buffer;
5535 GtkTextIter start, end;
5540 if ((fp = g_fopen(file, "wb")) == NULL) {
5541 FILE_OP_ERROR(file, "fopen");
5545 /* chmod for security */
5546 if (change_file_mode_rw(fp, file) < 0) {
5547 FILE_OP_ERROR(file, "chmod");
5548 g_warning("can't change file mode\n");
5551 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5552 gtk_text_buffer_get_start_iter(buffer, &start);
5553 gtk_text_buffer_get_end_iter(buffer, &end);
5554 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5556 chars = conv_codeset_strdup
5557 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5560 if (!chars) return -1;
5563 len = strlen(chars);
5564 if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5565 FILE_OP_ERROR(file, "fwrite");
5574 if (fclose(fp) == EOF) {
5575 FILE_OP_ERROR(file, "fclose");
5582 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5585 MsgInfo *msginfo = compose->targetinfo;
5587 cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5588 if (!msginfo) return -1;
5590 if (!force && MSG_IS_LOCKED(msginfo->flags))
5593 item = msginfo->folder;
5594 cm_return_val_if_fail(item != NULL, -1);
5596 if (procmsg_msg_exist(msginfo) &&
5597 (folder_has_parent_of_type(item, F_QUEUE) ||
5598 folder_has_parent_of_type(item, F_DRAFT)
5599 || msginfo == compose->autosaved_draft)) {
5600 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5601 g_warning("can't remove the old message\n");
5604 debug_print("removed reedit target %d\n", msginfo->msgnum);
5611 static void compose_remove_draft(Compose *compose)
5614 MsgInfo *msginfo = compose->targetinfo;
5615 drafts = account_get_special_folder(compose->account, F_DRAFT);
5617 if (procmsg_msg_exist(msginfo)) {
5618 folder_item_remove_msg(drafts, msginfo->msgnum);
5623 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5624 gboolean remove_reedit_target)
5626 return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5629 static gboolean compose_warn_encryption(Compose *compose)
5631 const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5632 AlertValue val = G_ALERTALTERNATE;
5634 if (warning == NULL)
5637 val = alertpanel_full(_("Encryption warning"), warning,
5638 GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5639 TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5640 if (val & G_ALERTDISABLE) {
5641 val &= ~G_ALERTDISABLE;
5642 if (val == G_ALERTALTERNATE)
5643 privacy_inhibit_encrypt_warning(compose->privacy_system,
5647 if (val == G_ALERTALTERNATE) {
5654 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
5655 gchar **msgpath, gboolean check_subject,
5656 gboolean remove_reedit_target)
5663 static gboolean lock = FALSE;
5664 PrefsAccount *mailac = NULL, *newsac = NULL;
5665 gboolean err = FALSE;
5667 debug_print("queueing message...\n");
5668 cm_return_val_if_fail(compose->account != NULL, -1);
5672 if (compose_check_entries(compose, check_subject) == FALSE) {
5674 if (compose->batch) {
5675 gtk_widget_show_all(compose->window);
5680 if (!compose->to_list && !compose->newsgroup_list) {
5681 g_warning("can't get recipient list.");
5686 if (compose->to_list) {
5687 if (compose->account->protocol != A_NNTP)
5688 mailac = compose->account;
5689 else if (cur_account && cur_account->protocol != A_NNTP)
5690 mailac = cur_account;
5691 else if (!(mailac = compose_current_mail_account())) {
5693 alertpanel_error(_("No account for sending mails available!"));
5698 if (compose->newsgroup_list) {
5699 if (compose->account->protocol == A_NNTP)
5700 newsac = compose->account;
5703 alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
5708 /* write queue header */
5709 tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5710 G_DIR_SEPARATOR, compose, (guint) rand());
5711 debug_print("queuing to %s\n", tmp);
5712 if ((fp = g_fopen(tmp, "wb")) == NULL) {
5713 FILE_OP_ERROR(tmp, "fopen");
5719 if (change_file_mode_rw(fp, tmp) < 0) {
5720 FILE_OP_ERROR(tmp, "chmod");
5721 g_warning("can't change file mode\n");
5724 /* queueing variables */
5725 err |= (fprintf(fp, "AF:\n") < 0);
5726 err |= (fprintf(fp, "NF:0\n") < 0);
5727 err |= (fprintf(fp, "PS:10\n") < 0);
5728 err |= (fprintf(fp, "SRH:1\n") < 0);
5729 err |= (fprintf(fp, "SFN:\n") < 0);
5730 err |= (fprintf(fp, "DSR:\n") < 0);
5732 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5734 err |= (fprintf(fp, "MID:\n") < 0);
5735 err |= (fprintf(fp, "CFG:\n") < 0);
5736 err |= (fprintf(fp, "PT:0\n") < 0);
5737 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5738 err |= (fprintf(fp, "RQ:\n") < 0);
5740 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5742 err |= (fprintf(fp, "SSV:\n") < 0);
5744 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5746 err |= (fprintf(fp, "NSV:\n") < 0);
5747 err |= (fprintf(fp, "SSH:\n") < 0);
5748 /* write recepient list */
5749 if (compose->to_list) {
5750 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5751 for (cur = compose->to_list->next; cur != NULL;
5753 err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5754 err |= (fprintf(fp, "\n") < 0);
5756 /* write newsgroup list */
5757 if (compose->newsgroup_list) {
5758 err |= (fprintf(fp, "NG:") < 0);
5759 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5760 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5761 err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5762 err |= (fprintf(fp, "\n") < 0);
5764 /* Sylpheed account IDs */
5766 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5768 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5771 if (compose->privacy_system != NULL) {
5772 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5773 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5774 if (compose->use_encryption) {
5776 if (!compose_warn_encryption(compose)) {
5783 if (mailac && mailac->encrypt_to_self) {
5784 GSList *tmp_list = g_slist_copy(compose->to_list);
5785 tmp_list = g_slist_append(tmp_list, compose->account->address);
5786 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5787 g_slist_free(tmp_list);
5789 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5791 if (encdata != NULL) {
5792 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5793 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5794 err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n",
5796 } /* else we finally dont want to encrypt */
5798 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5799 /* and if encdata was null, it means there's been a problem in
5811 /* Save copy folder */
5812 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5813 gchar *savefolderid;
5815 savefolderid = compose_get_save_to(compose);
5816 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5817 g_free(savefolderid);
5819 /* Save copy folder */
5820 if (compose->return_receipt) {
5821 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5823 /* Message-ID of message replying to */
5824 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5827 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5828 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5831 /* Message-ID of message forwarding to */
5832 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5835 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5836 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5840 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5841 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
5843 /* end of headers */
5844 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5846 if (compose->redirect_filename != NULL) {
5847 if (compose_redirect_write_to_file(compose, fp) < 0) {
5856 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5861 return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5865 g_warning("failed to write queue message\n");
5872 if (fclose(fp) == EOF) {
5873 FILE_OP_ERROR(tmp, "fclose");
5880 if (item && *item) {
5883 queue = account_get_special_folder(compose->account, F_QUEUE);
5886 g_warning("can't find queue folder\n");
5892 folder_item_scan(queue);
5893 if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5894 g_warning("can't queue the message\n");
5901 if (msgpath == NULL) {
5907 if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5908 compose_remove_reedit_target(compose, FALSE);
5911 if ((msgnum != NULL) && (item != NULL)) {
5919 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
5922 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5924 struct stat statbuf;
5925 gchar *type, *subtype;
5926 GtkTreeModel *model;
5929 model = gtk_tree_view_get_model(tree_view);
5931 if (!gtk_tree_model_get_iter_first(model, &iter))
5934 gtk_tree_model_get(model, &iter,
5938 if (!is_file_exist(ainfo->file)) {
5939 gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
5940 AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
5941 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
5943 if (val == G_ALERTDEFAULT) {
5948 mimepart = procmime_mimeinfo_new();
5949 mimepart->content = MIMECONTENT_FILE;
5950 mimepart->data.filename = g_strdup(ainfo->file);
5951 mimepart->tmp = FALSE; /* or we destroy our attachment */
5952 mimepart->offset = 0;
5954 g_stat(ainfo->file, &statbuf);
5955 mimepart->length = statbuf.st_size;
5957 type = g_strdup(ainfo->content_type);
5959 if (!strchr(type, '/')) {
5961 type = g_strdup("application/octet-stream");
5964 subtype = strchr(type, '/') + 1;
5965 *(subtype - 1) = '\0';
5966 mimepart->type = procmime_get_media_type(type);
5967 mimepart->subtype = g_strdup(subtype);
5970 if (mimepart->type == MIMETYPE_MESSAGE &&
5971 !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5972 mimepart->disposition = DISPOSITIONTYPE_INLINE;
5975 if (mimepart->type == MIMETYPE_APPLICATION &&
5976 !strcmp2(mimepart->subtype, "octet-stream"))
5977 g_hash_table_insert(mimepart->typeparameters,
5978 g_strdup("name"), g_strdup(ainfo->name));
5979 g_hash_table_insert(mimepart->dispositionparameters,
5980 g_strdup("filename"), g_strdup(ainfo->name));
5981 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5985 if (compose->use_signing) {
5986 if (ainfo->encoding == ENC_7BIT)
5987 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5988 else if (ainfo->encoding == ENC_8BIT)
5989 ainfo->encoding = ENC_BASE64;
5992 procmime_encode_content(mimepart, ainfo->encoding);
5994 g_node_append(parent->node, mimepart->node);
5995 } while (gtk_tree_model_iter_next(model, &iter));
6000 #define IS_IN_CUSTOM_HEADER(header) \
6001 (compose->account->add_customhdr && \
6002 custom_header_find(compose->account->customhdr_list, header) != NULL)
6004 static void compose_add_headerfield_from_headerlist(Compose *compose,
6006 const gchar *fieldname,
6007 const gchar *seperator)
6009 gchar *str, *fieldname_w_colon;
6010 gboolean add_field = FALSE;
6012 ComposeHeaderEntry *headerentry;
6013 const gchar *headerentryname;
6014 const gchar *trans_fieldname;
6017 if (IS_IN_CUSTOM_HEADER(fieldname))
6020 debug_print("Adding %s-fields\n", fieldname);
6022 fieldstr = g_string_sized_new(64);
6024 fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6025 trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6027 for (list = compose->header_list; list; list = list->next) {
6028 headerentry = ((ComposeHeaderEntry *)list->data);
6029 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6031 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6032 str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6034 if (str[0] != '\0') {
6036 g_string_append(fieldstr, seperator);
6037 g_string_append(fieldstr, str);
6046 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6047 compose_convert_header
6048 (compose, buf, fieldstr->len * 4 + 256, fieldstr->str,
6049 strlen(fieldname) + 2, TRUE);
6050 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6054 g_free(fieldname_w_colon);
6055 g_string_free(fieldstr, TRUE);
6060 static gchar *compose_get_header(Compose *compose)
6062 gchar buf[BUFFSIZE];
6063 const gchar *entry_str;
6067 gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6069 gchar *from_name = NULL, *from_address = NULL;
6072 cm_return_val_if_fail(compose->account != NULL, NULL);
6073 cm_return_val_if_fail(compose->account->address != NULL, NULL);
6075 header = g_string_sized_new(64);
6078 get_rfc822_date(buf, sizeof(buf));
6079 g_string_append_printf(header, "Date: %s\n", buf);
6083 if (compose->account->name && *compose->account->name) {
6085 QUOTE_IF_REQUIRED(buf, compose->account->name);
6086 tmp = g_strdup_printf("%s <%s>",
6087 buf, compose->account->address);
6089 tmp = g_strdup_printf("%s",
6090 compose->account->address);
6092 if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6093 || strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6095 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6096 from_address = g_strdup(compose->account->address);
6098 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6099 /* extract name and address */
6100 if (strstr(spec, " <") && strstr(spec, ">")) {
6101 from_address = g_strdup(strrchr(spec, '<')+1);
6102 *(strrchr(from_address, '>')) = '\0';
6103 from_name = g_strdup(spec);
6104 *(strrchr(from_name, '<')) = '\0';
6107 from_address = g_strdup(spec);
6114 if (from_name && *from_name) {
6115 compose_convert_header
6116 (compose, buf, sizeof(buf), from_name,
6117 strlen("From: "), TRUE);
6118 QUOTE_IF_REQUIRED(name, buf);
6120 g_string_append_printf(header, "From: %s <%s>\n",
6121 name, from_address);
6123 g_string_append_printf(header, "From: %s\n", from_address);
6126 g_free(from_address);
6129 compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6132 compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6135 compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6139 * If this account is a NNTP account remove Bcc header from
6140 * message body since it otherwise will be publicly shown
6142 if (compose->account->protocol != A_NNTP)
6143 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6146 str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6148 if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6151 compose_convert_header(compose, buf, sizeof(buf), str,
6152 strlen("Subject: "), FALSE);
6153 g_string_append_printf(header, "Subject: %s\n", buf);
6159 if (compose->account->set_domain && compose->account->domain) {
6160 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain);
6161 } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
6162 g_snprintf(buf, sizeof(buf), "%s",
6163 strchr(compose->account->address, '@') ?
6164 strchr(compose->account->address, '@')+1 :
6165 compose->account->address);
6167 g_snprintf(buf, sizeof(buf), "%s", "");
6170 if (compose->account->gen_msgid) {
6172 if (compose->account->msgid_with_addr) {
6173 addr = compose->account->address;
6175 generate_msgid(buf, sizeof(buf), addr);
6176 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
6177 compose->msgid = g_strdup(buf);
6179 compose->msgid = NULL;
6182 if (compose->remove_references == FALSE) {
6184 if (compose->inreplyto && compose->to_list)
6185 g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6188 if (compose->references)
6189 g_string_append_printf(header, "References: %s\n", compose->references);
6193 compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6196 compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6199 if (compose->account->organization &&
6200 strlen(compose->account->organization) &&
6201 !IS_IN_CUSTOM_HEADER("Organization")) {
6202 compose_convert_header(compose, buf, sizeof(buf),
6203 compose->account->organization,
6204 strlen("Organization: "), FALSE);
6205 g_string_append_printf(header, "Organization: %s\n", buf);
6208 /* Program version and system info */
6209 if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6210 !compose->newsgroup_list) {
6211 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6213 gtk_major_version, gtk_minor_version, gtk_micro_version,
6216 if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6217 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6219 gtk_major_version, gtk_minor_version, gtk_micro_version,
6223 /* custom headers */
6224 if (compose->account->add_customhdr) {
6227 for (cur = compose->account->customhdr_list; cur != NULL;
6229 CustomHeader *chdr = (CustomHeader *)cur->data;
6231 if (custom_header_is_allowed(chdr->name)) {
6232 compose_convert_header
6233 (compose, buf, sizeof(buf),
6234 chdr->value ? chdr->value : "",
6235 strlen(chdr->name) + 2, FALSE);
6236 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6241 /* Automatic Faces and X-Faces */
6242 if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6243 g_string_append_printf(header, "X-Face: %s\n", buf);
6245 else if (get_default_xface (buf, sizeof(buf)) == 0) {
6246 g_string_append_printf(header, "X-Face: %s\n", buf);
6248 if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6249 g_string_append_printf(header, "Face: %s\n", buf);
6251 else if (get_default_face (buf, sizeof(buf)) == 0) {
6252 g_string_append_printf(header, "Face: %s\n", buf);
6256 switch (compose->priority) {
6257 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6258 "X-Priority: 1 (Highest)\n");
6260 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6261 "X-Priority: 2 (High)\n");
6263 case PRIORITY_NORMAL: break;
6264 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6265 "X-Priority: 4 (Low)\n");
6267 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6268 "X-Priority: 5 (Lowest)\n");
6270 default: debug_print("compose: priority unknown : %d\n",
6274 /* Request Return Receipt */
6275 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6276 if (compose->return_receipt) {
6277 if (compose->account->name
6278 && *compose->account->name) {
6279 compose_convert_header(compose, buf, sizeof(buf),
6280 compose->account->name,
6281 strlen("Disposition-Notification-To: "),
6283 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6285 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6289 /* get special headers */
6290 for (list = compose->header_list; list; list = list->next) {
6291 ComposeHeaderEntry *headerentry;
6294 gchar *headername_wcolon;
6295 const gchar *headername_trans;
6298 gboolean standard_header = FALSE;
6300 headerentry = ((ComposeHeaderEntry *)list->data);
6302 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6304 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6309 if (!strstr(tmp, ":")) {
6310 headername_wcolon = g_strconcat(tmp, ":", NULL);
6311 headername = g_strdup(tmp);
6313 headername_wcolon = g_strdup(tmp);
6314 headername = g_strdup(strtok(tmp, ":"));
6318 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6319 Xstrdup_a(headervalue, entry_str, return NULL);
6320 subst_char(headervalue, '\r', ' ');
6321 subst_char(headervalue, '\n', ' ');
6322 string = std_headers;
6323 while (*string != NULL) {
6324 headername_trans = prefs_common_translated_header_name(*string);
6325 if (!strcmp(headername_trans, headername_wcolon))
6326 standard_header = TRUE;
6329 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6330 g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6333 g_free(headername_wcolon);
6337 g_string_free(header, FALSE);
6342 #undef IS_IN_CUSTOM_HEADER
6344 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6345 gint header_len, gboolean addr_field)
6347 gchar *tmpstr = NULL;
6348 const gchar *out_codeset = NULL;
6350 cm_return_if_fail(src != NULL);
6351 cm_return_if_fail(dest != NULL);
6353 if (len < 1) return;
6355 tmpstr = g_strdup(src);
6357 subst_char(tmpstr, '\n', ' ');
6358 subst_char(tmpstr, '\r', ' ');
6361 if (!g_utf8_validate(tmpstr, -1, NULL)) {
6362 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6363 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6368 codeconv_set_strict(TRUE);
6369 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6370 conv_get_charset_str(compose->out_encoding));
6371 codeconv_set_strict(FALSE);
6373 if (!dest || *dest == '\0') {
6374 gchar *test_conv_global_out = NULL;
6375 gchar *test_conv_reply = NULL;
6377 /* automatic mode. be automatic. */
6378 codeconv_set_strict(TRUE);
6380 out_codeset = conv_get_outgoing_charset_str();
6382 debug_print("trying to convert to %s\n", out_codeset);
6383 test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6386 if (!test_conv_global_out && compose->orig_charset
6387 && strcmp(compose->orig_charset, CS_US_ASCII)) {
6388 out_codeset = compose->orig_charset;
6389 debug_print("failure; trying to convert to %s\n", out_codeset);
6390 test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6393 if (!test_conv_global_out && !test_conv_reply) {
6395 out_codeset = CS_INTERNAL;
6396 debug_print("finally using %s\n", out_codeset);
6398 g_free(test_conv_global_out);
6399 g_free(test_conv_reply);
6400 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field,
6402 codeconv_set_strict(FALSE);
6407 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6411 cm_return_if_fail(user_data != NULL);
6413 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6414 g_strstrip(address);
6415 if (*address != '\0') {
6416 gchar *name = procheader_get_fromname(address);
6417 extract_address(address);
6418 addressbook_add_contact(name, address, NULL, NULL);
6423 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6425 GtkWidget *menuitem;
6428 cm_return_if_fail(menu != NULL);
6429 cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6431 menuitem = gtk_separator_menu_item_new();
6432 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6433 gtk_widget_show(menuitem);
6435 menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6436 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6438 address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6439 g_strstrip(address);
6440 if (*address == '\0') {
6441 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6444 g_signal_connect(G_OBJECT(menuitem), "activate",
6445 G_CALLBACK(compose_add_to_addressbook_cb), entry);
6446 gtk_widget_show(menuitem);
6449 static void compose_create_header_entry(Compose *compose)
6451 gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6458 const gchar *header = NULL;
6459 ComposeHeaderEntry *headerentry;
6460 gboolean standard_header = FALSE;
6461 GtkListStore *model;
6463 #if !(GTK_CHECK_VERSION(2,12,0))
6464 GtkTooltips *tips = compose->tooltips;
6467 headerentry = g_new0(ComposeHeaderEntry, 1);
6470 model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6471 combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6472 COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6474 COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6476 COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6478 COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6479 COMPOSE_NEWSGROUPS);
6480 COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6482 COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6483 COMPOSE_FOLLOWUPTO);
6485 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6486 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6487 G_CALLBACK(compose_grab_focus_cb), compose);
6488 gtk_widget_show(combo);
6489 gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6490 compose->header_nextrow, compose->header_nextrow+1,
6491 GTK_SHRINK, GTK_FILL, 0, 0);
6492 if (compose->header_last && (compose->draft_timeout_tag != -2)) {
6493 const gchar *last_header_entry = gtk_entry_get_text(
6494 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6496 while (*string != NULL) {
6497 if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6498 standard_header = TRUE;
6501 if (standard_header)
6502 header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6504 if (!compose->header_last || !standard_header) {
6505 switch(compose->account->protocol) {
6507 header = prefs_common_translated_header_name("Newsgroups:");
6510 header = prefs_common_translated_header_name("To:");
6515 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6517 g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6518 G_CALLBACK(compose_grab_focus_cb), compose);
6520 /* Entry field with cleanup button */
6521 button = gtk_button_new();
6522 gtk_button_set_image(GTK_BUTTON(button),
6523 gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
6524 gtk_widget_show(button);
6525 CLAWS_SET_TIP(button,
6526 _("Delete entry contents"));
6527 entry = gtk_entry_new();
6528 gtk_widget_show(entry);
6529 CLAWS_SET_TIP(entry,
6530 _("Use <tab> to autocomplete from addressbook"));
6531 hbox = gtk_hbox_new (FALSE, 0);
6532 gtk_widget_show(hbox);
6533 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
6534 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
6535 gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
6536 compose->header_nextrow, compose->header_nextrow+1,
6537 GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6539 g_signal_connect(G_OBJECT(entry), "key-press-event",
6540 G_CALLBACK(compose_headerentry_key_press_event_cb),
6542 g_signal_connect(G_OBJECT(entry), "changed",
6543 G_CALLBACK(compose_headerentry_changed_cb),
6545 g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6546 G_CALLBACK(compose_grab_focus_cb), compose);
6548 g_signal_connect(G_OBJECT(button), "clicked",
6549 G_CALLBACK(compose_headerentry_button_clicked_cb),
6553 gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types,
6554 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6555 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6556 g_signal_connect(G_OBJECT(entry), "drag_data_received",
6557 G_CALLBACK(compose_header_drag_received_cb),
6559 g_signal_connect(G_OBJECT(entry), "drag-drop",
6560 G_CALLBACK(compose_drag_drop),
6562 g_signal_connect(G_OBJECT(entry), "populate-popup",
6563 G_CALLBACK(compose_entry_popup_extend),
6566 address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6568 headerentry->compose = compose;
6569 headerentry->combo = combo;
6570 headerentry->entry = entry;
6571 headerentry->button = button;
6572 headerentry->hbox = hbox;
6573 headerentry->headernum = compose->header_nextrow;
6574 headerentry->type = PREF_NONE;
6576 compose->header_nextrow++;
6577 compose->header_last = headerentry;
6578 compose->header_list =
6579 g_slist_append(compose->header_list,
6583 static void compose_add_header_entry(Compose *compose, const gchar *header,
6584 gchar *text, ComposePrefType pref_type)
6586 ComposeHeaderEntry *last_header = compose->header_last;
6587 gchar *tmp = g_strdup(text), *email;
6588 gboolean replyto_hdr;
6590 replyto_hdr = (!strcasecmp(header,
6591 prefs_common_translated_header_name("Reply-To:")) ||
6593 prefs_common_translated_header_name("Followup-To:")) ||
6595 prefs_common_translated_header_name("In-Reply-To:")));
6597 extract_address(tmp);
6598 email = g_utf8_strdown(tmp, -1);
6600 if (replyto_hdr == FALSE &&
6601 g_hash_table_lookup(compose->email_hashtable, email) != NULL)
6603 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
6604 header, text, (gint) pref_type);
6610 if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
6611 gtk_entry_set_text(GTK_ENTRY(
6612 gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
6614 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
6615 gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6616 last_header->type = pref_type;
6618 if (replyto_hdr == FALSE)
6619 g_hash_table_insert(compose->email_hashtable, email,
6620 GUINT_TO_POINTER(1));
6627 static void compose_destroy_headerentry(Compose *compose,
6628 ComposeHeaderEntry *headerentry)
6630 gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6633 extract_address(text);
6634 email = g_utf8_strdown(text, -1);
6635 g_hash_table_remove(compose->email_hashtable, email);
6639 gtk_widget_destroy(headerentry->combo);
6640 gtk_widget_destroy(headerentry->entry);
6641 gtk_widget_destroy(headerentry->button);
6642 gtk_widget_destroy(headerentry->hbox);
6643 g_free(headerentry);
6646 static void compose_remove_header_entries(Compose *compose)
6649 for (list = compose->header_list; list; list = list->next)
6650 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
6652 compose->header_last = NULL;
6653 g_slist_free(compose->header_list);
6654 compose->header_list = NULL;
6655 compose->header_nextrow = 1;
6656 compose_create_header_entry(compose);
6659 static GtkWidget *compose_create_header(Compose *compose)
6661 GtkWidget *from_optmenu_hbox;
6662 GtkWidget *header_scrolledwin;
6663 GtkWidget *header_table;
6667 /* header labels and entries */
6668 header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6669 gtk_widget_show(header_scrolledwin);
6670 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6672 header_table = gtk_table_new(2, 2, FALSE);
6673 gtk_widget_show(header_table);
6674 gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6675 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6676 gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6679 /* option menu for selecting accounts */
6680 from_optmenu_hbox = compose_account_option_menu_create(compose);
6681 gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6682 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6685 compose->header_table = header_table;
6686 compose->header_list = NULL;
6687 compose->header_nextrow = count;
6689 compose_create_header_entry(compose);
6691 compose->table = NULL;
6693 return header_scrolledwin ;
6696 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6698 Compose *compose = (Compose *)data;
6699 GdkEventButton event;
6702 event.time = gtk_get_current_event_time();
6704 return attach_button_pressed(compose->attach_clist, &event, compose);
6707 static GtkWidget *compose_create_attach(Compose *compose)
6709 GtkWidget *attach_scrwin;
6710 GtkWidget *attach_clist;
6712 GtkListStore *store;
6713 GtkCellRenderer *renderer;
6714 GtkTreeViewColumn *column;
6715 GtkTreeSelection *selection;
6717 /* attachment list */
6718 attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6719 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6720 GTK_POLICY_AUTOMATIC,
6721 GTK_POLICY_AUTOMATIC);
6722 gtk_widget_set_size_request(attach_scrwin, -1, 80);
6724 store = gtk_list_store_new(N_ATTACH_COLS,
6729 G_TYPE_AUTO_POINTER,
6731 attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6732 (GTK_TREE_MODEL(store)));
6733 gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6734 g_object_unref(store);
6736 renderer = gtk_cell_renderer_text_new();
6737 column = gtk_tree_view_column_new_with_attributes
6738 (_("Mime type"), renderer, "text",
6739 COL_MIMETYPE, NULL);
6740 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6742 renderer = gtk_cell_renderer_text_new();
6743 column = gtk_tree_view_column_new_with_attributes
6744 (_("Size"), renderer, "text",
6746 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6748 renderer = gtk_cell_renderer_text_new();
6749 column = gtk_tree_view_column_new_with_attributes
6750 (_("Name"), renderer, "text",
6752 gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6754 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6755 prefs_common.use_stripes_everywhere);
6756 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6757 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6759 g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6760 G_CALLBACK(attach_selected), compose);
6761 g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6762 G_CALLBACK(attach_button_pressed), compose);
6764 g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6765 G_CALLBACK(popup_attach_button_pressed), compose);
6767 gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6768 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6769 g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6770 G_CALLBACK(popup_attach_button_pressed), compose);
6772 g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6773 G_CALLBACK(attach_key_pressed), compose);
6776 gtk_drag_dest_set(attach_clist,
6777 GTK_DEST_DEFAULT_ALL, compose_mime_types,
6778 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6779 GDK_ACTION_COPY | GDK_ACTION_MOVE);
6780 g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6781 G_CALLBACK(compose_attach_drag_received_cb),
6783 g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6784 G_CALLBACK(compose_drag_drop),
6787 compose->attach_scrwin = attach_scrwin;
6788 compose->attach_clist = attach_clist;
6790 return attach_scrwin;
6793 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6794 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6796 static GtkWidget *compose_create_others(Compose *compose)
6799 GtkWidget *savemsg_checkbtn;
6800 GtkWidget *savemsg_combo;
6801 GtkWidget *savemsg_select;
6804 gchar *folderidentifier;
6806 /* Table for settings */
6807 table = gtk_table_new(3, 1, FALSE);
6808 gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6809 gtk_widget_show(table);
6810 gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6813 /* Save Message to folder */
6814 savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6815 gtk_widget_show(savemsg_checkbtn);
6816 gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6817 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6818 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6820 g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6821 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6823 savemsg_combo = gtk_combo_box_entry_new_text();
6824 compose->savemsg_checkbtn = savemsg_checkbtn;
6825 compose->savemsg_combo = savemsg_combo;
6826 gtk_widget_show(savemsg_combo);
6828 if (prefs_common.compose_save_to_history)
6829 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
6830 prefs_common.compose_save_to_history);
6832 gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
6833 gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
6834 g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
6835 G_CALLBACK(compose_grab_focus_cb), compose);
6836 if (account_get_special_folder(compose->account, F_OUTBOX)) {
6837 folderidentifier = folder_item_get_identifier(account_get_special_folder
6838 (compose->account, F_OUTBOX));
6839 compose_set_save_to(compose, folderidentifier);
6840 g_free(folderidentifier);
6843 savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6844 gtk_widget_show(savemsg_select);
6845 gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6846 g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6847 G_CALLBACK(compose_savemsg_select_cb),
6855 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose)
6857 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
6858 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6861 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6866 dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6869 path = folder_item_get_identifier(dest);
6871 compose_set_save_to(compose, path);
6875 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6876 GdkAtom clip, GtkTextIter *insert_place);
6879 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6883 GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6885 if (event->button == 3) {
6887 GtkTextIter sel_start, sel_end;
6888 gboolean stuff_selected;
6890 /* move the cursor to allow GtkAspell to check the word
6891 * under the mouse */
6892 if (event->x && event->y) {
6893 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6894 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6896 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6899 GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6900 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6903 stuff_selected = gtk_text_buffer_get_selection_bounds(
6905 &sel_start, &sel_end);
6907 gtk_text_buffer_place_cursor (buffer, &iter);
6908 /* reselect stuff */
6910 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6911 gtk_text_buffer_select_range(buffer,
6912 &sel_start, &sel_end);
6914 return FALSE; /* pass the event so that the right-click goes through */
6917 if (event->button == 2) {
6922 /* get the middle-click position to paste at the correct place */
6923 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6924 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6926 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6929 entry_paste_clipboard(compose, text,
6930 prefs_common.linewrap_pastes,
6931 GDK_SELECTION_PRIMARY, &iter);
6939 static void compose_spell_menu_changed(void *data)
6941 Compose *compose = (Compose *)data;
6943 GtkWidget *menuitem;
6944 GtkWidget *parent_item;
6945 GtkMenu *menu = GTK_MENU(gtk_menu_new());
6948 if (compose->gtkaspell == NULL)
6951 parent_item = gtk_ui_manager_get_widget(compose->ui_manager,
6952 "/Menu/Spelling/Options");
6954 /* setting the submenu removes /Spelling/Options from the factory
6955 * so we need to save it */
6957 if (parent_item == NULL) {
6958 parent_item = compose->aspell_options_menu;
6959 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6961 compose->aspell_options_menu = parent_item;
6963 spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6965 spell_menu = g_slist_reverse(spell_menu);
6966 for (items = spell_menu;
6967 items; items = items->next) {
6968 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6969 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6970 gtk_widget_show(GTK_WIDGET(menuitem));
6972 g_slist_free(spell_menu);
6974 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6975 gtk_widget_show(parent_item);
6978 static void compose_dict_changed(void *data)
6980 Compose *compose = (Compose *) data;
6982 if(compose->gtkaspell &&
6983 compose->gtkaspell->recheck_when_changing_dict == FALSE)
6986 gtkaspell_highlight_all(compose->gtkaspell);
6987 claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
6991 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6993 Compose *compose = (Compose *)data;
6994 GdkEventButton event;
6997 event.time = gtk_get_current_event_time();
7001 return text_clicked(compose->text, &event, compose);
7004 static gboolean compose_force_window_origin = TRUE;
7005 static Compose *compose_create(PrefsAccount *account,
7014 GtkWidget *handlebox;
7016 GtkWidget *notebook;
7018 GtkWidget *attach_hbox;
7019 GtkWidget *attach_lab1;
7020 GtkWidget *attach_lab2;
7025 GtkWidget *subject_hbox;
7026 GtkWidget *subject_frame;
7027 GtkWidget *subject_entry;
7031 GtkWidget *edit_vbox;
7032 GtkWidget *ruler_hbox;
7034 GtkWidget *scrolledwin;
7036 GtkTextBuffer *buffer;
7037 GtkClipboard *clipboard;
7040 UndoMain *undostruct;
7042 gchar *titles[N_ATTACH_COLS];
7043 GtkWidget *popupmenu;
7044 GtkWidget *tmpl_menu;
7045 GtkActionGroup *action_group = NULL;
7048 GtkAspell * gtkaspell = NULL;
7051 static GdkGeometry geometry;
7053 cm_return_val_if_fail(account != NULL, NULL);
7055 debug_print("Creating compose window...\n");
7056 compose = g_new0(Compose, 1);
7058 titles[COL_MIMETYPE] = _("MIME type");
7059 titles[COL_SIZE] = _("Size");
7060 titles[COL_NAME] = _("Name");
7062 compose->batch = batch;
7063 compose->account = account;
7064 compose->folder = folder;
7066 compose->mutex = g_mutex_new();
7067 compose->set_cursor_pos = -1;
7069 #if !(GTK_CHECK_VERSION(2,12,0))
7070 compose->tooltips = tips;
7073 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7075 gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7076 gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
7078 if (!geometry.max_width) {
7079 geometry.max_width = gdk_screen_width();
7080 geometry.max_height = gdk_screen_height();
7083 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7084 &geometry, GDK_HINT_MAX_SIZE);
7085 if (!geometry.min_width) {
7086 geometry.min_width = 600;
7087 geometry.min_height = 440;
7089 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7090 &geometry, GDK_HINT_MIN_SIZE);
7092 #ifndef GENERIC_UMPC
7093 if (compose_force_window_origin)
7094 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x,
7095 prefs_common.compose_y);
7097 g_signal_connect(G_OBJECT(window), "delete_event",
7098 G_CALLBACK(compose_delete_cb), compose);
7099 MANAGE_WINDOW_SIGNALS_CONNECT(window);
7100 gtk_widget_realize(window);
7102 gtkut_widget_set_composer_icon(window);
7104 vbox = gtk_vbox_new(FALSE, 0);
7105 gtk_container_add(GTK_CONTAINER(window), vbox);
7107 compose->ui_manager = gtk_ui_manager_new();
7108 action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7109 G_N_ELEMENTS(compose_entries), (gpointer)compose);
7110 gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7111 G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7112 gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7113 G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7114 gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7115 G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7116 gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7117 G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7120 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7122 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_POPUP)
7125 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7126 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7128 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7130 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7131 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7132 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7135 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7136 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7137 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7138 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7139 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7140 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7141 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7142 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7143 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7144 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7147 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7148 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7149 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7151 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7152 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7153 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7155 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7156 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7157 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7158 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7160 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7162 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7163 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7164 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7165 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7166 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7167 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7168 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7169 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7170 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7171 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7172 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7173 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7174 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7175 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7176 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7178 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7180 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7181 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7182 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7183 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7184 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7186 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7188 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7192 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7193 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7194 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7195 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7196 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7197 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7201 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7202 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7203 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7204 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7205 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7207 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7208 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7209 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7210 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7211 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7214 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7215 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7216 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7217 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7218 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7219 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7220 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7222 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7223 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7224 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7225 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7226 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7228 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7230 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7231 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7232 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7233 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7234 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7236 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7237 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)
7238 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)
7239 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7241 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7243 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7244 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)
7245 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)
7247 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7249 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7250 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)
7251 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7253 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7254 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)
7255 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7257 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7259 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7260 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)
7261 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7262 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7263 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7265 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7266 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)
7267 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)
7268 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7269 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7271 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7272 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7273 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7274 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7275 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7276 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7278 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7279 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7280 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)
7282 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7283 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7284 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7288 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7289 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7290 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7291 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7292 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7293 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7296 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7298 menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7299 gtk_widget_show_all(menubar);
7301 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7303 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7305 hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
7308 if (prefs_common.toolbar_detachable) {
7309 handlebox = gtk_handle_box_new();
7311 handlebox = gtk_hbox_new(FALSE, 0);
7313 gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7315 gtk_widget_realize(handlebox);
7317 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
7320 compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7324 vbox2 = gtk_vbox_new(FALSE, 2);
7325 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7326 gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7329 notebook = gtk_notebook_new();
7330 gtk_widget_set_size_request(notebook, -1, 130);
7331 gtk_widget_show(notebook);
7333 /* header labels and entries */
7334 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7335 compose_create_header(compose),
7336 gtk_label_new_with_mnemonic(_("Hea_der")));
7337 /* attachment list */
7338 attach_hbox = gtk_hbox_new(FALSE, 0);
7339 gtk_widget_show(attach_hbox);
7341 attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7342 gtk_widget_show(attach_lab1);
7343 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7345 attach_lab2 = gtk_label_new("");
7346 gtk_widget_show(attach_lab2);
7347 gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7349 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7350 compose_create_attach(compose),
7353 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7354 compose_create_others(compose),
7355 gtk_label_new_with_mnemonic(_("Othe_rs")));
7358 subject_hbox = gtk_hbox_new(FALSE, 0);
7359 gtk_widget_show(subject_hbox);
7361 subject_frame = gtk_frame_new(NULL);
7362 gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7363 gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7364 gtk_widget_show(subject_frame);
7366 subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7367 gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7368 gtk_widget_show(subject);
7370 label = gtk_label_new(_("Subject:"));
7371 gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7372 gtk_widget_show(label);
7375 subject_entry = claws_spell_entry_new();
7377 subject_entry = gtk_entry_new();
7379 gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7380 g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7381 G_CALLBACK(compose_grab_focus_cb), compose);
7382 gtk_widget_show(subject_entry);
7383 compose->subject_entry = subject_entry;
7384 gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7386 edit_vbox = gtk_vbox_new(FALSE, 0);
7388 gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7391 ruler_hbox = gtk_hbox_new(FALSE, 0);
7392 gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7394 ruler = gtk_shruler_new();
7395 gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7396 gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7400 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7401 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7402 GTK_POLICY_AUTOMATIC,
7403 GTK_POLICY_AUTOMATIC);
7404 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7406 gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7407 gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7409 text = gtk_text_view_new();
7410 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7411 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7412 gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7413 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7414 gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7416 gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7418 g_signal_connect_after(G_OBJECT(text), "size_allocate",
7419 G_CALLBACK(compose_edit_size_alloc),
7421 g_signal_connect(G_OBJECT(buffer), "changed",
7422 G_CALLBACK(compose_changed_cb), compose);
7423 g_signal_connect(G_OBJECT(text), "grab_focus",
7424 G_CALLBACK(compose_grab_focus_cb), compose);
7425 g_signal_connect(G_OBJECT(buffer), "insert_text",
7426 G_CALLBACK(text_inserted), compose);
7427 g_signal_connect(G_OBJECT(text), "button_press_event",
7428 G_CALLBACK(text_clicked), compose);
7430 g_signal_connect(G_OBJECT(text), "popup-menu",
7431 G_CALLBACK(compose_popup_menu), compose);
7433 gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7434 GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7435 g_signal_connect(G_OBJECT(text), "tap-and-hold",
7436 G_CALLBACK(compose_popup_menu), compose);
7438 g_signal_connect(G_OBJECT(subject_entry), "changed",
7439 G_CALLBACK(compose_changed_cb), compose);
7442 gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types,
7443 sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7444 GDK_ACTION_COPY | GDK_ACTION_MOVE);
7445 g_signal_connect(G_OBJECT(text), "drag_data_received",
7446 G_CALLBACK(compose_insert_drag_received_cb),
7448 g_signal_connect(G_OBJECT(text), "drag-drop",
7449 G_CALLBACK(compose_drag_drop),
7451 gtk_widget_show_all(vbox);
7453 /* pane between attach clist and text */
7454 paned = gtk_vpaned_new();
7455 gtk_container_add(GTK_CONTAINER(vbox2), paned);
7457 if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7458 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7460 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7462 gtk_paned_add1(GTK_PANED(paned), notebook);
7463 gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7464 gtk_widget_show_all(paned);
7467 if (prefs_common.textfont) {
7468 PangoFontDescription *font_desc;
7470 font_desc = pango_font_description_from_string
7471 (prefs_common.textfont);
7473 gtk_widget_modify_font(text, font_desc);
7474 pango_font_description_free(font_desc);
7478 gtk_action_group_add_actions(action_group, compose_popup_entries,
7479 G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7480 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7481 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7482 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7483 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7484 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7485 MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7487 popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7489 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7490 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7491 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7493 tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7495 undostruct = undo_init(text);
7496 undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7499 address_completion_start(window);
7501 compose->window = window;
7502 compose->vbox = vbox;
7503 compose->menubar = menubar;
7504 compose->handlebox = handlebox;
7506 compose->vbox2 = vbox2;
7508 compose->paned = paned;
7510 compose->attach_label = attach_lab2;
7512 compose->notebook = notebook;
7513 compose->edit_vbox = edit_vbox;
7514 compose->ruler_hbox = ruler_hbox;
7515 compose->ruler = ruler;
7516 compose->scrolledwin = scrolledwin;
7517 compose->text = text;
7519 compose->focused_editable = NULL;
7521 compose->popupmenu = popupmenu;
7523 compose->tmpl_menu = tmpl_menu;
7525 compose->mode = mode;
7526 compose->rmode = mode;
7528 compose->targetinfo = NULL;
7529 compose->replyinfo = NULL;
7530 compose->fwdinfo = NULL;
7532 compose->email_hashtable = g_hash_table_new_full(g_str_hash,
7533 g_str_equal, (GDestroyNotify) g_free, NULL);
7535 compose->replyto = NULL;
7537 compose->bcc = NULL;
7538 compose->followup_to = NULL;
7540 compose->ml_post = NULL;
7542 compose->inreplyto = NULL;
7543 compose->references = NULL;
7544 compose->msgid = NULL;
7545 compose->boundary = NULL;
7547 compose->autowrap = prefs_common.autowrap;
7548 compose->autoindent = prefs_common.auto_indent;
7549 compose->use_signing = FALSE;
7550 compose->use_encryption = FALSE;
7551 compose->privacy_system = NULL;
7553 compose->modified = FALSE;
7555 compose->return_receipt = FALSE;
7557 compose->to_list = NULL;
7558 compose->newsgroup_list = NULL;
7560 compose->undostruct = undostruct;
7562 compose->sig_str = NULL;
7564 compose->exteditor_file = NULL;
7565 compose->exteditor_pid = -1;
7566 compose->exteditor_tag = -1;
7567 compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7570 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7571 if (mode != COMPOSE_REDIRECT) {
7572 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7573 strcmp(prefs_common.dictionary, "")) {
7574 gtkaspell = gtkaspell_new(prefs_common.dictionary,
7575 prefs_common.alt_dictionary,
7576 conv_get_locale_charset_str(),
7577 prefs_common.misspelled_col,
7578 prefs_common.check_while_typing,
7579 prefs_common.recheck_when_changing_dict,
7580 prefs_common.use_alternate,
7581 prefs_common.use_both_dicts,
7582 GTK_TEXT_VIEW(text),
7583 GTK_WINDOW(compose->window),
7584 compose_dict_changed,
7585 compose_spell_menu_changed,
7588 alertpanel_error(_("Spell checker could not "
7590 gtkaspell_checkers_strerror());
7591 gtkaspell_checkers_reset_error();
7593 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7597 compose->gtkaspell = gtkaspell;
7598 compose_spell_menu_changed(compose);
7599 claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
7602 compose_select_account(compose, account, TRUE);
7604 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7605 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
7607 if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7608 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7610 if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT)
7611 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7613 if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7614 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7616 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7617 if (account->protocol != A_NNTP)
7618 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7619 prefs_common_translated_header_name("To:"));
7621 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7622 prefs_common_translated_header_name("Newsgroups:"));
7624 addressbook_set_target_compose(compose);
7626 if (mode != COMPOSE_REDIRECT)
7627 compose_set_template_menu(compose);
7629 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7632 compose_list = g_list_append(compose_list, compose);
7634 if (!prefs_common.show_ruler)
7635 gtk_widget_hide(ruler_hbox);
7637 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7640 compose->priority = PRIORITY_NORMAL;
7641 compose_update_priority_menu_item(compose);
7643 compose_set_out_encoding(compose);
7646 compose_update_actions_menu(compose);
7648 /* Privacy Systems menu */
7649 compose_update_privacy_systems_menu(compose);
7651 activate_privacy_system(compose, account, TRUE);
7652 toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7654 gtk_widget_realize(window);
7656 gtk_widget_show(window);
7658 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7659 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7666 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7671 GtkWidget *optmenubox;
7674 GtkWidget *from_name = NULL;
7675 #if !(GTK_CHECK_VERSION(2,12,0))
7676 GtkTooltips *tips = compose->tooltips;
7679 gint num = 0, def_menu = 0;
7681 accounts = account_get_list();
7682 cm_return_val_if_fail(accounts != NULL, NULL);
7684 optmenubox = gtk_event_box_new();
7685 optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7686 menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7688 hbox = gtk_hbox_new(FALSE, 6);
7689 from_name = gtk_entry_new();
7691 g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7692 G_CALLBACK(compose_grab_focus_cb), compose);
7694 for (; accounts != NULL; accounts = accounts->next, num++) {
7695 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7696 gchar *name, *from = NULL;
7698 if (ac == compose->account) def_menu = num;
7700 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7703 if (ac == compose->account) {
7704 if (ac->name && *ac->name) {
7706 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7707 from = g_strdup_printf("%s <%s>",
7709 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7711 from = g_strdup_printf("%s",
7713 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7716 COMBOBOX_ADD(menu, name, ac->account_id);
7721 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7723 g_signal_connect(G_OBJECT(optmenu), "changed",
7724 G_CALLBACK(account_activated),
7726 g_signal_connect(G_OBJECT(from_name), "populate-popup",
7727 G_CALLBACK(compose_entry_popup_extend),
7730 gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7731 gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7733 CLAWS_SET_TIP(optmenubox,
7734 _("Account to use for this email"));
7735 CLAWS_SET_TIP(from_name,
7736 _("Sender address to be used"));
7738 compose->account_combo = optmenu;
7739 compose->from_name = from_name;
7744 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7746 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7747 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7748 Compose *compose = (Compose *) data;
7750 compose->priority = value;
7754 static void compose_reply_change_mode(Compose *compose,
7757 gboolean was_modified = compose->modified;
7759 gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7761 cm_return_if_fail(compose->replyinfo != NULL);
7763 if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7765 if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7767 if (action == COMPOSE_REPLY_TO_ALL)
7769 if (action == COMPOSE_REPLY_TO_SENDER)
7771 if (action == COMPOSE_REPLY_TO_LIST)
7774 compose_remove_header_entries(compose);
7775 compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7776 if (compose->account->set_autocc && compose->account->auto_cc)
7777 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
7779 if (compose->account->set_autobcc && compose->account->auto_bcc)
7780 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
7782 if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7783 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
7784 compose_show_first_last_header(compose, TRUE);
7785 compose->modified = was_modified;
7786 compose_set_title(compose);
7789 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7791 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7792 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7793 Compose *compose = (Compose *) data;
7796 compose_reply_change_mode(compose, value);
7799 static void compose_update_priority_menu_item(Compose * compose)
7801 GtkWidget *menuitem = NULL;
7802 switch (compose->priority) {
7803 case PRIORITY_HIGHEST:
7804 menuitem = gtk_ui_manager_get_widget
7805 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7808 menuitem = gtk_ui_manager_get_widget
7809 (compose->ui_manager, "/Menu/Options/Priority/High");
7811 case PRIORITY_NORMAL:
7812 menuitem = gtk_ui_manager_get_widget
7813 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7816 menuitem = gtk_ui_manager_get_widget
7817 (compose->ui_manager, "/Menu/Options/Priority/Low");
7819 case PRIORITY_LOWEST:
7820 menuitem = gtk_ui_manager_get_widget
7821 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7824 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7827 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7829 Compose *compose = (Compose *) data;
7831 gboolean can_sign = FALSE, can_encrypt = FALSE;
7833 cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7835 if (!GTK_CHECK_MENU_ITEM(widget)->active)
7838 systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7839 g_free(compose->privacy_system);
7840 compose->privacy_system = NULL;
7841 if (systemid != NULL) {
7842 compose->privacy_system = g_strdup(systemid);
7844 can_sign = privacy_system_can_sign(systemid);
7845 can_encrypt = privacy_system_can_encrypt(systemid);
7848 debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7850 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7851 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7854 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7856 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7857 GtkWidget *menuitem = NULL;
7859 gboolean can_sign = FALSE, can_encrypt = FALSE;
7860 gboolean found = FALSE;
7862 if (compose->privacy_system != NULL) {
7864 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7865 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7866 cm_return_if_fail(menuitem != NULL);
7868 amenu = GTK_MENU_SHELL(menuitem)->children;
7870 while (amenu != NULL) {
7871 GList *alist = amenu->next;
7873 systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7874 if (systemid != NULL) {
7875 if (strcmp(systemid, compose->privacy_system) == 0 &&
7876 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7877 menuitem = GTK_WIDGET(amenu->data);
7879 can_sign = privacy_system_can_sign(systemid);
7880 can_encrypt = privacy_system_can_encrypt(systemid);
7884 } else if (strlen(compose->privacy_system) == 0 &&
7885 GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7886 menuitem = GTK_WIDGET(amenu->data);
7889 can_encrypt = FALSE;
7896 if (menuitem != NULL)
7897 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7899 if (warn && !found && strlen(compose->privacy_system)) {
7900 alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7901 "will not be able to sign or encrypt this message."),
7902 compose->privacy_system);
7906 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7907 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7910 static void compose_set_out_encoding(Compose *compose)
7912 CharSet out_encoding;
7913 const gchar *branch = NULL;
7914 out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7916 switch(out_encoding) {
7917 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7918 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7919 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7920 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7921 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7922 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7923 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7924 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7925 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7926 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7927 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7928 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7929 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7930 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7931 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7932 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7933 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7934 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7935 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7936 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7937 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7938 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7939 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7940 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
7941 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7942 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7943 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7944 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7945 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7946 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7947 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7948 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7949 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7951 cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7954 static void compose_set_template_menu(Compose *compose)
7956 GSList *tmpl_list, *cur;
7960 tmpl_list = template_get_config();
7962 menu = gtk_menu_new();
7964 gtk_menu_set_accel_group (GTK_MENU (menu),
7965 gtk_ui_manager_get_accel_group(compose->ui_manager));
7966 for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7967 Template *tmpl = (Template *)cur->data;
7968 gchar *accel_path = NULL;
7969 item = gtk_menu_item_new_with_label(tmpl->name);
7970 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7971 g_signal_connect(G_OBJECT(item), "activate",
7972 G_CALLBACK(compose_template_activate_cb),
7974 g_object_set_data(G_OBJECT(item), "template", tmpl);
7975 gtk_widget_show(item);
7976 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7977 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7981 gtk_widget_show(menu);
7982 gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7985 void compose_update_actions_menu(Compose *compose)
7987 action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7990 static void compose_update_privacy_systems_menu(Compose *compose)
7992 static gchar *branch_path = "/Menu/Options/PrivacySystem";
7993 GSList *systems, *cur;
7995 GtkWidget *system_none;
7997 GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7998 GtkWidget *privacy_menu = gtk_menu_new();
8000 system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8001 g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8003 g_signal_connect(G_OBJECT(system_none), "activate",
8004 G_CALLBACK(compose_set_privacy_system_cb), compose);
8006 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8007 gtk_widget_show(system_none);
8009 systems = privacy_get_system_ids();
8010 for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8011 gchar *systemid = cur->data;
8013 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8014 widget = gtk_radio_menu_item_new_with_label(group,
8015 privacy_system_get_name(systemid));
8016 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8017 g_strdup(systemid), g_free);
8018 g_signal_connect(G_OBJECT(widget), "activate",
8019 G_CALLBACK(compose_set_privacy_system_cb), compose);
8021 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8022 gtk_widget_show(widget);
8025 g_slist_free(systems);
8026 gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8027 gtk_widget_show_all(privacy_menu);
8028 gtk_widget_show_all(privacy_menuitem);
8031 void compose_reflect_prefs_all(void)
8036 for (cur = compose_list; cur != NULL; cur = cur->next) {
8037 compose = (Compose *)cur->data;
8038 compose_set_template_menu(compose);
8042 void compose_reflect_prefs_pixmap_theme(void)
8047 for (cur = compose_list; cur != NULL; cur = cur->next) {
8048 compose = (Compose *)cur->data;
8049 toolbar_update(TOOLBAR_COMPOSE, compose);
8053 static const gchar *compose_quote_char_from_context(Compose *compose)
8055 const gchar *qmark = NULL;
8057 cm_return_val_if_fail(compose != NULL, NULL);
8059 switch (compose->mode) {
8060 /* use forward-specific quote char */
8061 case COMPOSE_FORWARD:
8062 case COMPOSE_FORWARD_AS_ATTACH:
8063 case COMPOSE_FORWARD_INLINE:
8064 if (compose->folder && compose->folder->prefs &&
8065 compose->folder->prefs->forward_with_format)
8066 qmark = compose->folder->prefs->forward_quotemark;
8067 else if (compose->account->forward_with_format)
8068 qmark = compose->account->forward_quotemark;
8070 qmark = prefs_common.fw_quotemark;
8073 /* use reply-specific quote char in all other modes */
8075 if (compose->folder && compose->folder->prefs &&
8076 compose->folder->prefs->reply_with_format)
8077 qmark = compose->folder->prefs->reply_quotemark;
8078 else if (compose->account->reply_with_format)
8079 qmark = compose->account->reply_quotemark;
8081 qmark = prefs_common.quotemark;
8085 if (qmark == NULL || *qmark == '\0')
8091 static void compose_template_apply(Compose *compose, Template *tmpl,
8095 GtkTextBuffer *buffer;
8099 gchar *parsed_str = NULL;
8100 gint cursor_pos = 0;
8101 const gchar *err_msg = _("The body of the template has an error at line %d.");
8104 /* process the body */
8106 text = GTK_TEXT_VIEW(compose->text);
8107 buffer = gtk_text_view_get_buffer(text);
8110 qmark = compose_quote_char_from_context(compose);
8112 if (compose->replyinfo != NULL) {
8115 gtk_text_buffer_set_text(buffer, "", -1);
8116 mark = gtk_text_buffer_get_insert(buffer);
8117 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8119 parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8120 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8122 } else if (compose->fwdinfo != NULL) {
8125 gtk_text_buffer_set_text(buffer, "", -1);
8126 mark = gtk_text_buffer_get_insert(buffer);
8127 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8129 parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8130 tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8133 MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8135 GtkTextIter start, end;
8138 gtk_text_buffer_get_start_iter(buffer, &start);
8139 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8140 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8142 /* clear the buffer now */
8144 gtk_text_buffer_set_text(buffer, "", -1);
8146 parsed_str = compose_quote_fmt(compose, dummyinfo,
8147 tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8148 procmsg_msginfo_free( dummyinfo );
8154 gtk_text_buffer_set_text(buffer, "", -1);
8155 mark = gtk_text_buffer_get_insert(buffer);
8156 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8159 if (replace && parsed_str && compose->account->auto_sig)
8160 compose_insert_sig(compose, FALSE);
8162 if (replace && parsed_str) {
8163 gtk_text_buffer_get_start_iter(buffer, &iter);
8164 gtk_text_buffer_place_cursor(buffer, &iter);
8168 cursor_pos = quote_fmt_get_cursor_pos();
8169 compose->set_cursor_pos = cursor_pos;
8170 if (cursor_pos == -1)
8172 gtk_text_buffer_get_start_iter(buffer, &iter);
8173 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8174 gtk_text_buffer_place_cursor(buffer, &iter);
8177 /* process the other fields */
8179 compose_template_apply_fields(compose, tmpl);
8180 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8181 quote_fmt_reset_vartable();
8182 compose_changed_cb(NULL, compose);
8185 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8186 gtkaspell_highlight_all(compose->gtkaspell);
8190 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8192 MsgInfo* dummyinfo = NULL;
8193 MsgInfo *msginfo = NULL;
8196 if (compose->replyinfo != NULL)
8197 msginfo = compose->replyinfo;
8198 else if (compose->fwdinfo != NULL)
8199 msginfo = compose->fwdinfo;
8201 dummyinfo = compose_msginfo_new_from_compose(compose);
8202 msginfo = dummyinfo;
8205 if (tmpl->from && *tmpl->from != '\0') {
8207 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8208 compose->gtkaspell);
8210 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8212 quote_fmt_scan_string(tmpl->from);
8215 buf = quote_fmt_get_buffer();
8217 alertpanel_error(_("Template From format error."));
8219 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8223 if (tmpl->to && *tmpl->to != '\0') {
8225 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8226 compose->gtkaspell);
8228 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8230 quote_fmt_scan_string(tmpl->to);
8233 buf = quote_fmt_get_buffer();
8235 alertpanel_error(_("Template To format error."));
8237 compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8241 if (tmpl->cc && *tmpl->cc != '\0') {
8243 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8244 compose->gtkaspell);
8246 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8248 quote_fmt_scan_string(tmpl->cc);
8251 buf = quote_fmt_get_buffer();
8253 alertpanel_error(_("Template Cc format error."));
8255 compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8259 if (tmpl->bcc && *tmpl->bcc != '\0') {
8261 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8262 compose->gtkaspell);
8264 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8266 quote_fmt_scan_string(tmpl->bcc);
8269 buf = quote_fmt_get_buffer();
8271 alertpanel_error(_("Template Bcc format error."));
8273 compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8277 /* process the subject */
8278 if (tmpl->subject && *tmpl->subject != '\0') {
8280 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8281 compose->gtkaspell);
8283 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8285 quote_fmt_scan_string(tmpl->subject);
8288 buf = quote_fmt_get_buffer();
8290 alertpanel_error(_("Template subject format error."));
8292 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8296 procmsg_msginfo_free( dummyinfo );
8299 static void compose_destroy(Compose *compose)
8301 GtkTextBuffer *buffer;
8302 GtkClipboard *clipboard;
8304 compose_list = g_list_remove(compose_list, compose);
8306 if (compose->updating) {
8307 debug_print("danger, not destroying anything now\n");
8308 compose->deferred_destroy = TRUE;
8311 /* NOTE: address_completion_end() does nothing with the window
8312 * however this may change. */
8313 address_completion_end(compose->window);
8315 slist_free_strings(compose->to_list);
8316 g_slist_free(compose->to_list);
8317 slist_free_strings(compose->newsgroup_list);
8318 g_slist_free(compose->newsgroup_list);
8319 slist_free_strings(compose->header_list);
8320 g_slist_free(compose->header_list);
8322 compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8324 g_hash_table_destroy(compose->email_hashtable);
8326 procmsg_msginfo_free(compose->targetinfo);
8327 procmsg_msginfo_free(compose->replyinfo);
8328 procmsg_msginfo_free(compose->fwdinfo);
8330 g_free(compose->replyto);
8331 g_free(compose->cc);
8332 g_free(compose->bcc);
8333 g_free(compose->newsgroups);
8334 g_free(compose->followup_to);
8336 g_free(compose->ml_post);
8338 g_free(compose->inreplyto);
8339 g_free(compose->references);
8340 g_free(compose->msgid);
8341 g_free(compose->boundary);
8343 g_free(compose->redirect_filename);
8344 if (compose->undostruct)
8345 undo_destroy(compose->undostruct);
8347 g_free(compose->sig_str);
8349 g_free(compose->exteditor_file);
8351 g_free(compose->orig_charset);
8353 g_free(compose->privacy_system);
8355 if (addressbook_get_target_compose() == compose)
8356 addressbook_set_target_compose(NULL);
8359 if (compose->gtkaspell) {
8360 gtkaspell_delete(compose->gtkaspell);
8361 compose->gtkaspell = NULL;
8365 if (!compose->batch) {
8366 prefs_common.compose_width = compose->scrolledwin->allocation.width;
8367 prefs_common.compose_height = compose->window->allocation.height;
8370 if (!gtk_widget_get_parent(compose->paned))
8371 gtk_widget_destroy(compose->paned);
8372 gtk_widget_destroy(compose->popupmenu);
8374 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8375 clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8376 gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8378 gtk_widget_destroy(compose->window);
8379 toolbar_destroy(compose->toolbar);
8380 g_free(compose->toolbar);
8381 g_mutex_free(compose->mutex);
8385 static void compose_attach_info_free(AttachInfo *ainfo)
8387 g_free(ainfo->file);
8388 g_free(ainfo->content_type);
8389 g_free(ainfo->name);
8393 static void compose_attach_update_label(Compose *compose)
8398 GtkTreeModel *model;
8403 model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8404 if(!gtk_tree_model_get_iter_first(model, &iter)) {
8405 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");
8409 while(gtk_tree_model_iter_next(model, &iter))
8412 text = g_strdup_printf("(%d)", i);
8413 gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8417 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8419 Compose *compose = (Compose *)data;
8420 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8421 GtkTreeSelection *selection;
8423 GtkTreeModel *model;
8425 selection = gtk_tree_view_get_selection(tree_view);
8426 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8431 for (cur = sel; cur != NULL; cur = cur->next) {
8432 GtkTreePath *path = cur->data;
8433 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8436 gtk_tree_path_free(path);
8439 for (cur = sel; cur != NULL; cur = cur->next) {
8440 GtkTreeRowReference *ref = cur->data;
8441 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8444 if (gtk_tree_model_get_iter(model, &iter, path))
8445 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8447 gtk_tree_path_free(path);
8448 gtk_tree_row_reference_free(ref);
8452 compose_attach_update_label(compose);
8455 static struct _AttachProperty
8458 GtkWidget *mimetype_entry;
8459 GtkWidget *encoding_optmenu;
8460 GtkWidget *path_entry;
8461 GtkWidget *filename_entry;
8463 GtkWidget *cancel_btn;
8466 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8468 gtk_tree_path_free((GtkTreePath *)ptr);
8471 static void compose_attach_property(GtkAction *action, gpointer data)
8473 Compose *compose = (Compose *)data;
8474 GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8476 GtkComboBox *optmenu;
8477 GtkTreeSelection *selection;
8479 GtkTreeModel *model;
8482 static gboolean cancelled;
8484 /* only if one selected */
8485 selection = gtk_tree_view_get_selection(tree_view);
8486 if (gtk_tree_selection_count_selected_rows(selection) != 1)
8489 sel = gtk_tree_selection_get_selected_rows(selection, &model);
8493 path = (GtkTreePath *) sel->data;
8494 gtk_tree_model_get_iter(model, &iter, path);
8495 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
8498 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8504 if (!attach_prop.window)
8505 compose_attach_property_create(&cancelled);
8506 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
8507 gtk_widget_grab_focus(attach_prop.ok_btn);
8508 gtk_widget_show(attach_prop.window);
8509 manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8511 optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8512 if (ainfo->encoding == ENC_UNKNOWN)
8513 combobox_select_by_data(optmenu, ENC_BASE64);
8515 combobox_select_by_data(optmenu, ainfo->encoding);
8517 gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8518 ainfo->content_type ? ainfo->content_type : "");
8519 gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8520 ainfo->file ? ainfo->file : "");
8521 gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8522 ainfo->name ? ainfo->name : "");
8525 const gchar *entry_text;
8527 gchar *cnttype = NULL;
8534 gtk_widget_hide(attach_prop.window);
8535 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
8540 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8541 if (*entry_text != '\0') {
8544 text = g_strstrip(g_strdup(entry_text));
8545 if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8546 cnttype = g_strdup(text);
8549 alertpanel_error(_("Invalid MIME type."));
8555 ainfo->encoding = combobox_get_active_data(optmenu);
8557 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8558 if (*entry_text != '\0') {
8559 if (is_file_exist(entry_text) &&
8560 (size = get_file_size(entry_text)) > 0)
8561 file = g_strdup(entry_text);
8564 (_("File doesn't exist or is empty."));
8570 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8571 if (*entry_text != '\0') {
8572 g_free(ainfo->name);
8573 ainfo->name = g_strdup(entry_text);
8577 g_free(ainfo->content_type);
8578 ainfo->content_type = cnttype;
8581 g_free(ainfo->file);
8585 ainfo->size = (goffset)size;
8587 /* update tree store */
8588 text = to_human_readable(ainfo->size);
8589 gtk_tree_model_get_iter(model, &iter, path);
8590 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8591 COL_MIMETYPE, ainfo->content_type,
8593 COL_NAME, ainfo->name,
8599 gtk_tree_path_free(path);
8602 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8604 label = gtk_label_new(str); \
8605 gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8606 GTK_FILL, 0, 0, 0); \
8607 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8609 entry = gtk_entry_new(); \
8610 gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8611 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8614 static void compose_attach_property_create(gboolean *cancelled)
8620 GtkWidget *mimetype_entry;
8623 GtkListStore *optmenu_menu;
8624 GtkWidget *path_entry;
8625 GtkWidget *filename_entry;
8628 GtkWidget *cancel_btn;
8629 GList *mime_type_list, *strlist;
8632 debug_print("Creating attach_property window...\n");
8634 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8635 gtk_widget_set_size_request(window, 480, -1);
8636 gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8637 gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8638 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8639 g_signal_connect(G_OBJECT(window), "delete_event",
8640 G_CALLBACK(attach_property_delete_event),
8642 g_signal_connect(G_OBJECT(window), "key_press_event",
8643 G_CALLBACK(attach_property_key_pressed),
8646 vbox = gtk_vbox_new(FALSE, 8);
8647 gtk_container_add(GTK_CONTAINER(window), vbox);
8649 table = gtk_table_new(4, 2, FALSE);
8650 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8651 gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8652 gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8654 label = gtk_label_new(_("MIME type"));
8655 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1),
8657 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8658 mimetype_entry = gtk_combo_box_entry_new_text();
8659 gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1),
8660 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8662 /* stuff with list */
8663 mime_type_list = procmime_get_mime_type_list();
8665 for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8666 MimeType *type = (MimeType *) mime_type_list->data;
8669 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8671 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8674 strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8675 (GCompareFunc)strcmp2);
8678 for (mime_type_list = strlist; mime_type_list != NULL;
8679 mime_type_list = mime_type_list->next) {
8680 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8681 g_free(mime_type_list->data);
8683 g_list_free(strlist);
8684 gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);
8685 mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));
8687 label = gtk_label_new(_("Encoding"));
8688 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8690 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8692 hbox = gtk_hbox_new(FALSE, 0);
8693 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8694 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8696 optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8697 optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8699 COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8700 COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8701 COMBOBOX_ADD(optmenu_menu, "quoted-printable", ENC_QUOTED_PRINTABLE);
8702 COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8703 gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8705 gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8707 SET_LABEL_AND_ENTRY(_("Path"), path_entry, 2);
8708 SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8710 gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8711 &ok_btn, GTK_STOCK_OK,
8713 gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8714 gtk_widget_grab_default(ok_btn);
8716 g_signal_connect(G_OBJECT(ok_btn), "clicked",
8717 G_CALLBACK(attach_property_ok),
8719 g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8720 G_CALLBACK(attach_property_cancel),
8723 gtk_widget_show_all(vbox);
8725 attach_prop.window = window;
8726 attach_prop.mimetype_entry = mimetype_entry;
8727 attach_prop.encoding_optmenu = optmenu;
8728 attach_prop.path_entry = path_entry;
8729 attach_prop.filename_entry = filename_entry;
8730 attach_prop.ok_btn = ok_btn;
8731 attach_prop.cancel_btn = cancel_btn;
8734 #undef SET_LABEL_AND_ENTRY
8736 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8742 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8748 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8749 gboolean *cancelled)
8757 static gboolean attach_property_key_pressed(GtkWidget *widget,
8759 gboolean *cancelled)
8761 if (event && event->keyval == GDK_Escape) {
8765 if (event && event->keyval == GDK_Return) {
8773 static void compose_exec_ext_editor(Compose *compose)
8780 tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8781 G_DIR_SEPARATOR, compose);
8783 if (pipe(pipe_fds) < 0) {
8789 if ((pid = fork()) < 0) {
8796 /* close the write side of the pipe */
8799 compose->exteditor_file = g_strdup(tmp);
8800 compose->exteditor_pid = pid;
8802 compose_set_ext_editor_sensitive(compose, FALSE);
8805 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8807 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
8809 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8813 } else { /* process-monitoring process */
8819 /* close the read side of the pipe */
8822 if (compose_write_body_to_file(compose, tmp) < 0) {
8823 fd_write_all(pipe_fds[1], "2\n", 2);
8827 pid_ed = compose_exec_ext_editor_real(tmp);
8829 fd_write_all(pipe_fds[1], "1\n", 2);
8833 /* wait until editor is terminated */
8834 waitpid(pid_ed, NULL, 0);
8836 fd_write_all(pipe_fds[1], "0\n", 2);
8843 #endif /* G_OS_UNIX */
8847 static gint compose_exec_ext_editor_real(const gchar *file)
8854 cm_return_val_if_fail(file != NULL, -1);
8856 if ((pid = fork()) < 0) {
8861 if (pid != 0) return pid;
8863 /* grandchild process */
8865 if (setpgid(0, getppid()))
8868 if (prefs_common_get_ext_editor_cmd() &&
8869 (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8870 *(p + 1) == 's' && !strchr(p + 2, '%')) {
8871 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8873 if (prefs_common_get_ext_editor_cmd())
8874 g_warning("External editor command-line is invalid: '%s'\n",
8875 prefs_common_get_ext_editor_cmd());
8876 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8879 cmdline = strsplit_with_quote(buf, " ", 1024);
8880 execvp(cmdline[0], cmdline);
8883 g_strfreev(cmdline);
8888 static gboolean compose_ext_editor_kill(Compose *compose)
8890 pid_t pgid = compose->exteditor_pid * -1;
8893 ret = kill(pgid, 0);
8895 if (ret == 0 || (ret == -1 && EPERM == errno)) {
8899 msg = g_strdup_printf
8900 (_("The external editor is still working.\n"
8901 "Force terminating the process?\n"
8902 "process group id: %d"), -pgid);
8903 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8904 NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8908 if (val == G_ALERTALTERNATE) {
8909 g_source_remove(compose->exteditor_tag);
8910 g_io_channel_shutdown(compose->exteditor_ch,
8912 g_io_channel_unref(compose->exteditor_ch);
8914 if (kill(pgid, SIGTERM) < 0) perror("kill");
8915 waitpid(compose->exteditor_pid, NULL, 0);
8917 g_warning("Terminated process group id: %d", -pgid);
8918 g_warning("Temporary file: %s",
8919 compose->exteditor_file);
8921 compose_set_ext_editor_sensitive(compose, TRUE);
8923 g_free(compose->exteditor_file);
8924 compose->exteditor_file = NULL;
8925 compose->exteditor_pid = -1;
8926 compose->exteditor_ch = NULL;
8927 compose->exteditor_tag = -1;
8935 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8939 Compose *compose = (Compose *)data;
8942 debug_print(_("Compose: input from monitoring process\n"));
8944 g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8946 g_io_channel_shutdown(source, FALSE, NULL);
8947 g_io_channel_unref(source);
8949 waitpid(compose->exteditor_pid, NULL, 0);
8951 if (buf[0] == '0') { /* success */
8952 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8953 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8955 gtk_text_buffer_set_text(buffer, "", -1);
8956 compose_insert_file(compose, compose->exteditor_file);
8957 compose_changed_cb(NULL, compose);
8958 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
8960 if (claws_unlink(compose->exteditor_file) < 0)
8961 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8962 } else if (buf[0] == '1') { /* failed */
8963 g_warning("Couldn't exec external editor\n");
8964 if (claws_unlink(compose->exteditor_file) < 0)
8965 FILE_OP_ERROR(compose->exteditor_file, "unlink");
8966 } else if (buf[0] == '2') {
8967 g_warning("Couldn't write to file\n");
8968 } else if (buf[0] == '3') {
8969 g_warning("Pipe read failed\n");
8972 compose_set_ext_editor_sensitive(compose, TRUE);
8974 g_free(compose->exteditor_file);
8975 compose->exteditor_file = NULL;
8976 compose->exteditor_pid = -1;
8977 compose->exteditor_ch = NULL;
8978 compose->exteditor_tag = -1;
8983 static void compose_set_ext_editor_sensitive(Compose *compose,
8986 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8987 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8988 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8989 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8990 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8991 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8992 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8994 gtk_widget_set_sensitive(compose->text, sensitive);
8995 if (compose->toolbar->send_btn)
8996 gtk_widget_set_sensitive(compose->toolbar->send_btn, sensitive);
8997 if (compose->toolbar->sendl_btn)
8998 gtk_widget_set_sensitive(compose->toolbar->sendl_btn, sensitive);
8999 if (compose->toolbar->draft_btn)
9000 gtk_widget_set_sensitive(compose->toolbar->draft_btn, sensitive);
9001 if (compose->toolbar->insert_btn)
9002 gtk_widget_set_sensitive(compose->toolbar->insert_btn, sensitive);
9003 if (compose->toolbar->sig_btn)
9004 gtk_widget_set_sensitive(compose->toolbar->sig_btn, sensitive);
9005 if (compose->toolbar->exteditor_btn)
9006 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9007 if (compose->toolbar->linewrap_current_btn)
9008 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9009 if (compose->toolbar->linewrap_all_btn)
9010 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9012 #endif /* G_OS_UNIX */
9015 * compose_undo_state_changed:
9017 * Change the sensivity of the menuentries undo and redo
9019 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9020 gint redo_state, gpointer data)
9022 Compose *compose = (Compose *)data;
9024 switch (undo_state) {
9025 case UNDO_STATE_TRUE:
9026 if (!undostruct->undo_state) {
9027 undostruct->undo_state = TRUE;
9028 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9031 case UNDO_STATE_FALSE:
9032 if (undostruct->undo_state) {
9033 undostruct->undo_state = FALSE;
9034 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9037 case UNDO_STATE_UNCHANGED:
9039 case UNDO_STATE_REFRESH:
9040 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9043 g_warning("Undo state not recognized");
9047 switch (redo_state) {
9048 case UNDO_STATE_TRUE:
9049 if (!undostruct->redo_state) {
9050 undostruct->redo_state = TRUE;
9051 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9054 case UNDO_STATE_FALSE:
9055 if (undostruct->redo_state) {
9056 undostruct->redo_state = FALSE;
9057 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9060 case UNDO_STATE_UNCHANGED:
9062 case UNDO_STATE_REFRESH:
9063 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9066 g_warning("Redo state not recognized");
9071 /* callback functions */
9073 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9074 * includes "non-client" (windows-izm) in calculation, so this calculation
9075 * may not be accurate.
9077 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9078 GtkAllocation *allocation,
9079 GtkSHRuler *shruler)
9081 if (prefs_common.show_ruler) {
9082 gint char_width = 0, char_height = 0;
9083 gint line_width_in_chars;
9085 gtkut_get_font_size(GTK_WIDGET(widget),
9086 &char_width, &char_height);
9087 line_width_in_chars =
9088 (allocation->width - allocation->x) / char_width;
9090 /* got the maximum */
9091 gtk_ruler_set_range(GTK_RULER(shruler),
9092 0.0, line_width_in_chars, 0,
9093 /*line_width_in_chars*/ char_width);
9102 ComposePrefType type;
9103 gboolean entry_marked;
9106 static void account_activated(GtkComboBox *optmenu, gpointer data)
9108 Compose *compose = (Compose *)data;
9111 gchar *folderidentifier;
9112 gint account_id = 0;
9115 GSList *list, *saved_list = NULL;
9116 HeaderEntryState *state;
9117 GtkRcStyle *style = NULL;
9118 static GdkColor yellow;
9119 static gboolean color_set = FALSE;
9121 /* Get ID of active account in the combo box */
9122 menu = gtk_combo_box_get_model(optmenu);
9123 gtk_combo_box_get_active_iter(optmenu, &iter);
9124 gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9126 ac = account_find_from_id(account_id);
9127 cm_return_if_fail(ac != NULL);
9129 if (ac != compose->account) {
9130 compose_select_account(compose, ac, FALSE);
9132 for (list = compose->header_list; list; list = list->next) {
9133 ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9135 if (hentry->type == PREF_ACCOUNT || !list->next) {
9136 compose_destroy_headerentry(compose, hentry);
9140 state = g_malloc0(sizeof(HeaderEntryState));
9141 state->header = gtk_editable_get_chars(GTK_EDITABLE(
9142 gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9143 state->entry = gtk_editable_get_chars(
9144 GTK_EDITABLE(hentry->entry), 0, -1);
9145 state->type = hentry->type;
9148 gdk_color_parse("#f5f6be", &yellow);
9149 color_set = gdk_colormap_alloc_color(
9150 gdk_colormap_get_system(),
9151 &yellow, FALSE, TRUE);
9154 style = gtk_widget_get_modifier_style(hentry->entry);
9155 state->entry_marked = gdk_color_equal(&yellow,
9156 &style->base[GTK_STATE_NORMAL]);
9158 saved_list = g_slist_append(saved_list, state);
9159 compose_destroy_headerentry(compose, hentry);
9162 compose->header_last = NULL;
9163 g_slist_free(compose->header_list);
9164 compose->header_list = NULL;
9165 compose->header_nextrow = 1;
9166 compose_create_header_entry(compose);
9168 if (ac->set_autocc && ac->auto_cc)
9169 compose_entry_append(compose, ac->auto_cc,
9170 COMPOSE_CC, PREF_ACCOUNT);
9172 if (ac->set_autobcc && ac->auto_bcc)
9173 compose_entry_append(compose, ac->auto_bcc,
9174 COMPOSE_BCC, PREF_ACCOUNT);
9176 if (ac->set_autoreplyto && ac->auto_replyto)
9177 compose_entry_append(compose, ac->auto_replyto,
9178 COMPOSE_REPLYTO, PREF_ACCOUNT);
9180 for (list = saved_list; list; list = list->next) {
9181 state = (HeaderEntryState *) list->data;
9183 compose_add_header_entry(compose, state->header,
9184 state->entry, state->type);
9185 if (state->entry_marked)
9186 compose_entry_mark_default_to(compose, state->entry);
9188 g_free(state->header);
9189 g_free(state->entry);
9192 g_slist_free(saved_list);
9194 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9195 (ac->protocol == A_NNTP) ?
9196 COMPOSE_NEWSGROUPS : COMPOSE_TO);
9199 /* Set message save folder */
9200 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9203 g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9204 G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9206 compose_set_save_to(compose, NULL);
9207 if (account_get_special_folder(compose->account, F_OUTBOX)) {
9208 folderidentifier = folder_item_get_identifier(account_get_special_folder
9209 (compose->account, F_OUTBOX));
9210 compose_set_save_to(compose, folderidentifier);
9211 g_free(folderidentifier);
9215 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9216 GtkTreeViewColumn *column, Compose *compose)
9218 compose_attach_property(NULL, compose);
9221 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9224 Compose *compose = (Compose *)data;
9225 GtkTreeSelection *attach_selection;
9226 gint attach_nr_selected;
9228 if (!event) return FALSE;
9230 if (event->button == 3) {
9231 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9232 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9234 if (attach_nr_selected > 0)
9236 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
9237 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
9239 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
9240 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
9243 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9244 NULL, NULL, event->button, event->time);
9251 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9254 Compose *compose = (Compose *)data;
9256 if (!event) return FALSE;
9258 switch (event->keyval) {
9260 compose_attach_remove_selected(NULL, compose);
9266 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9268 toolbar_comp_set_sensitive(compose, allow);
9269 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9270 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9272 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9274 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9275 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9276 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9278 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9282 static void compose_send_cb(GtkAction *action, gpointer data)
9284 Compose *compose = (Compose *)data;
9286 if (prefs_common.work_offline &&
9287 !inc_offline_should_override(TRUE,
9288 _("Claws Mail needs network access in order "
9289 "to send this email.")))
9292 if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
9293 g_source_remove(compose->draft_timeout_tag);
9294 compose->draft_timeout_tag = -1;
9297 compose_send(compose);
9300 static void compose_send_later_cb(GtkAction *action, gpointer data)
9302 Compose *compose = (Compose *)data;
9306 compose_allow_user_actions(compose, FALSE);
9307 val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
9308 compose_allow_user_actions(compose, TRUE);
9312 compose_close(compose);
9313 } else if (val == -1) {
9314 alertpanel_error(_("Could not queue message."));
9315 } else if (val == -2) {
9316 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
9317 } else if (val == -3) {
9318 if (privacy_peek_error())
9319 alertpanel_error(_("Could not queue message for sending:\n\n"
9320 "Signature failed: %s"), privacy_get_error());
9321 } else if (val == -4) {
9322 alertpanel_error(_("Could not queue message for sending:\n\n"
9323 "Charset conversion failed."));
9324 } else if (val == -5) {
9325 alertpanel_error(_("Could not queue message for sending:\n\n"
9326 "Couldn't get recipient encryption key."));
9327 } else if (val == -6) {
9330 toolbar_main_set_sensitive(mainwindow_get_mainwindow());
9333 #define DRAFTED_AT_EXIT "drafted_at_exit"
9334 static void compose_register_draft(MsgInfo *info)
9336 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9337 DRAFTED_AT_EXIT, NULL);
9338 FILE *fp = g_fopen(filepath, "ab");
9341 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder),
9349 gboolean compose_draft (gpointer data, guint action)
9351 Compose *compose = (Compose *)data;
9355 MsgFlags flag = {0, 0};
9356 static gboolean lock = FALSE;
9357 MsgInfo *newmsginfo;
9359 gboolean target_locked = FALSE;
9360 gboolean err = FALSE;
9362 if (lock) return FALSE;
9364 if (compose->sending)
9367 draft = account_get_special_folder(compose->account, F_DRAFT);
9368 cm_return_val_if_fail(draft != NULL, FALSE);
9370 if (!g_mutex_trylock(compose->mutex)) {
9371 /* we don't want to lock the mutex once it's available,
9372 * because as the only other part of compose.c locking
9373 * it is compose_close - which means once unlocked,
9374 * the compose struct will be freed */
9375 debug_print("couldn't lock mutex, probably sending\n");
9381 tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
9382 G_DIR_SEPARATOR, compose);
9383 if ((fp = g_fopen(tmp, "wb")) == NULL) {
9384 FILE_OP_ERROR(tmp, "fopen");
9388 /* chmod for security */
9389 if (change_file_mode_rw(fp, tmp) < 0) {
9390 FILE_OP_ERROR(tmp, "chmod");
9391 g_warning("can't change file mode\n");
9394 /* Save draft infos */
9395 err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
9396 err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
9398 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
9399 gchar *savefolderid;
9401 savefolderid = compose_get_save_to(compose);
9402 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
9403 g_free(savefolderid);
9405 if (compose->return_receipt) {
9406 err |= (fprintf(fp, "RRCPT:1\n") < 0);
9408 if (compose->privacy_system) {
9409 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
9410 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
9411 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
9414 /* Message-ID of message replying to */
9415 if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
9418 folderid = folder_item_get_identifier(compose->replyinfo->folder);
9419 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
9422 /* Message-ID of message forwarding to */
9423 if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
9426 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9427 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9431 err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9432 err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9434 /* end of headers */
9435 err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9442 if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9446 if (fclose(fp) == EOF) {
9450 if (compose->targetinfo) {
9451 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9452 flag.perm_flags = target_locked?MSG_LOCKED:0;
9454 flag.tmp_flags = MSG_DRAFT;
9456 folder_item_scan(draft);
9457 if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9458 MsgInfo *tmpinfo = NULL;
9459 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9460 if (compose->msgid) {
9461 tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9464 msgnum = tmpinfo->msgnum;
9465 procmsg_msginfo_free(tmpinfo);
9466 debug_print("got draft msgnum %d from scanning\n", msgnum);
9468 debug_print("didn't get draft msgnum after scanning\n");
9471 debug_print("got draft msgnum %d from adding\n", msgnum);
9477 if (action != COMPOSE_AUTO_SAVE) {
9478 if (action != COMPOSE_DRAFT_FOR_EXIT)
9479 alertpanel_error(_("Could not save draft."));
9482 gtkut_window_popup(compose->window);
9483 val = alertpanel_full(_("Could not save draft"),
9484 _("Could not save draft.\n"
9485 "Do you want to cancel exit or discard this email?"),
9486 _("_Cancel exit"), _("_Discard email"), NULL,
9487 FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9488 if (val == G_ALERTALTERNATE) {
9490 g_mutex_unlock(compose->mutex); /* must be done before closing */
9491 compose_close(compose);
9495 g_mutex_unlock(compose->mutex); /* must be done before closing */
9504 if (compose->mode == COMPOSE_REEDIT) {
9505 compose_remove_reedit_target(compose, TRUE);
9508 newmsginfo = folder_item_get_msginfo(draft, msgnum);
9511 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9513 procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9515 procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9516 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9517 procmsg_msginfo_set_flags(newmsginfo, 0,
9518 MSG_HAS_ATTACHMENT);
9520 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9521 compose_register_draft(newmsginfo);
9523 procmsg_msginfo_free(newmsginfo);
9526 folder_item_scan(draft);
9528 if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9530 g_mutex_unlock(compose->mutex); /* must be done before closing */
9531 compose_close(compose);
9537 path = folder_item_fetch_msg(draft, msgnum);
9539 debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9542 if (g_stat(path, &s) < 0) {
9543 FILE_OP_ERROR(path, "stat");
9549 procmsg_msginfo_free(compose->targetinfo);
9550 compose->targetinfo = procmsg_msginfo_new();
9551 compose->targetinfo->msgnum = msgnum;
9552 compose->targetinfo->size = (goffset)s.st_size;
9553 compose->targetinfo->mtime = s.st_mtime;
9554 compose->targetinfo->folder = draft;
9556 procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9557 compose->mode = COMPOSE_REEDIT;
9559 if (action == COMPOSE_AUTO_SAVE) {
9560 compose->autosaved_draft = compose->targetinfo;
9562 compose->modified = FALSE;
9563 compose_set_title(compose);
9567 g_mutex_unlock(compose->mutex);
9571 void compose_clear_exit_drafts(void)
9573 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9574 DRAFTED_AT_EXIT, NULL);
9575 if (is_file_exist(filepath))
9576 claws_unlink(filepath);
9581 void compose_reopen_exit_drafts(void)
9583 gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9584 DRAFTED_AT_EXIT, NULL);
9585 FILE *fp = g_fopen(filepath, "rb");
9589 while (fgets(buf, sizeof(buf), fp)) {
9590 gchar **parts = g_strsplit(buf, "\t", 2);
9591 const gchar *folder = parts[0];
9592 int msgnum = parts[1] ? atoi(parts[1]):-1;
9594 if (folder && *folder && msgnum > -1) {
9595 FolderItem *item = folder_find_item_from_identifier(folder);
9596 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9598 compose_reedit(info, FALSE);
9605 compose_clear_exit_drafts();
9608 static void compose_save_cb(GtkAction *action, gpointer data)
9610 Compose *compose = (Compose *)data;
9611 compose_draft(compose, COMPOSE_KEEP_EDITING);
9612 compose->rmode = COMPOSE_REEDIT;
9615 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9617 if (compose && file_list) {
9620 for ( tmp = file_list; tmp; tmp = tmp->next) {
9621 gchar *file = (gchar *) tmp->data;
9622 gchar *utf8_filename = conv_filename_to_utf8(file);
9623 compose_attach_append(compose, file, utf8_filename, NULL);
9624 compose_changed_cb(NULL, compose);
9629 g_free(utf8_filename);
9634 static void compose_attach_cb(GtkAction *action, gpointer data)
9636 Compose *compose = (Compose *)data;
9639 if (compose->redirect_filename != NULL)
9642 file_list = filesel_select_multiple_files_open(_("Select file"));
9645 compose_attach_from_list(compose, file_list, TRUE);
9646 g_list_free(file_list);
9650 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9652 Compose *compose = (Compose *)data;
9654 gint files_inserted = 0;
9656 file_list = filesel_select_multiple_files_open(_("Select file"));
9661 for ( tmp = file_list; tmp; tmp = tmp->next) {
9662 gchar *file = (gchar *) tmp->data;
9663 gchar *filedup = g_strdup(file);
9664 gchar *shortfile = g_path_get_basename(filedup);
9665 ComposeInsertResult res;
9666 /* insert the file if the file is short or if the user confirmed that
9667 he/she wants to insert the large file */
9668 res = compose_insert_file(compose, file);
9669 if (res == COMPOSE_INSERT_READ_ERROR) {
9670 alertpanel_error(_("File '%s' could not be read."), shortfile);
9671 } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9672 alertpanel_error(_("File '%s' contained invalid characters\n"
9673 "for the current encoding, insertion may be incorrect."),
9675 } else if (res == COMPOSE_INSERT_SUCCESS)
9682 g_list_free(file_list);
9686 if (files_inserted > 0 && compose->gtkaspell &&
9687 compose->gtkaspell->check_while_typing)
9688 gtkaspell_highlight_all(compose->gtkaspell);
9692 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9694 Compose *compose = (Compose *)data;
9696 compose_insert_sig(compose, FALSE);
9699 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9703 Compose *compose = (Compose *)data;
9705 gtkut_widget_get_uposition(widget, &x, &y);
9706 if (!compose->batch) {
9707 prefs_common.compose_x = x;
9708 prefs_common.compose_y = y;
9710 if (compose->sending || compose->updating)
9712 compose_close_cb(NULL, compose);
9716 void compose_close_toolbar(Compose *compose)
9718 compose_close_cb(NULL, compose);
9721 static void compose_close_cb(GtkAction *action, gpointer data)
9723 Compose *compose = (Compose *)data;
9727 if (compose->exteditor_tag != -1) {
9728 if (!compose_ext_editor_kill(compose))
9733 if (compose->modified) {
9734 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9735 if (!g_mutex_trylock(compose->mutex)) {
9736 /* we don't want to lock the mutex once it's available,
9737 * because as the only other part of compose.c locking
9738 * it is compose_close - which means once unlocked,
9739 * the compose struct will be freed */
9740 debug_print("couldn't lock mutex, probably sending\n");
9744 val = alertpanel(_("Discard message"),
9745 _("This message has been modified. Discard it?"),
9746 _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9748 val = alertpanel(_("Save changes"),
9749 _("This message has been modified. Save the latest changes?"),
9750 _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9752 g_mutex_unlock(compose->mutex);
9754 case G_ALERTDEFAULT:
9755 if (prefs_common.autosave && !reedit)
9756 compose_remove_draft(compose);
9758 case G_ALERTALTERNATE:
9759 compose_draft(data, COMPOSE_QUIT_EDITING);
9766 compose_close(compose);
9769 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9771 gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9772 gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9773 Compose *compose = (Compose *) data;
9776 compose->out_encoding = (CharSet)value;
9779 static void compose_address_cb(GtkAction *action, gpointer data)
9781 Compose *compose = (Compose *)data;
9783 addressbook_open(compose);
9786 static void about_show_cb(GtkAction *action, gpointer data)
9791 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9793 Compose *compose = (Compose *)data;
9798 tmpl = g_object_get_data(G_OBJECT(widget), "template");
9799 cm_return_if_fail(tmpl != NULL);
9801 msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9803 val = alertpanel(_("Apply template"), msg,
9804 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9807 if (val == G_ALERTDEFAULT)
9808 compose_template_apply(compose, tmpl, TRUE);
9809 else if (val == G_ALERTALTERNATE)
9810 compose_template_apply(compose, tmpl, FALSE);
9813 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9815 Compose *compose = (Compose *)data;
9817 compose_exec_ext_editor(compose);
9820 static void compose_undo_cb(GtkAction *action, gpointer data)
9822 Compose *compose = (Compose *)data;
9823 gboolean prev_autowrap = compose->autowrap;
9825 compose->autowrap = FALSE;
9826 undo_undo(compose->undostruct);
9827 compose->autowrap = prev_autowrap;
9830 static void compose_redo_cb(GtkAction *action, gpointer data)
9832 Compose *compose = (Compose *)data;
9833 gboolean prev_autowrap = compose->autowrap;
9835 compose->autowrap = FALSE;
9836 undo_redo(compose->undostruct);
9837 compose->autowrap = prev_autowrap;
9840 static void entry_cut_clipboard(GtkWidget *entry)
9842 if (GTK_IS_EDITABLE(entry))
9843 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9844 else if (GTK_IS_TEXT_VIEW(entry))
9845 gtk_text_buffer_cut_clipboard(
9846 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9847 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9851 static void entry_copy_clipboard(GtkWidget *entry)
9853 if (GTK_IS_EDITABLE(entry))
9854 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9855 else if (GTK_IS_TEXT_VIEW(entry))
9856 gtk_text_buffer_copy_clipboard(
9857 gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9858 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9861 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry,
9862 gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9864 if (GTK_IS_TEXT_VIEW(entry)) {
9865 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9866 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9867 GtkTextIter start_iter, end_iter;
9869 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9871 if (contents == NULL)
9874 /* we shouldn't delete the selection when middle-click-pasting, or we
9875 * can't mid-click-paste our own selection */
9876 if (clip != GDK_SELECTION_PRIMARY) {
9877 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9878 gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9881 if (insert_place == NULL) {
9882 /* if insert_place isn't specified, insert at the cursor.
9883 * used for Ctrl-V pasting */
9884 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9885 start = gtk_text_iter_get_offset(&start_iter);
9886 gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9888 /* if insert_place is specified, paste here.
9889 * used for mid-click-pasting */
9890 start = gtk_text_iter_get_offset(insert_place);
9891 gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9892 if (prefs_common.primary_paste_unselects)
9893 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
9897 /* paste unwrapped: mark the paste so it's not wrapped later */
9898 end = start + strlen(contents);
9899 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9900 gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9901 gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9902 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9903 /* rewrap paragraph now (after a mid-click-paste) */
9904 mark_start = gtk_text_buffer_get_insert(buffer);
9905 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9906 gtk_text_iter_backward_char(&start_iter);
9907 compose_beautify_paragraph(compose, &start_iter, TRUE);
9909 } else if (GTK_IS_EDITABLE(entry))
9910 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9912 compose->modified = TRUE;
9915 static void entry_allsel(GtkWidget *entry)
9917 if (GTK_IS_EDITABLE(entry))
9918 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9919 else if (GTK_IS_TEXT_VIEW(entry)) {
9920 GtkTextIter startiter, enditer;
9921 GtkTextBuffer *textbuf;
9923 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9924 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9925 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9927 gtk_text_buffer_move_mark_by_name(textbuf,
9928 "selection_bound", &startiter);
9929 gtk_text_buffer_move_mark_by_name(textbuf,
9930 "insert", &enditer);
9934 static void compose_cut_cb(GtkAction *action, gpointer data)
9936 Compose *compose = (Compose *)data;
9937 if (compose->focused_editable
9938 #ifndef GENERIC_UMPC
9939 && gtkut_widget_has_focus(compose->focused_editable)
9942 entry_cut_clipboard(compose->focused_editable);
9945 static void compose_copy_cb(GtkAction *action, gpointer data)
9947 Compose *compose = (Compose *)data;
9948 if (compose->focused_editable
9949 #ifndef GENERIC_UMPC
9950 && gtkut_widget_has_focus(compose->focused_editable)
9953 entry_copy_clipboard(compose->focused_editable);
9956 static void compose_paste_cb(GtkAction *action, gpointer data)
9958 Compose *compose = (Compose *)data;
9960 GtkTextBuffer *buffer;
9962 if (compose->focused_editable &&
9963 gtkut_widget_has_focus(compose->focused_editable))
9964 entry_paste_clipboard(compose, compose->focused_editable,
9965 prefs_common.linewrap_pastes,
9966 GDK_SELECTION_CLIPBOARD, NULL);
9970 if (gtkut_widget_has_focus(compose->text) &&
9971 compose->gtkaspell &&
9972 compose->gtkaspell->check_while_typing)
9973 gtkaspell_highlight_all(compose->gtkaspell);
9977 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9979 Compose *compose = (Compose *)data;
9980 gint wrap_quote = prefs_common.linewrap_quote;
9981 if (compose->focused_editable
9982 #ifndef GENERIC_UMPC
9983 && gtkut_widget_has_focus(compose->focused_editable)
9986 /* let text_insert() (called directly or at a later time
9987 * after the gtk_editable_paste_clipboard) know that
9988 * text is to be inserted as a quotation. implemented
9989 * by using a simple refcount... */
9990 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9991 G_OBJECT(compose->focused_editable),
9992 "paste_as_quotation"));
9993 g_object_set_data(G_OBJECT(compose->focused_editable),
9994 "paste_as_quotation",
9995 GINT_TO_POINTER(paste_as_quotation + 1));
9996 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9997 entry_paste_clipboard(compose, compose->focused_editable,
9998 prefs_common.linewrap_pastes,
9999 GDK_SELECTION_CLIPBOARD, NULL);
10000 prefs_common.linewrap_quote = wrap_quote;
10004 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10006 Compose *compose = (Compose *)data;
10007 gint prev_autowrap;
10008 GtkTextBuffer *buffer;
10010 if (compose->focused_editable
10011 #ifndef GENERIC_UMPC
10012 && gtkut_widget_has_focus(compose->focused_editable)
10015 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10016 GDK_SELECTION_CLIPBOARD, NULL);
10020 if (gtkut_widget_has_focus(compose->text) &&
10021 compose->gtkaspell &&
10022 compose->gtkaspell->check_while_typing)
10023 gtkaspell_highlight_all(compose->gtkaspell);
10027 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10029 Compose *compose = (Compose *)data;
10030 gint prev_autowrap;
10031 GtkTextBuffer *buffer;
10033 if (compose->focused_editable
10034 #ifndef GENERIC_UMPC
10035 && gtkut_widget_has_focus(compose->focused_editable)
10038 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10039 GDK_SELECTION_CLIPBOARD, NULL);
10043 if (gtkut_widget_has_focus(compose->text) &&
10044 compose->gtkaspell &&
10045 compose->gtkaspell->check_while_typing)
10046 gtkaspell_highlight_all(compose->gtkaspell);
10050 static void compose_allsel_cb(GtkAction *action, gpointer data)
10052 Compose *compose = (Compose *)data;
10053 if (compose->focused_editable
10054 #ifndef GENERIC_UMPC
10055 && gtkut_widget_has_focus(compose->focused_editable)
10058 entry_allsel(compose->focused_editable);
10061 static void textview_move_beginning_of_line (GtkTextView *text)
10063 GtkTextBuffer *buffer;
10067 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10069 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10070 mark = gtk_text_buffer_get_insert(buffer);
10071 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10072 gtk_text_iter_set_line_offset(&ins, 0);
10073 gtk_text_buffer_place_cursor(buffer, &ins);
10076 static void textview_move_forward_character (GtkTextView *text)
10078 GtkTextBuffer *buffer;
10082 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10084 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10085 mark = gtk_text_buffer_get_insert(buffer);
10086 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10087 if (gtk_text_iter_forward_cursor_position(&ins))
10088 gtk_text_buffer_place_cursor(buffer, &ins);
10091 static void textview_move_backward_character (GtkTextView *text)
10093 GtkTextBuffer *buffer;
10097 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10099 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10100 mark = gtk_text_buffer_get_insert(buffer);
10101 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10102 if (gtk_text_iter_backward_cursor_position(&ins))
10103 gtk_text_buffer_place_cursor(buffer, &ins);
10106 static void textview_move_forward_word (GtkTextView *text)
10108 GtkTextBuffer *buffer;
10113 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10115 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10116 mark = gtk_text_buffer_get_insert(buffer);
10117 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10118 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10119 if (gtk_text_iter_forward_word_ends(&ins, count)) {
10120 gtk_text_iter_backward_word_start(&ins);
10121 gtk_text_buffer_place_cursor(buffer, &ins);
10125 static void textview_move_backward_word (GtkTextView *text)
10127 GtkTextBuffer *buffer;
10132 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10134 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10135 mark = gtk_text_buffer_get_insert(buffer);
10136 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10137 count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10138 if (gtk_text_iter_backward_word_starts(&ins, 1))
10139 gtk_text_buffer_place_cursor(buffer, &ins);
10142 static void textview_move_end_of_line (GtkTextView *text)
10144 GtkTextBuffer *buffer;
10148 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10150 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10151 mark = gtk_text_buffer_get_insert(buffer);
10152 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10153 if (gtk_text_iter_forward_to_line_end(&ins))
10154 gtk_text_buffer_place_cursor(buffer, &ins);
10157 static void textview_move_next_line (GtkTextView *text)
10159 GtkTextBuffer *buffer;
10164 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10166 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10167 mark = gtk_text_buffer_get_insert(buffer);
10168 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10169 offset = gtk_text_iter_get_line_offset(&ins);
10170 if (gtk_text_iter_forward_line(&ins)) {
10171 gtk_text_iter_set_line_offset(&ins, offset);
10172 gtk_text_buffer_place_cursor(buffer, &ins);
10176 static void textview_move_previous_line (GtkTextView *text)
10178 GtkTextBuffer *buffer;
10183 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10185 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10186 mark = gtk_text_buffer_get_insert(buffer);
10187 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10188 offset = gtk_text_iter_get_line_offset(&ins);
10189 if (gtk_text_iter_backward_line(&ins)) {
10190 gtk_text_iter_set_line_offset(&ins, offset);
10191 gtk_text_buffer_place_cursor(buffer, &ins);
10195 static void textview_delete_forward_character (GtkTextView *text)
10197 GtkTextBuffer *buffer;
10199 GtkTextIter ins, end_iter;
10201 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10203 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10204 mark = gtk_text_buffer_get_insert(buffer);
10205 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10207 if (gtk_text_iter_forward_char(&end_iter)) {
10208 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10212 static void textview_delete_backward_character (GtkTextView *text)
10214 GtkTextBuffer *buffer;
10216 GtkTextIter ins, end_iter;
10218 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10220 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10221 mark = gtk_text_buffer_get_insert(buffer);
10222 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10224 if (gtk_text_iter_backward_char(&end_iter)) {
10225 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10229 static void textview_delete_forward_word (GtkTextView *text)
10231 GtkTextBuffer *buffer;
10233 GtkTextIter ins, end_iter;
10235 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10237 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10238 mark = gtk_text_buffer_get_insert(buffer);
10239 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10241 if (gtk_text_iter_forward_word_end(&end_iter)) {
10242 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10246 static void textview_delete_backward_word (GtkTextView *text)
10248 GtkTextBuffer *buffer;
10250 GtkTextIter ins, end_iter;
10252 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10254 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10255 mark = gtk_text_buffer_get_insert(buffer);
10256 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10258 if (gtk_text_iter_backward_word_start(&end_iter)) {
10259 gtk_text_buffer_delete(buffer, &end_iter, &ins);
10263 static void textview_delete_line (GtkTextView *text)
10265 GtkTextBuffer *buffer;
10267 GtkTextIter ins, start_iter, end_iter;
10269 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10271 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10272 mark = gtk_text_buffer_get_insert(buffer);
10273 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10276 gtk_text_iter_set_line_offset(&start_iter, 0);
10279 if (gtk_text_iter_ends_line(&end_iter)){
10280 if (!gtk_text_iter_forward_char(&end_iter))
10281 gtk_text_iter_backward_char(&start_iter);
10284 gtk_text_iter_forward_to_line_end(&end_iter);
10285 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
10288 static void textview_delete_to_line_end (GtkTextView *text)
10290 GtkTextBuffer *buffer;
10292 GtkTextIter ins, end_iter;
10294 cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10296 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10297 mark = gtk_text_buffer_get_insert(buffer);
10298 gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10300 if (gtk_text_iter_ends_line(&end_iter))
10301 gtk_text_iter_forward_char(&end_iter);
10303 gtk_text_iter_forward_to_line_end(&end_iter);
10304 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10307 #define DO_ACTION(name, act) { \
10308 if(!strcmp(name, a_name)) { \
10312 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
10314 const gchar *a_name = gtk_action_get_name(action);
10315 DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
10316 DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
10317 DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
10318 DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
10319 DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
10320 DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
10321 DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
10322 DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
10323 DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
10324 DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
10325 DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
10326 DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
10327 DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
10328 DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
10332 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
10334 Compose *compose = (Compose *)data;
10335 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10336 ComposeCallAdvancedAction action = -1;
10338 action = compose_call_advanced_action_from_path(gaction);
10341 void (*do_action) (GtkTextView *text);
10342 } action_table[] = {
10343 {textview_move_beginning_of_line},
10344 {textview_move_forward_character},
10345 {textview_move_backward_character},
10346 {textview_move_forward_word},
10347 {textview_move_backward_word},
10348 {textview_move_end_of_line},
10349 {textview_move_next_line},
10350 {textview_move_previous_line},
10351 {textview_delete_forward_character},
10352 {textview_delete_backward_character},
10353 {textview_delete_forward_word},
10354 {textview_delete_backward_word},
10355 {textview_delete_line},
10356 {textview_delete_to_line_end}
10359 if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return;
10361 if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
10362 action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
10363 if (action_table[action].do_action)
10364 action_table[action].do_action(text);
10366 g_warning("Not implemented yet.");
10370 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
10374 if (GTK_IS_EDITABLE(widget)) {
10375 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
10376 gtk_editable_set_position(GTK_EDITABLE(widget),
10379 if (widget->parent && widget->parent->parent
10380 && widget->parent->parent->parent) {
10381 if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
10382 gint y = widget->allocation.y;
10383 gint height = widget->allocation.height;
10384 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
10385 (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
10387 if (y < (int)shown->value) {
10388 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
10390 if (y + height > (int)shown->value + (int)shown->page_size) {
10391 if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
10392 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10393 y + height - (int)shown->page_size - 1);
10395 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown),
10396 (int)shown->upper - (int)shown->page_size - 1);
10403 if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
10404 compose->focused_editable = widget;
10406 #ifdef GENERIC_UMPC
10407 if (GTK_IS_TEXT_VIEW(widget)
10408 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
10409 g_object_ref(compose->notebook);
10410 g_object_ref(compose->edit_vbox);
10411 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10412 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10413 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
10414 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
10415 g_object_unref(compose->notebook);
10416 g_object_unref(compose->edit_vbox);
10417 g_signal_handlers_block_by_func(G_OBJECT(widget),
10418 G_CALLBACK(compose_grab_focus_cb),
10420 gtk_widget_grab_focus(widget);
10421 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10422 G_CALLBACK(compose_grab_focus_cb),
10424 } else if (!GTK_IS_TEXT_VIEW(widget)
10425 && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
10426 g_object_ref(compose->notebook);
10427 g_object_ref(compose->edit_vbox);
10428 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
10429 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
10430 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
10431 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
10432 g_object_unref(compose->notebook);
10433 g_object_unref(compose->edit_vbox);
10434 g_signal_handlers_block_by_func(G_OBJECT(widget),
10435 G_CALLBACK(compose_grab_focus_cb),
10437 gtk_widget_grab_focus(widget);
10438 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
10439 G_CALLBACK(compose_grab_focus_cb),
10445 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
10447 compose->modified = TRUE;
10448 // compose_beautify_paragraph(compose, NULL, TRUE);
10449 #ifndef GENERIC_UMPC
10450 compose_set_title(compose);
10454 static void compose_wrap_cb(GtkAction *action, gpointer data)
10456 Compose *compose = (Compose *)data;
10457 compose_beautify_paragraph(compose, NULL, TRUE);
10460 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
10462 Compose *compose = (Compose *)data;
10463 compose_wrap_all_full(compose, TRUE);
10466 static void compose_find_cb(GtkAction *action, gpointer data)
10468 Compose *compose = (Compose *)data;
10470 message_search_compose(compose);
10473 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10476 Compose *compose = (Compose *)data;
10477 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10478 if (compose->autowrap)
10479 compose_wrap_all_full(compose, TRUE);
10480 compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10483 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10486 Compose *compose = (Compose *)data;
10487 compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10490 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10492 Compose *compose = (Compose *)data;
10494 compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10497 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10499 Compose *compose = (Compose *)data;
10501 compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10504 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn)
10506 g_free(compose->privacy_system);
10508 compose->privacy_system = g_strdup(account->default_privacy_system);
10509 compose_update_privacy_system_menu_item(compose, warn);
10512 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10514 Compose *compose = (Compose *)data;
10516 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10517 gtk_widget_show(compose->ruler_hbox);
10518 prefs_common.show_ruler = TRUE;
10520 gtk_widget_hide(compose->ruler_hbox);
10521 gtk_widget_queue_resize(compose->edit_vbox);
10522 prefs_common.show_ruler = FALSE;
10526 static void compose_attach_drag_received_cb (GtkWidget *widget,
10527 GdkDragContext *context,
10530 GtkSelectionData *data,
10533 gpointer user_data)
10535 Compose *compose = (Compose *)user_data;
10538 if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list"))
10540 || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND"))
10542 ) && gtk_drag_get_source_widget(context) !=
10543 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10544 list = uri_list_extract_filenames((const gchar *)data->data);
10545 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10546 gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10547 compose_attach_append
10548 (compose, (const gchar *)tmp->data,
10549 utf8_filename, NULL);
10550 g_free(utf8_filename);
10552 if (list) compose_changed_cb(NULL, compose);
10553 list_free_strings(list);
10555 } else if (gtk_drag_get_source_widget(context)
10556 == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10557 /* comes from our summaryview */
10558 SummaryView * summaryview = NULL;
10559 GSList * list = NULL, *cur = NULL;
10561 if (mainwindow_get_mainwindow())
10562 summaryview = mainwindow_get_mainwindow()->summaryview;
10565 list = summary_get_selected_msg_list(summaryview);
10567 for (cur = list; cur; cur = cur->next) {
10568 MsgInfo *msginfo = (MsgInfo *)cur->data;
10569 gchar *file = NULL;
10571 file = procmsg_get_message_file_full(msginfo,
10574 compose_attach_append(compose, (const gchar *)file,
10575 (const gchar *)file, "message/rfc822");
10579 g_slist_free(list);
10583 static gboolean compose_drag_drop(GtkWidget *widget,
10584 GdkDragContext *drag_context,
10586 guint time, gpointer user_data)
10588 /* not handling this signal makes compose_insert_drag_received_cb
10593 static void compose_insert_drag_received_cb (GtkWidget *widget,
10594 GdkDragContext *drag_context,
10597 GtkSelectionData *data,
10600 gpointer user_data)
10602 Compose *compose = (Compose *)user_data;
10605 /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10608 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10610 if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) {
10612 AlertValue val = G_ALERTDEFAULT;
10614 list = uri_list_extract_filenames((const gchar *)data->data);
10615 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10616 /* Assume a list of no files, and data has ://, is a remote link */
10617 gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10618 gchar *tmpfile = get_tmp_file();
10619 str_write_to_file(tmpdata, tmpfile);
10621 compose_insert_file(compose, tmpfile);
10622 claws_unlink(tmpfile);
10624 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10625 compose_beautify_paragraph(compose, NULL, TRUE);
10628 switch (prefs_common.compose_dnd_mode) {
10629 case COMPOSE_DND_ASK:
10630 val = alertpanel_full(_("Insert or attach?"),
10631 _("Do you want to insert the contents of the file(s) "
10632 "into the message body, or attach it to the email?"),
10633 GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10634 TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10636 case COMPOSE_DND_INSERT:
10637 val = G_ALERTALTERNATE;
10639 case COMPOSE_DND_ATTACH:
10640 val = G_ALERTOTHER;
10643 /* unexpected case */
10644 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10647 if (val & G_ALERTDISABLE) {
10648 val &= ~G_ALERTDISABLE;
10649 /* remember what action to perform by default, only if we don't click Cancel */
10650 if (val == G_ALERTALTERNATE)
10651 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10652 else if (val == G_ALERTOTHER)
10653 prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10656 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10657 gtk_drag_finish(drag_context, FALSE, FALSE, time);
10658 list_free_strings(list);
10661 } else if (val == G_ALERTOTHER) {
10662 compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10663 list_free_strings(list);
10668 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10669 compose_insert_file(compose, (const gchar *)tmp->data);
10671 list_free_strings(list);
10673 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10678 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10681 static void compose_header_drag_received_cb (GtkWidget *widget,
10682 GdkDragContext *drag_context,
10685 GtkSelectionData *data,
10688 gpointer user_data)
10690 GtkEditable *entry = (GtkEditable *)user_data;
10691 gchar *email = (gchar *)data->data;
10693 /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10696 if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10697 gchar *decoded=g_new(gchar, strlen(email));
10700 email += strlen("mailto:");
10701 decode_uri(decoded, email); /* will fit */
10702 gtk_editable_delete_text(entry, 0, -1);
10703 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10704 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10708 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10711 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10713 Compose *compose = (Compose *)data;
10715 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10716 compose->return_receipt = TRUE;
10718 compose->return_receipt = FALSE;
10721 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10723 Compose *compose = (Compose *)data;
10725 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10726 compose->remove_references = TRUE;
10728 compose->remove_references = FALSE;
10731 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
10732 ComposeHeaderEntry *headerentry)
10734 gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
10738 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10739 GdkEventKey *event,
10740 ComposeHeaderEntry *headerentry)
10742 if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10743 ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10744 !(event->state & GDK_MODIFIER_MASK) &&
10745 (event->keyval == GDK_BackSpace) &&
10746 (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10747 gtk_container_remove
10748 (GTK_CONTAINER(headerentry->compose->header_table),
10749 headerentry->combo);
10750 gtk_container_remove
10751 (GTK_CONTAINER(headerentry->compose->header_table),
10752 headerentry->entry);
10753 headerentry->compose->header_list =
10754 g_slist_remove(headerentry->compose->header_list,
10756 g_free(headerentry);
10757 } else if (event->keyval == GDK_Tab) {
10758 if (headerentry->compose->header_last == headerentry) {
10759 /* Override default next focus, and give it to subject_entry
10760 * instead of notebook tabs
10762 g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event");
10763 gtk_widget_grab_focus(headerentry->compose->subject_entry);
10770 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10771 ComposeHeaderEntry *headerentry)
10773 if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10774 compose_create_header_entry(headerentry->compose);
10775 g_signal_handlers_disconnect_matched
10776 (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10777 0, 0, NULL, NULL, headerentry);
10779 /* Automatically scroll down */
10780 GTK_EVENTS_FLUSH();
10781 compose_show_first_last_header(headerentry->compose, FALSE);
10787 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10789 GtkAdjustment *vadj;
10791 cm_return_if_fail(compose);
10792 cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10793 cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10794 vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10795 gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size)));
10796 gtk_adjustment_changed(vadj);
10799 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10800 const gchar *text, gint len, Compose *compose)
10802 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10803 (G_OBJECT(compose->text), "paste_as_quotation"));
10806 cm_return_if_fail(text != NULL);
10808 g_signal_handlers_block_by_func(G_OBJECT(buffer),
10809 G_CALLBACK(text_inserted),
10811 if (paste_as_quotation) {
10813 const gchar *qmark;
10815 GtkTextIter start_iter;
10818 len = strlen(text);
10820 new_text = g_strndup(text, len);
10822 qmark = compose_quote_char_from_context(compose);
10824 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10825 gtk_text_buffer_place_cursor(buffer, iter);
10827 pos = gtk_text_iter_get_offset(iter);
10829 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10830 _("Quote format error at line %d."));
10831 quote_fmt_reset_vartable();
10833 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10834 GINT_TO_POINTER(paste_as_quotation - 1));
10836 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10837 gtk_text_buffer_place_cursor(buffer, iter);
10838 gtk_text_buffer_delete_mark(buffer, mark);
10840 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10841 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10842 compose_beautify_paragraph(compose, &start_iter, FALSE);
10843 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10844 gtk_text_buffer_delete_mark(buffer, mark);
10846 if (strcmp(text, "\n") || compose->automatic_break
10847 || gtk_text_iter_starts_line(iter)) {
10848 GtkTextIter before_ins;
10849 gtk_text_buffer_insert(buffer, iter, text, len);
10850 if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10851 before_ins = *iter;
10852 gtk_text_iter_backward_chars(&before_ins, len);
10853 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10856 /* check if the preceding is just whitespace or quote */
10857 GtkTextIter start_line;
10858 gchar *tmp = NULL, *quote = NULL;
10859 gint quote_len = 0, is_normal = 0;
10860 start_line = *iter;
10861 gtk_text_iter_set_line_offset(&start_line, 0);
10862 tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10865 if (*tmp == '\0') {
10868 quote = compose_get_quote_str(buffer, &start_line, "e_len);
10876 gtk_text_buffer_insert(buffer, iter, text, len);
10878 gtk_text_buffer_insert_with_tags_by_name(buffer,
10879 iter, text, len, "no_join", NULL);
10884 if (!paste_as_quotation) {
10885 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10886 compose_beautify_paragraph(compose, iter, FALSE);
10887 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10888 gtk_text_buffer_delete_mark(buffer, mark);
10891 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10892 G_CALLBACK(text_inserted),
10894 g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10896 if (prefs_common.autosave &&
10897 gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10898 compose->draft_timeout_tag != -2 /* disabled while loading */)
10899 compose->draft_timeout_tag = g_timeout_add
10900 (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10902 static gint compose_defer_auto_save_draft(Compose *compose)
10904 compose->draft_timeout_tag = -1;
10905 compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10910 static void compose_check_all(GtkAction *action, gpointer data)
10912 Compose *compose = (Compose *)data;
10913 if (!compose->gtkaspell)
10916 if (gtkut_widget_has_focus(compose->subject_entry))
10917 claws_spell_entry_check_all(
10918 CLAWS_SPELL_ENTRY(compose->subject_entry));
10920 gtkaspell_check_all(compose->gtkaspell);
10923 static void compose_highlight_all(GtkAction *action, gpointer data)
10925 Compose *compose = (Compose *)data;
10926 if (compose->gtkaspell) {
10927 claws_spell_entry_recheck_all(
10928 CLAWS_SPELL_ENTRY(compose->subject_entry));
10929 gtkaspell_highlight_all(compose->gtkaspell);
10933 static void compose_check_backwards(GtkAction *action, gpointer data)
10935 Compose *compose = (Compose *)data;
10936 if (!compose->gtkaspell) {
10937 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10941 if (gtkut_widget_has_focus(compose->subject_entry))
10942 claws_spell_entry_check_backwards(
10943 CLAWS_SPELL_ENTRY(compose->subject_entry));
10945 gtkaspell_check_backwards(compose->gtkaspell);
10948 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10950 Compose *compose = (Compose *)data;
10951 if (!compose->gtkaspell) {
10952 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10956 if (gtkut_widget_has_focus(compose->subject_entry))
10957 claws_spell_entry_check_forwards_go(
10958 CLAWS_SPELL_ENTRY(compose->subject_entry));
10960 gtkaspell_check_forwards_go(compose->gtkaspell);
10965 *\brief Guess originating forward account from MsgInfo and several
10966 * "common preference" settings. Return NULL if no guess.
10968 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10970 PrefsAccount *account = NULL;
10972 cm_return_val_if_fail(msginfo, NULL);
10973 cm_return_val_if_fail(msginfo->folder, NULL);
10974 cm_return_val_if_fail(msginfo->folder->prefs, NULL);
10976 if (msginfo->folder->prefs->enable_default_account)
10977 account = account_find_from_id(msginfo->folder->prefs->default_account);
10980 account = msginfo->folder->folder->account;
10982 if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10984 Xstrdup_a(to, msginfo->to, return NULL);
10985 extract_address(to);
10986 account = account_find_from_address(to, FALSE);
10989 if (!account && prefs_common.forward_account_autosel) {
10990 gchar cc[BUFFSIZE];
10991 if (!procheader_get_header_from_msginfo
10992 (msginfo, cc,sizeof cc , "Cc:")) {
10993 gchar *buf = cc + strlen("Cc:");
10994 extract_address(buf);
10995 account = account_find_from_address(buf, FALSE);
10999 if (!account && prefs_common.forward_account_autosel) {
11000 gchar deliveredto[BUFFSIZE];
11001 if (!procheader_get_header_from_msginfo
11002 (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) {
11003 gchar *buf = deliveredto + strlen("Delivered-To:");
11004 extract_address(buf);
11005 account = account_find_from_address(buf, FALSE);
11012 gboolean compose_close(Compose *compose)
11016 if (!g_mutex_trylock(compose->mutex)) {
11017 /* we have to wait for the (possibly deferred by auto-save)
11018 * drafting to be done, before destroying the compose under
11020 debug_print("waiting for drafting to finish...\n");
11021 compose_allow_user_actions(compose, FALSE);
11022 g_timeout_add (500, (GSourceFunc) compose_close, compose);
11025 cm_return_val_if_fail(compose, FALSE);
11026 gtkut_widget_get_uposition(compose->window, &x, &y);
11027 if (!compose->batch) {
11028 prefs_common.compose_x = x;
11029 prefs_common.compose_y = y;
11031 g_mutex_unlock(compose->mutex);
11032 compose_destroy(compose);
11037 * Add entry field for each address in list.
11038 * \param compose E-Mail composition object.
11039 * \param listAddress List of (formatted) E-Mail addresses.
11041 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11044 node = listAddress;
11046 addr = ( gchar * ) node->data;
11047 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11048 node = g_list_next( node );
11052 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list,
11053 guint action, gboolean opening_multiple)
11055 gchar *body = NULL;
11056 GSList *new_msglist = NULL;
11057 MsgInfo *tmp_msginfo = NULL;
11058 gboolean originally_enc = FALSE;
11059 gboolean originally_sig = FALSE;
11060 Compose *compose = NULL;
11061 gchar *s_system = NULL;
11063 cm_return_if_fail(msgview != NULL);
11065 cm_return_if_fail(msginfo_list != NULL);
11067 if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11068 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11069 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11071 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE &&
11072 !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11073 tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11074 orig_msginfo, mimeinfo);
11075 if (tmp_msginfo != NULL) {
11076 new_msglist = g_slist_append(NULL, tmp_msginfo);
11078 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11079 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11080 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11082 tmp_msginfo->folder = orig_msginfo->folder;
11083 tmp_msginfo->msgnum = orig_msginfo->msgnum;
11084 if (orig_msginfo->tags) {
11085 tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11086 tmp_msginfo->folder->tags_dirty = TRUE;
11092 if (!opening_multiple)
11093 body = messageview_get_selection(msgview);
11096 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11097 procmsg_msginfo_free(tmp_msginfo);
11098 g_slist_free(new_msglist);
11100 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11102 if (compose && originally_enc) {
11103 compose_force_encryption(compose, compose->account, FALSE, s_system);
11106 if (compose && originally_sig && compose->account->default_sign_reply) {
11107 compose_force_signing(compose, compose->account, s_system);
11111 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11114 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
11117 if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD)
11118 && action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11119 GSList *cur = msginfo_list;
11120 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11121 "messages. Opening the windows "
11122 "could take some time. Do you "
11123 "want to continue?"),
11124 g_slist_length(msginfo_list));
11125 if (g_slist_length(msginfo_list) > 9
11126 && alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11127 != G_ALERTALTERNATE) {
11132 /* We'll open multiple compose windows */
11133 /* let the WM place the next windows */
11134 compose_force_window_origin = FALSE;
11135 for (; cur; cur = cur->next) {
11137 tmplist.data = cur->data;
11138 tmplist.next = NULL;
11139 compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11141 compose_force_window_origin = TRUE;
11143 /* forwarding multiple mails as attachments is done via a
11144 * single compose window */
11145 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11149 void compose_check_for_email_account(Compose *compose)
11151 PrefsAccount *ac = NULL, *curr = NULL;
11157 if (compose->account && compose->account->protocol == A_NNTP) {
11158 ac = account_get_cur_account();
11159 if (ac->protocol == A_NNTP) {
11160 list = account_get_list();
11162 for( ; list != NULL ; list = g_list_next(list)) {
11163 curr = (PrefsAccount *) list->data;
11164 if (curr->protocol != A_NNTP) {
11170 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
11175 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo,
11176 const gchar *address)
11178 GSList *msginfo_list = NULL;
11179 gchar *body = messageview_get_selection(msgview);
11182 msginfo_list = g_slist_prepend(msginfo_list, msginfo);
11184 compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
11185 compose_check_for_email_account(compose);
11186 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
11187 compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
11188 compose_reply_set_subject(compose, msginfo);
11191 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11194 void compose_set_position(Compose *compose, gint pos)
11196 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11198 gtkut_text_view_set_position(text, pos);
11201 gboolean compose_search_string(Compose *compose,
11202 const gchar *str, gboolean case_sens)
11204 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11206 return gtkut_text_view_search_string(text, str, case_sens);
11209 gboolean compose_search_string_backward(Compose *compose,
11210 const gchar *str, gboolean case_sens)
11212 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11214 return gtkut_text_view_search_string_backward(text, str, case_sens);
11217 /* allocate a msginfo structure and populate its data from a compose data structure */
11218 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
11220 MsgInfo *newmsginfo;
11222 gchar buf[BUFFSIZE];
11224 cm_return_val_if_fail( compose != NULL, NULL );
11226 newmsginfo = procmsg_msginfo_new();
11229 get_rfc822_date(buf, sizeof(buf));
11230 newmsginfo->date = g_strdup(buf);
11233 if (compose->from_name) {
11234 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
11235 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
11239 if (compose->subject_entry)
11240 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
11242 /* to, cc, reply-to, newsgroups */
11243 for (list = compose->header_list; list; list = list->next) {
11244 gchar *header = gtk_editable_get_chars(
11246 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
11247 gchar *entry = gtk_editable_get_chars(
11248 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
11250 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
11251 if ( newmsginfo->to == NULL ) {
11252 newmsginfo->to = g_strdup(entry);
11253 } else if (entry && *entry) {
11254 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
11255 g_free(newmsginfo->to);
11256 newmsginfo->to = tmp;
11259 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
11260 if ( newmsginfo->cc == NULL ) {
11261 newmsginfo->cc = g_strdup(entry);
11262 } else if (entry && *entry) {
11263 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
11264 g_free(newmsginfo->cc);
11265 newmsginfo->cc = tmp;
11268 if ( strcasecmp(header,
11269 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
11270 if ( newmsginfo->newsgroups == NULL ) {
11271 newmsginfo->newsgroups = g_strdup(entry);
11272 } else if (entry && *entry) {
11273 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
11274 g_free(newmsginfo->newsgroups);
11275 newmsginfo->newsgroups = tmp;
11283 /* other data is unset */
11289 /* update compose's dictionaries from folder dict settings */
11290 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
11291 FolderItem *folder_item)
11293 cm_return_if_fail(compose != NULL);
11295 if (compose->gtkaspell && folder_item && folder_item->prefs) {
11296 FolderItemPrefs *prefs = folder_item->prefs;
11298 if (prefs->enable_default_dictionary)
11299 gtkaspell_change_dict(compose->gtkaspell,
11300 prefs->default_dictionary, FALSE);
11301 if (folder_item->prefs->enable_default_alt_dictionary)
11302 gtkaspell_change_alt_dict(compose->gtkaspell,
11303 prefs->default_alt_dictionary);
11304 if (prefs->enable_default_dictionary
11305 || prefs->enable_default_alt_dictionary)
11306 compose_spell_menu_changed(compose);